TEMP: Try to use 2 locks for swapchain resizing example

This commit is contained in:
Charles Giessen 2021-07-03 14:32:54 -06:00
parent 4a30810a87
commit 0b7529d0da
3 changed files with 140 additions and 90 deletions

View File

@ -22,10 +22,11 @@ std::atomic_bool should_resize;
uint32_t current_width = default_window_width; uint32_t current_width = default_window_width;
uint32_t current_height = default_window_height; uint32_t current_height = default_window_height;
std::mutex main_mutex; std::mutex swapchain_mutex;
std::mutex render_mutex;
std::mutex render_wait_mutex; std::mutex render_thread_sleep_mutex;
std::condition_variable render_wait_condition_variable; std::condition_variable render_thread_sleep_condition_variable;
const bool run_multithreaded = true; const bool run_multithreaded = true;
const bool use_refresh_callback = true; const bool use_refresh_callback = true;
@ -46,6 +47,8 @@ struct Renderer {
vkb::SwapchainManager swapchain_manager; vkb::SwapchainManager swapchain_manager;
vkb::SwapchainInfo swap_info; vkb::SwapchainInfo swap_info;
// where to place the new framebuffer when a resize event happens
VkFramebuffer new_framebuffer = VK_NULL_HANDLE;
vkb::DeletionQueue delete_queue; vkb::DeletionQueue delete_queue;
@ -73,7 +76,7 @@ void unlock(std::mutex& mutex) {
} }
int recreate_swapchain(Renderer& renderer); int recreate_swapchain(Renderer& renderer);
int draw_frame(Renderer& renderer); int draw_frame(Renderer& renderer, bool try_lock);
void glfw_resize_callback(GLFWwindow* window, int width, int height) { void glfw_resize_callback(GLFWwindow* window, int width, int height) {
if (!is_running || width == 0 || height == 0) { if (!is_running || width == 0 || height == 0) {
@ -82,8 +85,6 @@ void glfw_resize_callback(GLFWwindow* window, int width, int height) {
should_resize = true; should_resize = true;
current_width = width; current_width = width;
current_height = height; current_height = height;
std::lock_guard<std::mutex> lg(main_mutex);
Renderer* renderer = reinterpret_cast<Renderer*>(glfwGetWindowUserPointer(window)); Renderer* renderer = reinterpret_cast<Renderer*>(glfwGetWindowUserPointer(window));
int res = recreate_swapchain(*renderer); int res = recreate_swapchain(*renderer);
if (res < 0) { if (res < 0) {
@ -91,23 +92,23 @@ void glfw_resize_callback(GLFWwindow* window, int width, int height) {
return; return;
} }
if (!use_refresh_callback) { if (!use_refresh_callback) {
res = draw_frame(*renderer); res = draw_frame(*renderer, false);
if (res < 0) { if (res < 0) {
is_running = false; is_running = false;
} }
} }
should_resize = false; should_resize = false;
render_wait_condition_variable.notify_one(); render_thread_sleep_condition_variable.notify_one();
} }
void glfw_refresh_callback(GLFWwindow* window) { void glfw_refresh_callback(GLFWwindow* window) {
if (try_lock(main_mutex)) { // if (try_lock(render_mutex)) {
Renderer* renderer = reinterpret_cast<Renderer*>(glfwGetWindowUserPointer(window)); Renderer* renderer = reinterpret_cast<Renderer*>(glfwGetWindowUserPointer(window));
int res = draw_frame(*renderer); int res = draw_frame(*renderer, true);
if (res < 0) { if (res < 0) {
is_running = false; is_running = false;
}
unlock(main_mutex);
} }
// unlock(render_mutex);
//}
} }
inline VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, inline VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType, VkDebugUtilsMessageTypeFlagsEXT messageType,
@ -240,15 +241,14 @@ int create_render_pass(Renderer& renderer) {
return 0; return 0;
} }
int create_framebuffer(Renderer& renderer) { int create_framebuffer(Renderer& renderer, VkFramebuffer& output) {
vkb::ImagelessFramebufferBuilder if_builder(renderer.device); vkb::ImagelessFramebufferBuilder if_builder(renderer.device);
renderer.framebuffer = output = if_builder.set_renderpass(renderer.render_pass)
if_builder.set_renderpass(renderer.render_pass) .set_extent(renderer.swap_info.extent)
.set_extent(renderer.swap_info.extent) .set_layers(1)
.set_layers(1) .add_attachment(renderer.swap_info.image_usage_flags, renderer.swap_info.image_format)
.add_attachment(renderer.swap_info.image_usage_flags, renderer.swap_info.image_format) .build();
.build();
return 0; return 0;
} }
@ -507,41 +507,59 @@ int record_command_buffer(Renderer& renderer, VkCommandBuffer command_buffer, Vk
} }
int recreate_swapchain(Renderer& renderer) { int recreate_swapchain(Renderer& renderer) {
renderer.delete_queue.add_framebuffer(renderer.framebuffer); std::lock_guard<std::mutex> lg(swapchain_mutex);
renderer.framebuffer = VK_NULL_HANDLE;
auto ret = renderer.swapchain_manager.recreate(); auto ret = renderer.swapchain_manager.recreate();
if (!ret) { if (!ret) {
std::cout << "failed to recreate swapchain\n"; std::cout << "failed to recreate swapchain\n";
return -1; return -1;
} }
renderer.swap_info = ret.value(); renderer.swap_info = ret.value();
if (0 != create_framebuffer(renderer)) return -1; if (renderer.new_framebuffer != VK_NULL_HANDLE) {
renderer.delete_queue.add_framebuffer(renderer.new_framebuffer);
}
if (0 != create_framebuffer(renderer, renderer.new_framebuffer)) return -1;
return 0; return 0;
} }
int draw_frame(Renderer& renderer) { int draw_frame(Renderer& renderer, bool try_lock) {
std::unique_lock<std::mutex> lg(render_mutex, std::try_to_lock);
vkb::SwapchainAcquireInfo acquire_info; if (!try_lock && !lg.owns_lock()) {
auto acquire_ret = renderer.swapchain_manager.acquire_image(); lg.lock();
if (acquire_ret.matches_error(vkb::SwapchainManagerError::swapchain_out_of_date)) { } else if (!lg.owns_lock()) {
return 1; return 0;
} else if (!acquire_ret.has_value()) {
std::cout << "failed to acquire swapchain image\n";
return -1;
} }
vkb::SwapchainAcquireInfo acquire_info;
{
std::lock_guard<std::mutex> slg(swapchain_mutex);
// swap old framebuffer with new one now that both locks are held
if (renderer.new_framebuffer != VK_NULL_HANDLE) {
renderer.delete_queue.add_framebuffer(renderer.framebuffer);
renderer.framebuffer = renderer.new_framebuffer;
renderer.new_framebuffer = VK_NULL_HANDLE;
}
acquire_info = acquire_ret.value(); auto acquire_ret = renderer.swapchain_manager.acquire_image();
if (should_resize) return 1; if (acquire_ret.matches_error(vkb::SwapchainManagerError::swapchain_out_of_date)) {
return 1;
} else if (!acquire_ret.has_value()) {
std::cout << "failed to acquire swapchain image\n";
return -1;
}
acquire_info = acquire_ret.value();
}
if (should_resize) {
renderer.swapchain_manager.cancel_acquire_frame();
return 1;
}
renderer.dispatch.waitForFences(1, &renderer.fences[renderer.current_index], VK_TRUE, UINT64_MAX); renderer.dispatch.waitForFences(1, &renderer.fences[renderer.current_index], VK_TRUE, UINT64_MAX);
renderer.dispatch.resetFences(1, &renderer.fences[renderer.current_index]); renderer.dispatch.resetFences(1, &renderer.fences[renderer.current_index]);
record_command_buffer(renderer, renderer.command_buffers[renderer.current_index], acquire_info.image_view); record_command_buffer(renderer, renderer.command_buffers[renderer.current_index], acquire_info.image_view);
auto semaphores = renderer.swapchain_manager.get_submit_semaphores().value(); VkSemaphore wait_semaphores[1] = { acquire_info.wait_semaphore };
VkSemaphore signal_semaphores[1] = { acquire_info.signal_semaphore };
VkSemaphore wait_semaphores[1] = { semaphores.wait };
VkSemaphore signal_semaphores[1] = { semaphores.signal };
VkPipelineStageFlags wait_stages[1] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; VkPipelineStageFlags wait_stages[1] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
VkSubmitInfo submit_info = {}; VkSubmitInfo submit_info = {};
@ -554,21 +572,31 @@ int draw_frame(Renderer& renderer) {
submit_info.signalSemaphoreCount = 1; submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = signal_semaphores; submit_info.pSignalSemaphores = signal_semaphores;
if (renderer.dispatch.queueSubmit( VkResult res = renderer.dispatch.queueSubmit(
renderer.graphics_queue, 1, &submit_info, renderer.fences[renderer.current_index]) != VK_SUCCESS) { renderer.graphics_queue, 1, &submit_info, renderer.fences[renderer.current_index]);
std::cout << "failed to submit command buffer\n"; if (res != VK_SUCCESS) {
std::cout << "failed to submit command buffer" << res << "\n";
return -1; return -1;
} }
renderer.current_index = (renderer.current_index + 1) % MAX_FRAMES_IN_FLIGHT; renderer.current_index = (renderer.current_index + 1) % MAX_FRAMES_IN_FLIGHT;
if (should_resize) return 1;
auto present_ret = renderer.swapchain_manager.present(); if (should_resize) {
renderer.swapchain_manager.cancel_present_frame();
if (present_ret.matches_error(vkb::SwapchainManagerError::swapchain_out_of_date)) {
return 1; return 1;
} else if (!present_ret) { }
std::cout << "failed to present swapchain image\n"; {
return -1; std::lock_guard<std::mutex> slg(swapchain_mutex);
if (!renderer.swapchain_manager.able_to_present()) {
renderer.swapchain_manager.cancel_present_frame();
return 0;
}
auto present_ret = renderer.swapchain_manager.present();
if (present_ret.matches_error(vkb::SwapchainManagerError::swapchain_out_of_date)) {
return 1;
} else if (!present_ret) {
std::cout << "failed to present swapchain image " << present_ret.error().message() << "\n";
return -1;
}
} }
renderer.delete_queue.tick(); renderer.delete_queue.tick();
renderer.current_time = glfwGetTime(); renderer.current_time = glfwGetTime();
@ -600,21 +628,14 @@ void cleanup(Renderer& renderer) {
void render_loop(Renderer* renderer) { void render_loop(Renderer* renderer) {
while (is_running) { while (is_running) {
std::unique_lock<std::mutex> lg(main_mutex, std::try_to_lock); int res = draw_frame(*renderer, true);
if (!lg.owns_lock()) { if (res < 0) {
std::unique_lock<std::mutex> ulg(render_wait_mutex); is_running = false;
render_wait_condition_variable.wait(ulg); break;
continue; }
} else { if (res == 1) {
int res = draw_frame(*renderer); std::unique_lock<std::mutex> ulg(render_thread_sleep_mutex);
if (res < 0) { render_thread_sleep_condition_variable.wait(ulg);
is_running = false;
}
if (res == 1) {
lg.unlock();
std::unique_lock<std::mutex> ulg(render_wait_mutex);
render_wait_condition_variable.wait(ulg);
}
} }
} }
} }
@ -628,25 +649,24 @@ int main() {
if (0 != device_initialization(renderer)) return -1; if (0 != device_initialization(renderer)) return -1;
if (0 != get_queues(renderer)) return -1; if (0 != get_queues(renderer)) return -1;
if (0 != create_render_pass(renderer)) return -1; if (0 != create_render_pass(renderer)) return -1;
if (0 != create_framebuffer(renderer)) return -1; if (0 != create_framebuffer(renderer, renderer.framebuffer)) return -1;
if (0 != create_graphics_pipeline(renderer)) return -1; if (0 != create_graphics_pipeline(renderer)) return -1;
if (0 != create_command_buffers(renderer)) return -1; if (0 != create_command_buffers(renderer)) return -1;
is_running = true; is_running = true;
renderer.current_time = glfwGetTime(); renderer.current_time = glfwGetTime();
if (run_multithreaded) { if (run_multithreaded) {
std::thread render_thread{ render_loop, &renderer }; std::thread render_thread{ render_loop, &renderer };
while (!glfwWindowShouldClose(renderer.window) && is_running) { while (!glfwWindowShouldClose(renderer.window) && is_running) {
glfwPollEvents(); glfwPollEvents();
glfwWaitEvents(); glfwWaitEvents();
} }
is_running = false; is_running = false;
render_wait_condition_variable.notify_one(); render_thread_sleep_condition_variable.notify_one();
render_thread.join(); render_thread.join();
} else { } else {
while (!glfwWindowShouldClose(renderer.window) && is_running) { while (!glfwWindowShouldClose(renderer.window) && is_running) {
glfwPollEvents(); glfwPollEvents();
int res = draw_frame(renderer); int res = draw_frame(renderer, false);
if (res < 0) { if (res < 0) {
is_running = false; is_running = false;
} }

View File

@ -2299,6 +2299,8 @@ SwapchainManager::SwapchainManager(SwapchainBuilder const& builder,
detail::vulkan_functions().get_device_proc_addr(device, detail.fp_vkGetDeviceQueue, "vkGetDeviceQueue"); detail::vulkan_functions().get_device_proc_addr(device, detail.fp_vkGetDeviceQueue, "vkGetDeviceQueue");
detail::vulkan_functions().get_device_proc_addr(device, detail.fp_vkAcquireNextImageKHR, "vkAcquireNextImageKHR"); detail::vulkan_functions().get_device_proc_addr(device, detail.fp_vkAcquireNextImageKHR, "vkAcquireNextImageKHR");
detail::vulkan_functions().get_device_proc_addr(device, detail.fp_vkQueuePresentKHR, "vkQueuePresentKHR"); detail::vulkan_functions().get_device_proc_addr(device, detail.fp_vkQueuePresentKHR, "vkQueuePresentKHR");
detail::vulkan_functions().get_device_proc_addr(device, detail.fp_vkQueueSubmit, "vkQueueSubmit");
detail.fp_vkGetSwapchainImagesKHR = fp_vkGetSwapchainImagesKHR; detail.fp_vkGetSwapchainImagesKHR = fp_vkGetSwapchainImagesKHR;
detail.fp_vkCreateImageView = fp_vkCreateImageView; detail.fp_vkCreateImageView = fp_vkCreateImageView;
detail.fp_vkGetDeviceQueue(device, detail.builder.info.graphics_queue_index, 0, &detail.graphics_queue); detail.fp_vkGetDeviceQueue(device, detail.builder.info.graphics_queue_index, 0, &detail.graphics_queue);
@ -2387,21 +2389,15 @@ detail::Result<SwapchainAcquireInfo> SwapchainManager::acquire_image() noexcept
SwapchainAcquireInfo out{}; SwapchainAcquireInfo out{};
out.image_view = detail.swapchain_resources.image_views[detail.current_image_index]; out.image_view = detail.swapchain_resources.image_views[detail.current_image_index];
out.image_index = detail.current_image_index; out.image_index = detail.current_image_index;
out.wait_semaphore = detail.semaphore_manager.get_acquire_semaphore();
out.signal_semaphore = detail.semaphore_manager.get_submit_semaphore();
detail.current_acquire_semaphore = out.wait_semaphore;
detail.current_present_semaphore = out.signal_semaphore;
return out; return out;
} }
detail::Result<SwapchainSubmitSemaphores> SwapchainManager::get_submit_semaphores() noexcept { bool SwapchainManager::able_to_present() noexcept {
assert(detail.current_status != Status::destroyed && "SwapchainManager was destroyed!"); return detail.current_status == Status::ready_to_present;
if (detail.current_status == Status::expired) {
return make_error_code(SwapchainManagerError::swapchain_out_of_date);
}
if (detail.current_status == Status::ready_to_acquire) {
return make_error_code(SwapchainManagerError::must_call_acquire_image_first);
}
SwapchainSubmitSemaphores semaphores;
semaphores.signal = detail.semaphore_manager.get_submit_semaphore();
semaphores.wait = detail.semaphore_manager.get_acquire_semaphore();
return semaphores;
} }
detail::Result<detail::void_t> SwapchainManager::present() noexcept { detail::Result<detail::void_t> SwapchainManager::present() noexcept {
@ -2413,7 +2409,9 @@ detail::Result<detail::void_t> SwapchainManager::present() noexcept {
return make_error_code(SwapchainManagerError::must_call_acquire_image_first); return make_error_code(SwapchainManagerError::must_call_acquire_image_first);
} }
VkSemaphore wait_semaphores[1] = { detail.semaphore_manager.get_submit_semaphore() }; VkSemaphore wait_semaphores[1] = { detail.current_present_semaphore };
detail.current_acquire_semaphore = VK_NULL_HANDLE;
detail.current_present_semaphore = VK_NULL_HANDLE;
VkPresentInfoKHR present_info = {}; VkPresentInfoKHR present_info = {};
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
@ -2437,13 +2435,43 @@ detail::Result<detail::void_t> SwapchainManager::present() noexcept {
} else { } else {
detail.current_status = Status::ready_to_acquire; detail.current_status = Status::ready_to_acquire;
} }
// clean up old swapchain resources // clean up old swapchain resources
detail.delete_queue.tick(); detail.delete_queue.tick();
return detail::void_t{}; return detail::void_t{};
} }
void SwapchainManager::cancel_acquire_frame() noexcept {
VkPipelineStageFlags wait_stages[1] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT };
VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &detail.current_acquire_semaphore;
submit_info.pWaitDstStageMask = wait_stages;
VkResult res = detail.fp_vkQueueSubmit(detail.graphics_queue, 1, &submit_info, VK_NULL_HANDLE);
assert(res == VK_SUCCESS);
detail.current_acquire_semaphore = VK_NULL_HANDLE;
detail.current_present_semaphore = VK_NULL_HANDLE;
}
void SwapchainManager::cancel_present_frame() noexcept {
VkPipelineStageFlags wait_stages[1] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT };
VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &detail.current_present_semaphore;
submit_info.pWaitDstStageMask = wait_stages;
VkResult res = detail.fp_vkQueueSubmit(detail.graphics_queue, 1, &submit_info, VK_NULL_HANDLE);
assert(res == VK_SUCCESS);
detail.current_acquire_semaphore = VK_NULL_HANDLE;
detail.current_present_semaphore = VK_NULL_HANDLE;
}
detail::Result<SwapchainInfo> SwapchainManager::recreate(uint32_t width, uint32_t height) noexcept { detail::Result<SwapchainInfo> SwapchainManager::recreate(uint32_t width, uint32_t height) noexcept {
assert(detail.current_status != Status::destroyed && "SwapchainManager was destroyed!"); assert(detail.current_status != Status::destroyed && "SwapchainManager was destroyed!");

View File

@ -978,11 +978,8 @@ struct SwapchainAcquireInfo {
VkImageView image_view{}; VkImageView image_view{};
// index of the swapchain image to use this frame // index of the swapchain image to use this frame
uint32_t image_index = detail::INDEX_MAX_VALUE; uint32_t image_index = detail::INDEX_MAX_VALUE;
}; VkSemaphore signal_semaphore;
VkSemaphore wait_semaphore;
struct SwapchainSubmitSemaphores {
VkSemaphore signal;
VkSemaphore wait;
}; };
/** /**
@ -1098,10 +1095,12 @@ class SwapchainManager {
// Get a VkImageView handle to use in rendering // Get a VkImageView handle to use in rendering
detail::Result<SwapchainAcquireInfo> acquire_image() noexcept; detail::Result<SwapchainAcquireInfo> acquire_image() noexcept;
detail::Result<SwapchainSubmitSemaphores> get_submit_semaphores() noexcept; bool able_to_present() noexcept;
detail::Result<detail::void_t> present() noexcept; detail::Result<detail::void_t> present() noexcept;
void cancel_acquire_frame() noexcept;
void cancel_present_frame() noexcept;
// Recreate the swapchain, putting currently in-use internal resources in a delete queue // Recreate the swapchain, putting currently in-use internal resources in a delete queue
detail::Result<SwapchainInfo> recreate(uint32_t width = 0, uint32_t height = 0) noexcept; detail::Result<SwapchainInfo> recreate(uint32_t width = 0, uint32_t height = 0) noexcept;
@ -1138,6 +1137,9 @@ class SwapchainManager {
PFN_vkQueuePresentKHR fp_vkQueuePresentKHR; PFN_vkQueuePresentKHR fp_vkQueuePresentKHR;
PFN_vkGetSwapchainImagesKHR fp_vkGetSwapchainImagesKHR; PFN_vkGetSwapchainImagesKHR fp_vkGetSwapchainImagesKHR;
PFN_vkCreateImageView fp_vkCreateImageView; PFN_vkCreateImageView fp_vkCreateImageView;
PFN_vkQueueSubmit fp_vkQueueSubmit;
VkSemaphore current_acquire_semaphore;
VkSemaphore current_present_semaphore;
} detail; } detail;
explicit SwapchainManager(SwapchainBuilder const& builder, explicit SwapchainManager(SwapchainBuilder const& builder,