From 5949bbab9743e615d2580db5bc6ff6d202bd3bc1 Mon Sep 17 00:00:00 2001 From: BearOso Date: Wed, 12 Jun 2024 16:51:12 -0500 Subject: [PATCH] Vulkan: Utilize VK_EXT_swapchain_maintenance1. This is core in Vulkan 1.1. We can now change vsync state without a new swapchain. A fence is signaled when image is on screen, so we can possibly be a little more precise with timing and avoid a whole device wait. --- gtk/src/gtk_display_driver_vulkan.cpp | 1 + qt/src/EmuCanvasVulkan.cpp | 6 +-- vulkan/vulkan_context.cpp | 14 ++--- vulkan/vulkan_swapchain.cpp | 73 +++++++++++++++++++-------- vulkan/vulkan_swapchain.hpp | 5 ++ 5 files changed, 66 insertions(+), 33 deletions(-) diff --git a/gtk/src/gtk_display_driver_vulkan.cpp b/gtk/src/gtk_display_driver_vulkan.cpp index c1b44f53..66e5b164 100644 --- a/gtk/src/gtk_display_driver_vulkan.cpp +++ b/gtk/src/gtk_display_driver_vulkan.cpp @@ -222,6 +222,7 @@ void S9xVulkanDisplayDriver::update(uint16_t *buffer, int width, int height, int throttle.wait_for_frame_and_rebase_time(); } + context->swapchain->set_vsync(gui_config->sync_to_vblank); context->swapchain->swap(); if (gui_config->reduce_input_lag) diff --git a/qt/src/EmuCanvasVulkan.cpp b/qt/src/EmuCanvasVulkan.cpp index 4da80e0f..12557221 100644 --- a/qt/src/EmuCanvasVulkan.cpp +++ b/qt/src/EmuCanvasVulkan.cpp @@ -178,9 +178,6 @@ void EmuCanvasVulkan::draw() if (!window->isVisible()) return; - if (context->swapchain->set_vsync(config->enable_vsync)) - context->recreate_swapchain(); - if (S9xImGuiDraw(width() * devicePixelRatioF(), height() * devicePixelRatioF())) { auto draw_data = ImGui::GetDrawData(); @@ -205,10 +202,11 @@ void EmuCanvasVulkan::draw() if (retval) { throttle(); + context->swapchain->set_vsync(config->enable_vsync); context->swapchain->swap(); if (config->reduce_input_lag) { - context->wait_idle(); + context->swapchain->wait_on_frames(); } } } diff --git a/vulkan/vulkan_context.cpp b/vulkan/vulkan_context.cpp index d5f50084..90a337ea 100644 --- a/vulkan/vulkan_context.cpp +++ b/vulkan/vulkan_context.cpp @@ -219,8 +219,9 @@ static bool check_extensions(std::vector &required_extensions, vk: bool Context::init_device(int preferred_device) { - std::vector required_extensions; - required_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + std::vector required_extensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; auto device_list = instance->enumeratePhysicalDevices().value; physical_device = nullptr; @@ -247,15 +248,14 @@ bool Context::init_device(int preferred_device) auto extension_properties = physical_device.enumerateDeviceExtensionProperties().value; physical_device.getProperties(&physical_device_props); - if (find_extension(extension_properties, VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)) - required_extensions.push_back(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); - graphics_queue_family_index = find_graphics_queue(physical_device); if (graphics_queue_family_index == UINT32_MAX) return false; - vk::DeviceQueueCreateInfo dqci({}, graphics_queue_family_index, 1); - vk::DeviceCreateInfo dci({}, dqci, {}, required_extensions, {}); + std::vector priorities = { 1.0f }; + vk::DeviceQueueCreateInfo dqci({}, graphics_queue_family_index, priorities); + vk::DeviceCreateInfo dci({}, dqci, {}, required_extensions); + device = physical_device.createDevice(dci).value; queue = device.getQueue(graphics_queue_family_index, 0); diff --git a/vulkan/vulkan_swapchain.cpp b/vulkan/vulkan_swapchain.cpp index f5e288e8..bfee445a 100644 --- a/vulkan/vulkan_swapchain.cpp +++ b/vulkan/vulkan_swapchain.cpp @@ -1,5 +1,4 @@ #include "vulkan_swapchain.hpp" -#include namespace Vulkan { @@ -80,7 +79,9 @@ void Swapchain::create_render_pass() bool Swapchain::recreate(int new_width, int new_height) { - device.waitIdle(); + if (swapchain_object) + wait_on_frames(); + return create(num_swapchain_images, new_width, new_height); } @@ -164,24 +165,24 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width, extents.height = surface_capabilities.minImageExtent.height; auto present_modes = physical_device.getSurfacePresentModesKHR(surface).value; - bool mailbox_supported = + supports_mailbox = std::find(present_modes.begin(), present_modes.end(), vk::PresentModeKHR::eMailbox) != present_modes.end(); - bool immediate_supported = + supports_immediate = std::find(present_modes.begin(), present_modes.end(), vk::PresentModeKHR::eImmediate) != present_modes.end(); - vk::PresentModeKHR present_mode = vk::PresentModeKHR::eFifo; + auto present_mode = vk::PresentModeKHR::eFifo; if (!vsync) { - if (mailbox_supported) + if (supports_mailbox) present_mode = vk::PresentModeKHR::eMailbox; - if (immediate_supported) + if (supports_immediate) present_mode = vk::PresentModeKHR::eImmediate; } - if (present_mode == vk::PresentModeKHR::eMailbox) - num_swapchain_images++; + auto swapchain_maintenance_info = vk::SwapchainPresentModesCreateInfoEXT{} + .setPresentModes(present_modes); auto swapchain_create_info = vk::SwapchainCreateInfoKHR{} .setMinImageCount(num_swapchain_images) @@ -196,7 +197,8 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width, .setSurface(surface) .setPreTransform(vk::SurfaceTransformFlagBitsKHR::eIdentity) .setImageArrayLayers(1) - .setQueueFamilyIndices(graphics_queue_index); + .setQueueFamilyIndices(graphics_queue_index) + .setPNext(&swapchain_maintenance_info); swapchain_object.reset(); auto resval = device.createSwapchainKHRUnique(swapchain_create_info); @@ -207,12 +209,19 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width, } swapchain_object = std::move(resval.value); - auto swapchain_images = device.getSwapchainImagesKHR(swapchain_object.get()).value; - vk::CommandBufferAllocateInfo command_buffer_allocate_info(command_pool, vk::CommandBufferLevel::ePrimary, swapchain_images.size()); - auto command_buffers = device.allocateCommandBuffersUnique(command_buffer_allocate_info).value; + create_resources(); - if (imageviewfbs.size() > num_swapchain_images) - num_swapchain_images = imageviewfbs.size(); + return true; +} + +bool Swapchain::create_resources() +{ + auto swapchain_images = device.getSwapchainImagesKHR(swapchain_object.get()).value; + if (swapchain_images.size() > num_swapchain_images) + num_swapchain_images = swapchain_images.size(); + + vk::CommandBufferAllocateInfo command_buffer_allocate_info(command_pool, vk::CommandBufferLevel::ePrimary, num_swapchain_images); + auto command_buffers = device.allocateCommandBuffersUnique(command_buffer_allocate_info).value; frames.resize(num_swapchain_images); imageviewfbs.resize(num_swapchain_images); @@ -225,10 +234,10 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width, auto &frame = frames[i]; frame.command_buffer = std::move(command_buffers[i]); frame.fence = device.createFenceUnique(fence_create_info).value; + frame.freeable = device.createFenceUnique(fence_create_info).value; frame.acquire = device.createSemaphoreUnique({}).value; frame.complete = device.createSemaphoreUnique({}).value; } - current_frame = 0; for (unsigned int i = 0; i < num_swapchain_images; i++) { @@ -252,9 +261,8 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width, image.framebuffer = device.createFramebufferUnique(framebuffer_create_info).value; } - device.waitIdle(); - current_swapchain_image = 0; + current_frame = 0; return true; } @@ -269,7 +277,7 @@ bool Swapchain::begin_frame() auto &frame = frames[current_frame]; - auto result = device.waitForFences(frame.fence.get(), true, 33333333); + auto result = device.waitForFences({ frame.fence.get(), frame.freeable.get() }, true, 33333333); if (result != vk::Result::eSuccess) { printf("Timed out waiting for fence.\n"); @@ -330,8 +338,23 @@ bool Swapchain::swap() .setSwapchains(swapchain_object.get()) .setImageIndices(current_swapchain_image); - vk::Result result = vk::Result::eSuccess; - result = queue.presentKHR(present_info); + vk::SwapchainPresentModeInfoEXT present_mode_info; + vk::PresentModeKHR present_mode = vk::PresentModeKHR::eFifo; + if (!vsync) + { + if (supports_mailbox) + present_mode = vk::PresentModeKHR::eMailbox; + if (supports_immediate) + present_mode = vk::PresentModeKHR::eImmediate; + } + present_mode_info.setPresentModes(present_mode); + present_info.setPNext(&present_mode_info); + + device.resetFences(frames[current_frame].freeable.get()); + vk::SwapchainPresentFenceInfoEXT present_fence_info(frames[current_frame].freeable.get()); + present_mode_info.setPNext(&present_fence_info); + + vk::Result result = queue.presentKHR(present_info); if (result == vk::Result::eErrorOutOfDateKHR) { // NVIDIA binary drivers will set OutOfDate between acquire and @@ -389,10 +412,16 @@ void Swapchain::end_render_pass() bool Swapchain::wait_on_frame(int frame_num) { - auto result = device.waitForFences(frames[frame_num].fence.get(), true, 33000000); + auto result = device.waitForFences({ frames[frame_num].fence.get(), frames[frame_num].freeable.get() }, true, 133000000); return (result == vk::Result::eSuccess); } +void Swapchain::wait_on_frames() +{ + for (auto i = 0; i < frames.size(); i++) + wait_on_frame(i); +} + vk::Extent2D Swapchain::get_extents() { return extents; diff --git a/vulkan/vulkan_swapchain.hpp b/vulkan/vulkan_swapchain.hpp index c2ba4505..2fd65695 100644 --- a/vulkan/vulkan_swapchain.hpp +++ b/vulkan/vulkan_swapchain.hpp @@ -17,6 +17,7 @@ class Swapchain ~Swapchain(); bool create(unsigned int num_frames, int width = -1, int height = -1); bool recreate(int width = -1, int height = -1); + bool create_resources(); bool check_and_resize(int width = -1, int height = -1); bool begin_frame(); void begin_render_pass(); @@ -25,6 +26,7 @@ class Swapchain bool end_frame(); void end_frame_without_swap(); bool swap(); + void wait_on_frames(); // Returns true if vsync setting was changed, false if it was the same bool set_vsync(bool on); void on_render_pass_end(std::function function); @@ -43,6 +45,7 @@ class Swapchain struct Frame { vk::UniqueFence fence; + vk::UniqueFence freeable; vk::UniqueSemaphore acquire; vk::UniqueSemaphore complete; vk::UniqueCommandBuffer command_buffer; @@ -64,6 +67,8 @@ class Swapchain unsigned int current_swapchain_image = 0; unsigned int num_swapchain_images = 0; bool vsync = true; + bool supports_immediate = false; + bool supports_mailbox = false; std::vector frames; std::vector imageviewfbs;