Merge 'Fix and enhance Vulkan present timing and time domain handling' into 'main'

See merge request mesa/vulkan-wsi-layer!214
This commit is contained in:
Iason Paraskevopoulos 2025-11-25 10:12:34 +00:00
commit a0947a5ae8
13 changed files with 311 additions and 140 deletions

View file

@ -38,14 +38,15 @@ wsi_layer_vkGetCalibratedTimestampsKHR(VkDevice device, uint32_t timestampCount,
uint64_t *pMaxDeviation) VWL_API_POST uint64_t *pMaxDeviation) VWL_API_POST
{ {
auto &device_data = layer::device_private_data::get(device); auto &device_data = layer::device_private_data::get(device);
struct stage_local_index_and_offset struct stage_local_index_domain_offset
{ {
uint32_t index; uint32_t index;
uint64_t calibration_offset; uint64_t calibration_offset;
uint64_t domain;
}; };
util::vector<VkCalibratedTimestampInfoKHR> time_stamp_info{ util::allocator(device_data.get_allocator(), util::vector<VkCalibratedTimestampInfoKHR> time_stamp_info{ util::allocator(device_data.get_allocator(),
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT) }; VK_SYSTEM_ALLOCATION_SCOPE_OBJECT) };
util::vector<stage_local_index_and_offset> calibration_index_and_offset{ util::allocator( util::vector<stage_local_index_domain_offset> calibration_index_domain_offset{ util::allocator(
device_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT) }; device_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT) };
for (uint32_t i = 0; i < timestampCount; ++i) for (uint32_t i = 0; i < timestampCount; ++i)
@ -62,8 +63,12 @@ wsi_layer_vkGetCalibratedTimestampsKHR(VkDevice device, uint32_t timestampCount,
/* The layer is only handling for present stage local time domains, /* The layer is only handling for present stage local time domains,
every other time domains including swapchain local (VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT) every other time domains including swapchain local (VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT)
is not handled by the layer. */ is not handled by the layer. */
if ((ext != nullptr) && (pTimestampInfos[i].timeDomain == VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT)) if (pTimestampInfos[i].timeDomain == VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT)
{ {
/* If timeDomain is VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT or VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT,
* the pNext chain must include a VkSwapchainCalibratedTimestampInfoEXT structure.
*/
assert(ext != nullptr);
assert(ext->swapchain != VK_NULL_HANDLE); assert(ext->swapchain != VK_NULL_HANDLE);
if (!device_data.layer_owns_swapchain(ext->swapchain)) if (!device_data.layer_owns_swapchain(ext->swapchain))
{ {
@ -76,9 +81,10 @@ wsi_layer_vkGetCalibratedTimestampsKHR(VkDevice device, uint32_t timestampCount,
wsi::swapchain_calibrated_time calibrated_time; wsi::swapchain_calibrated_time calibrated_time;
TRY_LOG_CALL(present_timing_extension->get_swapchain_time_domains().calibrate( TRY_LOG_CALL(present_timing_extension->get_swapchain_time_domains().calibrate(
static_cast<VkPresentStageFlagBitsEXT>(ext->presentStage), &calibrated_time)); static_cast<VkPresentStageFlagBitsEXT>(ext->presentStage), &calibrated_time));
stage_local_index_and_offset index_and_offset = { i, calibrated_time.offset }; stage_local_index_domain_offset index_domain_offset = { i, calibrated_time.offset,
calibrated_time.time_domain };
if (!calibration_index_and_offset.try_push_back(index_and_offset)) if (!calibration_index_domain_offset.try_push_back(index_domain_offset))
{ {
return VK_ERROR_OUT_OF_HOST_MEMORY; return VK_ERROR_OUT_OF_HOST_MEMORY;
} }
@ -88,10 +94,21 @@ wsi_layer_vkGetCalibratedTimestampsKHR(VkDevice device, uint32_t timestampCount,
TRY_LOG_CALL(device_data.disp.GetCalibratedTimestampsKHR(device, timestampCount, &time_stamp_info[0], pTimestamps, TRY_LOG_CALL(device_data.disp.GetCalibratedTimestampsKHR(device, timestampCount, &time_stamp_info[0], pTimestamps,
pMaxDeviation)); pMaxDeviation));
/* Loop through the calibration_index_and_offset vector and update the timestamps that are stage local /* Loop through the calibration_index_domain_offset vector and update the timestamps that are stage local
with its respective offset. */ with its respective offset. */
for (const auto &iter : calibration_index_and_offset) for (const auto &iter : calibration_index_domain_offset)
{ {
/* For device domain, convert ticks to nanoseconds */
if (iter.domain == VK_TIME_DOMAIN_DEVICE_KHR)
{
VkPhysicalDeviceProperties2KHR physical_device_properties{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
nullptr,
{} };
device_data.instance_data.disp.GetPhysicalDeviceProperties2KHR(device_data.physical_device,
&physical_device_properties);
pTimestamps[iter.index] =
wsi::ticks_to_ns(pTimestamps[iter.index], physical_device_properties.properties.limits.timestampPeriod);
}
pTimestamps[iter.index] += iter.calibration_offset; pTimestamps[iter.index] += iter.calibration_offset;
} }
return VK_SUCCESS; return VK_SUCCESS;
@ -104,4 +121,56 @@ wsi_layer_vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount,
{ {
return wsi_layer_vkGetCalibratedTimestampsKHR(device, timestampCount, pTimestampInfos, pTimestamps, pMaxDeviation); return wsi_layer_vkGetCalibratedTimestampsKHR(device, timestampCount, pTimestampInfos, pTimestamps, pMaxDeviation);
} }
VWL_VKAPI_CALL(VkResult)
wsi_layer_vkGetPhysicalDeviceCalibrateableTimeDomainsKHR(VkPhysicalDevice physicalDevice, uint32_t *pTimeDomainCount,
VkTimeDomainKHR *pTimeDomains) VWL_API_POST
{
auto &instance_data = layer::instance_private_data::get(physicalDevice);
assert(pTimeDomainCount != nullptr);
VkSwapchainTimeDomainPropertiesEXT swapchain_time_domain_properties = {
VK_STRUCTURE_TYPE_SWAPCHAIN_TIME_DOMAIN_PROPERTIES_EXT, nullptr, 0, nullptr, nullptr
};
uint32_t requested_domain_count = *pTimeDomainCount;
TRY(instance_data.disp.GetPhysicalDeviceCalibrateableTimeDomainsKHR(physicalDevice, pTimeDomainCount, pTimeDomains));
auto present_timing_supported = wsi::present_timing_dependencies_supported(physicalDevice);
if (std::holds_alternative<VkResult>(present_timing_supported))
{
/* Return error code */
return std::get<VkResult>(present_timing_supported);
}
if (!std::get<bool>(present_timing_supported))
{
/* Present timing dependencies not supported */
return VK_SUCCESS;
}
if (pTimeDomains == nullptr)
{
TRY(
wsi::swapchain_time_domains::get_swapchain_time_domain_properties(&swapchain_time_domain_properties, nullptr));
*pTimeDomainCount += swapchain_time_domain_properties.timeDomainCount;
return VK_SUCCESS;
}
if (requested_domain_count == *pTimeDomainCount)
{
return VK_INCOMPLETE;
}
swapchain_time_domain_properties.pTimeDomains = &pTimeDomains[*pTimeDomainCount];
swapchain_time_domain_properties.timeDomainCount = requested_domain_count - *pTimeDomainCount;
VkResult result =
wsi::swapchain_time_domains::get_swapchain_time_domain_properties(&swapchain_time_domain_properties, nullptr);
if ((result == VK_SUCCESS) || (result == VK_INCOMPLETE))
{
*pTimeDomainCount += swapchain_time_domain_properties.timeDomainCount;
}
return result;
}
VWL_VKAPI_CALL(VkResult)
wsi_layer_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(VkPhysicalDevice physicalDevice, uint32_t *pTimeDomainCount,
VkTimeDomainEXT *pTimeDomains) VWL_API_POST
{
return wsi_layer_vkGetPhysicalDeviceCalibrateableTimeDomainsKHR(physicalDevice, pTimeDomainCount, pTimeDomains);
}
#endif /* VULKAN_WSI_LAYER_EXPERIMENTAL */ #endif /* VULKAN_WSI_LAYER_EXPERIMENTAL */

View file

@ -42,4 +42,11 @@ VWL_VKAPI_CALL(VkResult)
wsi_layer_vkGetCalibratedTimestampsKHR(VkDevice device, uint32_t timestampCount, wsi_layer_vkGetCalibratedTimestampsKHR(VkDevice device, uint32_t timestampCount,
const VkCalibratedTimestampInfoKHR *pTimestampInfos, uint64_t *pTimestamps, const VkCalibratedTimestampInfoKHR *pTimestampInfos, uint64_t *pTimestamps,
uint64_t *pMaxDeviation) VWL_API_POST; uint64_t *pMaxDeviation) VWL_API_POST;
VWL_VKAPI_CALL(VkResult)
wsi_layer_vkGetPhysicalDeviceCalibrateableTimeDomainsKHR(VkPhysicalDevice physicalDevice, uint32_t *pTimeDomainCount,
VkTimeDomainKHR *pTimeDomains) VWL_API_POST;
VWL_VKAPI_CALL(VkResult)
wsi_layer_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(VkPhysicalDevice physicalDevice, uint32_t *pTimeDomainCount,
VkTimeDomainEXT *pTimeDomains) VWL_API_POST;
#endif #endif

View file

@ -796,6 +796,10 @@ wsi_layer_vkGetInstanceProcAddr(VkInstance instance, const char *funcName) VWL_A
GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceCapabilities2KHR); GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceCapabilities2KHR);
GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceFormats2KHR); GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceFormats2KHR);
} }
#if VULKAN_WSI_LAYER_EXPERIMENTAL
GET_PROC_ADDR(vkGetPhysicalDeviceCalibrateableTimeDomainsKHR);
GET_PROC_ADDR(vkGetPhysicalDeviceCalibrateableTimeDomainsEXT);
#endif
if (instance_data.is_instance_extension_enabled(VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME)) if (instance_data.is_instance_extension_enabled(VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME))
{ {

View file

@ -40,20 +40,19 @@
#define VK_ERROR_PRESENT_TIMING_QUEUE_FULL_EXT ((VkResult)(-1000208000)) #define VK_ERROR_PRESENT_TIMING_QUEUE_FULL_EXT ((VkResult)(-1000208000))
#define VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT ((VkTimeDomainEXT)(1000208000)) #define VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT ((VkTimeDomainEXT)(1000208000))
#define VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT ((VkTimeDomainEXT)(1000208001)) #define VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT ((VkTimeDomainEXT)(1000208001))
#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_TIMING_FEATURES_EXT ((VkStructureType)1000208002) #define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_TIMING_FEATURES_EXT ((VkStructureType)1000208000)
#define VK_STRUCTURE_TYPE_PRESENT_TIMING_SURFACE_CAPABILITIES_EXT ((VkStructureType)1000208003) #define VK_STRUCTURE_TYPE_PRESENT_TIMING_SURFACE_CAPABILITIES_EXT ((VkStructureType)1000208008)
#define VK_STRUCTURE_TYPE_SWAPCHAIN_TIMING_PROPERTIES_EXT ((VkStructureType)1000208004) #define VK_STRUCTURE_TYPE_SWAPCHAIN_TIMING_PROPERTIES_EXT ((VkStructureType)1000208001)
#define VK_STRUCTURE_TYPE_SWAPCHAIN_TIME_DOMAIN_PROPERTIES_EXT ((VkStructureType)1000208005) #define VK_STRUCTURE_TYPE_SWAPCHAIN_TIME_DOMAIN_PROPERTIES_EXT ((VkStructureType)1000208002)
#define VK_STRUCTURE_TYPE_SWAPCHAIN_CALIBRATED_TIMESTAMP_INFO_EXT ((VkStructureType)1000208006) #define VK_STRUCTURE_TYPE_SWAPCHAIN_CALIBRATED_TIMESTAMP_INFO_EXT ((VkStructureType)1000208009)
#define VK_STRUCTURE_TYPE_PAST_PRESENTATION_TIMING_EXT ((VkStructureType)1000208007) #define VK_STRUCTURE_TYPE_PAST_PRESENTATION_TIMING_EXT ((VkStructureType)1000208007)
#define VK_STRUCTURE_TYPE_PAST_PRESENTATION_TIMING_PROPERTIES_EXT ((VkStructureType)1000208008) #define VK_STRUCTURE_TYPE_PAST_PRESENTATION_TIMING_PROPERTIES_EXT ((VkStructureType)1000208006)
#define VK_STRUCTURE_TYPE_PAST_PRESENTATION_TIMING_INFO_EXT ((VkStructureType)1000208009) #define VK_STRUCTURE_TYPE_PAST_PRESENTATION_TIMING_INFO_EXT ((VkStructureType)1000208005)
#define VK_STRUCTURE_TYPE_PRESENT_TIMING_INFO_EXT ((VkStructureType)1000208010) #define VK_STRUCTURE_TYPE_PRESENT_TIMING_INFO_EXT ((VkStructureType)1000208004)
#define VK_STRUCTURE_TYPE_PRESENT_TIMINGS_INFO_EXT ((VkStructureType)1000208011) #define VK_STRUCTURE_TYPE_PRESENT_TIMINGS_INFO_EXT ((VkStructureType)1000208003)
/* Placeholder. Need to get the real value. */ /* Placeholder. Need to get the real value. */
#define VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT ((VkSwapchainCreateFlagsKHR)0x00010000) #define VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT ((VkSwapchainCreateFlagsKHR)0x00000200)
typedef VkFlags VkPresentStageFlagsEXT; typedef VkFlags VkPresentStageFlagsEXT;
typedef VkFlags VkPresentTimingInfoFlagsEXT; typedef VkFlags VkPresentTimingInfoFlagsEXT;

View file

@ -29,7 +29,6 @@
*/ */
#include <array> #include <array>
#include <cassert> #include <cassert>
#include <cmath>
#include <wsi/swapchain_base.hpp> #include <wsi/swapchain_base.hpp>
#include <util/helpers.hpp> #include <util/helpers.hpp>
@ -114,15 +113,6 @@ VkResult wsi_ext_present_timing::get_pixel_out_timing_to_queue(
return VK_SUCCESS; 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));
}
swapchain_presentation_timing *wsi_ext_present_timing::get_pending_stage_timing(uint32_t image_index, swapchain_presentation_timing *wsi_ext_present_timing::get_pending_stage_timing(uint32_t image_index,
VkPresentStageFlagBitsEXT stage) VkPresentStageFlagBitsEXT stage)
{ {
@ -268,7 +258,7 @@ VkResult wsi_ext_present_timing::add_presentation_query_entry(VkQueue queue, uin
return VK_ERROR_PRESENT_TIMING_QUEUE_FULL_EXT; return VK_ERROR_PRESENT_TIMING_QUEUE_FULL_EXT;
} }
wsi::swapchain_presentation_entry presentation_entry(target_time, present_stage_queries, present_id, image_index, wsi::swapchain_presentation_entry presentation_entry(target_time, present_stage_queries, present_id, image_index,
m_device.get_best_queue_family_index()); m_device.get_best_queue_family_index(), stages_supported());
if (!m_queue.try_push_back(std::move(presentation_entry))) if (!m_queue.try_push_back(std::move(presentation_entry)))
{ {
return VK_ERROR_OUT_OF_HOST_MEMORY; return VK_ERROR_OUT_OF_HOST_MEMORY;
@ -314,6 +304,17 @@ VkResult wsi_ext_present_timing::add_presentation_entry(VkQueue queue, uint64_t
return VK_SUCCESS; return VK_SUCCESS;
} }
VkResult wsi_ext_present_timing::queue_has_space()
{
const util::unique_lock<util::mutex> lock(m_queue_mutex);
if (!lock)
{
WSI_LOG_WARNING("Failed to acquire queue mutex while checking for space in the queue");
return VK_ERROR_UNKNOWN;
}
return (m_queue.size() == m_queue.capacity()) ? VK_ERROR_PRESENT_TIMING_QUEUE_FULL_EXT : VK_SUCCESS;
}
swapchain_time_domains &wsi_ext_present_timing::get_swapchain_time_domains() swapchain_time_domains &wsi_ext_present_timing::get_swapchain_time_domains()
{ {
return m_time_domains; return m_time_domains;
@ -360,46 +361,20 @@ VkResult wsi_ext_present_timing::get_past_presentation_results(
return VK_SUCCESS; return VK_SUCCESS;
} }
uint64_t timing_count = 0, removed_entries = 0;
VkPastPresentationTimingEXT *timings = past_present_timing_properties->pPresentationTimings; VkPastPresentationTimingEXT *timings = past_present_timing_properties->pPresentationTimings;
bool seen_zero = false;
size_t last_zero_entry = 0;
uint64_t in = 0;
uint64_t out = 0;
uint64_t removed_entries = 0;
const bool allow_partial = (flags & VK_PAST_PRESENTATION_TIMING_ALLOW_PARTIAL_RESULTS_BIT_EXT) != 0; const bool allow_partial = (flags & VK_PAST_PRESENTATION_TIMING_ALLOW_PARTIAL_RESULTS_BIT_EXT) != 0;
const bool allow_out_of_order = (flags & VK_PAST_PRESENTATION_TIMING_ALLOW_OUT_OF_ORDER_RESULTS_BIT_EXT) != 0; const bool allow_out_of_order = (flags & VK_PAST_PRESENTATION_TIMING_ALLOW_OUT_OF_ORDER_RESULTS_BIT_EXT) != 0;
/* bool incomplete = false;
* Single forward pass over the caller-supplied pPresentationTimings array:
*
* Locate the first matching presentation slot in `m_queue`.
*
* When a matching slot exists and at least one stage has available timings,
* copy its timestamps into the current entry. Valid results are compacted
* in-place by writing to the `out` cursor while `in` continues to scan,
* so gaps are skipped without repeated shifting.
*/
while (in < past_present_timing_properties->presentationTimingCount)
{
const uint64_t present_id = timings[in].presentId;
/*
* If presentId != 0, match the exact ID.
* If presentId == 0, pick the next unused zero-ID slot appearing
* after `last_zero_entry`, ensuring we never report the same slot twice.
*/
auto slot = std::find_if(m_queue.begin(), m_queue.end(), [&](const swapchain_presentation_entry &e) {
bool zero_extra_cond =
(present_id == 0 && seen_zero) ? (&e - m_queue.data()) > static_cast<ptrdiff_t>(last_zero_entry) : true;
return (e.m_present_id == present_id) && zero_extra_cond;
});
if (slot != m_queue.end()) for (uint64_t i = 0; (i - removed_entries) < m_queue.size(); ++i)
{ {
auto slot = m_queue.begin() + (i - removed_entries);
if (!slot->has_completed_stages(allow_partial)) if (!slot->has_completed_stages(allow_partial))
{ {
if (allow_out_of_order) if (allow_out_of_order)
{ {
in++;
continue; continue;
} }
else else
@ -408,35 +383,22 @@ VkResult wsi_ext_present_timing::get_past_presentation_results(
} }
} }
if (present_id == 0) if (timing_count < past_present_timing_properties->presentationTimingCount)
{ {
seen_zero = true; slot->populate(timings[timing_count]);
last_zero_entry = std::distance(m_queue.begin(), slot); if (timings[timing_count++].reportComplete)
}
slot->populate(timings[in]);
if (in != out)
{
timings[out] = timings[in];
}
++out;
if (timings[in].reportComplete)
{ {
m_queue.erase(slot); m_queue.erase(slot);
removed_entries++; removed_entries++;
} }
} }
else
++in; {
/* There are available results but were not returned. */
incomplete = true;
} }
}
past_present_timing_properties->presentationTimingCount = out; past_present_timing_properties->presentationTimingCount = timing_count;
const bool incomplete = (out < in) || (out < (get_num_available_results(flags) + removed_entries));
return incomplete ? VK_INCOMPLETE : VK_SUCCESS; return incomplete ? VK_INCOMPLETE : VK_SUCCESS;
} }
@ -472,7 +434,8 @@ VkResult wsi_ext_present_timing::physical_device_has_supported_queue_family(VkPh
swapchain_presentation_entry::swapchain_presentation_entry(uint64_t target_time, swapchain_presentation_entry::swapchain_presentation_entry(uint64_t target_time,
VkPresentStageFlagsEXT present_stage_queries, VkPresentStageFlagsEXT present_stage_queries,
uint64_t present_id, uint32_t image_index, uint64_t present_id, uint32_t image_index,
uint32_t queue_family) uint32_t queue_family,
VkPresentStageFlagsEXT stages_supported)
: m_target_time(target_time) : m_target_time(target_time)
, m_target_stages(0) , m_target_stages(0)
, m_present_id(present_id) , m_present_id(present_id)
@ -482,22 +445,25 @@ swapchain_presentation_entry::swapchain_presentation_entry(uint64_t target_time,
{ {
if (present_stage_queries & VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT) if (present_stage_queries & VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT)
{ {
m_queue_end_timing = swapchain_presentation_timing(); m_queue_end_timing =
swapchain_presentation_timing(stages_supported & VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT);
m_num_present_stages++; m_num_present_stages++;
} }
if (present_stage_queries & VK_PRESENT_STAGE_REQUEST_DEQUEUED_BIT_EXT) if (present_stage_queries & VK_PRESENT_STAGE_REQUEST_DEQUEUED_BIT_EXT)
{ {
m_latch_timing = swapchain_presentation_timing(); m_latch_timing = swapchain_presentation_timing(stages_supported & VK_PRESENT_STAGE_REQUEST_DEQUEUED_BIT_EXT);
m_num_present_stages++; m_num_present_stages++;
} }
if (present_stage_queries & VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT) if (present_stage_queries & VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT)
{ {
m_first_pixel_out_timing = swapchain_presentation_timing(); m_first_pixel_out_timing =
swapchain_presentation_timing(stages_supported & VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT);
m_num_present_stages++; m_num_present_stages++;
} }
if (present_stage_queries & VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT) if (present_stage_queries & VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT)
{ {
m_first_pixel_visible_timing = swapchain_presentation_timing(); m_first_pixel_visible_timing =
swapchain_presentation_timing(stages_supported & VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT);
m_num_present_stages++; m_num_present_stages++;
} }
} }
@ -615,6 +581,8 @@ void swapchain_presentation_entry::populate(VkPastPresentationTimingEXT &timing)
timing.reportComplete = !has_outstanding_stages(); timing.reportComplete = !has_outstanding_stages();
timing.targetTime = m_target_time; timing.targetTime = m_target_time;
} }
/* Set how many stages have definitive results */
timing.presentStageCount = stage_index;
} }
VkResult swapchain_time_domains::calibrate(VkPresentStageFlagBitsEXT present_stage, VkResult swapchain_time_domains::calibrate(VkPresentStageFlagBitsEXT present_stage,
@ -632,6 +600,15 @@ VkResult swapchain_time_domains::calibrate(VkPresentStageFlagBitsEXT present_sta
return VK_ERROR_OUT_OF_HOST_MEMORY; return VK_ERROR_OUT_OF_HOST_MEMORY;
} }
bool swapchain_time_domains::add_time_domain(util::unique_ptr<swapchain_time_domain> time_domain)
{
if (time_domain)
{
return m_time_domains.try_push_back(std::move(time_domain));
}
return false;
}
VkResult swapchain_time_domains::get_swapchain_time_domain_properties( VkResult swapchain_time_domains::get_swapchain_time_domain_properties(
VkSwapchainTimeDomainPropertiesEXT *pSwapchainTimeDomainProperties, uint64_t *pTimeDomainsCounter) VkSwapchainTimeDomainPropertiesEXT *pSwapchainTimeDomainProperties, uint64_t *pTimeDomainsCounter)
{ {
@ -653,23 +630,19 @@ VkResult swapchain_time_domains::get_swapchain_time_domain_properties(
const uint32_t requested_domains_count = pSwapchainTimeDomainProperties->timeDomainCount; const uint32_t requested_domains_count = pSwapchainTimeDomainProperties->timeDomainCount;
const uint32_t domains_count_to_write = std::min(requested_domains_count, available_domains_count); const uint32_t domains_count_to_write = std::min(requested_domains_count, available_domains_count);
if (pSwapchainTimeDomainProperties->pTimeDomains != nullptr)
{
pSwapchainTimeDomainProperties->pTimeDomains[0] = VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT; pSwapchainTimeDomainProperties->pTimeDomains[0] = VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT;
}
if (pSwapchainTimeDomainProperties->pTimeDomainIds != nullptr)
{
pSwapchainTimeDomainProperties->pTimeDomainIds[0] = 0; pSwapchainTimeDomainProperties->pTimeDomainIds[0] = 0;
}
pSwapchainTimeDomainProperties->timeDomainCount = domains_count_to_write; pSwapchainTimeDomainProperties->timeDomainCount = domains_count_to_write;
return (domains_count_to_write < available_domains_count) ? VK_INCOMPLETE : VK_SUCCESS; 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)
{
if (time_domain)
{
return m_time_domains.try_push_back(std::move(time_domain));
}
return false;
}
VkResult check_time_domain_support(VkPhysicalDevice physical_device, std::tuple<VkTimeDomainEXT, bool> *domains, VkResult check_time_domain_support(VkPhysicalDevice physical_device, std::tuple<VkTimeDomainEXT, bool> *domains,
size_t domain_size) size_t domain_size)
{ {

View file

@ -40,6 +40,7 @@
#include <functional> #include <functional>
#include <cassert> #include <cassert>
#include <variant> #include <variant>
#include <cmath>
#include <layer/wsi_layer_experimental.hpp> #include <layer/wsi_layer_experimental.hpp>
#include <layer/private_data.hpp> #include <layer/private_data.hpp>
@ -78,7 +79,15 @@ struct swapchain_presentation_timing
*/ */
bool m_set{}; bool m_set{};
swapchain_presentation_timing() /**
* For unsupported present stages (i.e. when stage_supported = false), the m_set is
* set to true. This allows the application to request stages that are not supported
* by the backend and get a response of zero.
*/
swapchain_presentation_timing(bool stage_supported)
: m_timedomain_id(0)
, m_time(0)
, m_set(!stage_supported)
{ {
} }
@ -148,7 +157,7 @@ struct swapchain_presentation_entry
std::optional<swapchain_presentation_timing> m_first_pixel_visible_timing; std::optional<swapchain_presentation_timing> m_first_pixel_visible_timing;
swapchain_presentation_entry(uint64_t target_time, VkPresentStageFlagsEXT present_stage_queries, uint64_t present_id, swapchain_presentation_entry(uint64_t target_time, VkPresentStageFlagsEXT present_stage_queries, uint64_t present_id,
uint32_t image_index, uint32_t queue_family); uint32_t image_index, uint32_t queue_family, VkPresentStageFlagsEXT stages_supported);
swapchain_presentation_entry(swapchain_presentation_entry &&) noexcept = default; swapchain_presentation_entry(swapchain_presentation_entry &&) noexcept = default;
swapchain_presentation_entry &operator=(swapchain_presentation_entry &&) noexcept = default; swapchain_presentation_entry &operator=(swapchain_presentation_entry &&) noexcept = default;
@ -299,8 +308,8 @@ public:
* *
* @return Returns VK_SUCCESS on success, otherwise an appropriate error code. * @return Returns VK_SUCCESS on success, otherwise an appropriate error code.
*/ */
VkResult get_swapchain_time_domain_properties(VkSwapchainTimeDomainPropertiesEXT *pSwapchainTimeDomainProperties, static VkResult get_swapchain_time_domain_properties(
uint64_t *pTimeDomainsCounter); VkSwapchainTimeDomainPropertiesEXT *pSwapchainTimeDomainProperties, uint64_t *pTimeDomainsCounter);
private: private:
util::vector<util::unique_ptr<swapchain_time_domain>> m_time_domains; util::vector<util::unique_ptr<swapchain_time_domain>> m_time_domains;
@ -533,6 +542,13 @@ public:
VkResult add_presentation_entry(VkQueue queue, uint64_t present_id, uint32_t image_index, VkResult add_presentation_entry(VkQueue queue, uint64_t present_id, uint32_t image_index,
const VkPresentTimingInfoEXT &timing_info); const VkPresentTimingInfoEXT &timing_info);
/**
* @brief Check whether the queue has space for an entry.
*
* @return VK_SUCCESS when the there is space left in the queue, error otherwise.
*/
VkResult queue_has_space();
/** /**
* @brief Set the time for a stage, if it exists and is pending. * @brief Set the time for a stage, if it exists and is pending.
* *
@ -622,6 +638,13 @@ public:
*/ */
static VkResult physical_device_has_supported_queue_family(VkPhysicalDevice physical_device, bool &out); static VkResult physical_device_has_supported_queue_family(VkPhysicalDevice physical_device, bool &out);
/**
* @brief This function is used to check the present timing stages that are supported. It can be overriden by specific backends.
*
* @return The stages that are supported.
*/
virtual VkPresentStageFlagsEXT stages_supported() = 0;
protected: protected:
/** /**
* @brief User provided memory allocation callbacks. * @brief User provided memory allocation callbacks.
@ -781,6 +804,44 @@ private:
VkResult check_time_domain_support(VkPhysicalDevice physical_device, std::tuple<VkTimeDomainEXT, bool> *domains, VkResult check_time_domain_support(VkPhysicalDevice physical_device, std::tuple<VkTimeDomainEXT, bool> *domains,
size_t domain_size); size_t domain_size);
/**
* @brief Checks whether present timing dependencies are supported on a given physical device.
*
* This function queries the list of available device extensions for the specified
* Vulkan physical device and determines whether the `VK_KHR_maintenance9` extension
* is supported. If the extension is found, it further queries the device features
* to check if the `maintenance9` feature is enabled.
*
* @param physical_device Physical device used for the query
*
* @return std::variant<bool, VkResult>
* - `true` if the device supports present timing dependencies
* (i.e., `VK_KHR_maintenance9` extension and its feature are available).
* - `false` if the device does not support the `VK_KHR_maintenance9` extension or its feature.
* - `VkResult` (e.g., `VK_ERROR_OUT_OF_HOST_MEMORY`) if an error occurs during property enumeration.
*/
std::variant<bool, VkResult> present_timing_dependencies_supported(VkPhysicalDevice physical_device); std::variant<bool, VkResult> present_timing_dependencies_supported(VkPhysicalDevice physical_device);
/**
* @brief Converts a hardware tick count to nanoseconds.
*
* This function converts a given number of GPU hardware timestamp ticks
* into nanoseconds using the provided timestamp period.
*
* @param ticks The number of timestamp ticks to convert.
* @param timestamp_period The duration of a single tick, in nanoseconds per tick.
*
* @return The equivalent time duration in nanoseconds.
*
*/
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));
}
} /* namespace wsi */ } /* namespace wsi */
#endif #endif

View file

@ -163,4 +163,13 @@ void wsi_ext_present_timing_headless::set_first_pixel_visible_timestamp_for_last
{ {
m_first_pixel_visible_timestamp_for_last_image = timestamp; m_first_pixel_visible_timestamp_for_last_image = timestamp;
} }
VkPresentStageFlagsEXT wsi_ext_present_timing_headless::stages_supported()
{
VkPresentStageFlagsEXT stages =
VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT | VK_PRESENT_STAGE_REQUEST_DEQUEUED_BIT_EXT |
VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT | VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT;
return stages;
}
#endif #endif

View file

@ -80,6 +80,13 @@ public:
*/ */
void set_first_pixel_visible_timestamp_for_last_image(uint64_t timestamp); void set_first_pixel_visible_timestamp_for_last_image(uint64_t timestamp);
/*
* @brief The stages that are supported by the headless backend.
*
* @return A bitmask of supported presentation stages.
*/
VkPresentStageFlagsEXT stages_supported() override;
private: 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); std::optional<VkTimeDomainEXT> monotonic_domain);

View file

@ -601,6 +601,21 @@ VkResult swapchain_base::notify_presentation_engine(const pending_present_reques
VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info,
const swapchain_presentation_parameters &submit_info) const swapchain_presentation_parameters &submit_info)
{ {
#if VULKAN_WSI_LAYER_EXPERIMENTAL
const VkPresentTimingInfoEXT *present_timing_info = nullptr;
const auto *present_timings_info =
util::find_extension<VkPresentTimingsInfoEXT>(VK_STRUCTURE_TYPE_PRESENT_TIMINGS_INFO_EXT, present_info->pNext);
if (present_timings_info != nullptr)
{
present_timing_info = present_timings_info->pTimingInfos;
assert(present_timing_info != nullptr);
auto *ext_present_timing = get_swapchain_extension<wsi::wsi_ext_present_timing>(true);
if (present_timing_info->presentStageQueries)
{
TRY(ext_present_timing->queue_has_space());
}
}
#endif
if (submit_info.switch_presentation_mode) if (submit_info.switch_presentation_mode)
{ {
/* Assert when a presentation mode switch is requested and the swapchain_maintenance1 extension which implements this is not available */ /* Assert when a presentation mode switch is requested and the swapchain_maintenance1 extension which implements this is not available */
@ -649,20 +664,13 @@ VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *pr
m_swapchain_images[submit_info.pending_present.image_index].get_present_fence_wait_semaphore(); m_swapchain_images[submit_info.pending_present.image_index].get_present_fence_wait_semaphore();
} }
#if VULKAN_WSI_LAYER_EXPERIMENTAL #if VULKAN_WSI_LAYER_EXPERIMENTAL
const VkPresentTimingInfoEXT *present_timing_info = nullptr; if ((present_timing_info != nullptr) &&
const auto *present_timings_info = (present_timing_info->presentStageQueries & VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT))
util::find_extension<VkPresentTimingsInfoEXT>(VK_STRUCTURE_TYPE_PRESENT_TIMINGS_INFO_EXT, present_info->pNext);
if (present_timings_info != nullptr)
{
present_timing_info = present_timings_info->pTimingInfos;
assert(present_timing_info != nullptr);
if (present_timing_info->presentStageQueries & VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT)
{ {
auto *ext_present_timing = get_swapchain_extension<wsi::wsi_ext_present_timing>(true); auto *ext_present_timing = get_swapchain_extension<wsi::wsi_ext_present_timing>(true);
signal_semaphores[count_signal_semaphores++] = signal_semaphores[count_signal_semaphores++] =
ext_present_timing->get_image_present_semaphore(submit_info.pending_present.image_index); ext_present_timing->get_image_present_semaphore(submit_info.pending_present.image_index);
} }
}
#endif #endif
queue_submit_semaphores semaphores = { queue_submit_semaphores semaphores = {
wait_semaphores, wait_semaphores,

View file

@ -38,9 +38,10 @@ namespace wayland
{ {
wsi_ext_present_timing_wayland::wsi_ext_present_timing_wayland( wsi_ext_present_timing_wayland::wsi_ext_present_timing_wayland(
const util::allocator &allocator, VkDevice device, uint32_t num_images, const util::allocator &allocator, VkDevice device, uint32_t num_images,
util::vector<std::optional<uint64_t>> &&timestamp_first_pixel_out_storage) util::vector<std::optional<uint64_t>> &&timestamp_first_pixel_out_storage, bool stage_first_pixel_out_supported)
: wsi_ext_present_timing(allocator, device, num_images) : wsi_ext_present_timing(allocator, device, num_images)
, m_timestamp_first_pixel_out(allocator) , m_timestamp_first_pixel_out(allocator)
, m_stage_first_pixel_out_supported(stage_first_pixel_out_supported)
{ {
m_timestamp_first_pixel_out.swap(timestamp_first_pixel_out_storage); m_timestamp_first_pixel_out.swap(timestamp_first_pixel_out_storage);
} }
@ -57,6 +58,8 @@ util::unique_ptr<wsi_ext_present_timing_wayland> wsi_ext_present_timing_wayland:
return nullptr; return nullptr;
} }
util::vector<std::optional<uint64_t>> timestamp_first_pixel_out_storage(allocator);
bool stage_first_pixel_out_supported = false;
if (image_first_pixel_out_time_domain.has_value()) if (image_first_pixel_out_time_domain.has_value())
{ {
std::tuple<VkTimeDomainEXT, bool> monotonic_query = { *image_first_pixel_out_time_domain, false }; std::tuple<VkTimeDomainEXT, bool> monotonic_query = { *image_first_pixel_out_time_domain, false };
@ -75,16 +78,22 @@ util::unique_ptr<wsi_ext_present_timing_wayland> wsi_ext_present_timing_wayland:
{ {
return nullptr; return nullptr;
} }
}
}
util::vector<std::optional<uint64_t>> timestamp_first_pixel_out_storage(allocator);
if (!timestamp_first_pixel_out_storage.try_resize(num_images)) if (!timestamp_first_pixel_out_storage.try_resize(num_images))
{ {
return nullptr; return nullptr;
} }
stage_first_pixel_out_supported = true;
}
}
else
{
timestamp_first_pixel_out_storage.clear();
timestamp_first_pixel_out_storage.shrink_to_fit();
}
return wsi_ext_present_timing::create<wsi_ext_present_timing_wayland>( return wsi_ext_present_timing::create<wsi_ext_present_timing_wayland>(
allocator, domains.data(), domains.size(), device, num_images, std::move(timestamp_first_pixel_out_storage)); allocator, domains.data(), domains.size(), device, num_images, std::move(timestamp_first_pixel_out_storage),
stage_first_pixel_out_supported);
} }
VkResult wsi_ext_present_timing_wayland::get_swapchain_timing_properties( VkResult wsi_ext_present_timing_wayland::get_swapchain_timing_properties(
@ -113,7 +122,7 @@ void wsi_ext_present_timing_wayland::mark_buffer_release(uint32_t image_index)
} }
presentation_feedback *wsi_ext_present_timing_wayland::insert_into_pending_present_feedback_list( presentation_feedback *wsi_ext_present_timing_wayland::insert_into_pending_present_feedback_list(
uint32_t image_index, struct wp_presentation_feedback *feedback_obj) uint32_t image_index, struct wp_presentation_feedback *feedback_obj, uint64_t id)
{ {
util::unique_lock<util::mutex> lock(m_pending_presents_lock); util::unique_lock<util::mutex> lock(m_pending_presents_lock);
@ -127,7 +136,7 @@ presentation_feedback *wsi_ext_present_timing_wayland::insert_into_pending_prese
* that would discard or mark the pending present request as completed which could be an inidcation of a bug somewhere. */ * that would discard or mark the pending present request as completed which could be an inidcation of a bug somewhere. */
assert(!m_pending_presents[image_index].has_value()); assert(!m_pending_presents[image_index].has_value());
m_pending_presents[image_index] = presentation_feedback(feedback_obj, this, image_index); m_pending_presents[image_index] = presentation_feedback(feedback_obj, this, image_index, id);
return &m_pending_presents[image_index].value(); return &m_pending_presents[image_index].value();
} }
@ -191,5 +200,16 @@ void wsi_ext_present_timing_wayland::init(wl_display *display, struct wl_event_q
m_queue = queue; m_queue = queue;
} }
VkPresentStageFlagsEXT wsi_ext_present_timing_wayland::stages_supported()
{
VkPresentStageFlagsEXT stages = VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT;
if (m_stage_first_pixel_out_supported)
{
stages |= VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT;
}
return stages;
}
} // namespace wayland } // namespace wayland
} // namespace wsi } // namespace wsi

View file

@ -56,7 +56,7 @@ class wsi_ext_present_timing_wayland : public wsi::wsi_ext_present_timing
public: public:
static util::unique_ptr<wsi_ext_present_timing_wayland> create( static util::unique_ptr<wsi_ext_present_timing_wayland> create(
VkDevice device, const util::allocator &allocator, VkDevice device, const util::allocator &allocator,
std::optional<VkTimeDomainKHR> image_first_pixel_visible_time_domain, uint32_t num_images); std::optional<VkTimeDomainKHR> image_first_pixel_out_time_domain, uint32_t num_images);
VkResult get_swapchain_timing_properties(uint64_t &timing_properties_counter, VkResult get_swapchain_timing_properties(uint64_t &timing_properties_counter,
VkSwapchainTimingPropertiesEXT &timing_properties) override; VkSwapchainTimingPropertiesEXT &timing_properties) override;
@ -89,7 +89,8 @@ public:
* @return Pointer to the presentation feedback object in the pending presents list. * @return Pointer to the presentation feedback object in the pending presents list.
*/ */
presentation_feedback *insert_into_pending_present_feedback_list(uint32_t image_index, presentation_feedback *insert_into_pending_present_feedback_list(uint32_t image_index,
struct wp_presentation_feedback *feedback_obj); struct wp_presentation_feedback *feedback_obj,
uint64_t id);
/* /*
* @brief Copies the pixel out timestamp from the internal array to the present timing queue. * @brief Copies the pixel out timestamp from the internal array to the present timing queue.
* *
@ -112,6 +113,13 @@ public:
*/ */
void init(wl_display *display, struct wl_event_queue *queue); void init(wl_display *display, struct wl_event_queue *queue);
/*
* @brief The stages that are supported by the wayland backend.
*
* @return A bitmask of supported presentation stages.
*/
VkPresentStageFlagsEXT stages_supported() override;
private: private:
/** /**
* @brief Mutex for synchronising accesses to the pending present id list. * @brief Mutex for synchronising accesses to the pending present id list.
@ -123,7 +131,8 @@ private:
*/ */
wsi_ext_present_timing_wayland(const util::allocator &allocator, VkDevice device, uint32_t num_images, wsi_ext_present_timing_wayland(const util::allocator &allocator, VkDevice device, uint32_t num_images,
util::vector<std::optional<uint64_t>> &&timestamp_first_pixel_out_storage); util::vector<std::optional<uint64_t>> &&timestamp_first_pixel_out_storage,
bool stage_first_pixel_out_supported);
/** /**
* @brief Updates the first pixel out timing in the internal array. * @brief Updates the first pixel out timing in the internal array.
@ -152,6 +161,11 @@ private:
*/ */
util::vector<std::optional<uint64_t>> m_timestamp_first_pixel_out; util::vector<std::optional<uint64_t>> m_timestamp_first_pixel_out;
/**
* @brief Whether the first pixel out stage is supported.
*/
bool m_stage_first_pixel_out_supported;
/* Allow util::allocator to access the private constructor */ /* Allow util::allocator to access the private constructor */
friend util::allocator; friend util::allocator;

View file

@ -153,22 +153,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; bool swapchain_support_enabled = swapchain_create_info->flags & VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT;
if (swapchain_support_enabled) if (swapchain_support_enabled)
{ {
std::optional<VkTimeDomainKHR> image_first_pixel_visible_time_domain; std::optional<VkTimeDomainKHR> image_first_pixel_out_time_domain;
if (m_wsi_surface->get_presentation_time_interface() != nullptr) if (m_wsi_surface->get_presentation_time_interface() != nullptr)
{ {
switch (m_wsi_surface->clockid()) switch (m_wsi_surface->clockid())
{ {
case CLOCK_MONOTONIC: case CLOCK_MONOTONIC:
image_first_pixel_visible_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_KHR; image_first_pixel_out_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_KHR;
break; break;
case CLOCK_MONOTONIC_RAW: case CLOCK_MONOTONIC_RAW:
image_first_pixel_visible_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_KHR; image_first_pixel_out_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_KHR;
break; break;
} }
} }
if (!add_swapchain_extension(wsi_ext_present_timing_wayland::create( if (!add_swapchain_extension(wsi_ext_present_timing_wayland::create(
m_device, m_allocator, image_first_pixel_visible_time_domain, swapchain_create_info->minImageCount))) m_device, m_allocator, image_first_pixel_out_time_domain, swapchain_create_info->minImageCount)))
{ {
return VK_ERROR_OUT_OF_HOST_MEMORY; return VK_ERROR_OUT_OF_HOST_MEMORY;
} }
@ -444,8 +444,8 @@ void swapchain::present_image(const pending_present_request &pending_present)
wp_presentation *pres = m_wsi_surface->get_presentation_time_interface(); wp_presentation *pres = m_wsi_surface->get_presentation_time_interface();
struct wp_presentation_feedback *feedback = wp_presentation_feedback(pres, m_wsi_surface->get_wl_surface()); struct wp_presentation_feedback *feedback = wp_presentation_feedback(pres, m_wsi_surface->get_wl_surface());
wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(feedback), m_buffer_queue); wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(feedback), m_buffer_queue);
presentation_feedback *feedback_obj = presentation_feedback *feedback_obj = present_timing_ext->insert_into_pending_present_feedback_list(
present_timing_ext->insert_into_pending_present_feedback_list(pending_present.image_index, feedback); pending_present.image_index, feedback, pending_present.present_id);
if (feedback_obj == nullptr) if (feedback_obj == nullptr)
{ {
WSI_LOG_ERROR("Error adding to pending present feedback list"); WSI_LOG_ERROR("Error adding to pending present feedback list");

View file

@ -57,10 +57,10 @@ class presentation_feedback
{ {
public: public:
presentation_feedback(struct wp_presentation_feedback *feedback, wsi_ext_present_timing_wayland *ext_present_timing, presentation_feedback(struct wp_presentation_feedback *feedback, wsi_ext_present_timing_wayland *ext_present_timing,
uint32_t image_index) uint32_t image_index, uint64_t present_id)
: m_feedback(feedback) : m_feedback(feedback)
, m_ext_present_id(nullptr) , m_ext_present_id(nullptr)
, m_present_id(0) , m_present_id(present_id)
, m_ext_present_timing(ext_present_timing) , m_ext_present_timing(ext_present_timing)
, m_image_index(image_index) , m_image_index(image_index)
{ {