Implement vkWaitForPresentKHR entrypoint

This commit is contained in:
Normunds Rieksts 2025-05-09 16:04:39 +00:00 committed by Iason Paraskevopoulos
parent 01ae039cde
commit ab138f30ef
20 changed files with 841 additions and 9 deletions

View file

@ -138,6 +138,7 @@ if(BUILD_WSI_WAYLAND)
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)
target_sources(wayland_wsi PRIVATE wsi/wayland/present_wait_wayland.cpp)
endif()
pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client)
@ -217,6 +218,7 @@ if(BUILD_WSI_HEADLESS)
if(VULKAN_WSI_LAYER_EXPERIMENTAL)
target_sources(wsi_headless PRIVATE wsi/headless/present_timing_handler.cpp)
target_sources(wsi_headless PRIVATE wsi/headless/present_wait_headless.cpp)
endif()
target_include_directories(wsi_headless PRIVATE
@ -239,6 +241,10 @@ if (BUILD_WSI_DISPLAY)
wsi/display/swapchain.cpp
wsi/display/surface.cpp)
if(VULKAN_WSI_LAYER_EXPERIMENTAL)
target_sources(wsi_display PRIVATE wsi/display/present_wait_display.cpp)
endif()
pkg_check_modules(LIBDRM REQUIRED libdrm)
message(STATUS "Using libdrm include directories: ${LIBDRM_INCLUDE_DIRS}")
message(STATUS "Using libdrm ldflags: ${LIBDRM_LDFLAGS}")
@ -290,6 +296,8 @@ add_library(${PROJECT_NAME} SHARED
if (VULKAN_WSI_LAYER_EXPERIMENTAL)
target_sources(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/layer/present_timing_api.cpp)
target_sources(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/wsi/extensions/present_timing.cpp)
target_sources(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/layer/present_wait_api.cpp)
target_sources(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/wsi/extensions/present_wait.cpp)
add_definitions("-DVULKAN_WSI_LAYER_EXPERIMENTAL=1")
else()
list(APPEND JSON_COMMANDS COMMAND sed -Ei '/VK_EXT_present_timing|VK_KHR_present_wait/d' ${CMAKE_CURRENT_BINARY_DIR}/VkLayer_window_system_integration.json)

View file

@ -31,6 +31,7 @@
#include <vulkan/vulkan.h>
#include "layer/calibrated_timestamps_api.hpp"
#include "present_wait_api.hpp"
#include "wsi_layer_experimental.hpp"
#include "private_data.hpp"
#include "surface_api.hpp"
@ -372,6 +373,15 @@ VKAPI_ATTR VkResult create_device(VkPhysicalDevice physicalDevice, const VkDevic
physical_device_swapchain_maintenance1_features->swapchainMaintenance1);
}
#if VULKAN_WSI_LAYER_EXPERIMENTAL
auto *present_wait_features = util::find_extension<VkPhysicalDevicePresentWaitFeaturesKHR>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR, pCreateInfo->pNext);
if (present_wait_features != nullptr)
{
device_data.set_present_wait_enabled(present_wait_features->presentWait);
}
#endif
return VK_SUCCESS;
}
@ -580,6 +590,12 @@ wsi_layer_vkGetDeviceProcAddr(VkDevice device, const char *funcName) VWL_API_POS
{
GET_PROC_ADDR(vkGetCalibratedTimestampsKHR);
}
#if VULKAN_WSI_LAYER_EXPERIMENTAL
if (layer::device_private_data::get(device).is_device_extension_enabled(VK_KHR_PRESENT_WAIT_EXTENSION_NAME))
{
GET_PROC_ADDR(vkWaitForPresentKHR);
}
#endif
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

@ -0,0 +1,59 @@
/*
* 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_wait_api.cpp
*
* @brief Contains the Vulkan entrypoints for the present wait.
*/
#include <cassert>
#include <wsi/extensions/present_wait.hpp>
#include <wsi/swapchain_base.hpp>
#include "present_wait_api.hpp"
#if VULKAN_WSI_LAYER_EXPERIMENTAL
/**
* @brief Implements vkSetSwapchainPresentTimingQueueSizeEXT Vulkan entrypoint.
*/
VWL_VKAPI_CALL(VkResult)
wsi_layer_vkWaitForPresentKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t present_id,
uint64_t timeout) VWL_API_POST
{
assert(swapchain != VK_NULL_HANDLE);
auto &device_data = layer::device_private_data::get(device);
if (!device_data.layer_owns_swapchain(swapchain))
{
return device_data.disp.WaitForPresentKHR(device, swapchain, present_id, timeout);
}
auto *sc = reinterpret_cast<wsi::swapchain_base *>(swapchain);
auto *ext = sc->get_swapchain_extension<wsi::wsi_ext_present_wait>(true);
return ext->wait_for_present_id(present_id, timeout);
}
#endif /* VULKAN_WSI_LAYER_EXPERIMENTAL */

View file

@ -0,0 +1,39 @@
/*
* 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_wait_api.cpp
*
* @brief Contains the Vulkan entrypoints for the present wait.
*/
#include <vulkan/vulkan.h>
#include <util/macros.hpp>
#if VULKAN_WSI_LAYER_EXPERIMENTAL
VWL_VKAPI_CALL(VkResult)
wsi_layer_vkWaitForPresentKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t present_id,
uint64_t timeout) VWL_API_POST;
#endif /* VULKAN_WSI_LAYER_EXPERIMENTAL */

View file

@ -620,4 +620,16 @@ bool device_private_data::is_swapchain_maintenance1_enabled() const
return swapchain_maintenance1_enabled;
}
#if VULKAN_WSI_LAYER_EXPERIMENTAL
void device_private_data::set_present_wait_enabled(bool enable)
{
present_wait_enabled = enable;
}
bool device_private_data::is_present_wait_enabled()
{
return present_wait_enabled;
}
#endif
} /* namespace layer */

View file

@ -349,10 +349,11 @@ private:
*/
#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, ) \
EP(WaitForPresentKHR, VK_KHR_PRESENT_WAIT_EXTENSION_NAME, API_VERSION_MAX, false, )
#else
#define DEVICE_ENTRYPOINTS_LIST_EXPERIMENTAL(EP)
#endif
@ -905,6 +906,22 @@ public:
*/
bool is_swapchain_maintenance1_enabled() const;
#if VULKAN_WSI_LAYER_EXPERIMENTAL
/**
* @brief Set whether present wait feature is enabled.
*
* @return true if enabled, false otherwise.
*/
void set_present_wait_enabled(bool enable);
/**
* @brief Check whether present wait feature has been enabled.
*
* @return true if supported, false otherwise.
*/
bool is_present_wait_enabled();
#endif
private:
/* Allow util::allocator to access the private constructor */
friend util::allocator;
@ -955,18 +972,24 @@ private:
* @brief Stores whether the device supports the present ID feature.
*
*/
bool present_id_enabled;
bool present_id_enabled{ false };
/**
* @brief Stores whether the device has enabled support for the swapchain maintenance1 features.
*/
bool swapchain_maintenance1_enabled;
bool swapchain_maintenance1_enabled{ false };
#if VULKAN_WSI_LAYER_EXPERIMENTAL
/**
* @brief Stores whether the device supports the present wait feature.
*
*/
bool present_wait_enabled{ false };
/**
* @brief Stores whether the device has enabled support for the present timing features.
*/
bool present_timing_enabled;
bool present_timing_enabled{ false };
#endif
};

View file

@ -0,0 +1,49 @@
/*
* 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_wait_headless.cpp
*
* @brief Contains the base class declaration for the VK_KHR_present_wait extension.
*/
#include "present_wait_display.hpp"
namespace wsi
{
namespace display
{
wsi_ext_present_wait_display::wsi_ext_present_wait_display(wsi_ext_present_id &present_id_extension)
: wsi_ext_present_wait(present_id_extension)
{
}
VkResult wsi_ext_present_wait_display::wait_for_update(uint64_t present_id, uint64_t timeout_in_ns)
{
return m_present_id_ext.wait_for_present_id(present_id, timeout_in_ns) ? VK_SUCCESS : VK_TIMEOUT;
}
} /* namespace wayland */
} /* namespace wsi */

View file

@ -0,0 +1,72 @@
/*
* 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_wait_headless.hpp
*
* @brief Contains the base class declaration for the VK_KHR_present_wait extension.
*/
#pragma once
#include <wsi/extensions/present_wait.hpp>
namespace wsi
{
namespace display
{
/**
* @brief Present wait extension class
*
* This class defines the present wait extension
* implementation.
*/
class wsi_ext_present_wait_display : public wsi::wsi_ext_present_wait
{
public:
/**
* @brief Constructs present ID class.
*
* @param present_id_extension Present ID extension that this class will use to query
* the last delivered present ID for the swapchain. This extension pointer must outlive
* this class.
*/
wsi_ext_present_wait_display(wsi_ext_present_id &present_id_extension);
private:
/**
* @brief Backend specific implementation that will wait for the present ID to
* be updated.
* @param present_id Present ID value to wait for.
* @param timeout_in_ns Timeout in nanoseconds.
* @return VK_SUCCESS if present ID value was updated to be above or equal to the requested wait value.
* Other error codes otherwise.
*/
VkResult wait_for_update(uint64_t present_id, uint64_t timeout_in_ns) override;
};
} /* namespace wayland */
} /* namespace wsi */

View file

@ -38,6 +38,7 @@
#include <wsi/swapchain_base.hpp>
#include "swapchain.hpp"
#include "present_wait_display.hpp"
namespace wsi
{
@ -105,6 +106,17 @@ VkResult swapchain::add_required_extensions(VkDevice device, const VkSwapchainCr
}
}
#if VULKAN_WSI_LAYER_EXPERIMENTAL
if (m_device_data.is_present_wait_enabled())
{
if (!add_swapchain_extension(
m_allocator.make_unique<wsi_ext_present_wait_display>(*get_swapchain_extension<wsi_ext_present_id>(true))))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
}
#endif
return VK_SUCCESS;
}

View file

@ -38,8 +38,36 @@ void wsi_ext_present_id::mark_delivered(uint64_t present_id)
/* 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))
{
std::unique_lock lock(m_mutex);
m_last_delivered_id.store(present_id, std::memory_order_relaxed);
}
m_present_id_changed.notify_all();
}
bool wsi_ext_present_id::wait_for_present_id(uint64_t present_id, uint64_t timeout_in_ns)
{
if (m_last_delivered_id.load() >= present_id)
{
return VK_SUCCESS;
}
std::unique_lock lock(m_mutex);
try
{
return m_present_id_changed.wait_for(lock, std::chrono::nanoseconds(timeout_in_ns),
[&]() { return m_last_delivered_id.load() >= present_id; });
}
catch (const std::system_error &e)
{
WSI_LOG_ERROR("Failed to wait for conditional variable. Code: %d, message: %s\n", e.code().value(), e.what());
}
return false;
}
uint64_t wsi_ext_present_id::get_last_delivered_present_id() const
{
return m_last_delivered_id.load();
}
};

View file

@ -32,7 +32,9 @@
#include <util/custom_allocator.hpp>
#include <util/macros.hpp>
#include <util/log.hpp>
#include <atomic>
#include <condition_variable>
#include "wsi_extension.hpp"
@ -58,11 +60,37 @@ public:
*/
void mark_delivered(uint64_t present_id);
/**
* @brief Waits for present ID to be above or equal to the @p value.
*
* @param value The value to wait for.
* @param timeout_in_ns Timeout in nanoseconds.
* @return true The present ID value is equal or higher than @p value
* @return false The present ID is lower than @p value and timeout occured
*/
bool wait_for_present_id(uint64_t present_id, uint64_t timeout_in_ns);
/**
* @brief Get the last delivered present ID value.
* @return Present ID
*/
uint64_t get_last_delivered_present_id() const;
private:
/**
* @brief Most recently delivered present ID for this swapchain.
*/
std::atomic<uint64_t> m_last_delivered_id{ 0 };
/**
* @brief Conditional variable that notifies whenever present ID value has changed.
*/
std::condition_variable m_present_id_changed;
/**
* @brief Mutex for m_present_id_changed conditional variable.
*/
std::mutex m_mutex;
};
} /* namespace wsi */

View file

@ -0,0 +1,53 @@
/*
* 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_wait.cpp
*
* @brief Contains the base class declaration for the VK_KHR_present_wait extension.
*/
#include "present_wait.hpp"
#include <util/macros.hpp>
namespace wsi
{
wsi_ext_present_wait::wsi_ext_present_wait(wsi_ext_present_id &present_id_extension)
: m_present_id_ext(present_id_extension)
{
}
VkResult wsi_ext_present_wait::wait_for_present_id(uint64_t present_id, uint64_t timeout_in_ns)
{
if (m_present_id_ext.get_last_delivered_present_id() >= present_id)
{
return VK_SUCCESS;
}
/* We don't need to check if swapchain is OUT_OF_DATE as any queue submissions that were successful
* should be dispatched by the swapchain even if it has been deprecated. */
return wait_for_update(present_id, timeout_in_ns);
}
};

View file

@ -0,0 +1,91 @@
/*
* 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_wait.hpp
*
* @brief Contains the base class declaration for the VK_KHR_present_wait extension.
*/
#pragma once
#include "wsi_extension.hpp"
#include "present_id.hpp"
#include <vulkan/vulkan.h>
namespace wsi
{
/**
* @brief Present wait extension class
*
* This class defines the present wait extension
* implementation.
*/
class wsi_ext_present_wait : public wsi_ext
{
public:
/**
* @brief The name of the extension.
*/
WSI_DEFINE_EXTENSION(VK_KHR_PRESENT_WAIT_EXTENSION_NAME);
/**
* @brief Constructs present ID class.
*
* @param present_id_extension Present ID extension that this class will use to query
* the last delivered present ID for the swapchain. This extension pointer must outlive
* this class.
*/
wsi_ext_present_wait(wsi_ext_present_id &present_id_extension);
/**
* @brief Waits for present ID to be updated.
*
* @param present_id Present ID value to wait for.
* @param timeout_in_ns Timeout in nanoseconds.
* @return VK_SUCCESS if present ID value was updated to be above or equal to the requested wait value.
* Other error codes otherwise.
*/
VkResult wait_for_present_id(uint64_t present_id, uint64_t timeout_in_ns);
protected:
/**
* @brief Backend specific implementation that will wait for the present ID to
* be updated.
* @param present_id Present ID value to wait for.
* @param timeout_in_ns Timeout in nanoseconds.
* @return VK_SUCCESS if present ID value was updated to be above or equal to the requested wait value.
* Other error codes otherwise.
*/
virtual VkResult wait_for_update(uint64_t present_id, uint64_t timeout_in_ns) = 0;
/**
* @brief Present ID extension pointer.
*/
wsi_ext_present_id &m_present_id_ext;
};
} /* namespace wsi */

View file

@ -0,0 +1,49 @@
/*
* 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_wait_headless.cpp
*
* @brief Contains the base class declaration for the VK_KHR_present_wait extension.
*/
#include "present_wait_headless.hpp"
namespace wsi
{
namespace headless
{
wsi_ext_present_wait_headless::wsi_ext_present_wait_headless(wsi_ext_present_id &present_id_extension)
: wsi_ext_present_wait(present_id_extension)
{
}
VkResult wsi_ext_present_wait_headless::wait_for_update(uint64_t present_id, uint64_t timeout_in_ns)
{
return m_present_id_ext.wait_for_present_id(present_id, timeout_in_ns) ? VK_SUCCESS : VK_TIMEOUT;
}
} /* namespace wayland */
} /* namespace wsi */

View file

@ -0,0 +1,72 @@
/*
* 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_wait_headless.hpp
*
* @brief Contains the base class declaration for the VK_KHR_present_wait extension.
*/
#pragma once
#include <wsi/extensions/present_wait.hpp>
namespace wsi
{
namespace headless
{
/**
* @brief Present wait extension class
*
* This class defines the present wait extension
* implementation.
*/
class wsi_ext_present_wait_headless : public wsi::wsi_ext_present_wait
{
public:
/**
* @brief Constructs present ID class.
*
* @param present_id_extension Present ID extension that this class will use to query
* the last delivered present ID for the swapchain. This extension pointer must outlive
* this class.
*/
wsi_ext_present_wait_headless(wsi_ext_present_id &present_id_extension);
private:
/**
* @brief Backend specific implementation that will wait for the present ID to
* be updated.
* @param present_id Present ID value to wait for.
* @param timeout_in_ns Timeout in nanoseconds.
* @return VK_SUCCESS if present ID value was updated to be above or equal to the requested wait value.
* Other error codes otherwise.
*/
VkResult wait_for_update(uint64_t present_id, uint64_t timeout_in_ns) override;
};
} /* namespace wayland */
} /* namespace wsi */

View file

@ -42,6 +42,7 @@
#include "util/macros.hpp"
#include "present_timing_handler.hpp"
#include "present_wait_headless.hpp"
namespace wsi
{
@ -112,6 +113,17 @@ VkResult swapchain::add_required_extensions(VkDevice device, const VkSwapchainCr
}
#endif
#if VULKAN_WSI_LAYER_EXPERIMENTAL
if (m_device_data.is_present_wait_enabled())
{
if (!add_swapchain_extension(m_allocator.make_unique<wsi_ext_present_wait_headless>(
*get_swapchain_extension<wsi_ext_present_id>(true))))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
}
#endif
return VK_SUCCESS;
}

View file

@ -0,0 +1,97 @@
/*
* 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_wait_wayland.cpp
*
* @brief Contains the base class declaration for the VK_KHR_present_wait extension.
*/
#include "present_wait_wayland.hpp"
#include "wl_helpers.hpp"
#include <limits.h>
namespace wsi
{
namespace wayland
{
wsi_ext_present_wait_wayland::wsi_ext_present_wait_wayland(wsi_ext_present_id &present_id_extension)
: wsi_ext_present_wait(present_id_extension)
{
}
void wsi_ext_present_wait_wayland::set_wayland_dispatcher(wl_display *display, struct wl_event_queue *queue)
{
/* These objects shouldn't be set twice */
assert(m_display == nullptr);
assert(m_queue == nullptr);
m_display = display;
m_queue = queue;
}
VkResult wsi_ext_present_wait_wayland::wait_for_update(uint64_t present_id, uint64_t timeout_in_ns)
{
assert(m_display != nullptr);
assert(m_queue != nullptr);
do
{
if (m_present_id_ext.get_last_delivered_present_id() >= present_id)
{
return VK_SUCCESS;
}
int delay_in_ms = 0;
if (timeout_in_ns != UINT64_MAX)
{
uint64_t min_delay_in_ns = std::min(static_cast<uint64_t>(5000000ULL /* 5ms */), timeout_in_ns);
timeout_in_ns -= min_delay_in_ns;
delay_in_ms = std::min(static_cast<unsigned long long>(INT_MAX), min_delay_in_ns / 1000ULL / 1000UL);
}
else
{
/* For the infinite wait, do a repeated timeout of 5s to avoid deadlock where
* the queue could be dispatched by another thread and no other events would be
* dispatched from the queue anymore from the application until this call returns. */
delay_in_ms = 5000;
}
/* timeout in dispatch_queue is not an issue as it could have been dispatched by another thread
* at the same time. */
if (dispatch_queue(m_display, m_queue, delay_in_ms) == -1 /*error but not timeout*/)
{
return VK_ERROR_SURFACE_LOST_KHR;
}
} while (timeout_in_ns > 0);
return VK_TIMEOUT;
}
} /* namespace wayland */
} /* namespace wsi */

View file

@ -0,0 +1,92 @@
/*
* 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_wait_wayland.hpp
*
* @brief Contains the base class declaration for the VK_KHR_present_wait extension.
*/
#pragma once
#include <wsi/extensions/present_wait.hpp>
#include <wayland-client.h>
namespace wsi
{
namespace wayland
{
/**
* @brief Present wait extension class
*
* This class defines the present wait extension
* implementation.
*/
class wsi_ext_present_wait_wayland : public wsi::wsi_ext_present_wait
{
public:
/**
* @brief Constructs present ID class.
*
* @param present_id_extension Present ID extension that this class will use to query
* the last delivered present ID for the swapchain. This extension pointer must outlive
* this class.
*/
wsi_ext_present_wait_wayland(wsi_ext_present_id &present_id_extension);
/**
* @brief Set the wayland dispatch object.
*
* @param m_display The Wayland display object
* @param m_queue The Wayland queue object.
*/
void set_wayland_dispatcher(wl_display *m_display, struct wl_event_queue *m_queue);
private:
/**
* @brief Backend specific implementation that will wait for the present ID to
* be updated.
* @param present_id Present ID value to wait for.
* @param timeout_in_ns Timeout in nanoseconds.
* @return VK_SUCCESS if present ID value was updated to be above or equal to the requested wait value.
* Other error codes otherwise.
*/
VkResult wait_for_update(uint64_t present_id, uint64_t timeout_in_ns) override;
private:
/**
* @brief Wayland display object.
*/
wl_display *m_display{};
/**
* @brief Wayland queue object.
*/
struct wl_event_queue *m_queue{};
};
} /* namespace wayland */
} /* namespace wsi */

View file

@ -49,6 +49,7 @@
#include "present_timing_handler.hpp"
#include "present_id_wayland.hpp"
#include "present_wait_wayland.hpp"
#include "wp_presentation_feedback.hpp"
namespace wsi
@ -115,6 +116,17 @@ VkResult swapchain::add_required_extensions(VkDevice device, const VkSwapchainCr
}
}
#if VULKAN_WSI_LAYER_EXPERIMENTAL
if (m_device_data.is_present_wait_enabled())
{
if (!add_swapchain_extension(
m_allocator.make_unique<wsi_ext_present_wait_wayland>(*get_swapchain_extension<wsi_ext_present_id>(true))))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
}
#endif
if (m_device_data.should_layer_handle_frame_boundary_events())
{
if (!add_swapchain_extension(m_allocator.make_unique<wsi_ext_frame_boundary>(m_device_data)))
@ -182,6 +194,14 @@ VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKH
use_presentation_thread =
WAYLAND_FIFO_PRESENTATION_THREAD_ENABLED && (m_present_mode != VK_PRESENT_MODE_MAILBOX_KHR);
#if VULKAN_WSI_LAYER_EXPERIMENTAL
auto present_wait = get_swapchain_extension<wsi_ext_present_wait_wayland>();
if (present_wait)
{
present_wait->set_wayland_dispatcher(m_display, m_buffer_queue);
}
#endif
return VK_SUCCESS;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2019, 2021, 2024 Arm Limited.
* Copyright (c) 2017-2019, 2021, 2024-2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@ -44,7 +44,7 @@
* @param display Wayland display to dispatch events from
* @param queue Event queue to dispatch events from; other event queues will not have their handlers called from
* within this function
* @param timeout Maximum time to wait for events to arrive, in milliseconds
* @param timeout Maximum time to wait for events to arrive, in milliseconds. A negative value means infinite timeout.
* @return 1 if one or more events were dispatched on this queue, 0 if the timeout was reached without any
* events being dispatched, or -1 on error.
*/