Skip to content

Commit

Permalink
Switch between compatible modes without swapchain recreation
Browse files Browse the repository at this point in the history
  • Loading branch information
ShabbyX committed Mar 7, 2023
1 parent 1da27cb commit cca2486
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 5 deletions.
112 changes: 107 additions & 5 deletions samples/api/swapchain_recreation/swapchain_recreation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ void SwapchainRecreation::get_queue()

if (!supports_present)
{
throw std::runtime_error("Default graphics queue does not support preesnt.");
throw std::runtime_error("Default graphics queue does not support present.");
}
}

Expand Down Expand Up @@ -142,6 +142,41 @@ void SwapchainRecreation::query_present_modes()
adjust_desired_present_mode();
}

/**
* @brief Get the list of present modes compatible with the current mode. If present mode is
* changed and the two modes are compatible, swapchain is not recreated.
*/
void SwapchainRecreation::query_compatible_present_modes(VkPresentModeKHR present_mode)
{
// If manually overriden, or if VK_EXT_surface_maintenance1 is not supported, assume no
// compatible present modes.
if (!has_maintenance1 || recreate_swapchain_on_present_mode_change)
{
compatible_modes.resize(1);
compatible_modes[0] = present_mode;
return;
}

VkPhysicalDeviceSurfaceInfo2KHR surface_info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR};
surface_info.surface = get_surface();

VkSurfacePresentModeEXT surface_present_mode{VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT};
surface_present_mode.presentMode = present_mode;
surface_info.pNext = &surface_present_mode;

VkSurfaceCapabilities2KHR surface_caps{VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR};

VkSurfacePresentModeCompatibilityEXT modes{VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT};
modes.presentModeCount = 0;
surface_caps.pNext = &modes;

VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilities2KHR(get_gpu_handle(), &surface_info, &surface_caps));

compatible_modes.resize(modes.presentModeCount);
modes.pPresentModes = compatible_modes.data();
VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilities2KHR(get_gpu_handle(), &surface_info, &surface_caps));
}

void SwapchainRecreation::adjust_desired_present_mode()
{
// The FIFO present mode is guaranteed to be present.
Expand Down Expand Up @@ -193,6 +228,26 @@ void SwapchainRecreation::create_render_pass()
VK_CHECK(vkCreateRenderPass(get_device_handle(), &rp_info, nullptr, &render_pass));
}

bool SwapchainRecreation::are_present_modes_compatible()
{
// No change in present mode, no need to recreate the swapchain.
if (current_present_mode == desired_present_mode)
{
return true;
}

// Compatible present modes are introduced by VK_EXT_surface_maintenance1.
if (!has_maintenance1)
{
return false;
}

// Look in the list of compatible present modes (which was created for
// current_present_mode). If desired_present_mode is in that list, then there's no need to
// recreate the swapchain.
return std::find(compatible_modes.begin(), compatible_modes.end(), desired_present_mode) != compatible_modes.end();
}

/**
* @brief Initializes the Vulkan swapchain.
*/
Expand All @@ -210,7 +265,7 @@ void SwapchainRecreation::init_swapchain()
swapchain_extents = surface_properties.currentExtent;
}

// Do tripple-buffering when possible. This is clamped to the min and max image count limits.
// Do triple-buffering when possible. This is clamped to the min and max image count limits.
uint32_t desired_swapchain_images = std::max(surface_properties.minImageCount, 3u);
if (surface_properties.maxImageCount > 0)
{
Expand Down Expand Up @@ -253,13 +308,26 @@ void SwapchainRecreation::init_swapchain()
info.clipped = true;
info.oldSwapchain = old_swapchain;

VkSwapchainPresentModesCreateInfoEXT compatible_modes_info{VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODES_CREATE_INFO_EXT};
if (has_maintenance1)
{
// When VK_EXT_swapchain_maintenance1 is available, you can optionally amortize the
// cost of swapchain image allocations over multiple frames.
info.flags |= VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT;

// If there are multiple present modes that are compatible, give that list to create
// info. When switching present modes between compatible ones, swapchain doesn't
// need to be recreated.
query_compatible_present_modes(desired_present_mode);
if (compatible_modes.size() > 1)
{
compatible_modes_info.presentModeCount = static_cast<uint32_t>(compatible_modes.size());
compatible_modes_info.pPresentModes = compatible_modes.data();
info.pNext = &compatible_modes_info;
}
}

LOGI("Creating new swapchain");
VK_CHECK(vkCreateSwapchainKHR(get_device_handle(), &info, nullptr, &swapchain));

current_present_mode = desired_present_mode;
Expand Down Expand Up @@ -354,7 +422,7 @@ bool SwapchainRecreation::recreate_swapchain()
// Only rebuild the swapchain if the dimensions have changed
if (surface_properties.currentExtent.width == swapchain_extents.width &&
surface_properties.currentExtent.height == swapchain_extents.height &&
desired_present_mode == current_present_mode)
are_present_modes_compatible())
{
return false;
}
Expand Down Expand Up @@ -572,6 +640,7 @@ VkResult SwapchainRecreation::present_image(uint32_t index)
// which is signaled when the resources associated with present operation can be freed.
VkFence present_fence = VK_NULL_HANDLE;
VkSwapchainPresentFenceInfoEXT fence_info{VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT};
VkSwapchainPresentModeInfoEXT present_mode{VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT};
if (has_maintenance1)
{
present_fence = get_fence();
Expand All @@ -580,6 +649,21 @@ VkResult SwapchainRecreation::present_image(uint32_t index)
fence_info.pFences = &present_fence;

present.pNext = &fence_info;

// If present mode has changed but the two modes are compatible, change the present
// mode at present time.
if (current_present_mode != desired_present_mode)
{
// Can't reach here if the modes are not compatible.
assert(are_present_modes_compatible());

current_present_mode = desired_present_mode;

present_mode.swapchainCount = 1;
present_mode.pPresentModes = &current_present_mode;

fence_info.pNext = &present_mode;
}
}

VkResult result = vkQueuePresentKHR(queue->get_handle(), &present);
Expand Down Expand Up @@ -863,8 +947,10 @@ SwapchainRecreation::SwapchainRecreation()
{
LOGI("------------------------------------");
LOGI("USAGE:");
LOGI(" - Press v to enable v-sync");
LOGI(" - Press v to enable v-sync (default)");
LOGI(" - Press n to disable v-sync");
LOGI(" - Press p to enable switching between compatible present modes (default)");
LOGI(" - Press r to disable switching between compatible present modes");
LOGI("Set environment variable USE_MAINTENANCE1=no to avoid VK_EXT_surface_maintenance1");
LOGI("------------------------------------");
}
Expand Down Expand Up @@ -961,7 +1047,7 @@ void SwapchainRecreation::update(float delta_time)

setup_frame();

if (desired_present_mode != current_present_mode)
if (!are_present_modes_compatible())
{
recreate_swapchain();
}
Expand Down Expand Up @@ -1035,6 +1121,22 @@ void SwapchainRecreation::input_event(const vkb::InputEvent &input_event)
desired_present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
}
break;
case vkb::KeyCode::P:
if (recreate_swapchain_on_present_mode_change)
{
LOGI("Switch between compatible present modes: Enabled");
recreate_swapchain_on_present_mode_change = false;
compatible_modes.clear();
}
break;
case vkb::KeyCode::R:
if (!recreate_swapchain_on_present_mode_change)
{
LOGI("Switch between compatible present modes: Disabled");
recreate_swapchain_on_present_mode_change = true;
compatible_modes.clear();
}
break;
default:
break;
}
Expand Down
7 changes: 7 additions & 0 deletions samples/api/swapchain_recreation/swapchain_recreation.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class SwapchainRecreation : public vkb::VulkanSample
/// Surface data.
VkSurfaceFormatKHR surface_format = {};
std::vector<VkPresentModeKHR> present_modes = {};
std::vector<VkPresentModeKHR> compatible_modes = {};
VkExtent2D swapchain_extents = {};

/// The swapchain.
Expand Down Expand Up @@ -162,13 +163,19 @@ class SwapchainRecreation : public vkb::VulkanSample
float fps_timer = 0;
uint32_t fps_last_logged_frame_number = 0;

// User toggles.
bool recreate_swapchain_on_present_mode_change = false;

void get_queue();
void check_for_maintenance1();
void query_surface_format();
void query_present_modes();
void query_compatible_present_modes(VkPresentModeKHR present_mode);
void adjust_desired_present_mode();
void create_render_pass();

bool are_present_modes_compatible();

void init_swapchain();
void init_swapchain_image(uint32_t index);
void cleanup_swapchain_objects(SwapchainObjects &garbage);
Expand Down

0 comments on commit cca2486

Please sign in to comment.