Update time domain handling to comply with Vulkan spec

- Refactor vkGetSwapchainTimeDomainPropertiesEXT to follow spec
- Ensure all time values are returned in nanoseconds
  according to the active time domain

Signed-off-by: Maged Elnaggar <maged.elnaggar@arm.com>
Change-Id: Id4c6b86577bd509ec7bb5a946785dbe6642cfb1c
This commit is contained in:
Maged Elnaggar 2025-07-11 10:13:34 +00:00 committed by Iason Paraskevopoulos
parent 9296f8032e
commit 3ffd85a26d
4 changed files with 51 additions and 26 deletions

View file

@ -530,14 +530,16 @@ wsi_layer_vkGetPhysicalDeviceFeatures2(VkPhysicalDevice physical_device,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_TIMING_FEATURES_EXT, pFeatures->pNext);
if (present_timing_features != nullptr)
{
VkPhysicalDeviceProperties physical_device_properties = {};
instance.disp.GetPhysicalDeviceProperties(physical_device, &physical_device_properties);
VkPhysicalDeviceProperties2KHR physical_device_properties{};
physical_device_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
instance.disp.GetPhysicalDeviceProperties2KHR(physical_device, &physical_device_properties);
/* The presentTimingSupported is set based on whether the device can support timestamp queries
* and the graphics, compute pipelines can support time stamps. Only the graphics and compute pipelines
* are checked here which means queue present if happens on a different queue family,
* the time stamps might not be supported. */
present_timing_features->presentTiming = ((physical_device_properties.limits.timestampPeriod != 0) &&
physical_device_properties.limits.timestampComputeAndGraphics);
present_timing_features->presentTiming =
((physical_device_properties.properties.limits.timestampPeriod != 0) &&
physical_device_properties.properties.limits.timestampComputeAndGraphics);
present_timing_features->presentAtAbsoluteTime = VK_TRUE;
present_timing_features->presentAtRelativeTime = VK_TRUE;
}

View file

@ -29,6 +29,7 @@
*/
#include <array>
#include <cassert>
#include <cmath>
#include <wsi/swapchain_base.hpp>
#include <util/helpers.hpp>
@ -59,7 +60,14 @@ wsi_ext_present_timing::wsi_ext_present_timing(const util::allocator &allocator,
, m_queue(allocator)
, m_num_images(num_images)
, m_present_semaphore(allocator)
, m_timestamp_period(0.f)
{
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);
auto &inst = layer::instance_private_data::get(dev.physical_device);
inst.disp.GetPhysicalDeviceProperties2KHR(dev.physical_device, &physical_device_properties);
m_timestamp_period = physical_device_properties.properties.limits.timestampPeriod;
}
wsi_ext_present_timing::~wsi_ext_present_timing()
@ -143,6 +151,15 @@ VkResult wsi_ext_present_timing::init_timing_resources()
return VK_SUCCESS;
}
static inline uint64_t ticks_to_ns(uint64_t ticks, const float &timestamp_period)
{
/* timestamp_period is float (ns per tick). Use double so we keep
52-bit integer precision (4.5×10¹ ticks) without overflow. */
assert(std::isfinite(timestamp_period) && timestamp_period > 0.0f);
double ns = static_cast<double>(ticks) * static_cast<double>(timestamp_period);
return static_cast<uint64_t>(std::llround(ns));
}
VkResult wsi_ext_present_timing::get_queue_end_timing_to_queue(uint32_t image_index)
{
for (auto &slot : m_queue)
@ -154,7 +171,7 @@ VkResult wsi_ext_present_timing::get_queue_end_timing_to_queue(uint32_t image_in
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(time);
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;
@ -505,7 +522,7 @@ std::optional<std::reference_wrapper<swapchain_presentation_timing>> swapchain_p
void swapchain_presentation_entry::populate(VkPastPresentationTimingEXT &timing)
{
uint64_t stage_index = 0;
uint32_t stage_index = 0;
for (const auto &stage : g_present_stages)
{
auto stage_timing_optional = get_stage_timing(stage);
@ -549,30 +566,30 @@ VkResult swapchain_time_domains::calibrate(VkPresentStageFlagBitsEXT present_sta
VkResult swapchain_time_domains::get_swapchain_time_domain_properties(
VkSwapchainTimeDomainPropertiesEXT *pSwapchainTimeDomainProperties, uint64_t *pTimeDomainsCounter)
{
/* Since we only have a single time domain available we don't need to check
* timeDomainCount since it can only be >= 1 */
constexpr uint32_t available_domains_count = 1;
if (pTimeDomainsCounter != nullptr)
{
*pTimeDomainsCounter = 1;
}
if (pSwapchainTimeDomainProperties != nullptr)
if (pSwapchainTimeDomainProperties->pTimeDomains == nullptr &&
pSwapchainTimeDomainProperties->pTimeDomainIds == nullptr)
{
if ((pSwapchainTimeDomainProperties->pTimeDomains == nullptr &&
pSwapchainTimeDomainProperties->pTimeDomainIds == nullptr) ||
pSwapchainTimeDomainProperties->timeDomainCount == 0)
{
pSwapchainTimeDomainProperties->timeDomainCount = 1;
}
else
{
/* Since we only have a single time domain available we don't need to check
* timeDomainCount since it can only be >= 1 */
pSwapchainTimeDomainProperties->timeDomainCount = 1;
pSwapchainTimeDomainProperties->pTimeDomains[0] = VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT;
pSwapchainTimeDomainProperties->pTimeDomainIds[0] = 0;
}
pSwapchainTimeDomainProperties->timeDomainCount = available_domains_count;
return VK_SUCCESS;
}
return VK_SUCCESS;
const uint32_t requested_domains_count = pSwapchainTimeDomainProperties->timeDomainCount;
const uint32_t domains_count_to_write = std::min(requested_domains_count, available_domains_count);
pSwapchainTimeDomainProperties->pTimeDomains[0] = VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT;
pSwapchainTimeDomainProperties->pTimeDomainIds[0] = 0;
pSwapchainTimeDomainProperties->timeDomainCount = domains_count_to_write;
return (domains_count_to_write < available_domains_count) ? VK_INCOMPLETE : VK_SUCCESS;
}
bool swapchain_time_domains::add_time_domain(util::unique_ptr<swapchain_time_domain> time_domain)

View file

@ -455,6 +455,11 @@ private:
*/
util::vector<VkSemaphore> m_present_semaphore;
/**
* @brief The timestamp period from the device properties.
*/
float m_timestamp_period;
/**
* @brief Perform a queue submission for getting the queue end timing.
*

View file

@ -104,11 +104,12 @@ void get_surface_capabilities_common(VkPhysicalDevice physical_device, VkSurface
surface_capabilities->currentExtent = { 0xffffffff, 0xffffffff };
surface_capabilities->minImageExtent = { 1, 1 };
/* Ask the device for max */
VkPhysicalDeviceProperties dev_props = {};
layer::instance_private_data::get(physical_device).disp.GetPhysicalDeviceProperties(physical_device, &dev_props);
VkPhysicalDeviceProperties2KHR dev_props{};
dev_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
layer::instance_private_data::get(physical_device).disp.GetPhysicalDeviceProperties2KHR(physical_device, &dev_props);
surface_capabilities->maxImageExtent = { dev_props.limits.maxImageDimension2D,
dev_props.limits.maxImageDimension2D };
surface_capabilities->maxImageExtent = { dev_props.properties.limits.maxImageDimension2D,
dev_props.properties.limits.maxImageDimension2D };
surface_capabilities->maxImageArrayLayers = 1;
/* Surface transforms */