Merge remote-tracking branch 'main' into add_swapchain_image_creator

Change-Id: I22a4c697c8b1181127242848f37ea081243f0d03
Signed-off-by: Iason Paraskevopoulos <iason.paraskevopoulos@arm.com>
This commit is contained in:
Iason Paraskevopoulos 2025-04-25 15:29:33 +01:00
commit 281049aa78
28 changed files with 900 additions and 207 deletions

View file

@ -149,7 +149,9 @@ if(BUILD_WSI_WAYLAND)
wsi/swapchain_image_create_extensions/external_memory_extension.cpp)
if(VULKAN_WSI_LAYER_EXPERIMENTAL)
target_sources(wayland_wsi PRIVATE wsi/wayland/present_id_wayland.cpp)
target_sources(wayland_wsi PRIVATE wsi/wayland/present_timing_handler.cpp)
target_sources(wayland_wsi PRIVATE wsi/wayland/wp_presentation_feedback.cpp)
endif()
pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client)
@ -281,6 +283,7 @@ endif()
# Layer
add_library(${PROJECT_NAME} SHARED
layer/calibrated_timestamps_api.cpp
layer/layer.cpp
layer/private_data.cpp
layer/surface_api.cpp
@ -308,7 +311,7 @@ if (VULKAN_WSI_LAYER_EXPERIMENTAL)
target_sources(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/wsi/extensions/present_timing.cpp)
add_definitions("-DVULKAN_WSI_LAYER_EXPERIMENTAL=1")
else()
list(APPEND JSON_COMMANDS COMMAND sed -i '/VK_EXT_present_timing/d' ${CMAKE_CURRENT_BINARY_DIR}/VkLayer_window_system_integration.json)
list(APPEND JSON_COMMANDS COMMAND sed -i '/VK_EXT_present_timing|VK_KHR_present_wait/d' ${CMAKE_CURRENT_BINARY_DIR}/VkLayer_window_system_integration.json)
add_definitions("-DVULKAN_WSI_LAYER_EXPERIMENTAL=0")
endif()

View file

@ -54,7 +54,7 @@ The ICDs installed in the system are required to support the following extension
### Vulkan Header Version
The Vulkan WSI Layer has been validated against Vulkan header version 1.4.299.
The Vulkan WSI Layer has been validated against Vulkan header version 1.4.305.
If you are using a Vulkan header version newer than this, a warning will appear during compilation.

View file

@ -53,10 +53,17 @@
"vkGetSwapchainTimeDomainPropertiesEXT",
"vkGetPastPresentationTimingEXT"
]
},
{
"name": "VK_KHR_present_wait",
"spec_version": "1",
"entrypoints": [
"vkWaitForPresentKHR"
]
}
],
"disable_environment": {
"DISABLE_WSI_LAYER": "1"
}
}
}
}

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @file calibrated_timestamps_api.cpp
*
* @brief Contains the Vulkan entrypoints for the calibrated timestamps extension.
*/
#include <wsi/extensions/present_timing.hpp>
#include <wsi/swapchain_base.hpp>
VWL_VKAPI_CALL(VkResult)
wsi_layer_vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount,
const VkCalibratedTimestampInfoKHR *pTimestampInfos, uint64_t *pTimestamps,
uint64_t *pMaxDeviation) VWL_API_POST
{
auto &device_data = layer::device_private_data::get(device);
auto get_calibrated_timestamps =
device_data.disp.get_fn<PFN_vkGetCalibratedTimestampsEXT>("PFN_vkGetCalibratedTimestampsEXT").has_value() ?
device_data.disp.get_fn<PFN_vkGetCalibratedTimestampsEXT>("vkGetCalibratedTimestampsEXT") :
device_data.disp.get_fn<PFN_vkGetCalibratedTimestampsEXT>("vkGetCalibratedTimestampsKHR");
assert(get_calibrated_timestamps.has_value());
#if VULKAN_WSI_LAYER_EXPERIMENTAL
struct stage_local_index_and_offset
{
uint32_t index;
uint64_t calibration_offset;
};
util::vector<VkCalibratedTimestampInfoKHR> time_stamp_info{ util::allocator(device_data.get_allocator(),
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT) };
util::vector<stage_local_index_and_offset> calibration_index_and_offset{ util::allocator(
device_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT) };
for (uint32_t i = 0; i < timestampCount; ++i)
{
/* Intercept VkSwapchainCalibratedTimestampInfoEXT struct. */
auto *ext = util::find_extension<VkSwapchainCalibratedTimestampInfoEXT>(
VK_STRUCTURE_TYPE_SWAPCHAIN_CALIBRATED_TIMESTAMP_INFO_EXT, pTimestampInfos[i].pNext);
/* Make a copy of the pTimestampsInfo. */
if (!time_stamp_info.try_push_back(pTimestampInfos[i]))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
/* The layer is only handling for present stage local time domains,
every other time domains including swapchain local (VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT)
is not handled by the layer. */
if ((ext != nullptr) && (pTimestampInfos[i].timeDomain == VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT))
{
assert(ext->swapchain != VK_NULL_HANDLE);
if (!device_data.layer_owns_swapchain(ext->swapchain))
{
continue;
}
/* Check only one present stage is stated. */
assert(((ext->presentStage & (~(ext->presentStage) + 1)) == ext->presentStage));
auto *swapchain = reinterpret_cast<wsi::swapchain_base *>(ext->swapchain);
auto *present_timing_extension = swapchain->get_swapchain_extension<wsi::wsi_ext_present_timing>(true);
wsi::swapchain_calibrated_time calibrated_time;
TRY_LOG_CALL(present_timing_extension->get_swapchain_time_domains().calibrate(
static_cast<VkPresentStageFlagBitsEXT>(ext->presentStage), &calibrated_time));
stage_local_index_and_offset index_and_offset = { i, calibrated_time.offset };
if (!calibration_index_and_offset.try_push_back(index_and_offset))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
time_stamp_info[i].timeDomain = calibrated_time.time_domain;
}
}
TRY_LOG_CALL((*get_calibrated_timestamps)(device, timestampCount, &time_stamp_info[0], pTimestamps, pMaxDeviation));
/* Loop through the calibration_index_and_offset vector and update the timestamps that are stage local
with its respective offset. */
for (const auto &iter : calibration_index_and_offset)
{
pTimestamps[iter.index] += iter.calibration_offset;
}
return VK_SUCCESS;
#endif /* VULKAN_WSI_LAYER_EXPERIMENTAL */
return (*get_calibrated_timestamps)(device, timestampCount, pTimestampInfos, pTimestamps, pMaxDeviation);
}
VWL_VKAPI_CALL(VkResult)
wsi_layer_vkGetCalibratedTimestampsKHR(VkDevice device, uint32_t timestampCount,
const VkCalibratedTimestampInfoKHR *pTimestampInfos, uint64_t *pTimestamps,
uint64_t *pMaxDeviation) VWL_API_POST
{
return wsi_layer_vkGetCalibratedTimestampsEXT(device, timestampCount, pTimestampInfos, pTimestamps, pMaxDeviation);
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @file calibrated_timestamps_api.hpp
*
* @brief Contains the Vulkan entrypoints for Calibrated Timestamps extension.
*
*/
#pragma once
#include "util/macros.hpp"
VWL_VKAPI_CALL(VkResult)
wsi_layer_vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount,
const VkCalibratedTimestampInfoKHR *pTimestampInfos, uint64_t *pTimestamps,
uint64_t *pMaxDeviation) VWL_API_POST;
VWL_VKAPI_CALL(VkResult)
wsi_layer_vkGetCalibratedTimestampsKHR(VkDevice device, uint32_t timestampCount,
const VkCalibratedTimestampInfoKHR *pTimestampInfos, uint64_t *pTimestamps,
uint64_t *pMaxDeviation) VWL_API_POST;

View file

@ -30,6 +30,7 @@
#include <vulkan/vk_layer.h>
#include <vulkan/vulkan.h>
#include "layer/calibrated_timestamps_api.hpp"
#include "wsi_layer_experimental.hpp"
#include "private_data.hpp"
#include "surface_api.hpp"
@ -41,7 +42,6 @@
#include "util/log.hpp"
#include "util/macros.hpp"
#include "util/helpers.hpp"
#include "wsi/unsupported_surfaces.hpp"
#define VK_LAYER_API_VERSION VK_MAKE_VERSION(1, 2, VK_HEADER_VERSION)
@ -155,18 +155,6 @@ VKAPI_ATTR VkResult create_instance(const VkInstanceCreateInfo *pCreateInfo, con
modified_info.ppEnabledExtensionNames = modified_enabled_extensions.data();
modified_info.enabledExtensionCount = modified_enabled_extensions.size();
bool maintainance1_support = true;
/* Loop through unsupported extensions and check if they exist in enabled extensions */
for (const auto &unsupported_surface_ext : wsi::unsupported_surfaces_ext_array)
{
if (extensions.contains(unsupported_surface_ext))
{
maintainance1_support = false;
WSI_LOG_ERROR(
"Warning: Swapchain maintenance feature is unsupported for the current surface and ICD configuration.\n");
}
}
/* Advance the link info for the next element on the chain. */
layer_link_info->u.pLayerInfo = layer_link_info->u.pLayerInfo->pNext;
@ -192,17 +180,15 @@ VKAPI_ATTR VkResult create_instance(const VkInstanceCreateInfo *pCreateInfo, con
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
TRY_LOG_CALL(table->populate(*pInstance, fpGetInstanceProcAddr));
table->set_user_enabled_extensions(pCreateInfo->ppEnabledExtensionNames, pCreateInfo->enabledExtensionCount);
uint32_t api_version =
pCreateInfo->pApplicationInfo != nullptr ? pCreateInfo->pApplicationInfo->apiVersion : VK_API_VERSION_1_3;
TRY_LOG_CALL(table->populate(*pInstance, fpGetInstanceProcAddr, api_version));
table->set_user_enabled_extensions(pCreateInfo->ppEnabledExtensionNames, pCreateInfo->enabledExtensionCount);
TRY_LOG_CALL(instance_private_data::associate(*pInstance, std::move(*table), loader_callback,
layer_platforms_to_enable, api_version, instance_allocator));
/* Set the swapchain maintenance flag to true or false based on the enabled extensions checked above*/
instance_private_data::get(*pInstance).set_maintainance1_support(maintainance1_support);
/*
* Store the enabled instance extensions in order to return nullptr in
* vkGetInstanceProcAddr for functions of disabled extensions.
@ -328,7 +314,7 @@ VKAPI_ATTR VkResult create_device(VkPhysicalDevice physicalDevice, const VkDevic
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
VkResult result = table->populate(*pDevice, fpGetDeviceProcAddr);
VkResult result = table->populate(*pDevice, fpGetDeviceProcAddr, inst_data.api_version);
if (result != VK_SUCCESS)
{
fn_destroy_device(*pDevice, pAllocator);
@ -477,13 +463,23 @@ wsi_layer_vkGetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures2 *pFeatures) VWL_API_POST
{
auto &instance = layer::instance_private_data::get(physicalDevice);
auto *physical_device_swapchain_maintenance1_features =
util::find_extension<VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT, pFeatures->pNext);
if (physical_device_swapchain_maintenance1_features != nullptr)
auto *swapchain_maintenance1_features = util::find_extension<VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT, pFeatures->pNext);
if (swapchain_maintenance1_features != nullptr)
{
physical_device_swapchain_maintenance1_features->swapchainMaintenance1 = false;
swapchain_maintenance1_features->swapchainMaintenance1 = VK_FALSE;
}
#if VULKAN_WSI_LAYER_EXPERIMENTAL
auto *present_wait_features = util::find_extension<VkPhysicalDevicePresentWaitFeaturesKHR>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR, pFeatures->pNext);
if (present_wait_features != nullptr)
{
present_wait_features->presentWait = VK_FALSE;
}
#endif
instance.disp.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures);
auto *image_compression_control_swapchain_features =
@ -499,12 +495,21 @@ wsi_layer_vkGetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physicalDevice,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR, pFeatures->pNext);
if (present_id_features != nullptr)
{
present_id_features->presentId = true;
present_id_features->presentId = VK_TRUE;
}
wsi::set_swapchain_maintenance1_state(physicalDevice, physical_device_swapchain_maintenance1_features);
wsi::set_swapchain_maintenance1_state(physicalDevice, swapchain_maintenance1_features);
#if VULKAN_WSI_LAYER_EXPERIMENTAL
if (present_wait_features != nullptr)
{
/* If there is an surface extension in use that is unsupported by the layer, defer to the ICD */
if (!instance.is_unsupported_surface_extension_enabled())
{
present_wait_features->presentWait = VK_TRUE;
}
}
auto *present_timing_features = util::find_extension<VkPhysicalDevicePresentTimingFeaturesEXT>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_TIMING_FEATURES_EXT, pFeatures->pNext);
if (present_timing_features != nullptr)
@ -523,6 +528,7 @@ wsi_layer_vkGetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physicalDevice,
VWL_VKAPI_CALL(PFN_vkVoidFunction)
wsi_layer_vkGetDeviceProcAddr(VkDevice device, const char *funcName) VWL_API_POST
{
uint64_t api_version = layer::device_private_data::get(device).instance_data.api_version;
if (layer::device_private_data::get(device).is_device_extension_enabled(VK_KHR_SWAPCHAIN_EXTENSION_NAME))
{
GET_PROC_ADDR(vkCreateSwapchainKHR);
@ -553,12 +559,27 @@ wsi_layer_vkGetDeviceProcAddr(VkDevice device, const char *funcName) VWL_API_POS
GET_PROC_ADDR(vkCreateImage);
GET_PROC_ADDR(vkBindImageMemory2);
if (!strcmp(funcName, "vkBindImageMemory2KHR") &&
layer::device_private_data::get(device).disp.get_user_enabled_entrypoint(device, api_version, funcName) !=
nullptr)
{
return (PFN_vkVoidFunction)&wsi_layer_vkBindImageMemory2;
}
/* VK_EXT_swapchain_maintenance1 */
if (layer::device_private_data::get(device).is_device_extension_enabled(
VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME))
{
GET_PROC_ADDR(vkReleaseSwapchainImagesEXT);
}
if (layer::device_private_data::get(device).is_device_extension_enabled(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME))
{
GET_PROC_ADDR(vkGetCalibratedTimestampsEXT);
}
if (layer::device_private_data::get(device).is_device_extension_enabled(VK_KHR_CALIBRATED_TIMESTAMPS_EXTENSION_NAME))
{
GET_PROC_ADDR(vkGetCalibratedTimestampsKHR);
}
return layer::device_private_data::get(device).disp.get_user_enabled_entrypoint(
device, layer::device_private_data::get(device).instance_data.api_version, funcName);

View file

@ -27,6 +27,7 @@
#include "private_data.hpp"
#include "wsi/wsi_factory.hpp"
#include "wsi/surface.hpp"
#include "wsi/unsupported_surfaces.hpp"
#include "util/unordered_map.hpp"
#include "util/log.hpp"
#include "util/helpers.hpp"
@ -44,17 +45,17 @@ static std::mutex g_data_lock;
static util::unordered_map<void *, instance_private_data *> g_instance_data{ util::allocator::get_generic() };
static util::unordered_map<void *, device_private_data *> g_device_data{ util::allocator::get_generic() };
VkResult instance_dispatch_table::populate(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc)
VkResult instance_dispatch_table::populate(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc,
uint32_t instance_api_version)
{
static constexpr entrypoint entrypoints_init[] = {
#define DISPATCH_TABLE_ENTRY(name, ext_name, api_version, required) \
{ "vk" #name, ext_name, nullptr, api_version, false, required },
#define DISPATCH_TABLE_ENTRY(name, ext_name, api_version, required, alias) \
{ "vk" #name, ext_name, nullptr, api_version, false, required, "vk" #alias },
INSTANCE_ENTRYPOINTS_LIST(DISPATCH_TABLE_ENTRY)
#undef DISPATCH_TABLE_ENTRY
};
static constexpr auto num_entrypoints = std::distance(std::begin(entrypoints_init), std::end(entrypoints_init));
for (size_t i = 0; i < num_entrypoints; i++)
{
const entrypoint *entrypoint = &entrypoints_init[i];
@ -67,6 +68,12 @@ VkResult instance_dispatch_table::populate(VkInstance instance, PFN_vkGetInstanc
e.fn = ret;
e.user_visible = false;
if (entrypoint->alias != nullptr && strcmp(entrypoint->alias, "vk") != 0 &&
instance_api_version >= entrypoint->api_version)
{
e.fn = get_proc(instance, entrypoint->alias);
}
if (!m_entrypoints->try_insert(std::make_pair(e.name, e)).has_value())
{
WSI_LOG_ERROR("Failed to allocate memory for instance dispatch table entry.");
@ -113,11 +120,12 @@ PFN_vkVoidFunction instance_dispatch_table::get_user_enabled_entrypoint(VkInstan
return GetInstanceProcAddr(instance, fn_name).value_or(nullptr);
}
VkResult device_dispatch_table::populate(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc_fn)
VkResult device_dispatch_table::populate(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc_fn,
uint32_t instance_api_version)
{
static constexpr entrypoint entrypoints_init[] = {
#define DISPATCH_TABLE_ENTRY(name, ext_name, api_version, required) \
{ "vk" #name, ext_name, nullptr, api_version, false, required },
#define DISPATCH_TABLE_ENTRY(name, ext_name, api_version, required, alias) \
{ "vk" #name, ext_name, nullptr, api_version, false, required, "vk" #alias },
DEVICE_ENTRYPOINTS_LIST(DISPATCH_TABLE_ENTRY)
#undef DISPATCH_TABLE_ENTRY
};
@ -125,16 +133,22 @@ VkResult device_dispatch_table::populate(VkDevice dev, PFN_vkGetDeviceProcAddr g
for (size_t i = 0; i < num_entrypoints; i++)
{
const entrypoint entrypoint = entrypoints_init[i];
PFN_vkVoidFunction ret = get_proc_fn(dev, entrypoint.name);
if (!ret && entrypoint.required)
const entrypoint *entrypoint = &entrypoints_init[i];
PFN_vkVoidFunction ret = get_proc_fn(dev, entrypoint->name);
if (!ret && entrypoint->required)
{
return VK_ERROR_INITIALIZATION_FAILED;
}
struct entrypoint e = entrypoint;
struct entrypoint e = *entrypoint;
e.fn = ret;
e.user_visible = false;
if (entrypoint->alias != nullptr && strcmp(entrypoint->alias, "vk") != 0 &&
instance_api_version >= entrypoint->api_version)
{
e.fn = get_proc_fn(dev, entrypoint->alias);
}
if (!m_entrypoints->try_insert(std::make_pair(e.name, e)).has_value())
{
WSI_LOG_ERROR("Failed to allocate memory for device dispatch table entry.");
@ -385,7 +399,22 @@ bool instance_private_data::has_frame_boundary_support(VkPhysicalDevice phys_dev
VkResult instance_private_data::set_instance_enabled_extensions(const char *const *extension_names,
size_t extension_count)
{
return enabled_extensions.add(extension_names, extension_count);
VkResult result = enabled_extensions.add(extension_names, extension_count);
/* Check for unsupported surface extension */
has_enabled_unsupported_extension = false;
for (const auto &unsupported_surface_ext : wsi::unsupported_surfaces_ext_array)
{
if (enabled_extensions.contains(unsupported_surface_ext))
{
has_enabled_unsupported_extension = true;
WSI_LOG_ERROR(
"Warning: Swapchain maintenance feature is unsupported for the current surface and ICD configuration.\n");
break;
}
}
return result;
}
bool instance_private_data::is_instance_extension_enabled(const char *extension_name) const
@ -393,16 +422,6 @@ bool instance_private_data::is_instance_extension_enabled(const char *extension_
return enabled_extensions.contains(extension_name);
}
void instance_private_data::set_maintainance1_support(bool enabled_unsupport_ext)
{
enabled_unsupported_swapchain_maintenance1_extensions = enabled_unsupport_ext;
}
bool instance_private_data::get_maintainance1_support()
{
return enabled_unsupported_swapchain_maintenance1_extensions;
}
device_private_data::device_private_data(instance_private_data &inst_data, VkPhysicalDevice phys_dev, VkDevice dev,
device_dispatch_table table, PFN_vkSetDeviceLoaderData set_loader_data,
const util::allocator &alloc)

View file

@ -65,6 +65,7 @@ struct entrypoint
uint32_t api_version;
bool user_visible;
bool required;
const char *alias;
};
/**
@ -210,55 +211,58 @@ static constexpr uint32_t API_VERSION_MAX = UINT32_MAX;
* extension_name: Name of the extension that provides the entrypoint.
* api_version: Vulkan API version where the entrypoint is part of the core specification, or API_VERSION_MAX.
* required: Boolean to indicate whether the entrypoint is required by the WSI layer or optional.
* alias: Name of the promoted entrypoint alias if different to entrypoint_name.
*/
#define INSTANCE_ENTRYPOINTS_LIST(EP) \
/* Vulkan 1.0 */ \
EP(GetInstanceProcAddr, "", VK_API_VERSION_1_0, true) \
EP(DestroyInstance, "", VK_API_VERSION_1_0, true) \
EP(GetPhysicalDeviceProperties, "", VK_API_VERSION_1_0, true) \
EP(GetPhysicalDeviceImageFormatProperties, "", VK_API_VERSION_1_0, true) \
EP(EnumerateDeviceExtensionProperties, "", VK_API_VERSION_1_0, true) \
/* VK_KHR_surface */ \
EP(DestroySurfaceKHR, VK_KHR_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(GetPhysicalDeviceSurfaceCapabilitiesKHR, VK_KHR_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(GetPhysicalDeviceSurfaceFormatsKHR, VK_KHR_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(GetPhysicalDeviceSurfacePresentModesKHR, VK_KHR_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(GetPhysicalDeviceSurfaceSupportKHR, VK_KHR_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false) \
/* VK_EXT_headless_surface */ \
EP(CreateHeadlessSurfaceEXT, VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false) \
/* VK_KHR_wayland_surface */ \
EP(CreateWaylandSurfaceKHR, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false) \
/* VK_KHR_display */ \
EP(CreateDisplayPlaneSurfaceKHR, VK_KHR_DISPLAY_EXTENSION_NAME, API_VERSION_MAX, false) \
/* VK_KHR_get_surface_capabilities2 */ \
EP(GetPhysicalDeviceSurfaceCapabilities2KHR, VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, API_VERSION_MAX, \
false) \
EP(GetPhysicalDeviceSurfaceFormats2KHR, VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, API_VERSION_MAX, false) \
/* VK_KHR_get_physical_device_properties2 or */ \
/* 1.1 (without KHR suffix) */ \
EP(GetPhysicalDeviceImageFormatProperties2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, \
VK_API_VERSION_1_1, false) \
EP(GetPhysicalDeviceFormatProperties2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, \
VK_API_VERSION_1_1, false) \
EP(GetPhysicalDeviceFeatures2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, VK_API_VERSION_1_1, \
false) \
EP(GetPhysicalDeviceProperties2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, VK_API_VERSION_1_1, \
false) \
EP(GetPhysicalDeviceQueueFamilyProperties2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, \
VK_API_VERSION_1_1, false) \
EP(GetPhysicalDeviceMemoryProperties2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, \
VK_API_VERSION_1_1, false) \
EP(GetPhysicalDeviceSparseImageFormatProperties2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, \
VK_API_VERSION_1_1, false) \
/* VK_KHR_device_group + VK_KHR_surface or */ \
/* 1.1 with VK_KHR_swapchain */ \
EP(GetPhysicalDevicePresentRectanglesKHR, VK_KHR_DEVICE_GROUP_EXTENSION_NAME, VK_API_VERSION_1_1, false) \
/* VK_KHR_external_fence_capabilities or */ \
/* 1.1 (without KHR suffix) */ \
EP(GetPhysicalDeviceExternalFencePropertiesKHR, VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, \
VK_API_VERSION_1_1, false) \
EP(GetPhysicalDeviceExternalBufferPropertiesKHR, VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, \
VK_API_VERSION_1_1, false)
#define INSTANCE_ENTRYPOINTS_LIST(EP) \
/* Vulkan 1.0 */ \
EP(GetInstanceProcAddr, "", VK_API_VERSION_1_0, true, ) \
EP(DestroyInstance, "", VK_API_VERSION_1_0, true, ) \
EP(GetPhysicalDeviceProperties, "", VK_API_VERSION_1_0, true, ) \
EP(GetPhysicalDeviceImageFormatProperties, "", VK_API_VERSION_1_0, true, ) \
EP(EnumerateDeviceExtensionProperties, "", VK_API_VERSION_1_0, true, ) \
/* VK_KHR_surface */ \
EP(DestroySurfaceKHR, VK_KHR_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(GetPhysicalDeviceSurfaceCapabilitiesKHR, VK_KHR_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(GetPhysicalDeviceSurfaceFormatsKHR, VK_KHR_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(GetPhysicalDeviceSurfacePresentModesKHR, VK_KHR_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(GetPhysicalDeviceSurfaceSupportKHR, VK_KHR_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false, ) \
/* VK_EXT_headless_surface */ \
EP(CreateHeadlessSurfaceEXT, VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false, ) \
/* VK_KHR_wayland_surface */ \
EP(CreateWaylandSurfaceKHR, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, API_VERSION_MAX, false, ) \
/* VK_KHR_display */ \
EP(CreateDisplayPlaneSurfaceKHR, VK_KHR_DISPLAY_EXTENSION_NAME, API_VERSION_MAX, false, ) \
/* VK_KHR_get_surface_capabilities2 */ \
EP(GetPhysicalDeviceSurfaceCapabilities2KHR, VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, API_VERSION_MAX, \
false, ) \
EP(GetPhysicalDeviceSurfaceFormats2KHR, VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, API_VERSION_MAX, false, ) \
/* VK_KHR_get_physical_device_properties2 or 1.1 (without KHR suffix) */ \
/* Not all of these entrypoints are used by the layer but need to be listed here to hide them from the application. */ \
EP(GetPhysicalDeviceImageFormatProperties2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, \
VK_API_VERSION_1_1, false, GetPhysicalDeviceImageFormatProperties2) \
EP(GetPhysicalDeviceFormatProperties2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, \
VK_API_VERSION_1_1, false, GetPhysicalDeviceFormatProperties2) \
EP(GetPhysicalDeviceFeatures2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, VK_API_VERSION_1_1, \
false, GetPhysicalDeviceFeatures2) \
EP(GetPhysicalDeviceProperties2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, VK_API_VERSION_1_1, \
false, GetPhysicalDeviceProperties2) \
EP(GetPhysicalDeviceQueueFamilyProperties2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, \
VK_API_VERSION_1_1, false, GetPhysicalDeviceQueueFamilyProperties2) \
EP(GetPhysicalDeviceMemoryProperties2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, \
VK_API_VERSION_1_1, false, GetPhysicalDeviceMemoryProperties2) \
EP(GetPhysicalDeviceSparseImageFormatProperties2KHR, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, \
VK_API_VERSION_1_1, false, GetPhysicalDeviceSparseImageFormatProperties2) \
/* VK_KHR_device_group + VK_KHR_surface or */ \
/* 1.1 with VK_KHR_swapchain */ \
EP(GetPhysicalDevicePresentRectanglesKHR, VK_KHR_DEVICE_GROUP_EXTENSION_NAME, VK_API_VERSION_1_1, false, ) \
/* VK_KHR_external_fence_capabilities or 1.1 (without KHR suffix) */ \
EP(GetPhysicalDeviceExternalFencePropertiesKHR, VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, \
VK_API_VERSION_1_1, false, GetPhysicalDeviceExternalFenceProperties) \
/* VK_KHR_external_memory_capabilities or 1.1 (without KHR suffix) */ \
/* The layer does not use these entrypoints directly but does use VkExternalImageFormatPropertiesKHR introduced by */ \
/* this extension. These are listed here in order to hide them from the application. */ \
EP(GetPhysicalDeviceExternalBufferPropertiesKHR, VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, \
VK_API_VERSION_1_1, false, GetPhysicalDeviceExternalBufferProperties)
/**
* @brief Struct representing the instance dispatch table.
@ -284,9 +288,10 @@ public:
*
* @param instance The instance for which the dispatch table will be populated.
* @param get_proc The pointer to vkGetInstanceProcAddr function.
* @param api_version The Vulkan API version being used.
* @return VkResult VK_SUCCESS if successful, otherwise an error.
*/
VkResult populate(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc);
VkResult populate(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc, uint32_t api_version);
/**
* @brief Get the user enabled instance extension entrypoint by name
@ -304,7 +309,7 @@ public:
* disp.GetInstanceProcAddr(instance, fn_name);
* The result type will be matching the function signature, so there is no need for casting.
*/
#define DISPATCH_TABLE_SHORTCUT(name, unused1, unused2, unused3) \
#define DISPATCH_TABLE_SHORTCUT(name, unused1, unused2, unused3, unused4) \
template <class... Args> \
auto name(Args &&...args) const \
{ \
@ -340,13 +345,14 @@ private:
* extension_name: Name of the extension that provides the entrypoint.
* api_version: Vulkan API version where the entrypoint is part of the core specification, or API_VERSION_MAX.
* required: Boolean to indicate whether the entrypoint is required by the WSI layer or optional.
* alias: Name of the promoted entrypoint alias if different to entrypoint_name.
*/
#if VULKAN_WSI_LAYER_EXPERIMENTAL
#define DEVICE_ENTRYPOINTS_LIST_EXPERIMENTAL(EP) \
EP(GetSwapchainTimeDomainPropertiesEXT, VK_EXT_PRESENT_TIMING_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(GetSwapchainTimingPropertiesEXT, VK_EXT_PRESENT_TIMING_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(SetSwapchainPresentTimingQueueSizeEXT, VK_EXT_PRESENT_TIMING_EXTENSION_NAME, API_VERSION_MAX, false)
#define DEVICE_ENTRYPOINTS_LIST_EXPERIMENTAL(EP) \
EP(GetSwapchainTimeDomainPropertiesEXT, VK_EXT_PRESENT_TIMING_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(GetSwapchainTimingPropertiesEXT, VK_EXT_PRESENT_TIMING_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(SetSwapchainPresentTimingQueueSizeEXT, VK_EXT_PRESENT_TIMING_EXTENSION_NAME, API_VERSION_MAX, false, )
#else
#define DEVICE_ENTRYPOINTS_LIST_EXPERIMENTAL(EP)
#endif
@ -354,71 +360,84 @@ private:
/* Define a list of custom entrypoints that might rely on preprocessor conditions and similar */
#define DEVICE_ENTRYPOINTS_LIST_EXPANSION(EP) DEVICE_ENTRYPOINTS_LIST_EXPERIMENTAL(EP)
#define DEVICE_ENTRYPOINTS_LIST(EP) \
/* Vulkan 1.0 */ \
EP(GetDeviceProcAddr, "", VK_API_VERSION_1_0, true) \
EP(GetDeviceQueue, "", VK_API_VERSION_1_0, true) \
EP(QueueSubmit, "", VK_API_VERSION_1_0, true) \
EP(QueueWaitIdle, "", VK_API_VERSION_1_0, true) \
EP(CreateCommandPool, "", VK_API_VERSION_1_0, true) \
EP(DestroyCommandPool, "", VK_API_VERSION_1_0, true) \
EP(AllocateCommandBuffers, "", VK_API_VERSION_1_0, true) \
EP(FreeCommandBuffers, "", VK_API_VERSION_1_0, true) \
EP(ResetCommandBuffer, "", VK_API_VERSION_1_0, true) \
EP(BeginCommandBuffer, "", VK_API_VERSION_1_0, true) \
EP(EndCommandBuffer, "", VK_API_VERSION_1_0, true) \
EP(CreateImage, "", VK_API_VERSION_1_0, true) \
EP(DestroyImage, "", VK_API_VERSION_1_0, true) \
EP(GetImageMemoryRequirements, "", VK_API_VERSION_1_0, true) \
EP(BindImageMemory, "", VK_API_VERSION_1_0, true) \
EP(AllocateMemory, "", VK_API_VERSION_1_0, true) \
EP(FreeMemory, "", VK_API_VERSION_1_0, true) \
EP(CreateFence, "", VK_API_VERSION_1_0, true) \
EP(DestroyFence, "", VK_API_VERSION_1_0, true) \
EP(CreateSemaphore, "", VK_API_VERSION_1_0, true) \
EP(DestroySemaphore, "", VK_API_VERSION_1_0, true) \
EP(ResetFences, "", VK_API_VERSION_1_0, true) \
EP(WaitForFences, "", VK_API_VERSION_1_0, true) \
EP(DestroyDevice, "", VK_API_VERSION_1_0, true) \
/* VK_KHR_swapchain */ \
EP(CreateSwapchainKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(DestroySwapchainKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(GetSwapchainImagesKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(AcquireNextImageKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(QueuePresentKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME, API_VERSION_MAX, false) \
/* VK_KHR_shared_presentable_image */ \
EP(GetSwapchainStatusKHR, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME, API_VERSION_MAX, false) \
/* VK_KHR_device_group + VK_KHR_swapchain or */ /* 1.1 with VK_KHR_swapchain */ \
EP(AcquireNextImage2KHR, VK_KHR_DEVICE_GROUP_EXTENSION_NAME, VK_API_VERSION_1_1, false) \
/* VK_KHR_device_group + VK_KHR_surface or */ /* 1.1 with VK_KHR_swapchain */ \
EP(GetDeviceGroupSurfacePresentModesKHR, VK_KHR_DEVICE_GROUP_EXTENSION_NAME, VK_API_VERSION_1_1, false) \
EP(GetDeviceGroupPresentCapabilitiesKHR, VK_KHR_DEVICE_GROUP_EXTENSION_NAME, VK_API_VERSION_1_1, \
false) /* VK_KHR_external_memory_fd */ \
EP(GetMemoryFdKHR, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(GetMemoryFdPropertiesKHR, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, API_VERSION_MAX, false) \
/* VK_KHR_bind_memory2 or */ /* 1.1 (without KHR suffix) */ \
EP(BindImageMemory2KHR, VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, VK_API_VERSION_1_1, false) \
EP(BindBufferMemory2KHR, VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, VK_API_VERSION_1_1, \
false) /* VK_KHR_external_fence_fd */ \
EP(GetFenceFdKHR, VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(ImportFenceFdKHR, VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, API_VERSION_MAX, \
false) /* VK_KHR_external_semaphore_fd */ \
EP(ImportSemaphoreFdKHR, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, API_VERSION_MAX, false) \
EP(GetSemaphoreFdKHR, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, API_VERSION_MAX, \
false) /* VK_KHR_image_drm_format_modifier */ \
EP(GetImageDrmFormatModifierPropertiesEXT, VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME, API_VERSION_MAX, \
false) /* VK_KHR_sampler_ycbcr_conversion */ \
EP(CreateSamplerYcbcrConversionKHR, VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, VK_API_VERSION_1_1, false) \
EP(DestroySamplerYcbcrConversionKHR, VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, VK_API_VERSION_1_1, \
false) /* VK_KHR_maintenance1 */ \
EP(TrimCommandPoolKHR, VK_KHR_MAINTENANCE1_EXTENSION_NAME, VK_API_VERSION_1_1, \
false) /* VK_KHR_get_memory_requirements2 */ \
EP(GetImageMemoryRequirements2KHR, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, VK_API_VERSION_1_1, false) \
EP(GetBufferMemoryRequirements2KHR, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, VK_API_VERSION_1_1, false) \
EP(GetImageSparseMemoryRequirements2KHR, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, VK_API_VERSION_1_1, \
false) \
EP(ReleaseSwapchainImagesEXT, VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, VK_API_VERSION_1_1, false) \
/* Custom entrypoints */ \
#define DEVICE_ENTRYPOINTS_LIST(EP) \
/* Vulkan 1.0 */ \
EP(GetDeviceProcAddr, "", VK_API_VERSION_1_0, true, ) \
EP(GetDeviceQueue, "", VK_API_VERSION_1_0, true, ) \
EP(QueueSubmit, "", VK_API_VERSION_1_0, true, ) \
EP(QueueWaitIdle, "", VK_API_VERSION_1_0, true, ) \
EP(CreateCommandPool, "", VK_API_VERSION_1_0, true, ) \
EP(DestroyCommandPool, "", VK_API_VERSION_1_0, true, ) \
EP(AllocateCommandBuffers, "", VK_API_VERSION_1_0, true, ) \
EP(FreeCommandBuffers, "", VK_API_VERSION_1_0, true, ) \
EP(ResetCommandBuffer, "", VK_API_VERSION_1_0, true, ) \
EP(BeginCommandBuffer, "", VK_API_VERSION_1_0, true, ) \
EP(EndCommandBuffer, "", VK_API_VERSION_1_0, true, ) \
EP(CreateImage, "", VK_API_VERSION_1_0, true, ) \
EP(DestroyImage, "", VK_API_VERSION_1_0, true, ) \
EP(GetImageMemoryRequirements, "", VK_API_VERSION_1_0, true, ) \
EP(BindImageMemory, "", VK_API_VERSION_1_0, true, ) \
EP(AllocateMemory, "", VK_API_VERSION_1_0, true, ) \
EP(FreeMemory, "", VK_API_VERSION_1_0, true, ) \
EP(CreateFence, "", VK_API_VERSION_1_0, true, ) \
EP(DestroyFence, "", VK_API_VERSION_1_0, true, ) \
EP(CreateSemaphore, "", VK_API_VERSION_1_0, true, ) \
EP(DestroySemaphore, "", VK_API_VERSION_1_0, true, ) \
EP(ResetFences, "", VK_API_VERSION_1_0, true, ) \
EP(WaitForFences, "", VK_API_VERSION_1_0, true, ) \
EP(DestroyDevice, "", VK_API_VERSION_1_0, true, ) \
/* VK_KHR_swapchain */ \
EP(CreateSwapchainKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(DestroySwapchainKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(GetSwapchainImagesKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(AcquireNextImageKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(QueuePresentKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME, API_VERSION_MAX, false, ) \
/* VK_KHR_shared_presentable_image */ \
EP(GetSwapchainStatusKHR, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME, API_VERSION_MAX, false, ) \
/* VK_KHR_device_group + VK_KHR_swapchain or 1.1 with VK_KHR_swapchain */ \
EP(AcquireNextImage2KHR, VK_KHR_DEVICE_GROUP_EXTENSION_NAME, VK_API_VERSION_1_1, false, ) \
/* VK_KHR_device_group + VK_KHR_surface or 1.1 with VK_KHR_swapchain */ \
EP(GetDeviceGroupSurfacePresentModesKHR, VK_KHR_DEVICE_GROUP_EXTENSION_NAME, VK_API_VERSION_1_1, false, ) \
EP(GetDeviceGroupPresentCapabilitiesKHR, VK_KHR_DEVICE_GROUP_EXTENSION_NAME, VK_API_VERSION_1_1, false, ) \
/* VK_KHR_external_memory_fd */ \
EP(GetMemoryFdKHR, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(GetMemoryFdPropertiesKHR, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, API_VERSION_MAX, false, ) \
/* VK_KHR_bind_memory2 or 1.1 (without KHR suffix) */ \
EP(BindImageMemory2KHR, VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, VK_API_VERSION_1_1, false, BindImageMemory2) \
EP(BindBufferMemory2KHR, VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, VK_API_VERSION_1_1, false, BindBufferMemory2) \
/* VK_KHR_external_fence_fd */ \
EP(GetFenceFdKHR, VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(ImportFenceFdKHR, VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, API_VERSION_MAX, false, ) \
/* VK_KHR_external_semaphore_fd */ \
EP(ImportSemaphoreFdKHR, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(GetSemaphoreFdKHR, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, API_VERSION_MAX, false, ) \
/* VK_EXT_image_drm_format_modifier */ \
/* Note the layer doesn't use these entrypoints directly but does use the structures introduced */ \
/* by this extension. These entrypoints are listed to hide the entrypoints from the application. */ \
EP(GetImageDrmFormatModifierPropertiesEXT, VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME, API_VERSION_MAX, \
false, ) \
/* VK_KHR_sampler_ycbcr_conversion (promoted in 1.1 with KHR suffix removed but the samplerYcbcrConversion capability */ \
/* is still optional). */ \
/* Note the layer doesn't use these entrypoints directly but does use VK_IMAGE_CREATE_DISJOINT_BIT_KHR introduced */ \
/* by this extension. These entrypoints are listed to hide the entrypoints from the application. */ \
EP(CreateSamplerYcbcrConversionKHR, VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, VK_API_VERSION_1_1, false, ) \
EP(DestroySamplerYcbcrConversionKHR, VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, VK_API_VERSION_1_1, false, ) \
/* VK_KHR_maintenance1 */ \
/* This extension is not used directly by the layer but is a dependency of VK_KHR_sampler_ycbcr_conversion */ \
EP(TrimCommandPoolKHR, VK_KHR_MAINTENANCE1_EXTENSION_NAME, VK_API_VERSION_1_1, false, ) \
/* VK_KHR_get_memory_requirements2 or 1.1 (without KHR suffix) \
* This extension is not used directly by the layer but is a dependency of VK_KHR_sampler_ycbcr_conversion */ \
EP(GetImageMemoryRequirements2KHR, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, VK_API_VERSION_1_1, false, \
GetImageMemoryRequirements2) \
EP(GetBufferMemoryRequirements2KHR, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, VK_API_VERSION_1_1, false, \
GetBufferMemoryRequirements2) \
EP(GetImageSparseMemoryRequirements2KHR, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, VK_API_VERSION_1_1, \
false, GetImageSparseMemoryRequirements2KHR) \
/* VK_EXT_swapchain_maintenance1 */ \
EP(ReleaseSwapchainImagesEXT, VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, VK_API_VERSION_1_1, false, ) \
EP(GetCalibratedTimestampsEXT, VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME, API_VERSION_MAX, false, ) \
EP(GetCalibratedTimestampsKHR, VK_KHR_CALIBRATED_TIMESTAMPS_EXTENSION_NAME, API_VERSION_MAX, false, ) \
/* Custom entrypoints */ \
DEVICE_ENTRYPOINTS_LIST_EXPANSION(EP)
/**
@ -445,9 +464,10 @@ public:
*
* @param device The device for which the dispatch table will be populated.
* @param get_proc The pointer to vkGetDeviceProcAddr function.
* @param api_version The Vulkan API version being used.
* @return VkResult VK_SUCCESS if successful, otherwise an error.
*/
VkResult populate(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc);
VkResult populate(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc, uint32_t api_version);
/**
* @brief Get the user enabled device extension entrypoint by name
@ -465,7 +485,7 @@ public:
* disp.GetDeviceProcAddr(instance, fn_name);
* The result type will be matching the function signature, so there is no need for casting.
*/
#define DISPATCH_TABLE_SHORTCUT(name, unused1, unused2, unused3) \
#define DISPATCH_TABLE_SHORTCUT(name, unused1, unused2, unused3, unused4) \
template <class... Args> \
auto name(Args &&...args) const \
{ \
@ -634,7 +654,7 @@ public:
/**
* @brief Check whether an instance extension is enabled.
*
* param extension_name Extension's name.
* @param extension_name Extension's name.
*
* @return true if is enabled, false otherwise.
*/
@ -644,12 +664,14 @@ public:
const uint32_t api_version;
/**
* @brief Set if swapchain maintainance1 support is enabled.
*
* @param enabled_unsupport_ext True if it is enabled, false otherwise.
* @brief Check whether there is an enabled surface extension that is not supported by the layer.
*
* @return true if there is an unsupported, enabled surface extension
*/
void set_maintainance1_support(bool enabled_unsupport_ext);
bool is_unsupported_surface_extension_enabled() const
{
return has_enabled_unsupported_extension;
}
/**
* @brief Check if swapchain maintainance1 support is enabled.
@ -657,7 +679,10 @@ public:
* @return true if it is enabled, false otherwise.
*
*/
bool get_maintainance1_support();
bool get_maintainance1_support() const
{
return !has_enabled_unsupported_extension;
}
private:
/* Allow util::allocator to access the private constructor */
@ -711,9 +736,9 @@ private:
util::extension_list enabled_extensions;
/**
* @brief True if unsupported extensions are enabled.
* @brief True if any unsupported extensions are enabled.
*/
bool enabled_unsupported_swapchain_maintenance1_extensions;
bool has_enabled_unsupported_extension;
};
/**

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022 Arm Limited.
* Copyright (c) 2021-2022, 2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@ -26,6 +26,8 @@
#include <memory>
#include <optional>
#pragma once
namespace util
{

View file

@ -561,7 +561,7 @@ 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->set_present_id(pending_present.present_id);
ext->mark_delivered(pending_present.present_id);
}
/* And release the old one. */

View file

@ -27,17 +27,18 @@
*
* @brief Contains the implementation for the VK_KHR_present_id extension.
*/
#include "present_id.hpp"
namespace wsi
{
void wsi_ext_present_id::set_present_id(uint64_t value)
void wsi_ext_present_id::mark_delivered(uint64_t present_id)
{
if (value != 0)
/* Stale reads are acceptable as we only care that the ID is increasing */
if (present_id > m_last_delivered_id.load(std::memory_order_relaxed))
{
assert(value > m_present_id);
m_present_id = value;
m_last_delivered_id.store(present_id, std::memory_order_relaxed);
}
}

View file

@ -32,6 +32,7 @@
#include <util/custom_allocator.hpp>
#include <util/macros.hpp>
#include <atomic>
#include "wsi_extension.hpp"
@ -53,17 +54,15 @@ public:
WSI_DEFINE_EXTENSION(VK_KHR_PRESENT_ID_EXTENSION_NAME);
/**
* @brief Set the present ID for the swapchain.
*
* @param value Value to set for the present_id.
* @brief Marks the given present ID delivered (i.e. its image has been displayed).
*/
void set_present_id(uint64_t value);
void mark_delivered(uint64_t present_id);
private:
/**
* @brief Current present ID for this swapchain.
* @brief Most recently delivered present ID for this swapchain.
*/
uint64_t m_present_id{ 0 };
std::atomic<uint64_t> m_last_delivered_id{ 0 };
};
} /* namespace wsi */

View file

@ -37,6 +37,7 @@
#include <iterator>
#include <type_traits>
#include <array>
#include "wsi_extension.hpp"

View file

@ -30,6 +30,7 @@
#include "present_timing_handler.hpp"
#include <cstdint>
#include <array>
wsi_ext_present_timing_headless::wsi_ext_present_timing_headless(const util::allocator &allocator)
: wsi::wsi_ext_present_timing(allocator)

View file

@ -201,8 +201,9 @@ 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->set_present_id(pending_present.present_id);
ext->mark_delivered(pending_present.present_id);
}
unpresent_image(pending_present.image_index);
}

View file

@ -26,7 +26,7 @@
#include <vulkan/vulkan.h>
#include <array>
/* Define the patch version directly as macros */
#define WSI_LAYER_VK_PATCH 299
#define WSI_LAYER_VK_PATCH 305
/* Convert macros to string */
#define STRINGIFY(x) #x

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @file present_id_wayland.cpp
*
* @brief Contains the functionality to implement Wayland specific features for present ID extension.
*/
#if VULKAN_WSI_LAYER_EXPERIMENTAL
#include "present_id_wayland.hpp"
namespace wsi
{
namespace wayland
{
presentation_feedback *wsi_ext_present_id_wayland::insert_into_pending_present_feedback_list(
uint64_t present_id, struct wp_presentation_feedback *feedback_obj)
{
scoped_mutex lock(m_pending_presents_lock);
bool ret = m_pending_presents.push_back(presentation_feedback(present_id, feedback_obj, this));
if (!ret)
{
return nullptr;
}
return m_pending_presents.back();
}
void wsi_ext_present_id_wayland::remove_from_pending_present_feedback_list(uint64_t present_id)
{
scoped_mutex lock(m_pending_presents_lock);
while (m_pending_presents.size() > 0 && m_pending_presents.front()->present_id() <= present_id)
{
m_pending_presents.pop_front();
}
}
} // namespace wayland
} // namespace wsi
#endif /* VULKAN_WSI_LAYER_EXPERIMENTAL */

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @file present_id_wayland.hpp
*
* @brief Contains the functionality to implement Wayland specific features for present id extension.
*/
#pragma once
#if VULKAN_WSI_LAYER_EXPERIMENTAL
#include <wsi/extensions/present_id.hpp>
#include <util/ring_buffer.hpp>
#include "surface_properties.hpp"
#include "wp_presentation_feedback.hpp"
#include <mutex>
namespace wsi
{
namespace wayland
{
/**
* @brief Present ID extension class
*
* This class implements present ID features declarations that are specific to the Wayland backend.
*/
class wsi_ext_present_id_wayland : public wsi::wsi_ext_present_id
{
public:
/**
* @brief Insert into pending present id list.
*/
presentation_feedback *insert_into_pending_present_feedback_list(uint64_t present_id,
struct wp_presentation_feedback *feedback_obj);
/**
* @brief Remove a present id from the pending present id list.
*/
void remove_from_pending_present_feedback_list(uint64_t present_id);
private:
/**
* @brief Mutex for synchronising accesses to the pending present id list.
*/
std::mutex m_pending_presents_lock;
/**
* @brief Stores the presentation feedbacks that have been queued.
*/
util::ring_buffer<presentation_feedback, wsi::surface_properties::MAX_SWAPCHAIN_IMAGE_COUNT * 2> m_pending_presents;
};
} // namespace wayland
} // namespace wsi
#endif /* VULKAN_WSI_LAYER_EXPERIMENTAL */

View file

@ -29,6 +29,7 @@
*/
#include "present_timing_handler.hpp"
#include <array>
wsi_ext_present_timing_wayland::wsi_ext_present_timing_wayland(const util::allocator &allocator)
: wsi_ext_present_timing(allocator)
@ -36,11 +37,13 @@ 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(
const util::allocator &allocator)
VkTimeDomainKHR image_first_pixel_visible_time_domain, const util::allocator &allocator)
{
std::array<util::unique_ptr<wsi::vulkan_time_domain>, 1> time_domains_array = {
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)
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);
@ -54,4 +57,4 @@ VkResult wsi_ext_present_timing_wayland::get_swapchain_timing_properties(
timing_properties.variableRefreshDelay = 0;
return VK_SUCCESS;
}
}

View file

@ -41,7 +41,8 @@
class wsi_ext_present_timing_wayland : public wsi::wsi_ext_present_timing
{
public:
static util::unique_ptr<wsi_ext_present_timing_wayland> create(const util::allocator &allocator);
static util::unique_ptr<wsi_ext_present_timing_wayland> create(VkTimeDomainKHR image_first_pixel_visible_time_domain,
const util::allocator &allocator);
VkResult get_swapchain_timing_properties(uint64_t &timing_properties_counter,
VkSwapchainTimingPropertiesEXT &timing_properties) override;

View file

@ -72,9 +72,21 @@ zwp_linux_dmabuf_v1_modifier_impl(void *data, struct zwp_linux_dmabuf_v1 *dma_bu
drm_supported_formats->is_out_of_memory = !drm_supported_formats->formats->try_push_back(format);
}
}
/* Handler for clock_id event of the wp_presentation interface. */
VWL_CAPI_CALL(void)
wp_presentation_clock_id_impl(void *data, struct wp_presentation *wp_presentation,
uint32_t compositor_clockid) VWL_API_POST
{
UNUSED(wp_presentation);
clockid_t *clockid = static_cast<clockid_t *>(data);
*clockid = compositor_clockid;
}
} // namespace
/*
/**
* @brief Get supported formats and modifiers using the zwp_linux_dmabuf_v1 interface.
*
* @param[in] display The wl_display that is being used.
@ -122,6 +134,35 @@ static VkResult get_supported_formats_and_modifiers(wl_display *display, wl_even
return VK_SUCCESS;
}
/**
* @brief Set the clock_id using the wp_presentation interface
*
* @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)
{
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)
{
WSI_LOG_ERROR("Failed to add wp_presentation listener.");
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;
}
struct surface::init_parameters
{
const util::allocator &allocator;
@ -262,6 +303,12 @@ bool surface::init()
return false;
}
vk_res = get_clock_id(wayland_display, surface_queue.get(), presentation_time_interface.get(), &m_clockid);
if (vk_res != VK_SUCCESS)
{
return false;
}
return true;
}

View file

@ -31,6 +31,7 @@
#ifndef __STDC_VERSION__
#define __STDC_VERSION__ 0
#endif
#include <sys/types.h>
#include <wayland-client.h>
#include "wsi/surface.hpp"
@ -113,6 +114,17 @@ public:
return surface_sync_interface.get();
}
/**
* @brief Returns a pointer to the Wayland wp_presentation interface obtained for the wayland
* surface.
*
* The raw pointer is valid for the lifetime of the surface.
*/
struct wp_presentation *get_presentation_time_interface()
{
return presentation_time_interface.get();
}
/**
* @brief Returns a reference to a list of DRM formats supported by the Wayland surface.
*
@ -140,6 +152,14 @@ public:
*/
bool wait_next_frame_event();
/**
* @brief Return the clockid of the surface
*/
clockid_t clockid()
{
return m_clockid;
}
private:
/**
* @brief Initialize the WSI surface by creating Wayland queues and linking to Wayland protocols.
@ -180,7 +200,7 @@ private:
wayland_owner<wp_presentation> presentation_time_interface;
/**
* Container for a callback object for the latest frame done event.
* @brief Container for a callback object for the latest frame done event.
*
* The callback object should be destroyed before the queue so any new events
* on the queue will be discarded. If a proxy object is destroyed after a queue,
@ -196,6 +216,11 @@ private:
* callback to indicate the server is ready for the next buffer.
*/
bool present_pending;
/**
* @brief Stores the clock ID reported by the wp_presentation interface
*/
clockid_t m_clockid;
};
} // namespace wayland

View file

@ -424,7 +424,8 @@ void surface_properties::get_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;
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->presentStageTargets = 0;
}
#endif

View file

@ -44,12 +44,14 @@
#include "wl_helpers.hpp"
#include <wsi/extensions/image_compression_control.hpp>
#include <wsi/extensions/present_id.hpp>
#include <wsi/extensions/swapchain_maintenance.hpp>
#include <wsi/extensions/present_id.hpp>
#include <wsi/swapchain_image_create_extensions/external_memory_extension.hpp>
#include "present_timing_handler.hpp"
#include "present_id_wayland.hpp"
#include "wp_presentation_feedback.hpp"
namespace wsi
{
@ -91,7 +93,11 @@ VkResult swapchain::add_required_extensions(VkDevice device, const VkSwapchainCr
if (m_device_data.is_present_id_enabled())
{
#if VULKAN_WSI_LAYER_EXPERIMENTAL
if (!add_swapchain_extension(m_allocator.make_unique<wsi_ext_present_id_wayland>()))
#else
if (!add_swapchain_extension(m_allocator.make_unique<wsi_ext_present_id>()))
#endif
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
@ -117,7 +123,18 @@ 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)
{
if (!add_swapchain_extension(wsi_ext_present_timing_wayland::create(m_allocator)))
/*
* 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)
{
image_first_pixel_visible_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_KHR;
}
if (!add_swapchain_extension(
wsi_ext_present_timing_wayland::create(image_first_pixel_visible_time_domain, m_allocator)))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
@ -523,6 +540,28 @@ void swapchain::present_image(const pending_present_request &pending_present)
}
}
#if VULKAN_WSI_LAYER_EXPERIMENTAL
if (m_device_data.is_present_id_enabled())
{
auto *ext = get_swapchain_extension<wsi_ext_present_id_wayland>(true);
if (m_wsi_surface->get_presentation_time_interface() != nullptr)
{
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());
wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(feedback), m_buffer_queue);
presentation_feedback *feedback_obj =
ext->insert_into_pending_present_feedback_list(pending_present.present_id, feedback);
if (feedback_obj == nullptr)
{
WSI_LOG_ERROR("Error adding to pending present feedback list");
set_error_state(VK_ERROR_SURFACE_LOST_KHR);
return;
}
register_wp_presentation_feedback_listener(feedback, feedback_obj);
}
}
#endif
wl_surface_commit(m_surface);
res = wl_display_flush(m_display);
if (res < 0)
@ -534,8 +573,15 @@ void swapchain::present_image(const pending_present_request &pending_present)
if (m_device_data.is_present_id_enabled())
{
#if VULKAN_WSI_LAYER_EXPERIMENTAL
auto *ext = get_swapchain_extension<wsi_ext_present_id_wayland>(true);
if (m_wsi_surface->get_presentation_time_interface() == nullptr)
#else
auto *ext = get_swapchain_extension<wsi_ext_present_id>(true);
ext->set_present_id(pending_present.present_id);
#endif
{
ext->mark_delivered(pending_present.present_id);
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2024 Arm Limited.
* Copyright (c) 2021-2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@ -31,7 +31,7 @@
#include <linux-dmabuf-unstable-v1-client-protocol.h>
#include <linux-explicit-synchronization-unstable-v1-protocol.h>
#include <presentation-time-client-protocol.h>
#include <memory.h>
#include <memory>
#include <functional>
namespace wsi
@ -74,6 +74,11 @@ static inline void wayland_object_destroy(wl_event_queue *obj)
wl_event_queue_destroy(obj);
}
static inline void wayland_object_destroy(struct wp_presentation_feedback *obj)
{
wp_presentation_feedback_destroy(obj);
}
template <typename T>
struct wayland_deleter
{

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "wp_presentation_feedback.hpp"
#include "present_id_wayland.hpp"
namespace wsi
{
namespace wayland
{
VWL_CAPI_CALL(void)
wp_presentation_feedback_presented(void *data, struct wp_presentation_feedback *, uint32_t, uint32_t, uint32_t,
uint32_t, uint32_t, uint32_t, uint32_t)
{
auto feedback_obj = reinterpret_cast<wsi::wayland::presentation_feedback *>(data);
if (feedback_obj->ext() != nullptr)
{
feedback_obj->ext()->mark_delivered(feedback_obj->present_id());
feedback_obj->ext()->remove_from_pending_present_feedback_list(feedback_obj->present_id());
}
}
static const wp_presentation_feedback_listener presentation_listener = {
.sync_output = NULL,
.presented = wp_presentation_feedback_presented,
.discarded = NULL,
};
VkResult register_wp_presentation_feedback_listener(struct wp_presentation_feedback *wp_presentation_feedback,
void *data)
{
int res = wp_presentation_feedback_add_listener(wp_presentation_feedback, &presentation_listener, data);
if (res < 0)
{
WSI_LOG_ERROR("Failed to add wp_presentation_feedback listener.");
return VK_ERROR_INITIALIZATION_FAILED;
}
return VK_SUCCESS;
}
} // namespace wayland
} // namespace wsi

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/** @file
* @brief Contains functions for handling the wp_presentation_feedback Wayland protocol
*/
#pragma once
#include <vulkan/vulkan.h>
#include <presentation-time-client-protocol.h>
#include "wl_object_owner.hpp"
namespace wsi
{
namespace wayland
{
class wsi_ext_present_id_wayland;
/**
* @brief Registers the listeners for wp_presentation_feedback
* @param wp_presentation_feedback - wp_presentation_feedback interface
* @param data - Data to pass to the callbacks
* @return VK_SUCCESS on success, error otherwise.
*/
VkResult register_wp_presentation_feedback_listener(struct wp_presentation_feedback *wp_presentation_feedback,
void *data);
/**
* @brief Class to hold a presentation feedback and associated attributes.
*/
class presentation_feedback
{
public:
presentation_feedback(uint64_t present_id, struct wp_presentation_feedback *feedback,
wsi_ext_present_id_wayland *ext)
: m_present_id(present_id)
, m_feedback(feedback)
, m_ext(ext)
{
}
~presentation_feedback() = default;
presentation_feedback(const presentation_feedback &feedback_obj) = delete;
presentation_feedback &operator=(const presentation_feedback &feedback_obj) = delete;
presentation_feedback(presentation_feedback &&feedback_obj)
: m_present_id(feedback_obj.m_present_id)
, m_feedback(std::move(feedback_obj.m_feedback))
, m_ext(feedback_obj.m_ext)
{
feedback_obj.reset();
}
presentation_feedback &operator=(presentation_feedback &&feedback_obj)
{
if (this == &feedback_obj)
{
return *this;
}
if (m_feedback != nullptr)
{
wp_presentation_feedback_destroy(m_feedback.get());
}
m_present_id = feedback_obj.m_present_id;
m_feedback = std::move(feedback_obj.m_feedback);
m_ext = feedback_obj.m_ext;
feedback_obj.reset();
}
void reset()
{
m_present_id = 0;
m_feedback = nullptr;
m_ext = nullptr;
}
uint64_t present_id()
{
return m_present_id;
}
struct wp_presentation_feedback *feedback()
{
return m_feedback.get();
}
wsi_ext_present_id_wayland *ext()
{
return m_ext;
}
private:
uint64_t m_present_id;
wayland_owner<struct wp_presentation_feedback> m_feedback;
wsi_ext_present_id_wayland *m_ext;
};
} // namespace wayland
} // namespace wsi

View file

@ -171,6 +171,7 @@ VkResult add_device_extensions_required_by_layer(VkPhysicalDevice phys_dev,
#if ENABLE_INSTRUMENTATION
VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME,
#endif
VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
};
for (auto extension : optional_extensions)