From 441d95f1bb22a906d25b62274b76df3e3c7da22f Mon Sep 17 00:00:00 2001 From: Alex Bates Date: Wed, 18 Jun 2025 12:15:41 +0000 Subject: [PATCH] Adds support for present timing on Wayland even if the compositor does not support wp_presentation. Previously, support for wp_presentation was assumed. If the time domain used by the compositor matches one of the time domains supported by the ICD: * Advertise `VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT` in `VkPresentTimingSurfaceCapabilitiesEXT` * List `VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT` in `swapchain_time_domains` Also handles the case where neither `VK_TIME_DOMAIN_CLOCK_MONOTONIC_KHR` or `VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_KHR` is supported for all backends. Additionally fixes the issue where Wayland protocol was not dispatched correctly which resulted in Clock_ID event not being communicated back to the layer. Signed-off-by: Alex Bates Signed-off-by: Dennis Tsiang Signed-off-by: Normunds Rieksts --- layer/surface_api.cpp | 7 +- wsi/display/surface_properties.cpp | 6 +- wsi/display/surface_properties.hpp | 5 +- wsi/extensions/present_timing.cpp | 29 ++++++ wsi/extensions/present_timing.hpp | 27 ++++-- wsi/headless/present_timing_handler.cpp | 112 ++++++++++-------------- wsi/headless/surface_properties.cpp | 34 +++++-- wsi/headless/surface_properties.hpp | 3 +- wsi/surface_properties.hpp | 3 +- wsi/wayland/present_timing_handler.cpp | 43 ++++++--- wsi/wayland/present_timing_handler.hpp | 9 +- wsi/wayland/surface.cpp | 111 ++++++++++++----------- wsi/wayland/surface.hpp | 4 +- wsi/wayland/surface_properties.cpp | 45 +++++++++- wsi/wayland/surface_properties.hpp | 3 +- wsi/wayland/swapchain.cpp | 24 ++--- 16 files changed, 290 insertions(+), 175 deletions(-) diff --git a/layer/surface_api.cpp b/layer/surface_api.cpp index c9793f4..f86e541 100644 --- a/layer/surface_api.cpp +++ b/layer/surface_api.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, 2019, 2021-2022, 2024 Arm Limited. + * Copyright (c) 2016-2017, 2019, 2021-2022, 2024-2025 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -23,7 +23,10 @@ */ #include + #include +#include + #include "private_data.hpp" #include "surface_api.hpp" @@ -68,7 +71,7 @@ wsi_layer_vkGetPhysicalDeviceSurfaceCapabilities2KHR(VkPhysicalDevice physicalDe VK_STRUCTURE_TYPE_PRESENT_TIMING_SURFACE_CAPABILITIES_EXT, pSurfaceCapabilities); if (surf_caps_ext != nullptr) { - props->get_present_timing_surface_caps(surf_caps_ext); + TRY_LOG_CALL(props->get_present_timing_surface_caps(physicalDevice, surf_caps_ext)); } #endif diff --git a/wsi/display/surface_properties.cpp b/wsi/display/surface_properties.cpp index f0fb266..9582e24 100644 --- a/wsi/display/surface_properties.cpp +++ b/wsi/display/surface_properties.cpp @@ -532,14 +532,16 @@ bool surface_properties::is_surface_extension_enabled(const layer::instance_priv } #if VULKAN_WSI_LAYER_EXPERIMENTAL -void surface_properties::get_present_timing_surface_caps( - VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) +VkResult surface_properties::get_present_timing_surface_caps( + VkPhysicalDevice, VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) { present_timing_surface_caps->presentTimingSupported = VK_FALSE; present_timing_surface_caps->presentAtAbsoluteTimeSupported = VK_FALSE; present_timing_surface_caps->presentAtRelativeTimeSupported = VK_FALSE; present_timing_surface_caps->presentStageQueries = 0; present_timing_surface_caps->presentStageTargets = 0; + + return VK_SUCCESS; } #endif diff --git a/wsi/display/surface_properties.hpp b/wsi/display/surface_properties.hpp index 52651a1..0ec31b4 100644 --- a/wsi/display/surface_properties.hpp +++ b/wsi/display/surface_properties.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Arm Limited. + * Copyright (c) 2024-2025 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -67,7 +67,8 @@ public: bool is_surface_extension_enabled(const layer::instance_private_data &instance_data) override; #if VULKAN_WSI_LAYER_EXPERIMENTAL - void get_present_timing_surface_caps(VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) override; + VkResult get_present_timing_surface_caps( + VkPhysicalDevice physical_device, VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) override; #endif static surface_properties &get_instance(); diff --git a/wsi/extensions/present_timing.cpp b/wsi/extensions/present_timing.cpp index e4e3f13..2d98df0 100644 --- a/wsi/extensions/present_timing.cpp +++ b/wsi/extensions/present_timing.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "present_timing.hpp" @@ -552,6 +553,34 @@ bool swapchain_time_domains::add_time_domain(util::unique_ptr *domains, + size_t domain_size) +{ + auto &instance_data = layer::instance_private_data::get(physical_device); + + uint32_t supported_domains_count = 0; + TRY(instance_data.disp.GetPhysicalDeviceCalibrateableTimeDomainsKHR(physical_device, &supported_domains_count, + nullptr)); + + util::allocator allocator(instance_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + util::vector supported_domains(allocator); + if (!supported_domains.try_resize(supported_domains_count)) + { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + + TRY(instance_data.disp.GetPhysicalDeviceCalibrateableTimeDomainsKHR(physical_device, &supported_domains_count, + supported_domains.data())); + for (size_t i = 0; i < domain_size; i++) + { + std::get<1>(domains[i]) = + std::find(supported_domains.begin(), supported_domains.begin() + supported_domains_count, + std::get<0>(domains[i])) != (supported_domains.begin() + supported_domains_count); + } + + return VK_SUCCESS; +} + } /* namespace wsi */ #endif /* VULKAN_WSI_LAYER_EXPERIMENTAL */ diff --git a/wsi/extensions/present_timing.hpp b/wsi/extensions/present_timing.hpp index 365286f..e368906 100644 --- a/wsi/extensions/present_timing.hpp +++ b/wsi/extensions/present_timing.hpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -254,7 +255,7 @@ public: */ bool add_time_domain(util::unique_ptr time_domain); - /** + /* * @brief The calibrate returns a Vulkan time domain + an offset * * @param present_stage The present stage to calibrate @@ -292,15 +293,15 @@ public: */ WSI_DEFINE_EXTENSION(VK_EXT_PRESENT_TIMING_EXTENSION_NAME); - template + template static util::unique_ptr create(const util::allocator &allocator, - std::array, N> &domains, VkDevice device, - uint32_t num_images) + util::unique_ptr *domains, size_t domain_count, + VkDevice device, uint32_t num_images) { auto present_timing = allocator.make_unique(allocator, device, num_images); - for (auto &domain : domains) + for (size_t i = 0; i < domain_count; i++) { - if (!present_timing->get_swapchain_time_domains().add_time_domain(std::move(domain))) + if (!present_timing->get_swapchain_time_domains().add_time_domain(std::move(domains[i]))) { WSI_LOG_ERROR("Failed to add a time domain."); return nullptr; @@ -495,5 +496,17 @@ private: uint32_t get_num_available_results(); }; +/** + * @brief Check if any of the time domains are supported + * + * @param physical_device Physical device used for the query + * @param domains Array of time domains. The boolean will be modified to indicate + * whether the domain is supported. + * @param domain_size Size of the @p domains array + * @return Vulkan result code + */ +VkResult check_time_domain_support(VkPhysicalDevice physical_device, std::tuple *domains, + size_t domain_size); + } /* namespace wsi */ -#endif \ No newline at end of file +#endif diff --git a/wsi/headless/present_timing_handler.cpp b/wsi/headless/present_timing_handler.cpp index 3152293..1d359e0 100644 --- a/wsi/headless/present_timing_handler.cpp +++ b/wsi/headless/present_timing_handler.cpp @@ -30,7 +30,6 @@ #if VULKAN_WSI_LAYER_EXPERIMENTAL #include #include -#include #include #include "present_timing_handler.hpp" #include "layer/private_data.hpp" @@ -40,84 +39,68 @@ wsi_ext_present_timing_headless::wsi_ext_present_timing_headless(const util::all : wsi::wsi_ext_present_timing(allocator, device, num_images) { } -/** - * @brief Queries whether the driver supports the raw monotonic clock domain. - * - * This function invokes vkGetPhysicalDeviceCalibrateableTimeDomainsKHR twice: - * 1. To query the count of supported time domains. - * 2. To retrieve the list of supported time domains. - * - * @param device The Vulkan logical device whose physical device is queried. Must be valid. - * @return A std::optional with: - * - true if VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_KHR is supported. - * - false if VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_KHR is not supported. - * - std::nullopt if the query fails (e.g., vkGetPhysicalDeviceCalibrateableTimeDomainsKHR - * returns an error or memory allocation fails). - */ -static std::optional is_time_domain_clock_monotonic_raw_supported(const VkDevice &device) -{ - auto &dev_data = layer::device_private_data::get(device); - auto &physicalDevice = dev_data.physical_device; - auto &instance = dev_data.instance_data; - - uint32_t supported_domains_count = 0; - VkResult result = - instance.disp.GetPhysicalDeviceCalibrateableTimeDomainsKHR(physicalDevice, &supported_domains_count, nullptr); - if (result != VK_SUCCESS) - { - return std::nullopt; - } - - util::allocator allocator(instance.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); - util::vector supported_domains(allocator); - if (!supported_domains.try_resize(supported_domains_count)) - { - return std::nullopt; - } - - result = instance.disp.GetPhysicalDeviceCalibrateableTimeDomainsKHR(physicalDevice, &supported_domains_count, - supported_domains.data()); - if (result != VK_SUCCESS) - { - return std::nullopt; - } - - bool supported = std::find(supported_domains.begin(), supported_domains.end(), - VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_KHR) != supported_domains.end(); - return supported; -} util::unique_ptr wsi_ext_present_timing_headless::create( const VkDevice &device, const util::allocator &allocator, uint32_t num_images) { + + auto &dev_data = layer::device_private_data::get(device); + /* * Select the hardware raw monotonic clock domain (unaffected by NTP or adjtime adjustments) * when the driver supports it; otherwise use the standard monotonic clock. */ - VkTimeDomainKHR monotonic_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_KHR; - - auto clock_monotonic_raw_support = is_time_domain_clock_monotonic_raw_supported(device); - if (!clock_monotonic_raw_support.has_value()) + std::array monotonic_domains = { + std::tuple{ VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT, false }, + std::tuple{ VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT, false }, + }; + auto result = + wsi::check_time_domain_support(dev_data.physical_device, monotonic_domains.data(), monotonic_domains.size()); + if (result != VK_SUCCESS) { return nullptr; } - else if (clock_monotonic_raw_support.value() == false) + + VkTimeDomainEXT monotonic_domain_to_use = VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT; + bool monotonic_time_domain_supported = false; + for (auto [domain, supported] : monotonic_domains) { - monotonic_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_KHR; + monotonic_domain_to_use = domain; + if (supported) + { + monotonic_time_domain_supported = true; + break; + } } - std::array, 4> time_domains_array = { - allocator.make_unique(VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT, - VK_TIME_DOMAIN_DEVICE_KHR), - allocator.make_unique(VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT, monotonic_time_domain), - allocator.make_unique(VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT, - monotonic_time_domain), - allocator.make_unique(VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT, - monotonic_time_domain) - }; + util::vector> domains(allocator); + if (!domains.try_push_back(allocator.make_unique( + VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT, VK_TIME_DOMAIN_DEVICE_KHR))) + { + return nullptr; + } - return wsi_ext_present_timing::create(allocator, time_domains_array, device, - num_images); + if (monotonic_time_domain_supported) + { + if (!domains.try_push_back(allocator.make_unique(VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT, + monotonic_domain_to_use))) + { + return nullptr; + } + if (!domains.try_push_back(allocator.make_unique( + VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT, monotonic_domain_to_use))) + { + return nullptr; + } + if (!domains.try_push_back(allocator.make_unique( + VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT, monotonic_domain_to_use))) + { + return nullptr; + } + } + + return wsi_ext_present_timing::create(allocator, domains.data(), domains.size(), + device, num_images); } VkResult wsi_ext_present_timing_headless::get_swapchain_timing_properties( @@ -137,4 +120,5 @@ VkResult wsi_ext_present_timing_headless::get_swapchain_timing_properties( return VK_SUCCESS; } + #endif diff --git a/wsi/headless/surface_properties.cpp b/wsi/headless/surface_properties.cpp index d5bbf1d..1cb0198 100644 --- a/wsi/headless/surface_properties.cpp +++ b/wsi/headless/surface_properties.cpp @@ -39,6 +39,10 @@ #include "surface.hpp" #include "util/macros.hpp" +#if VULKAN_WSI_LAYER_EXPERIMENTAL +#include "present_timing_handler.hpp" +#endif + namespace wsi { namespace headless @@ -216,18 +220,34 @@ bool surface_properties::is_compatible_present_modes(VkPresentModeKHR present_mo } #if VULKAN_WSI_LAYER_EXPERIMENTAL -void surface_properties::get_present_timing_surface_caps( - VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) +VkResult surface_properties::get_present_timing_surface_caps( + VkPhysicalDevice physical_device, VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) { present_timing_surface_caps->presentTimingSupported = VK_TRUE; present_timing_surface_caps->presentAtAbsoluteTimeSupported = VK_TRUE; present_timing_surface_caps->presentAtRelativeTimeSupported = VK_TRUE; + + VkPresentStageFlagsEXT monotonic_present_stages_supported = 0; + std::array monotonic_domains = { + std::tuple{ VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT, false }, + std::tuple{ VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT, false }, + }; + TRY(wsi::check_time_domain_support(physical_device, monotonic_domains.data(), monotonic_domains.size())); + + auto it_monotonic_supported = std::find_if(monotonic_domains.begin(), monotonic_domains.end(), + [](auto &domain) { return std::get<1>(domain); }); + if (it_monotonic_supported != monotonic_domains.end()) + { + monotonic_present_stages_supported |= VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT | + VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT | + VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT; + } + present_timing_surface_caps->presentStageQueries = - VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT | VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT | - VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT | VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT; - present_timing_surface_caps->presentStageTargets = VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT | - VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT | - VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT; + VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT | monotonic_present_stages_supported; + present_timing_surface_caps->presentStageTargets = monotonic_present_stages_supported; + + return VK_SUCCESS; } #endif diff --git a/wsi/headless/surface_properties.hpp b/wsi/headless/surface_properties.hpp index 1e5470d..35a2cc4 100644 --- a/wsi/headless/surface_properties.hpp +++ b/wsi/headless/surface_properties.hpp @@ -65,7 +65,8 @@ public: bool is_compatible_present_modes(VkPresentModeKHR present_mode_a, VkPresentModeKHR present_mode_b) override; #if VULKAN_WSI_LAYER_EXPERIMENTAL - void get_present_timing_surface_caps(VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) override; + VkResult get_present_timing_surface_caps( + VkPhysicalDevice physical_device, VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) override; #endif private: diff --git a/wsi/surface_properties.hpp b/wsi/surface_properties.hpp index c9823d9..59301ca 100644 --- a/wsi/surface_properties.hpp +++ b/wsi/surface_properties.hpp @@ -117,7 +117,8 @@ public: /** * @brief Get the present timing surface capabilities for the specific VkSurface type. */ - virtual void get_present_timing_surface_caps(VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) = 0; + virtual VkResult get_present_timing_surface_caps( + VkPhysicalDevice physical_device, VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) = 0; #endif private: diff --git a/wsi/wayland/present_timing_handler.cpp b/wsi/wayland/present_timing_handler.cpp index 7058c9d..33a4405 100644 --- a/wsi/wayland/present_timing_handler.cpp +++ b/wsi/wayland/present_timing_handler.cpp @@ -29,7 +29,6 @@ */ #include "present_timing_handler.hpp" -#include wsi_ext_present_timing_wayland::wsi_ext_present_timing_wayland(const util::allocator &allocator, VkDevice device, uint32_t num_images) @@ -38,18 +37,40 @@ wsi_ext_present_timing_wayland::wsi_ext_present_timing_wayland(const util::alloc } util::unique_ptr wsi_ext_present_timing_wayland::create( - VkTimeDomainKHR image_first_pixel_visible_time_domain, const util::allocator &allocator, VkDevice device, - uint32_t num_images) + VkDevice device, const util::allocator &allocator, + std::optional image_first_pixel_visible_time_domain, uint32_t num_images) { - std::array, 2> time_domains_array = { - allocator.make_unique(VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT, - VK_TIME_DOMAIN_DEVICE_KHR), - allocator.make_unique(VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT, - image_first_pixel_visible_time_domain) - }; - return wsi_ext_present_timing::create(allocator, time_domains_array, device, - num_images); + util::vector> domains(allocator); + if (!domains.try_push_back(allocator.make_unique( + VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT, VK_TIME_DOMAIN_DEVICE_KHR))) + { + return nullptr; + } + + if (image_first_pixel_visible_time_domain.has_value()) + { + std::tuple monotonic_query = { *image_first_pixel_visible_time_domain, false }; + + const layer::device_private_data &device_data = layer::device_private_data::get(device); + auto result = wsi::check_time_domain_support(device_data.physical_device, &monotonic_query, 1); + if (result != VK_SUCCESS) + { + return nullptr; + } + + if (std::get<1>(monotonic_query)) + { + if (!domains.try_push_back(allocator.make_unique( + VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT, image_first_pixel_visible_time_domain.value()))) + { + return nullptr; + } + } + } + + return wsi_ext_present_timing::create(allocator, domains.data(), domains.size(), + device, num_images); } VkResult wsi_ext_present_timing_wayland::get_swapchain_timing_properties( diff --git a/wsi/wayland/present_timing_handler.hpp b/wsi/wayland/present_timing_handler.hpp index 02c1808..f03bd91 100644 --- a/wsi/wayland/present_timing_handler.hpp +++ b/wsi/wayland/present_timing_handler.hpp @@ -32,6 +32,7 @@ #if VULKAN_WSI_LAYER_EXPERIMENTAL #include +#include /** * @brief Present timing extension class @@ -41,9 +42,9 @@ class wsi_ext_present_timing_wayland : public wsi::wsi_ext_present_timing { public: - static util::unique_ptr create(VkTimeDomainKHR image_first_pixel_visible_time_domain, - const util::allocator &allocator, VkDevice device, - uint32_t num_images); + static util::unique_ptr create( + VkDevice device, const util::allocator &allocator, + std::optional image_first_pixel_visible_time_domain, uint32_t num_images); VkResult get_swapchain_timing_properties(uint64_t &timing_properties_counter, VkSwapchainTimingPropertiesEXT &timing_properties) override; @@ -55,4 +56,4 @@ private: friend util::allocator; }; -#endif \ No newline at end of file +#endif diff --git a/wsi/wayland/surface.cpp b/wsi/wayland/surface.cpp index 6af7ab4..a9f44c3 100644 --- a/wsi/wayland/surface.cpp +++ b/wsi/wayland/surface.cpp @@ -40,6 +40,11 @@ namespace wayland struct formats_vector { + formats_vector(util::vector *format_list) + : formats(format_list) + { + } + util::vector *formats{ nullptr }; bool is_out_of_memory{ false }; }; @@ -87,66 +92,55 @@ wp_presentation_clock_id_impl(void *data, struct wp_presentation *wp_presentatio } // namespace /** - * @brief Get supported formats and modifiers using the zwp_linux_dmabuf_v1 interface. + * @brief Listener for the zwp_linux_dmabuf_v1 interface + */ +const zwp_linux_dmabuf_v1_listener dma_buf_listener = { + .format = zwp_linux_dmabuf_v1_format_impl, + .modifier = zwp_linux_dmabuf_v1_modifier_impl, +}; + +/** + * @brief Register listener for the zwp_linux_dmabuf_v1 interface to query + * supported formats and modifiers. * - * @param[in] display The wl_display that is being used. - * @param[in] queue The wl_event_queue set for the @p dmabuf_interface - * @param[in] dmabuf_interface Object of the zwp_linux_dmabuf_v1 interface. - * @param[out] supported_formats Vector which will contain the supported drm - * formats and their modifiers. + * @param[in] dmabuf_interface Object of the zwp_linux_dmabuf_v1 interface. + * @param[out] drm_supported_format_query Vector which will be filled with the supported drm + * formats and their modifiers. * * @retval VK_SUCCESS Indicates success. * @retval VK_ERROR_UNKNOWN Indicates one of the Wayland functions failed. - * @retval VK_ERROR_OUT_OF_DEVICE_MEMORY Indicates the host went out of memory. */ -static VkResult get_supported_formats_and_modifiers(wl_display *display, wl_event_queue *queue, - zwp_linux_dmabuf_v1 *dmabuf_interface, - util::vector &supported_formats) +static VkResult register_supported_format_and_modifier_listener(zwp_linux_dmabuf_v1 *dmabuf_interface, + formats_vector *drm_supported_format_query) { - formats_vector drm_supported_formats; - drm_supported_formats.formats = &supported_formats; - - const zwp_linux_dmabuf_v1_listener dma_buf_listener = { - .format = zwp_linux_dmabuf_v1_format_impl, - .modifier = zwp_linux_dmabuf_v1_modifier_impl, - }; - int res = zwp_linux_dmabuf_v1_add_listener(dmabuf_interface, &dma_buf_listener, &drm_supported_formats); + int res = zwp_linux_dmabuf_v1_add_listener(dmabuf_interface, &dma_buf_listener, drm_supported_format_query); if (res < 0) { WSI_LOG_ERROR("Failed to add zwp_linux_dmabuf_v1 listener."); return VK_ERROR_UNKNOWN; } - /* Get all modifier events. */ - res = wl_display_roundtrip_queue(display, queue); - if (res < 0) - { - WSI_LOG_ERROR("Roundtrip failed."); - return VK_ERROR_UNKNOWN; - } - - if (drm_supported_formats.is_out_of_memory) - { - WSI_LOG_ERROR("Host got out of memory."); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - return VK_SUCCESS; } /** - * @brief Set the clock_id using the wp_presentation interface + * @brief Listener for the wp_presentation interface + */ +const wp_presentation_listener presentation_listener = { + .clock_id = wp_presentation_clock_id_impl, +}; + +/** + * @brief Register listener for clock_id event for wp_presentation interface + * + * @param presentation_interface wp_presentation interface + * @param clockid Clock ID pointer that will get the assigned clock_id from the event * * @retval VK_SUCCESS Indicates success. * @retval VK_ERROR_UNKNOWN Indicates one of the Wayland functions failed. */ -static VkResult get_clock_id(wl_display *display, wl_event_queue *queue, wp_presentation *presentation_interface, - clockid_t *clockid) +static VkResult register_clock_id_listener(wp_presentation *presentation_interface, clockid_t *clockid) { - const wp_presentation_listener presentation_listener = { - .clock_id = wp_presentation_clock_id_impl, - }; - int res = wp_presentation_add_listener(presentation_interface, &presentation_listener, clockid); if (res < 0) { @@ -154,12 +148,6 @@ static VkResult get_clock_id(wl_display *display, wl_event_queue *queue, wp_pres return VK_ERROR_UNKNOWN; } - res = wl_display_roundtrip_queue(display, queue); - if (res < 0) - { - WSI_LOG_ERROR("Roundtrip failed."); - return VK_ERROR_UNKNOWN; - } return VK_SUCCESS; } @@ -175,7 +163,7 @@ surface::surface(const init_parameters ¶ms) , wayland_display(params.display) , surface_queue(nullptr) , wayland_surface(params.surf) - , supported_formats(params.allocator) + , m_supported_formats(params.allocator) , properties(this, params.allocator) , last_frame_callback(nullptr) , present_pending(false) @@ -280,12 +268,6 @@ bool surface::init() return false; } - if (presentation_time_interface.get() == nullptr) - { - WSI_LOG_ERROR("Failed to obtain wp_presentation interface."); - return false; - } - auto surface_sync_obj = zwp_linux_explicit_synchronization_v1_get_synchronization(explicit_sync_interface.get(), wayland_surface); if (surface_sync_obj == nullptr) @@ -296,16 +278,33 @@ bool surface::init() surface_sync_interface.reset(surface_sync_obj); - VkResult vk_res = get_supported_formats_and_modifiers(wayland_display, surface_queue.get(), dmabuf_interface.get(), - supported_formats); + VkResult vk_res = VK_SUCCESS; + if (presentation_time_interface.get() != nullptr) + { + vk_res = register_clock_id_listener(presentation_time_interface.get(), &m_clockid); + if (vk_res != VK_SUCCESS) + { + return false; + } + } + + formats_vector drm_supported_formats(&m_supported_formats); + vk_res = register_supported_format_and_modifier_listener(dmabuf_interface.get(), &drm_supported_formats); if (vk_res != VK_SUCCESS) { return false; } - vk_res = get_clock_id(wayland_display, surface_queue.get(), presentation_time_interface.get(), &m_clockid); - if (vk_res != VK_SUCCESS) + res = wl_display_roundtrip_queue(wayland_display, surface_queue.get()); + if (res < 0) { + WSI_LOG_ERROR("Roundtrip failed."); + return false; + } + + if (drm_supported_formats.is_out_of_memory) + { + WSI_LOG_ERROR("Host got out of memory for DRM format query."); return false; } diff --git a/wsi/wayland/surface.hpp b/wsi/wayland/surface.hpp index 7d754a4..a882782 100644 --- a/wsi/wayland/surface.hpp +++ b/wsi/wayland/surface.hpp @@ -132,7 +132,7 @@ public: */ const util::vector &get_formats() const { - return supported_formats; + return m_supported_formats; } /** @@ -184,7 +184,7 @@ private: /** The native Wayland surface */ wl_surface *wayland_surface; /** A list of DRM formats supported by the Wayland compositor on this surface */ - util::vector supported_formats; + util::vector m_supported_formats; /** Surface properties specific to the Wayland surface. */ surface_properties properties; diff --git a/wsi/wayland/surface_properties.cpp b/wsi/wayland/surface_properties.cpp index 123883e..7c33960 100644 --- a/wsi/wayland/surface_properties.cpp +++ b/wsi/wayland/surface_properties.cpp @@ -46,6 +46,10 @@ #include "util/macros.hpp" #include "util/helpers.hpp" +#if VULKAN_WSI_LAYER_EXPERIMENTAL +#include "present_timing_handler.hpp" +#endif + namespace wsi { namespace wayland @@ -261,6 +265,7 @@ VkResult surface_properties::get_required_device_extensions(util::extension_list VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME, VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, + VK_KHR_CALIBRATED_TIMESTAMPS_EXTENSION_NAME, }; return extension_list.add(required_device_extensions.data(), required_device_extensions.size()); } @@ -419,15 +424,47 @@ bool surface_properties::is_compatible_present_modes(VkPresentModeKHR present_mo } #if VULKAN_WSI_LAYER_EXPERIMENTAL -void surface_properties::get_present_timing_surface_caps( - VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) +VkResult surface_properties::get_present_timing_surface_caps( + VkPhysicalDevice physical_device, VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) { present_timing_surface_caps->presentTimingSupported = VK_TRUE; present_timing_surface_caps->presentAtAbsoluteTimeSupported = VK_FALSE; present_timing_surface_caps->presentAtRelativeTimeSupported = VK_FALSE; - present_timing_surface_caps->presentStageQueries = - VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT | VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT; + present_timing_surface_caps->presentStageQueries = VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT; present_timing_surface_caps->presentStageTargets = 0; + + if (specific_surface->get_presentation_time_interface() != nullptr) + { + bool clock_domain_supported = true; + VkTimeDomainKHR image_first_pixel_visible_time_domain; + + /* Check if we can support any of the reported time domains */ + switch (specific_surface->clockid()) + { + case CLOCK_MONOTONIC: + image_first_pixel_visible_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_KHR; + break; + case CLOCK_MONOTONIC_RAW: + image_first_pixel_visible_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_KHR; + break; + default: + clock_domain_supported = false; + break; + } + + if (clock_domain_supported) + { + std::tuple monotonic_query = { image_first_pixel_visible_time_domain, false }; + TRY(wsi::check_time_domain_support(physical_device, &monotonic_query, 1)); + + if (std::get<1>(monotonic_query)) + { + present_timing_surface_caps->presentStageQueries |= VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT; + } + } + } + + return VK_SUCCESS; } #endif diff --git a/wsi/wayland/surface_properties.hpp b/wsi/wayland/surface_properties.hpp index f615cc1..1d604c6 100644 --- a/wsi/wayland/surface_properties.hpp +++ b/wsi/wayland/surface_properties.hpp @@ -76,7 +76,8 @@ public: bool is_compatible_present_modes(VkPresentModeKHR present_mode_a, VkPresentModeKHR present_mode_b) override; #if VULKAN_WSI_LAYER_EXPERIMENTAL - void get_present_timing_surface_caps(VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) override; + VkResult get_present_timing_surface_caps( + VkPhysicalDevice physical_device, VkPresentTimingSurfaceCapabilitiesEXT *present_timing_surface_caps) override; #endif private: surface_properties(); diff --git a/wsi/wayland/swapchain.cpp b/wsi/wayland/swapchain.cpp index d3fde86..709abf4 100644 --- a/wsi/wayland/swapchain.cpp +++ b/wsi/wayland/swapchain.cpp @@ -133,20 +133,22 @@ VkResult swapchain::add_required_extensions(VkDevice device, const VkSwapchainCr bool swapchain_support_enabled = swapchain_create_info->flags & VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT; if (swapchain_support_enabled) { - /* - * Default to a raw hardware-based time that is not subject to NTP adjustments or - * the incremental adjustments performed by adjtime(3) - */ - VkTimeDomainKHR image_first_pixel_visible_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_KHR; - - if (m_wsi_surface->clockid() == CLOCK_MONOTONIC) + std::optional image_first_pixel_visible_time_domain; + if (m_wsi_surface->get_presentation_time_interface() != nullptr) { - image_first_pixel_visible_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_KHR; + switch (m_wsi_surface->clockid()) + { + case CLOCK_MONOTONIC: + image_first_pixel_visible_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_KHR; + break; + case CLOCK_MONOTONIC_RAW: + image_first_pixel_visible_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_KHR; + break; + } } - if (!add_swapchain_extension( - wsi_ext_present_timing_wayland::create(image_first_pixel_visible_time_domain, m_allocator, m_device, - swapchain_create_info->minImageCount))) + if (!add_swapchain_extension(wsi_ext_present_timing_wayland::create( + m_device, m_allocator, image_first_pixel_visible_time_domain, swapchain_create_info->minImageCount))) { return VK_ERROR_OUT_OF_HOST_MEMORY; }