mirror of
https://gitlab.freedesktop.org/mesa/vulkan-wsi-layer.git
synced 2025-12-20 09:10:07 +01:00
Merge 'Implement monotonic stage timestamps on headless' into 'main'
See merge request mesa/vulkan-wsi-layer!176
This commit is contained in:
commit
e34e3775d6
6 changed files with 138 additions and 57 deletions
|
|
@ -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<uint64_t>(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<bool> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,36 +66,29 @@ struct swapchain_presentation_timing
|
|||
/**
|
||||
* Timestamp for this entry.
|
||||
*/
|
||||
std::atomic<uint64_t> 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<bool> 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 <typename T>
|
||||
template <typename T, typename... arg_types>
|
||||
static util::unique_ptr<T> create(const util::allocator &allocator,
|
||||
util::unique_ptr<wsi::vulkan_time_domain> *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<T>(allocator, device, num_images);
|
||||
auto present_timing = allocator.make_unique<T>(allocator, device, num_images, std::forward<arg_types>(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<std::mutex> 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<VkTimeDomainEXT> 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_headless> 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<VkTimeDomainEXT> 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_headless> wsi_ext_present_timing_headles
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (monotonic_time_domain_supported)
|
||||
if (monotonic_domain)
|
||||
{
|
||||
if (!domains.try_push_back(allocator.make_unique<wsi::vulkan_time_domain>(VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT,
|
||||
monotonic_domain_to_use)))
|
||||
if (!domains.try_push_back(
|
||||
allocator.make_unique<wsi::vulkan_time_domain>(VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT, *monotonic_domain)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (!domains.try_push_back(allocator.make_unique<wsi::vulkan_time_domain>(
|
||||
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<wsi::vulkan_time_domain>(
|
||||
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<wsi_ext_present_timing_headless>(allocator, domains.data(), domains.size(),
|
||||
device, num_images);
|
||||
device, num_images, monotonic_domain);
|
||||
}
|
||||
|
||||
VkResult wsi_ext_present_timing_headless::get_swapchain_timing_properties(
|
||||
|
|
|
|||
|
|
@ -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<VkTimeDomainEXT> 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<VkTimeDomainEXT> monotonic_domain);
|
||||
|
||||
/* Allow util::allocator to access the private constructor */
|
||||
friend util::allocator;
|
||||
|
||||
/* Monotonic time domain supported by the driver */
|
||||
std::optional<VkTimeDomainEXT> m_monotonic_domain;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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<wsi_ext_present_id>(true);
|
||||
ext->mark_delivered(pending_present.present_id);
|
||||
auto *ext_present_id = get_swapchain_extension<wsi_ext_present_id>(true);
|
||||
ext_present_id->mark_delivered(pending_present.present_id);
|
||||
}
|
||||
|
||||
#if VULKAN_WSI_LAYER_EXPERIMENTAL
|
||||
auto *ext_present_timing = get_swapchain_extension<wsi_ext_present_timing_headless>(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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue