From 05e98f4d17cde7d95e6cef57233ce0ce81be5545 Mon Sep 17 00:00:00 2001 From: Dennis Tsiang Date: Thu, 9 May 2024 09:32:42 +0100 Subject: [PATCH] Support switching presentation modes Add support for handling VkSwapchainPresentModeInfoEXT and VkSwapchainPresentModesCreateInfoEXT when supplied as part of the pNext chain of VkPresentInfoKHR and VkSwapchainCreateInfoKHR respectively. Since the headless backend has no effect when switching between FIFO and FIFO_RELAXED, and the wayland backend does not support switching presentation modes between MAILBOX and FIFO, the only real thing we can do is check that the presentation mode requested is one that is supported by the swapchain and is compatible with the current presentation mode. Change-Id: I8d5506e9b1a8fba079e2f9d6ee133038feb27dca Signed-off-by: Dennis Tsiang Signed-off-by: Fufu Fang --- layer/swapchain_api.cpp | 12 +++++-- wsi/headless/surface_properties.cpp | 5 +++ wsi/headless/surface_properties.hpp | 2 ++ wsi/surface_properties.hpp | 35 +++++++++++++++++++ wsi/swapchain_base.cpp | 54 ++++++++++++++++++++++++++--- wsi/swapchain_base.hpp | 50 ++++++++++++++++++++++++-- wsi/wayland/surface_properties.cpp | 5 +++ wsi/wayland/surface_properties.hpp | 2 ++ 8 files changed, 155 insertions(+), 10 deletions(-) diff --git a/layer/swapchain_api.cpp b/layer/swapchain_api.cpp index 22ec9e5..39b79bc 100644 --- a/layer/swapchain_api.cpp +++ b/layer/swapchain_api.cpp @@ -172,6 +172,8 @@ wsi_layer_vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo) VkResult ret = VK_SUCCESS; const auto present_fence_info = util::find_extension( VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT, present_info->pNext); + const auto swapchain_present_mode_info = util::find_extension( + VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT, present_info->pNext); for (uint32_t i = 0; i < pPresentInfo->swapchainCount; ++i) { VkSwapchainKHR swapc = pPresentInfo->pSwapchains[i]; @@ -179,9 +181,15 @@ wsi_layer_vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo) auto *sc = reinterpret_cast(swapc); assert(sc != nullptr); - const VkFence present_fence = (present_fence_info == nullptr) ? VK_NULL_HANDLE : present_fence_info->pFences[i]; + wsi::swapchain_presentation_parameters present_params; + present_params.present_fence = (present_fence_info == nullptr) ? VK_NULL_HANDLE : present_fence_info->pFences[i]; + if (swapchain_present_mode_info != nullptr) + { + present_params.switch_presentation_mode = true; + present_params.present_mode = swapchain_present_mode_info->pPresentModes[i]; + } VkResult res = sc->queue_present(queue, present_info, pPresentInfo->pImageIndices[i], use_image_present_semaphore, - present_fence); + present_params); if (pPresentInfo->pResults != nullptr) { pPresentInfo->pResults[i] = res; diff --git a/wsi/headless/surface_properties.cpp b/wsi/headless/surface_properties.cpp index 76f8d1c..21966c8 100644 --- a/wsi/headless/surface_properties.cpp +++ b/wsi/headless/surface_properties.cpp @@ -208,5 +208,10 @@ void surface_properties::get_surface_present_scaling_and_gravity( scaling_capabilities->supportedPresentGravityY = 0; } +bool surface_properties::is_compatible_present_modes(VkPresentModeKHR present_mode_a, VkPresentModeKHR present_mode_b) +{ + return is_compatible_present_modes_common(present_mode_a, present_mode_b, present_mode_compatibilities); +} + } /* namespace headless */ } /* namespace wsi */ diff --git a/wsi/headless/surface_properties.hpp b/wsi/headless/surface_properties.hpp index 53841bb..2c7af80 100644 --- a/wsi/headless/surface_properties.hpp +++ b/wsi/headless/surface_properties.hpp @@ -60,6 +60,8 @@ public: static surface_properties &get_instance(); + bool is_compatible_present_modes(VkPresentModeKHR present_mode_a, VkPresentModeKHR present_mode_b) override; + private: /* List of supported presentation modes */ std::array supported_modes; diff --git a/wsi/surface_properties.hpp b/wsi/surface_properties.hpp index 16a86b1..c98ed7a 100644 --- a/wsi/surface_properties.hpp +++ b/wsi/surface_properties.hpp @@ -121,6 +121,8 @@ public: virtual void get_surface_present_scaling_and_gravity( VkSurfacePresentScalingCapabilitiesEXT *scaling_capabilities) = 0; + virtual bool is_compatible_present_modes(VkPresentModeKHR present_mode_a, VkPresentModeKHR present_mode_b) = 0; + private: /** * @brief Set which presentation modes are compatible with each other for a particular surface @@ -368,4 +370,37 @@ void get_surface_present_mode_compatibility_common( surface_present_mode_compatibility->pPresentModes); } +/** + * @brief Common function for handling checking whether a present mode is compatible with another. + * + * @param present_mode_a First present mode. + * @param present_mode_b Second present mode to compare against. + * @param present_mode_compatibilities A table containing a mapping of presentation modes and what other modes they are compatible with. + * + * @return true if compatible, false otherwise. + */ +template +bool is_compatible_present_modes_common( + VkPresentModeKHR present_mode_a, VkPresentModeKHR present_mode_b, + const std::array &present_mode_compatibilities) +{ + auto it = std::find_if(present_mode_compatibilities.begin(), present_mode_compatibilities.end(), + [present_mode_a](present_mode_compatibility p) { return p.present_mode == present_mode_a; }); + if (it == present_mode_compatibilities.end()) + { + WSI_LOG_ERROR("Querying compatible presentation mode support for a presentation mode that is not supported."); + return false; + } + + const present_mode_compatibility &present_mode_comp = *it; + auto present_mode_it = + std::find_if(present_mode_comp.compatible_present_modes.begin(), present_mode_comp.compatible_present_modes.end(), + [present_mode_b](VkPresentModeKHR p) { return p == present_mode_b; }); + if (present_mode_it == present_mode_comp.compatible_present_modes.end()) + { + return false; + } + return true; +} + } /* namespace wsi */ diff --git a/wsi/swapchain_base.cpp b/wsi/swapchain_base.cpp index 57181cd..af87d9f 100644 --- a/wsi/swapchain_base.cpp +++ b/wsi/swapchain_base.cpp @@ -211,6 +211,7 @@ swapchain_base::swapchain_base(layer::device_private_data &dev_data, const VkAll , m_swapchain_images(m_allocator) , m_surface(VK_NULL_HANDLE) , m_present_mode(VK_PRESENT_MODE_IMMEDIATE_KHR) + , m_present_modes(m_allocator) , m_descendant(VK_NULL_HANDLE) , m_ancestor(VK_NULL_HANDLE) , m_device(VK_NULL_HANDLE) @@ -253,6 +254,30 @@ static VkResult handle_scaling_create_info(VkDevice device, const VkSwapchainCre return VK_SUCCESS; } +VkResult swapchain_base::handle_swapchain_present_modes_create_info( + VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info) +{ + const auto *swapchain_present_modes_create_info = util::find_extension( + VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODES_CREATE_INFO_EXT, swapchain_create_info->pNext); + if (swapchain_present_modes_create_info != nullptr) + { + if (!m_present_modes.try_resize(swapchain_present_modes_create_info->presentModeCount)) + { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + layer::device_private_data &device_data = layer::device_private_data::get(device); + auto &instance = device_data.instance_data; + wsi::surface_properties *props = wsi::get_surface_properties(instance, m_surface); + for (uint32_t i = 0; i < swapchain_present_modes_create_info->presentModeCount; i++) + { + assert( + props->is_compatible_present_modes(m_present_mode, swapchain_present_modes_create_info->pPresentModes[i])); + m_present_modes[i] = swapchain_present_modes_create_info->pPresentModes[i]; + } + } + return VK_SUCCESS; +} + VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info) { assert(device != VK_NULL_HANDLE); @@ -264,6 +289,8 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s m_present_mode = swapchain_create_info->presentMode; + TRY(handle_swapchain_present_modes_create_info(device, swapchain_create_info)); + #if WSI_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN const auto *image_compression_control = util::find_extension( VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT, swapchain_create_info->pNext); @@ -639,9 +666,15 @@ VkResult swapchain_base::notify_presentation_engine(uint32_t image_index) } VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, const uint32_t image_index, - bool use_image_present_semaphore, const VkFence present_fence) + bool use_image_present_semaphore, + swapchain_presentation_parameters presentation_parameters) { + if (presentation_parameters.switch_presentation_mode) + { + TRY(handle_switching_presentation_mode(presentation_parameters.present_mode)); + } + const VkSemaphore *wait_semaphores = &m_swapchain_images[image_index].present_semaphore; uint32_t sem_count = 1; if (!use_image_present_semaphore) @@ -660,19 +693,20 @@ VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *pr queue_submit_semaphores semaphores = { wait_semaphores, sem_count, - (present_fence != VK_NULL_HANDLE) ? &m_swapchain_images[image_index].present_fence_wait : nullptr, - (present_fence != VK_NULL_HANDLE) ? 1u : 0, + (presentation_parameters.present_fence != VK_NULL_HANDLE) ? &m_swapchain_images[image_index].present_fence_wait : + nullptr, + (presentation_parameters.present_fence != VK_NULL_HANDLE) ? 1u : 0, }; TRY_LOG_CALL(image_set_present_payload(m_swapchain_images[image_index], queue, semaphores)); - if (present_fence != VK_NULL_HANDLE) + if (presentation_parameters.present_fence != VK_NULL_HANDLE) { const queue_submit_semaphores wait_semaphores = { &m_swapchain_images[image_index].present_fence_wait, 1, nullptr, 0 }; /* * Here we chain wait_semaphores with present_fence through present_fence_wait. */ - TRY(sync_queue_submit(m_device_data, queue, present_fence, wait_semaphores)); + TRY(sync_queue_submit(m_device_data, queue, presentation_parameters.present_fence, wait_semaphores)); } TRY(notify_presentation_engine(image_index)); @@ -774,4 +808,14 @@ VkResult swapchain_base::is_bind_allowed(uint32_t image_index) const VK_ERROR_OUT_OF_HOST_MEMORY; } +VkResult swapchain_base::handle_switching_presentation_mode(VkPresentModeKHR swapchain_present_mode) +{ + assert(m_present_modes.size() > 0); + auto it = std::find_if(m_present_modes.begin(), m_present_modes.end(), + [swapchain_present_mode](VkPresentModeKHR p) { return p == swapchain_present_mode; }); + assert(it != m_present_modes.end()); + m_present_mode = swapchain_present_mode; + return VK_SUCCESS; +} + } /* namespace wsi */ diff --git a/wsi/swapchain_base.hpp b/wsi/swapchain_base.hpp index 75fa25a..ec56053 100644 --- a/wsi/swapchain_base.hpp +++ b/wsi/swapchain_base.hpp @@ -70,6 +70,18 @@ struct swapchain_image VkSemaphore present_fence_wait{ VK_NULL_HANDLE }; }; +struct swapchain_presentation_parameters +{ + /* Fence supplied by the application with VkSwapchainPresentFenceInfoEXT. */ + VkFence present_fence{ VK_NULL_HANDLE }; + + /* Whether the swapchain needs to switch to a different presentation mode. */ + VkBool32 switch_presentation_mode{ false }; + + /* The presentation mode to switch to. */ + VkPresentModeKHR present_mode; +}; + #if WSI_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN struct image_compression_control_params { @@ -150,14 +162,14 @@ public: * and not the semaphores that come with * \p present_info. * - * @param present_fence Fence supplied by the application with VkSwapchainPresentFenceInfoEXT. + * @param presentation_parameters Presentation parameters. * * @return If queue submission fails returns error of vkQueueSubmit, if the * swapchain has a descendant who started presenting returns VK_ERROR_OUT_OF_DATE_KHR, * otherwise returns VK_SUCCESS. */ VkResult queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, const uint32_t image_index, - bool use_image_present_semaphore, const VkFence present_fence); + bool use_image_present_semaphore, swapchain_presentation_parameters presentation_parameters); /** * @brief Get the allocator @@ -310,10 +322,15 @@ protected: VkSurfaceKHR m_surface; /** - * @brief present mode to use for this swapchain + * @brief Present mode currently being used for this swapchain */ VkPresentModeKHR m_present_mode; + /** + * @brief Possible presentation modes this swapchain is allowed to present with VkSwapchainPresentModesCreateInfoEXT + */ + util::vector m_present_modes; + /** * @brief Descendant of this swapchain. * Used to check whether or not a descendant of this swapchain has started @@ -624,6 +641,33 @@ private: * @brief A flag to track if swapchain has started presenting. */ bool m_started_presenting; + + /** + * @brief Handle presentation mode switching. + * + * If VkSwapchainPresentModeInfoEXT is supplied as part of the pNext chain of VkPresentInfoKHR + * then this function handles switching the swapchains(s)' presentation mode + * to the one(s) requested in VkSwapchainPresentModeInfoEXT structure. + * + * @param swapchain_present_mode presentation mode to switch to. + * + * @return VK_SUCCESS on success or an error code otherwise. + */ + virtual VkResult handle_switching_presentation_mode(VkPresentModeKHR swapchain_present_mode); + + /** + * @brief Handle VkSwapchainPresentModesCreateInfoEXT . + * + * If VkSwapchainPresentModesCreateInfoEXT is supplied as part of the pNext chain of VkSwapchainCreateInfoKHR + * then this function handles setting up the presentation modes for the swapchain. + * + * @param device VkDevice object. + * @param swapchain_create_info Pointer to the swapchain create info struct. + * + * @return VK_SUCCESS on success or an error code otherwise. + */ + VkResult handle_swapchain_present_modes_create_info(VkDevice device, + const VkSwapchainCreateInfoKHR *swapchain_create_info); }; } /* namespace wsi */ diff --git a/wsi/wayland/surface_properties.cpp b/wsi/wayland/surface_properties.cpp index f6fec8a..d705180 100644 --- a/wsi/wayland/surface_properties.cpp +++ b/wsi/wayland/surface_properties.cpp @@ -411,5 +411,10 @@ void surface_properties::get_surface_present_scaling_and_gravity( scaling_capabilities->supportedPresentGravityY = VK_PRESENT_GRAVITY_MIN_BIT_EXT; } +bool surface_properties::is_compatible_present_modes(VkPresentModeKHR present_mode_a, VkPresentModeKHR present_mode_b) +{ + return is_compatible_present_modes_common(present_mode_a, present_mode_b, present_mode_compatibilities); +} + } // namespace wayland } // namespace wsi diff --git a/wsi/wayland/surface_properties.hpp b/wsi/wayland/surface_properties.hpp index 27197e2..fa0f977 100644 --- a/wsi/wayland/surface_properties.hpp +++ b/wsi/wayland/surface_properties.hpp @@ -71,6 +71,8 @@ public: bool is_surface_extension_enabled(const layer::instance_private_data &instance_data) override; + bool is_compatible_present_modes(VkPresentModeKHR present_mode_a, VkPresentModeKHR present_mode_b) override; + private: surface_properties();