diff --git a/wsi/extensions/present_timing.cpp b/wsi/extensions/present_timing.cpp index 08c9141..5d10af0 100644 --- a/wsi/extensions/present_timing.cpp +++ b/wsi/extensions/present_timing.cpp @@ -62,6 +62,12 @@ wsi_ext_present_timing::wsi_ext_present_timing(const util::allocator &allocator, , m_present_semaphore(allocator) , m_timestamp_period(0.f) { + if (layer::device_private_data::get(m_device).is_present_id_enabled()) + { + WSI_LOG_ERROR(VK_EXT_PRESENT_TIMING_EXTENSION_NAME + " enabled but required extension " VK_KHR_PRESENT_ID_EXTENSION_NAME " is not enabled."); + } + VkPhysicalDeviceProperties2KHR physical_device_properties{}; physical_device_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; const auto &dev = layer::device_private_data::get(m_device); @@ -160,22 +166,28 @@ static inline uint64_t ticks_to_ns(uint64_t ticks, const float ×tamp_period return static_cast(std::llround(ns)); } +swapchain_presentation_timing *wsi_ext_present_timing::get_pending_stage_timing(uint32_t image_index, + VkPresentStageFlagBitsEXT stage) +{ + for (auto &entry : m_queue) + { + if (entry.m_image_index == image_index && entry.is_pending(stage)) + { + return &entry.get_stage_timing(stage)->get(); + } + } + return nullptr; +} + VkResult wsi_ext_present_timing::get_queue_end_timing_to_queue(uint32_t image_index) { - for (auto &slot : m_queue) + if (auto timing = get_pending_stage_timing(image_index, VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT)) { - if ((slot.m_image_index == image_index) && slot.is_pending(VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT)) - { - uint64_t time; - auto stage_timing_optional = slot.get_stage_timing(VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT); - const layer::device_private_data &device_data = layer::device_private_data::get(m_device); - TRY(device_data.disp.GetQueryPoolResults(m_device, m_query_pool, image_index, 1, sizeof(time), &time, 0, - VK_QUERY_RESULT_64_BIT)); - stage_timing_optional->get().m_time.store(ticks_to_ns(time, m_timestamp_period)); - stage_timing_optional->get().m_set.store(true, std::memory_order_release); - /* For an image index, there can only be one entry in the internal queue with pending results. */ - break; - } + uint64_t time; + const layer::device_private_data &device_data = layer::device_private_data::get(m_device); + TRY(device_data.disp.GetQueryPoolResults(m_device, m_query_pool, image_index, 1, sizeof(time), &time, 0, + VK_QUERY_RESULT_64_BIT)); + timing->set_time(ticks_to_ns(time, m_timestamp_period)); } return VK_SUCCESS; } @@ -449,14 +461,13 @@ std::optional swapchain_presentation_entry::is_complete(VkPresentStageFlag { return std::nullopt; } - return stage_timing_optional->get().m_set.load(std::memory_order_relaxed); + return stage_timing_optional->get().m_set; } bool swapchain_presentation_entry::is_pending(VkPresentStageFlagBitsEXT stage) { auto stage_timing_optional = get_stage_timing(stage); - return stage_timing_optional.has_value() ? !stage_timing_optional->get().m_set.load(std::memory_order_relaxed) : - false; + return stage_timing_optional.has_value() ? !stage_timing_optional->get().m_set : false; } bool swapchain_presentation_entry::has_outstanding_stages() @@ -531,12 +542,11 @@ void swapchain_presentation_entry::populate(VkPastPresentationTimingEXT &timing) continue; } - if (stage_timing_optional->get().m_set.load(std::memory_order_acquire)) + if (stage_timing_optional->get().m_set) { timing.timeDomainId = stage_timing_optional->get().m_timedomain_id; timing.pPresentStages[stage_index].stage = stage; - timing.pPresentStages[stage_index++].time = - stage_timing_optional->get().m_time.load(std::memory_order_relaxed); + timing.pPresentStages[stage_index++].time = stage_timing_optional->get().m_time; } } diff --git a/wsi/extensions/present_timing.hpp b/wsi/extensions/present_timing.hpp index faa0790..526e985 100644 --- a/wsi/extensions/present_timing.hpp +++ b/wsi/extensions/present_timing.hpp @@ -66,36 +66,29 @@ struct swapchain_presentation_timing /** * Timestamp for this entry. */ - std::atomic m_time{}; + uint64_t m_time{}; /** * Needed to mark “logically complete” timings even for presentations that the WSI * implementation ultimately rejected (e.g. in MAILBOX the presentation engine * rejected the one present request) */ - std::atomic m_set{}; + bool m_set{}; swapchain_presentation_timing() { } - swapchain_presentation_timing(swapchain_presentation_timing &&rhs) noexcept - { - m_timedomain_id = rhs.m_timedomain_id; - m_time.store(rhs.m_time.load()); - m_set.store(rhs.m_set.load()); - } - - swapchain_presentation_timing &operator=(swapchain_presentation_timing &&rhs) noexcept - { - m_timedomain_id = rhs.m_timedomain_id; - m_time.store(rhs.m_time.load()); - m_set.store(rhs.m_set.load()); - return *this; - } - + swapchain_presentation_timing(swapchain_presentation_timing &&) noexcept = default; + swapchain_presentation_timing &operator=(swapchain_presentation_timing &&) noexcept = default; swapchain_presentation_timing(const swapchain_presentation_timing &) = delete; swapchain_presentation_timing &operator=(const swapchain_presentation_timing &) = delete; + + void set_time(uint64_t time) + { + m_time = time; + m_set = true; + } }; /** @@ -299,12 +292,12 @@ public: */ WSI_DEFINE_EXTENSION(VK_EXT_PRESENT_TIMING_EXTENSION_NAME); - template + template static util::unique_ptr create(const util::allocator &allocator, util::unique_ptr *domains, size_t domain_count, - VkDevice device, uint32_t num_images) + VkDevice device, uint32_t num_images, arg_types &&...args) { - auto present_timing = allocator.make_unique(allocator, device, num_images); + auto present_timing = allocator.make_unique(allocator, device, num_images, std::forward(args)...); for (size_t i = 0; i < domain_count; i++) { if (!present_timing->get_swapchain_time_domains().add_time_domain(std::move(domains[i]))) @@ -364,6 +357,22 @@ public: VkResult add_presentation_entry(const layer::device_private_data &device, VkQueue queue, uint64_t present_id, uint32_t image_index, VkPresentStageFlagsEXT present_stage_queries); + /** + * @brief Set the time for a stage, if it exists and is pending. + * + * @param image_index The index of the image in the present queue. + * @param stage The present stage to set the time for. + * @param time The time to set for the stage. + */ + void set_pending_stage_time(uint32_t image_index, VkPresentStageFlagBitsEXT stage, uint64_t time) + { + const std::lock_guard lock(m_queue_mutex); + if (auto timing = get_pending_stage_timing(image_index, stage)) + { + timing->set_time(time); + } + } + /** * @brief Get the image's present semaphore. * @@ -479,16 +488,33 @@ private: */ VkResult init_timing_resources(); + /** + * @pre Caller must hold m_queue_mutex for the call and lifetime of the returned pointer. + * + * @brief Search for a pending presentation entry and access its timing info. + * + * For an image index, there can only be one entry in the queue with pending stages. + * This does not take a present ID because zero is a valid, nonunique value and thus cannot uniquely identify an + * entry. + * + * @param image_index The index of the image in the present queue. + * @param stage The present stage to get the entry for. + * + * @return Pointer to timing information for the stage, or nullptr if it is not found or it is not pending. + */ + swapchain_presentation_timing *get_pending_stage_timing(uint32_t image_index, VkPresentStageFlagBitsEXT stage); + /** * @pre Caller must hold m_queue_mutex. * * @brief Get the queue end timings for an image. * * Gets the queue end timings for a swapchain image and stores it in the internal queue. + * If there is no pending entry for the image index, no-op. * * @param image_index The index of the image in the swapchain. * - * @return VK_SUCCESS if the query is successful and error if otherwise. + * @return VK_SUCCESS if the query is successful and error otherwise. VK_SUCCESS if no pending entry is found. */ VkResult get_queue_end_timing_to_queue(uint32_t image_index); diff --git a/wsi/headless/present_timing_handler.cpp b/wsi/headless/present_timing_handler.cpp index 1d359e0..9436f40 100644 --- a/wsi/headless/present_timing_handler.cpp +++ b/wsi/headless/present_timing_handler.cpp @@ -35,8 +35,10 @@ #include "layer/private_data.hpp" wsi_ext_present_timing_headless::wsi_ext_present_timing_headless(const util::allocator &allocator, VkDevice device, - uint32_t num_images) + uint32_t num_images, + std::optional monotonic_domain) : wsi::wsi_ext_present_timing(allocator, device, num_images) + , m_monotonic_domain(monotonic_domain) { } @@ -61,14 +63,12 @@ util::unique_ptr wsi_ext_present_timing_headles return nullptr; } - VkTimeDomainEXT monotonic_domain_to_use = VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT; - bool monotonic_time_domain_supported = false; + std::optional monotonic_domain; for (auto [domain, supported] : monotonic_domains) { - monotonic_domain_to_use = domain; if (supported) { - monotonic_time_domain_supported = true; + monotonic_domain = domain; break; } } @@ -80,27 +80,27 @@ util::unique_ptr wsi_ext_present_timing_headles return nullptr; } - if (monotonic_time_domain_supported) + if (monotonic_domain) { - if (!domains.try_push_back(allocator.make_unique(VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT, - monotonic_domain_to_use))) + if (!domains.try_push_back( + allocator.make_unique(VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT, *monotonic_domain))) { return nullptr; } if (!domains.try_push_back(allocator.make_unique( - VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT, monotonic_domain_to_use))) + VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT, *monotonic_domain))) { return nullptr; } if (!domains.try_push_back(allocator.make_unique( - VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT, monotonic_domain_to_use))) + VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT, *monotonic_domain))) { return nullptr; } } return wsi_ext_present_timing::create(allocator, domains.data(), domains.size(), - device, num_images); + device, num_images, monotonic_domain); } VkResult wsi_ext_present_timing_headless::get_swapchain_timing_properties( diff --git a/wsi/headless/present_timing_handler.hpp b/wsi/headless/present_timing_handler.hpp index e4773ef..e3ba907 100644 --- a/wsi/headless/present_timing_handler.hpp +++ b/wsi/headless/present_timing_handler.hpp @@ -48,11 +48,27 @@ public: VkResult get_swapchain_timing_properties(uint64_t &timing_properties_counter, VkSwapchainTimingPropertiesEXT &timing_properties) override; + /** + * @brief Get a monotonic time domain supported by the driver. + * + * If both MONOTONIC_RAW and MONOTONIC are supported, MONOTONIC_RAW is preferred. + * + * @return A supported monotonic time domain, or std::nullopt if no monotonic time domain is supported. + */ + std::optional get_monotonic_domain() const + { + return m_monotonic_domain; + } + private: - wsi_ext_present_timing_headless(const util::allocator &allocator, VkDevice device, uint32_t num_images); + wsi_ext_present_timing_headless(const util::allocator &allocator, VkDevice device, uint32_t num_images, + std::optional monotonic_domain); /* Allow util::allocator to access the private constructor */ friend util::allocator; + + /* Monotonic time domain supported by the driver */ + std::optional m_monotonic_domain; }; -#endif \ No newline at end of file +#endif diff --git a/wsi/headless/swapchain.cpp b/wsi/headless/swapchain.cpp index c250b9d..8ab5fcd 100644 --- a/wsi/headless/swapchain.cpp +++ b/wsi/headless/swapchain.cpp @@ -211,10 +211,38 @@ void swapchain::present_image(const pending_present_request &pending_present) { if (m_device_data.is_present_id_enabled()) { - auto *ext = get_swapchain_extension(true); - ext->mark_delivered(pending_present.present_id); + auto *ext_present_id = get_swapchain_extension(true); + ext_present_id->mark_delivered(pending_present.present_id); } +#if VULKAN_WSI_LAYER_EXPERIMENTAL + auto *ext_present_timing = get_swapchain_extension(false); + if (ext_present_timing && ext_present_timing->get_monotonic_domain().has_value()) + { + clockid_t clockid = ext_present_timing->get_monotonic_domain().value() == VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT ? + CLOCK_MONOTONIC : + CLOCK_MONOTONIC_RAW; + struct timespec now = {}; + if (clock_gettime(clockid, &now) != 0) + { + WSI_LOG_ERROR("Failed to get time of clock %d, error: %d (%s)", clockid, errno, strerror(errno)); + } + else + { + uint64_t time = now.tv_sec * 1e9 + now.tv_nsec; + VkPresentStageFlagBitsEXT stages[] = { + 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, + }; + for (auto stage : stages) + { + ext_present_timing->set_pending_stage_time(pending_present.image_index, stage, time); + } + } + } +#endif + unpresent_image(pending_present.image_index); } diff --git a/wsi/swapchain_base.cpp b/wsi/swapchain_base.cpp index ae58be8..fe4a8ff 100644 --- a/wsi/swapchain_base.cpp +++ b/wsi/swapchain_base.cpp @@ -669,8 +669,6 @@ VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *pr TRY(sync_queue_submit(m_device_data, queue, submit_info.present_fence, wait_semaphores)); } - TRY(notify_presentation_engine(submit_info.pending_present)); - #if VULKAN_WSI_LAYER_EXPERIMENTAL if (present_timing_info != nullptr) { @@ -680,6 +678,9 @@ VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *pr present_timing_info->presentStageQueries)); } #endif + + TRY(notify_presentation_engine(submit_info.pending_present)); + return VK_SUCCESS; }