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 <alex.bates@arm.com>
Signed-off-by: Dennis Tsiang <dennis.tsiang@arm.com>
Signed-off-by: Normunds Rieksts <normunds.rieksts@arm.com>
This commit is contained in:
Alex Bates 2025-06-18 12:15:41 +00:00 committed by Iason Paraskevopoulos
parent 7c71bbc214
commit 441d95f1bb
16 changed files with 290 additions and 175 deletions

View file

@ -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 <cassert>
#include <wsi/wsi_factory.hpp>
#include <util/helpers.hpp>
#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

View file

@ -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

View file

@ -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();

View file

@ -30,6 +30,7 @@
#include <array>
#include <cassert>
#include <wsi/swapchain_base.hpp>
#include <util/helpers.hpp>
#include "present_timing.hpp"
@ -552,6 +553,34 @@ bool swapchain_time_domains::add_time_domain(util::unique_ptr<swapchain_time_dom
return false;
}
VkResult check_time_domain_support(VkPhysicalDevice physical_device, std::tuple<VkTimeDomainEXT, bool> *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<VkTimeDomainEXT> 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 */

View file

@ -40,6 +40,7 @@
#include <iterator>
#include <type_traits>
#include <array>
#include <tuple>
#include <optional>
#include <functional>
@ -254,7 +255,7 @@ public:
*/
bool add_time_domain(util::unique_ptr<swapchain_time_domain> 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 <typename T, std::size_t N>
template <typename T>
static util::unique_ptr<T> create(const util::allocator &allocator,
std::array<util::unique_ptr<wsi::vulkan_time_domain>, N> &domains, VkDevice device,
uint32_t num_images)
util::unique_ptr<wsi::vulkan_time_domain> *domains, size_t domain_count,
VkDevice device, uint32_t num_images)
{
auto present_timing = allocator.make_unique<T>(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<VkTimeDomainEXT, bool> *domains,
size_t domain_size);
} /* namespace wsi */
#endif
#endif

View file

@ -30,7 +30,6 @@
#if VULKAN_WSI_LAYER_EXPERIMENTAL
#include <cstdint>
#include <array>
#include <optional>
#include <algorithm>
#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<bool> 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<bool> 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<VkTimeDomainEXT> 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> 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<util::unique_ptr<wsi::vulkan_time_domain>, 4> time_domains_array = {
allocator.make_unique<wsi::vulkan_time_domain>(VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT,
VK_TIME_DOMAIN_DEVICE_KHR),
allocator.make_unique<wsi::vulkan_time_domain>(VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT, monotonic_time_domain),
allocator.make_unique<wsi::vulkan_time_domain>(VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT,
monotonic_time_domain),
allocator.make_unique<wsi::vulkan_time_domain>(VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT,
monotonic_time_domain)
};
util::vector<util::unique_ptr<wsi::vulkan_time_domain>> domains(allocator);
if (!domains.try_push_back(allocator.make_unique<wsi::vulkan_time_domain>(
VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT, VK_TIME_DOMAIN_DEVICE_KHR)))
{
return nullptr;
}
return wsi_ext_present_timing::create<wsi_ext_present_timing_headless>(allocator, time_domains_array, device,
num_images);
if (monotonic_time_domain_supported)
{
if (!domains.try_push_back(allocator.make_unique<wsi::vulkan_time_domain>(VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT,
monotonic_domain_to_use)))
{
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)))
{
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)))
{
return nullptr;
}
}
return wsi_ext_present_timing::create<wsi_ext_present_timing_headless>(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

View file

@ -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

View file

@ -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:

View file

@ -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:

View file

@ -29,7 +29,6 @@
*/
#include "present_timing_handler.hpp"
#include <array>
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> 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<VkTimeDomainKHR> image_first_pixel_visible_time_domain, uint32_t num_images)
{
std::array<util::unique_ptr<wsi::vulkan_time_domain>, 2> time_domains_array = {
allocator.make_unique<wsi::vulkan_time_domain>(VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT,
VK_TIME_DOMAIN_DEVICE_KHR),
allocator.make_unique<wsi::vulkan_time_domain>(VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT,
image_first_pixel_visible_time_domain)
};
return wsi_ext_present_timing::create<wsi_ext_present_timing_wayland>(allocator, time_domains_array, device,
num_images);
util::vector<util::unique_ptr<wsi::vulkan_time_domain>> domains(allocator);
if (!domains.try_push_back(allocator.make_unique<wsi::vulkan_time_domain>(
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<VkTimeDomainEXT, bool> 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<wsi::vulkan_time_domain>(
VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT, image_first_pixel_visible_time_domain.value())))
{
return nullptr;
}
}
}
return wsi_ext_present_timing::create<wsi_ext_present_timing_wayland>(allocator, domains.data(), domains.size(),
device, num_images);
}
VkResult wsi_ext_present_timing_wayland::get_swapchain_timing_properties(

View file

@ -32,6 +32,7 @@
#if VULKAN_WSI_LAYER_EXPERIMENTAL
#include <wsi/extensions/present_timing.hpp>
#include <optional>
/**
* @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<wsi_ext_present_timing_wayland> create(VkTimeDomainKHR image_first_pixel_visible_time_domain,
const util::allocator &allocator, VkDevice device,
uint32_t num_images);
static util::unique_ptr<wsi_ext_present_timing_wayland> create(
VkDevice device, const util::allocator &allocator,
std::optional<VkTimeDomainKHR> 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
#endif

View file

@ -40,6 +40,11 @@ namespace wayland
struct formats_vector
{
formats_vector(util::vector<util::drm::drm_format_pair> *format_list)
: formats(format_list)
{
}
util::vector<util::drm::drm_format_pair> *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<util::drm::drm_format_pair> &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 &params)
, 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;
}

View file

@ -132,7 +132,7 @@ public:
*/
const util::vector<util::drm::drm_format_pair> &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<util::drm::drm_format_pair> supported_formats;
util::vector<util::drm::drm_format_pair> m_supported_formats;
/** Surface properties specific to the Wayland surface. */
surface_properties properties;

View file

@ -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<VkTimeDomainEXT, bool> 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

View file

@ -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();

View file

@ -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<VkTimeDomainKHR> 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;
}