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();