From cde0782bdc00ec6556fcd8e4ab07ab07233289a0 Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Tue, 9 Dec 2025 09:33:48 +0000 Subject: [PATCH 1/7] Allow layers to expose new extensions --- generator/vk_layer/source/instance.cpp | 11 ++++++- generator/vk_layer/source/instance.hpp | 31 ++++++++++++++++++-- layer_example/source/instance.cpp | 15 ++++++++-- layer_example/source/instance.hpp | 31 ++++++++++++++++++-- layer_gpu_profile/source/instance.cpp | 13 ++++++-- layer_gpu_profile/source/instance.hpp | 31 ++++++++++++++++++-- layer_gpu_support/source/instance.cpp | 13 ++++++-- layer_gpu_support/source/instance.hpp | 31 ++++++++++++++++++-- layer_gpu_timeline/source/instance.cpp | 13 ++++++-- layer_gpu_timeline/source/instance.hpp | 31 ++++++++++++++++++-- source_common/framework/manual_functions.cpp | 10 +++++-- 11 files changed, 208 insertions(+), 22 deletions(-) diff --git a/generator/vk_layer/source/instance.cpp b/generator/vk_layer/source/instance.cpp index 164ffb4f..4b09e3e0 100644 --- a/generator/vk_layer/source/instance.cpp +++ b/generator/vk_layer/source/instance.cpp @@ -38,10 +38,19 @@ static std::unordered_map> g_instances; const APIVersion Instance::minAPIVersion { 1, 1 }; /* See header for documentation. */ -const std::vector Instance::extraExtensions { +const std::vector Instance::requiredDriverExtensions { VK_EXT_DEBUG_UTILS_EXTENSION_NAME }; +/* See header for documentation. */ +const std::vector> Instance::injectedInstanceExtensions {}; + +/* See header for documentation. */ +const std::vector> Instance::injectedDeviceExtensions {}; + +/* See header for documentation. */ +const std::vector Instance::injectedAPIFunctions {}; + /* See header for documentation. */ void Instance::store( VkInstance handle, diff --git a/generator/vk_layer/source/instance.hpp b/generator/vk_layer/source/instance.hpp index 8660e3c9..7864514e 100644 --- a/generator/vk_layer/source/instance.hpp +++ b/generator/vk_layer/source/instance.hpp @@ -143,7 +143,34 @@ class Instance static const APIVersion minAPIVersion; /** - * @brief The minimum set of instance extensions needed by this layer. + * @brief Required extensions from the driver. + * + * The layer will attempt to enable these even if the application does not. + */ + static const std::vector requiredDriverExtensions; + + /** + * @brief Additional instance extensions injected by the layer. + * + * The layer will expose these even if the driver does not. + */ + static const std::vector> injectedInstanceExtensions; + + /** + * @brief Additional device extensions injected by the layer. + * + * The layer will expose these even if the driver does not. + */ + static const std::vector> injectedDeviceExtensions; + + /** + * @brief Additional API functions that are injected by the layer. + * + * This list must include both instance and device functions, because + * vkGetInstanceProcAddr can (inefficiently) be used to return device + * functions. + * + * The layer will expose these even if the driver does not. */ - static const std::vector extraExtensions; + static const std::vector injectedAPIFunctions; }; diff --git a/layer_example/source/instance.cpp b/layer_example/source/instance.cpp index 78a7941d..37a066b5 100644 --- a/layer_example/source/instance.cpp +++ b/layer_example/source/instance.cpp @@ -35,10 +35,21 @@ static std::unordered_map> g_instances; /* See header for documentation. */ -const APIVersion Instance::minAPIVersion {1, 1}; +const APIVersion Instance::minAPIVersion { 1, 1 }; /* See header for documentation. */ -const std::vector Instance::extraExtensions {VK_EXT_DEBUG_UTILS_EXTENSION_NAME}; +const std::vector Instance::requiredDriverExtensions { + VK_EXT_DEBUG_UTILS_EXTENSION_NAME +}; + +/* See header for documentation. */ +const std::vector> Instance::injectedInstanceExtensions {}; + +/* See header for documentation. */ +const std::vector> Instance::injectedDeviceExtensions {}; + +/* See header for documentation. */ +const std::vector Instance::injectedAPIFunctions {}; /* See header for documentation. */ void Instance::store(VkInstance handle, std::unique_ptr& instance) diff --git a/layer_example/source/instance.hpp b/layer_example/source/instance.hpp index cc05dcb8..b92887ca 100644 --- a/layer_example/source/instance.hpp +++ b/layer_example/source/instance.hpp @@ -137,7 +137,34 @@ class Instance static const APIVersion minAPIVersion; /** - * @brief The minimum set of instance extensions needed by this layer. + * @brief Required extensions from the driver. + * + * The layer will attempt to enable these even if the application does not. + */ + static const std::vector requiredDriverExtensions; + + /** + * @brief Additional instance extensions injected by the layer. + * + * The layer will expose these even if the driver does not. + */ + static const std::vector> injectedInstanceExtensions; + + /** + * @brief Additional device extensions injected by the layer. + * + * The layer will expose these even if the driver does not. + */ + static const std::vector> injectedDeviceExtensions; + + /** + * @brief Additional API functions that are injected by the layer. + * + * This list must include both instance and device functions, because + * vkGetInstanceProcAddr can (inefficiently) be used to return device + * functions. + * + * The layer will expose these even if the driver does not. */ - static const std::vector extraExtensions; + static const std::vector injectedAPIFunctions; }; diff --git a/layer_gpu_profile/source/instance.cpp b/layer_gpu_profile/source/instance.cpp index 71bd4c18..2b13e4ad 100644 --- a/layer_gpu_profile/source/instance.cpp +++ b/layer_gpu_profile/source/instance.cpp @@ -35,13 +35,22 @@ static std::unordered_map> g_instances; /* See header for documentation. */ -const APIVersion Instance::minAPIVersion {1, 1}; +const APIVersion Instance::minAPIVersion { 1, 1 }; /* See header for documentation. */ -const std::vector Instance::extraExtensions { +const std::vector Instance::requiredDriverExtensions { VK_EXT_DEBUG_UTILS_EXTENSION_NAME, }; +/* See header for documentation. */ +const std::vector> Instance::injectedInstanceExtensions {}; + +/* See header for documentation. */ +const std::vector> Instance::injectedDeviceExtensions {}; + +/* See header for documentation. */ +const std::vector Instance::injectedAPIFunctions {}; + /* See header for documentation. */ void Instance::store(VkInstance handle, std::unique_ptr& instance) { diff --git a/layer_gpu_profile/source/instance.hpp b/layer_gpu_profile/source/instance.hpp index 854745f3..8d051b19 100644 --- a/layer_gpu_profile/source/instance.hpp +++ b/layer_gpu_profile/source/instance.hpp @@ -143,7 +143,34 @@ class Instance static const APIVersion minAPIVersion; /** - * @brief The minimum set of instance extensions needed by this layer. + * @brief Required extensions from the driver. + * + * The layer will attempt to enable these even if the application does not. + */ + static const std::vector requiredDriverExtensions; + + /** + * @brief Additional instance extensions injected by the layer. + * + * The layer will expose these even if the driver does not. + */ + static const std::vector> injectedInstanceExtensions; + + /** + * @brief Additional device extensions injected by the layer. + * + * The layer will expose these even if the driver does not. + */ + static const std::vector> injectedDeviceExtensions; + + /** + * @brief Additional API functions that are injected by the layer. + * + * This list must include both instance and device functions, because + * vkGetInstanceProcAddr can (inefficiently) be used to return device + * functions. + * + * The layer will expose these even if the driver does not. */ - static const std::vector extraExtensions; + static const std::vector injectedAPIFunctions; }; diff --git a/layer_gpu_support/source/instance.cpp b/layer_gpu_support/source/instance.cpp index 71bd4c18..2b13e4ad 100644 --- a/layer_gpu_support/source/instance.cpp +++ b/layer_gpu_support/source/instance.cpp @@ -35,13 +35,22 @@ static std::unordered_map> g_instances; /* See header for documentation. */ -const APIVersion Instance::minAPIVersion {1, 1}; +const APIVersion Instance::minAPIVersion { 1, 1 }; /* See header for documentation. */ -const std::vector Instance::extraExtensions { +const std::vector Instance::requiredDriverExtensions { VK_EXT_DEBUG_UTILS_EXTENSION_NAME, }; +/* See header for documentation. */ +const std::vector> Instance::injectedInstanceExtensions {}; + +/* See header for documentation. */ +const std::vector> Instance::injectedDeviceExtensions {}; + +/* See header for documentation. */ +const std::vector Instance::injectedAPIFunctions {}; + /* See header for documentation. */ void Instance::store(VkInstance handle, std::unique_ptr& instance) { diff --git a/layer_gpu_support/source/instance.hpp b/layer_gpu_support/source/instance.hpp index df78da73..057c196c 100644 --- a/layer_gpu_support/source/instance.hpp +++ b/layer_gpu_support/source/instance.hpp @@ -142,7 +142,34 @@ class Instance static const APIVersion minAPIVersion; /** - * @brief The minimum set of instance extensions needed by this layer. + * @brief Required extensions from the driver. + * + * The layer will attempt to enable these even if the application does not. + */ + static const std::vector requiredDriverExtensions; + + /** + * @brief Additional instance extensions injected by the layer. + * + * The layer will expose these even if the driver does not. + */ + static const std::vector> injectedInstanceExtensions; + + /** + * @brief Additional device extensions injected by the layer. + * + * The layer will expose these even if the driver does not. + */ + static const std::vector> injectedDeviceExtensions; + + /** + * @brief Additional API functions that are injected by the layer. + * + * This list must include both instance and device functions, because + * vkGetInstanceProcAddr can (inefficiently) be used to return device + * functions. + * + * The layer will expose these even if the driver does not. */ - static const std::vector extraExtensions; + static const std::vector injectedAPIFunctions; }; diff --git a/layer_gpu_timeline/source/instance.cpp b/layer_gpu_timeline/source/instance.cpp index 71bd4c18..2b13e4ad 100644 --- a/layer_gpu_timeline/source/instance.cpp +++ b/layer_gpu_timeline/source/instance.cpp @@ -35,13 +35,22 @@ static std::unordered_map> g_instances; /* See header for documentation. */ -const APIVersion Instance::minAPIVersion {1, 1}; +const APIVersion Instance::minAPIVersion { 1, 1 }; /* See header for documentation. */ -const std::vector Instance::extraExtensions { +const std::vector Instance::requiredDriverExtensions { VK_EXT_DEBUG_UTILS_EXTENSION_NAME, }; +/* See header for documentation. */ +const std::vector> Instance::injectedInstanceExtensions {}; + +/* See header for documentation. */ +const std::vector> Instance::injectedDeviceExtensions {}; + +/* See header for documentation. */ +const std::vector Instance::injectedAPIFunctions {}; + /* See header for documentation. */ void Instance::store(VkInstance handle, std::unique_ptr& instance) { diff --git a/layer_gpu_timeline/source/instance.hpp b/layer_gpu_timeline/source/instance.hpp index cc05dcb8..b92887ca 100644 --- a/layer_gpu_timeline/source/instance.hpp +++ b/layer_gpu_timeline/source/instance.hpp @@ -137,7 +137,34 @@ class Instance static const APIVersion minAPIVersion; /** - * @brief The minimum set of instance extensions needed by this layer. + * @brief Required extensions from the driver. + * + * The layer will attempt to enable these even if the application does not. + */ + static const std::vector requiredDriverExtensions; + + /** + * @brief Additional instance extensions injected by the layer. + * + * The layer will expose these even if the driver does not. + */ + static const std::vector> injectedInstanceExtensions; + + /** + * @brief Additional device extensions injected by the layer. + * + * The layer will expose these even if the driver does not. + */ + static const std::vector> injectedDeviceExtensions; + + /** + * @brief Additional API functions that are injected by the layer. + * + * This list must include both instance and device functions, because + * vkGetInstanceProcAddr can (inefficiently) be used to return device + * functions. + * + * The layer will expose these even if the driver does not. */ - static const std::vector extraExtensions; + static const std::vector injectedAPIFunctions; }; diff --git a/source_common/framework/manual_functions.cpp b/source_common/framework/manual_functions.cpp index 2f68292a..df63b4dc 100644 --- a/source_common/framework/manual_functions.cpp +++ b/source_common/framework/manual_functions.cpp @@ -381,7 +381,9 @@ std::vector cloneExtensionList(uint32_t extensionCount, const char* static void enableInstanceVkExtDebugUtils(vku::safe_VkInstanceCreateInfo& createInfo, const std::vector& supported) { - static const std::string target {VK_EXT_DEBUG_UTILS_EXTENSION_NAME}; + static const std::string target { + VK_EXT_DEBUG_UTILS_EXTENSION_NAME + }; // Test if the desired extension is supported. If supported list is // empty then we didn't query and assume extension is supported. @@ -411,7 +413,9 @@ void enableDeviceVkKhrTimelineSemaphore(Instance& instance, UNUSED(instance); UNUSED(physicalDevice); - static const std::string target {VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME}; + static const std::string target { + VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME + }; // We know we can const-cast here because createInfo is a safe-struct clone void* pNextBase = const_cast(createInfo.pNext); @@ -766,7 +770,7 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateInstance(const VkInsta newCreateInfoSafe.pApplicationInfo->apiVersion = VK_MAKE_API_VERSION(0, newVersion.first, newVersion.second, 0); // Enable extra extensions - for (const auto& newExt : Instance::extraExtensions) + for (const auto& newExt : Instance::requiredDriverExtensions) { if (newExt == VK_EXT_DEBUG_UTILS_EXTENSION_NAME) { From 32abeef315cb9522e2ee022390fda880efebe75d Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Tue, 9 Dec 2025 21:34:58 +0000 Subject: [PATCH 2/7] Expose extension strings --- docs/extension_support.md | 96 ++++++ generator/vk_layer/source/instance.cpp | 2 +- generator/vk_layer/source/instance.hpp | 5 +- layer_example/source/instance.cpp | 2 +- layer_example/source/instance.hpp | 5 +- layer_gpu_profile/source/instance.cpp | 2 +- layer_gpu_profile/source/instance.hpp | 5 +- layer_gpu_support/source/instance.cpp | 2 +- layer_gpu_support/source/instance.hpp | 5 +- .../source/layer_device_functions_image.cpp | 8 +- layer_gpu_timeline/source/CMakeLists.txt | 1 + layer_gpu_timeline/source/device.hpp | 8 + layer_gpu_timeline/source/instance.cpp | 4 +- layer_gpu_timeline/source/instance.hpp | 5 +- .../source/layer_device_functions.hpp | 8 + .../source/layer_device_functions_queue.cpp | 176 +++++++++- .../source/layer_instance_functions.cpp | 117 +++++++ .../source/layer_instance_functions.hpp | 50 +++ source_common/framework/manual_functions.cpp | 306 +++++++++++++++--- source_common/framework/manual_functions.hpp | 21 +- source_common/trackers/render_pass.cpp | 2 +- 21 files changed, 760 insertions(+), 70 deletions(-) create mode 100644 docs/extension_support.md create mode 100644 layer_gpu_timeline/source/layer_instance_functions.cpp create mode 100644 layer_gpu_timeline/source/layer_instance_functions.hpp diff --git a/docs/extension_support.md b/docs/extension_support.md new file mode 100644 index 00000000..c991a0d7 --- /dev/null +++ b/docs/extension_support.md @@ -0,0 +1,96 @@ +# Extension support in a layer + +It might be useful for some layers to implement an extension, such as +`VK_EXT_frame_boundary`, even if the underlying driver does not support it. +This page explains the general approach that needs to be taken, and the +specific API modifications that need to be applied for specific extensions. + +Note that the core framework allows you to expose additional extensions via +the default `vkEnumerate*ExtensionProperties()` implementation, but per-layer +code must implement the API modifications to other functions as needed. + +## Exposing a new extension + +New extensions are advertised to applications by adding the extension string to +the list returned by `vkEnumerate*ExtensionProperties()`. This functionality +is provided in the common framework default functions. Layer implementations +add the new extension information that they want to expose to either: + +* `Instance::injectedInstanceExtensions` for instance extensions. +* `Instance::injectedDeviceExtensions` for device extensions. + +Device extensions will be removed from this list if we can detect that the +underlying device already supports them, which means we can just pass through. + +### Handling extended API entry points + +All entrypoints that are touched by an extension need to be intercepted with +a `user_tag` version of a function, which will implement the functionality that +the layer requires. + +If the driver beneath the layer actually supports the extension, the extended +API parameters can be passed down to the driver without modification. This +scenario can be detected by checking that the extension name is no longer in +the `injectedExtensions` list, although the layer will probably want to cache +this check to reduce performance overhead. + +If the driver beneath the layer does not support the extension, the extended +API parameters must be rewritten to remove the extension before passing down +to the driver. User structure inputs to the Vulkan API are usually marked as +`const`, so we must take a safe-struct copy which we can modify and pass +that copy to the driver. + +## Common extension notes + +This section is a set of brief notes about extensions that we have implemented, +summarizing the changes needed and referencing where you can find an example +of the changes if you need something similar. + +### VK_EXT_frame_boundary + +This extension allows applications to annotate arbitrary submit calls to +indicate which frame the submitted work belongs to, instead of relying on +`vkQueuePresent()`. This can be useful for multi-threaded applications, +where CPU processing for frames can overlap, and for applications which +do not have frames, but that want to use tools such as RenderDoc that +require them. + +#### Exposing extension + +Adding exposure handling: + +* Add `VK_EXT_frame_boundary` to device extension list. +* Add `VkPhysicalDeviceFrameBoundary` to `VkPhysicalDeviceFeatures2.pNext` list + returned by `vkGetPhysicalDeviceFeatures2()`, or force existing structure to + `VK_TRUE`, if not supported by driver. +* Query `VkPhysicalDeviceFrameBoundary` in `VkDeviceCreateInfo.pNext` to see if + application enabled the extension. Strip if not supported by the driver. + +#### Implementing extension + +Adding implementation handling: + +* Add `VkFrameBoundaryEXT` extension struct handling to: + * `vkQueueSubmit()` + * `vkQueueSubmit2()` + * `vkQueuePresent()` + * `vkQueueBindSparse()` + +#### Implementation notes + +Most applications using this that I have seen are using it to demarcate frames +when using a single submitting render thread for off-screen rendering or +compute use cases that do not use `vkQueuePresent()`. In these systems just +detecting a change in frame ID is enough to indicate "frame changed", much +how we would use `vkQueuePresent()` to do the same without this extension. + +It is possible for applications to have multiple concurrent frames being +submitted in an overlapping manner, which can be handled by tagging work with +the frame ID found in the extension structure for each `vkQueue*()` call. This +will require downstream data handling to cope with overlapping frame +submissions, which most of our layers do not handle, as it is rarely +encountered. + +- - - + +_Copyright © 2025, Arm Limited and contributors._ diff --git a/generator/vk_layer/source/instance.cpp b/generator/vk_layer/source/instance.cpp index 4b09e3e0..edf2443e 100644 --- a/generator/vk_layer/source/instance.cpp +++ b/generator/vk_layer/source/instance.cpp @@ -46,7 +46,7 @@ const std::vector Instance::requiredDriverExtensions { const std::vector> Instance::injectedInstanceExtensions {}; /* See header for documentation. */ -const std::vector> Instance::injectedDeviceExtensions {}; +std::vector> Instance::injectedDeviceExtensions {}; /* See header for documentation. */ const std::vector Instance::injectedAPIFunctions {}; diff --git a/generator/vk_layer/source/instance.hpp b/generator/vk_layer/source/instance.hpp index 7864514e..f5995336 100644 --- a/generator/vk_layer/source/instance.hpp +++ b/generator/vk_layer/source/instance.hpp @@ -159,9 +159,10 @@ class Instance /** * @brief Additional device extensions injected by the layer. * - * The layer will expose these even if the driver does not. + * The layer will expose these even if the driver does not. Items are + * removed from the list if the driver already exposes the extension. */ - static const std::vector> injectedDeviceExtensions; + static std::vector> injectedDeviceExtensions; /** * @brief Additional API functions that are injected by the layer. diff --git a/layer_example/source/instance.cpp b/layer_example/source/instance.cpp index 37a066b5..2daee7b4 100644 --- a/layer_example/source/instance.cpp +++ b/layer_example/source/instance.cpp @@ -46,7 +46,7 @@ const std::vector Instance::requiredDriverExtensions { const std::vector> Instance::injectedInstanceExtensions {}; /* See header for documentation. */ -const std::vector> Instance::injectedDeviceExtensions {}; +std::vector> Instance::injectedDeviceExtensions {}; /* See header for documentation. */ const std::vector Instance::injectedAPIFunctions {}; diff --git a/layer_example/source/instance.hpp b/layer_example/source/instance.hpp index b92887ca..19ee1aab 100644 --- a/layer_example/source/instance.hpp +++ b/layer_example/source/instance.hpp @@ -153,9 +153,10 @@ class Instance /** * @brief Additional device extensions injected by the layer. * - * The layer will expose these even if the driver does not. + * The layer will expose these even if the driver does not. Items are + * removed from the list if the driver already exposes the extension. */ - static const std::vector> injectedDeviceExtensions; + static std::vector> injectedDeviceExtensions; /** * @brief Additional API functions that are injected by the layer. diff --git a/layer_gpu_profile/source/instance.cpp b/layer_gpu_profile/source/instance.cpp index 2b13e4ad..a49a1610 100644 --- a/layer_gpu_profile/source/instance.cpp +++ b/layer_gpu_profile/source/instance.cpp @@ -46,7 +46,7 @@ const std::vector Instance::requiredDriverExtensions { const std::vector> Instance::injectedInstanceExtensions {}; /* See header for documentation. */ -const std::vector> Instance::injectedDeviceExtensions {}; +std::vector> Instance::injectedDeviceExtensions {}; /* See header for documentation. */ const std::vector Instance::injectedAPIFunctions {}; diff --git a/layer_gpu_profile/source/instance.hpp b/layer_gpu_profile/source/instance.hpp index 8d051b19..b2b8b33e 100644 --- a/layer_gpu_profile/source/instance.hpp +++ b/layer_gpu_profile/source/instance.hpp @@ -159,9 +159,10 @@ class Instance /** * @brief Additional device extensions injected by the layer. * - * The layer will expose these even if the driver does not. + * The layer will expose these even if the driver does not. Items are + * removed from the list if the driver already exposes the extension. */ - static const std::vector> injectedDeviceExtensions; + static std::vector> injectedDeviceExtensions; /** * @brief Additional API functions that are injected by the layer. diff --git a/layer_gpu_support/source/instance.cpp b/layer_gpu_support/source/instance.cpp index 2b13e4ad..a49a1610 100644 --- a/layer_gpu_support/source/instance.cpp +++ b/layer_gpu_support/source/instance.cpp @@ -46,7 +46,7 @@ const std::vector Instance::requiredDriverExtensions { const std::vector> Instance::injectedInstanceExtensions {}; /* See header for documentation. */ -const std::vector> Instance::injectedDeviceExtensions {}; +std::vector> Instance::injectedDeviceExtensions {}; /* See header for documentation. */ const std::vector Instance::injectedAPIFunctions {}; diff --git a/layer_gpu_support/source/instance.hpp b/layer_gpu_support/source/instance.hpp index 057c196c..5f9724fa 100644 --- a/layer_gpu_support/source/instance.hpp +++ b/layer_gpu_support/source/instance.hpp @@ -158,9 +158,10 @@ class Instance /** * @brief Additional device extensions injected by the layer. * - * The layer will expose these even if the driver does not. + * The layer will expose these even if the driver does not. Items are + * removed from the list if the driver already exposes the extension. */ - static const std::vector> injectedDeviceExtensions; + static std::vector> injectedDeviceExtensions; /** * @brief Additional API functions that are injected by the layer. diff --git a/layer_gpu_support/source/layer_device_functions_image.cpp b/layer_gpu_support/source/layer_device_functions_image.cpp index e680e6d7..aad18102 100644 --- a/layer_gpu_support/source/layer_device_functions_image.cpp +++ b/layer_gpu_support/source/layer_device_functions_image.cpp @@ -122,10 +122,10 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateImage(VkDevice device, } // Create modifiable structures we can patch - vku::safe_VkImageCreateInfo newCreateInfoSafe(pCreateInfo); - auto* newCreateInfo = reinterpret_cast(&newCreateInfoSafe); + vku::safe_VkImageCreateInfo safeCreateInfo(pCreateInfo); + auto* newCreateInfo = reinterpret_cast(&safeCreateInfo); // We know we can const-cast here because this is a safe-struct clone - void* pNextBase = const_cast(newCreateInfoSafe.pNext); + void* pNextBase = const_cast(safeCreateInfo.pNext); // Create extra structures we can patch in VkImageCompressionControlEXT newCompressionControl = vku::InitStructHelper(); @@ -165,7 +165,7 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateImage(VkDevice device, // Add a config if not already configured by the application if (patchNeeded) { - vku::AddToPnext(newCreateInfoSafe, *compressionControl); + vku::AddToPnext(safeCreateInfo, *compressionControl); } return layer->driver.vkCreateImage(device, newCreateInfo, pAllocator, pImage); diff --git a/layer_gpu_timeline/source/CMakeLists.txt b/layer_gpu_timeline/source/CMakeLists.txt index efe08d75..86de9f87 100644 --- a/layer_gpu_timeline/source/CMakeLists.txt +++ b/layer_gpu_timeline/source/CMakeLists.txt @@ -54,6 +54,7 @@ add_library( layer_device_functions_render_pass.cpp layer_device_functions_trace_rays.cpp layer_device_functions_transfer.cpp + layer_instance_functions.cpp timeline_comms.cpp timeline_protobuf_encoder.cpp) diff --git a/layer_gpu_timeline/source/device.hpp b/layer_gpu_timeline/source/device.hpp index 937b0076..4778d313 100644 --- a/layer_gpu_timeline/source/device.hpp +++ b/layer_gpu_timeline/source/device.hpp @@ -185,6 +185,14 @@ class Device */ static const std::vector createInfoPatches; + /** + * @brief Is this layer emulating VK_EXT_frame_boundary? + * + * Set to @c true is layer is emulating on top a driver that doesn't + * support it, @c false if layer knows driver supports it. + */ + bool isEmulatingExtFrameBoundary { false }; + private: /** * @brief State tracker for this device. diff --git a/layer_gpu_timeline/source/instance.cpp b/layer_gpu_timeline/source/instance.cpp index 2b13e4ad..ceac9d3e 100644 --- a/layer_gpu_timeline/source/instance.cpp +++ b/layer_gpu_timeline/source/instance.cpp @@ -46,7 +46,9 @@ const std::vector Instance::requiredDriverExtensions { const std::vector> Instance::injectedInstanceExtensions {}; /* See header for documentation. */ -const std::vector> Instance::injectedDeviceExtensions {}; +std::vector> Instance::injectedDeviceExtensions { + {VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME, VK_EXT_FRAME_BOUNDARY_SPEC_VERSION} +}; /* See header for documentation. */ const std::vector Instance::injectedAPIFunctions {}; diff --git a/layer_gpu_timeline/source/instance.hpp b/layer_gpu_timeline/source/instance.hpp index b92887ca..19ee1aab 100644 --- a/layer_gpu_timeline/source/instance.hpp +++ b/layer_gpu_timeline/source/instance.hpp @@ -153,9 +153,10 @@ class Instance /** * @brief Additional device extensions injected by the layer. * - * The layer will expose these even if the driver does not. + * The layer will expose these even if the driver does not. Items are + * removed from the list if the driver already exposes the extension. */ - static const std::vector> injectedDeviceExtensions; + static std::vector> injectedDeviceExtensions; /** * @brief Additional API functions that are injected by the layer. diff --git a/layer_gpu_timeline/source/layer_device_functions.hpp b/layer_gpu_timeline/source/layer_device_functions.hpp index 1030b35f..38f78bea 100644 --- a/layer_gpu_timeline/source/layer_device_functions.hpp +++ b/layer_gpu_timeline/source/layer_device_functions.hpp @@ -504,3 +504,11 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkQueueSubmit2KHR(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence); + +/* See Vulkan API for documentation. */ +template <> +VKAPI_ATTR VkResult VKAPI_CALL layer_vkQueueBindSparse( + VkQueue queue, + uint32_t bindInfoCount, + const VkBindSparseInfo* pBindInfo, + VkFence fence); diff --git a/layer_gpu_timeline/source/layer_device_functions_queue.cpp b/layer_gpu_timeline/source/layer_device_functions_queue.cpp index 65f4ca59..5434ea17 100644 --- a/layer_gpu_timeline/source/layer_device_functions_queue.cpp +++ b/layer_gpu_timeline/source/layer_device_functions_queue.cpp @@ -29,8 +29,8 @@ #include "trackers/queue.hpp" #include - #include +#include extern std::mutex g_vulkanLock; @@ -93,6 +93,75 @@ static void emitCommandBufferMetadata(Device& layer, trackQueue.runSubmitCommandStream(LCS, workloadVisitor); } +/** + * @brief Check a pNext chain for a manual frame boundary marker. + * + * Emits the necessary metadata to emulate a vkQueuePresent. Note that this + * will generate a second metadata submit to be a container for any commands + * if there are submits remaining after the one tagged as end-of-frame. + * + * @param layer The layer context. + * @param queue The queue. + * @param pNext The submit pNext pointer. + * @param isLastSubmit Is this the last submit in the API call? + * @param workloadVisitor Visitor for the protobuf encoder. + */ +static void checkManualFrameBoundary( + Device* layer, + VkQueue queue, + const void* pNext, + bool isLastSubmit, + TimelineProtobufEncoder& workloadVisitor +) { + // Check for end of frame boundary + auto* ext = vku::FindStructInPNextChain(pNext); + if (ext && (ext->flags & VK_FRAME_BOUNDARY_FRAME_END_BIT_EXT)) + { + // Emulate a queue present to indicate end of frame + auto& tracker = layer->getStateTracker(); + tracker.queuePresent(); + + TimelineProtobufEncoder::emitFrame(*layer, tracker.totalStats.getFrameCount(), getClockMonotonicRaw()); + + // Emulate a new queue submit if work remains to submit + if (!isLastSubmit) + { + emitQueueMetadata(queue, workloadVisitor); + } + } +} + +/** + * @brief Remove emulated frame boundaries from the pNext chain. + * + * @param layer The layer context. + * @param pSubmits The list of user-supplied submits. + * @param submitCount The number of submits in the list. + * @param safeSubmits Storage for allocated copies if we need to patch. + * @param pSubmitsForCall Pointer passed to the API call. + */ +template +static void stripManualFrameBoundary( + Device* layer, + const T* pSubmits, + uint32_t submitCount, + std::vector& safeSubmits, + const T** pSubmitsForCall +) { + if (layer->isEmulatingExtFrameBoundary) + { + safeSubmits.reserve(submitCount); + + for (uint32_t i = 0; i < submitCount; i++) + { + safeSubmits.emplace_back(pSubmits + i); + vku::RemoveFromPnext(safeSubmits[i], VK_STRUCTURE_TYPE_FRAME_BOUNDARY_EXT); + } + + *pSubmitsForCall = reinterpret_cast(safeSubmits.data()); + } +} + /* See Vulkan API for documentation. */ template<> VKAPI_ATTR VkResult VKAPI_CALL layer_vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) @@ -106,13 +175,24 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkQueuePresentKHR(VkQueue queue, auto& tracker = layer->getStateTracker(); tracker.queuePresent(); - // This is run with the lock held to ensure that all queue submit - // messages are sent sequentially to the host tool + // Create a modifiable structure we can patch + vku::safe_VkPresentInfoKHR safePresentInfo(pPresentInfo); + auto* newPresentInfo = reinterpret_cast(&safePresentInfo); + + // Remove emulated frame boundaries + if (layer->isEmulatingExtFrameBoundary) + { + vku::RemoveFromPnext(safePresentInfo, VK_STRUCTURE_TYPE_FRAME_BOUNDARY_EXT); + } + + // Note that we assume QueuePresent is _always_ the end of a frame. + // This is run with the lock held to ensure that all queue submit messages + // are sent sequentially to the host tool TimelineProtobufEncoder::emitFrame(*layer, tracker.totalStats.getFrameCount(), getClockMonotonicRaw()); // Release the lock to call into the driver lock.unlock(); - return layer->driver.vkQueuePresentKHR(queue, pPresentInfo); + return layer->driver.vkQueuePresentKHR(queue, newPresentInfo); } /* See Vulkan API for documentation. */ @@ -142,11 +222,23 @@ VKAPI_ATTR VkResult VKAPI_CALL VkCommandBuffer commandBuffer = submit.pCommandBuffers[j]; emitCommandBufferMetadata(*layer, queue, commandBuffer, workloadVisitor); } + + // Check for end of frame boundary + bool isLast = i == submitCount - 1; + checkManualFrameBoundary(layer, queue, submit.pNext, isLast, workloadVisitor); } + // Remove emulated frame boundaries + const VkSubmitInfo* newSubmits = pSubmits; + std::vector safeSubmits; + + stripManualFrameBoundary( + layer, pSubmits, submitCount, + safeSubmits, &newSubmits); + // Release the lock to call into the driver lock.unlock(); - return layer->driver.vkQueueSubmit(queue, submitCount, pSubmits, fence); + return layer->driver.vkQueueSubmit(queue, submitCount, newSubmits, fence); } /* See Vulkan API for documentation. */ @@ -176,11 +268,23 @@ VKAPI_ATTR VkResult VKAPI_CALL VkCommandBuffer commandBuffer = submit.pCommandBufferInfos[j].commandBuffer; emitCommandBufferMetadata(*layer, queue, commandBuffer, workloadVisitor); } + + // Check for end of frame boundary + bool isLast = i == submitCount - 1; + checkManualFrameBoundary(layer, queue, submit.pNext, isLast, workloadVisitor); } + // Remove emulated frame boundaries + const VkSubmitInfo2* newSubmits = pSubmits; + std::vector safeSubmits; + + stripManualFrameBoundary( + layer, pSubmits, submitCount, + safeSubmits, &newSubmits); + // Release the lock to call into the driver lock.unlock(); - return layer->driver.vkQueueSubmit2(queue, submitCount, pSubmits, fence); + return layer->driver.vkQueueSubmit2(queue, submitCount, newSubmits, fence); } /* See Vulkan API for documentation. */ @@ -210,9 +314,67 @@ VKAPI_ATTR VkResult VKAPI_CALL VkCommandBuffer commandBuffer = submit.pCommandBufferInfos[j].commandBuffer; emitCommandBufferMetadata(*layer, queue, commandBuffer, workloadVisitor); } + + // Check for end of frame boundary + bool isLast = i == submitCount - 1; + checkManualFrameBoundary(layer, queue, submit.pNext, isLast, workloadVisitor); } + // Remove emulated frame boundaries + const VkSubmitInfo2* newSubmits = pSubmits; + std::vector safeSubmits; + stripManualFrameBoundary( + layer, pSubmits, submitCount, + safeSubmits, &newSubmits); + // Release the lock to call into the driver lock.unlock(); - return layer->driver.vkQueueSubmit2KHR(queue, submitCount, pSubmits, fence); + return layer->driver.vkQueueSubmit2KHR(queue, submitCount, newSubmits, fence); } + +/** + * See Vulkan API for documentation. + * + * Note: Modelling of this function is only implemented to support manual frame + * boundaries. There is no reporting of the workload associated with bind + * sparse submissions in the Mali timeline driver data model. + */ +template <> +VKAPI_ATTR VkResult VKAPI_CALL layer_vkQueueBindSparse( + VkQueue queue, + uint32_t bindInfoCount, + const VkBindSparseInfo* pBindInfo, + VkFence fence +) { + LAYER_TRACE(__func__); + + // Hold the lock to access layer-wide global store + std::unique_lock lock {g_vulkanLock}; + auto* layer = Device::retrieve(queue); + + // Scan infos for frame boundaries + for (uint32_t i = 0; i < bindInfoCount; i++) + { + const auto& info = pBindInfo[i]; + + auto* ext = vku::FindStructInPNextChain(info.pNext); + if (ext && (ext->flags & VK_FRAME_BOUNDARY_FRAME_END_BIT_EXT)) + { + // Emulate a queue present to indicate end of frame + auto& tracker = layer->getStateTracker(); + tracker.queuePresent(); + TimelineProtobufEncoder::emitFrame(*layer, tracker.totalStats.getFrameCount(), getClockMonotonicRaw()); + } + } + + // Remove emulated frame boundaries + const VkBindSparseInfo* newBindInfo = pBindInfo; + std::vector safeInfos; + stripManualFrameBoundary( + layer, pBindInfo, bindInfoCount, + safeInfos, &newBindInfo); + + // Release the lock to call into the driver + lock.unlock(); + return layer->driver.vkQueueBindSparse(queue, bindInfoCount, newBindInfo, fence); +} \ No newline at end of file diff --git a/layer_gpu_timeline/source/layer_instance_functions.cpp b/layer_gpu_timeline/source/layer_instance_functions.cpp new file mode 100644 index 00000000..713a8e3f --- /dev/null +++ b/layer_gpu_timeline/source/layer_instance_functions.cpp @@ -0,0 +1,117 @@ +/* + * SPDX-License-Identifier: MIT + * ---------------------------------------------------------------------------- + * Copyright (c) 2025 Arm Limited + * + * 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 "instance.hpp" +#include "device.hpp" + +#include +#include + +extern std::mutex g_vulkanLock; + +/* See header for documentation. */ +template <> +VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateDevice( + VkPhysicalDevice physicalDevice, + const VkDeviceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDevice* pDevice +) { + LAYER_TRACE(__func__); + + // Use the default function for the heavy-lifting + auto res = layer_vkCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice); + if (res != VK_SUCCESS) + { + return res; + } + + // Cache flags indicating extension emulation + std::unique_lock lock {g_vulkanLock}; + auto* layer = Device::retrieve(*pDevice); + + static const std::string target { VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME }; + for (auto& ext : layer->instance->injectedDeviceExtensions) + { + if (ext.first == target) + { + layer->isEmulatingExtFrameBoundary = true; + } + } + + return res; +} + +/* See Vulkan API for documentation. */ +template <> +VKAPI_ATTR void VKAPI_CALL layer_vkGetPhysicalDeviceFeatures2( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2* pFeatures +) { + LAYER_TRACE(__func__); + + // Hold the lock to access layer-wide global store + std::unique_lock lock { g_vulkanLock }; + auto* layer = Instance::retrieve(physicalDevice); + + // Release the lock to call into the driver + lock.unlock(); + layer->driver.vkGetPhysicalDeviceFeatures2(physicalDevice, pFeatures); + + // Patch the query response to show that it is supported + // TODO: We should hide this when calling down to the driver, and then + // copy results back to the user structure + auto* ext = vku::FindStructInPNextChain(pFeatures->pNext); + if (ext) + { + ext->frameBoundary = VK_TRUE; + } +} + +/* See Vulkan API for documentation. */ +template <> +VKAPI_ATTR void VKAPI_CALL layer_vkGetPhysicalDeviceFeatures2KHR( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2* pFeatures +) { + LAYER_TRACE(__func__); + + // Hold the lock to access layer-wide global store + std::unique_lock lock { g_vulkanLock }; + auto* layer = Instance::retrieve(physicalDevice); + + // Release the lock to call into the driver + lock.unlock(); + layer->driver.vkGetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures); + + // Patch the query response to show that it is supported + // TODO: We should hide this when calling down to the driver, and then + // copy results back to the user structure + auto* ext = vku::FindStructInPNextChain(pFeatures->pNext); + if (ext) + { + ext->frameBoundary = VK_TRUE; + } +} diff --git a/layer_gpu_timeline/source/layer_instance_functions.hpp b/layer_gpu_timeline/source/layer_instance_functions.hpp new file mode 100644 index 00000000..c2e6f1d0 --- /dev/null +++ b/layer_gpu_timeline/source/layer_instance_functions.hpp @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: MIT + * ---------------------------------------------------------------------------- + * Copyright (c) 2025 Arm Limited + * + * 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. + * ---------------------------------------------------------------------------- + */ + +#pragma once + +#include + +// Functions for devices + +/* See Vulkan API for documentation. */ +template <> +VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateDevice( + VkPhysicalDevice physicalDevice, + const VkDeviceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDevice* pDevice); + +/* See Vulkan API for documentation. */ +template <> +VKAPI_ATTR void VKAPI_CALL layer_vkGetPhysicalDeviceFeatures2( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2* pFeatures); + +/* See Vulkan API for documentation. */ +template <> +VKAPI_ATTR void VKAPI_CALL layer_vkGetPhysicalDeviceFeatures2KHR( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2* pFeatures); diff --git a/source_common/framework/manual_functions.cpp b/source_common/framework/manual_functions.cpp index df63b4dc..3f9f07bf 100644 --- a/source_common/framework/manual_functions.cpp +++ b/source_common/framework/manual_functions.cpp @@ -29,8 +29,11 @@ * implemented as library code which can be swapped for alternative * implementations on a per-layer basis if needed. */ - +#include +#include +#include #include +#include #include "framework/manual_functions.hpp" #include "utils/misc.hpp" @@ -527,6 +530,42 @@ void enableDeviceVkExtImageCompressionControl(Instance& instance, } } +/* See header for documentation. */ +void emulateDeviceVkExtFrameBoundary(Instance& instance, + VkPhysicalDevice physicalDevice, + vku::safe_VkDeviceCreateInfo& createInfo, + std::vector& supported) +{ + UNUSED(instance); + UNUSED(physicalDevice); + UNUSED(supported); + + static const std::string target {VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME}; + + // We only need to hide it if driver does not support it + bool isEmulated = false; + for (auto& ext : instance.injectedDeviceExtensions) + { + if (ext.first == target) + { + isEmulated = true; + break; + } + } + + if (!isEmulated) + { + return; + } + + // Mask extension if layer is emulating it + bool removed = vku::RemoveFromPnext(createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAME_BOUNDARY_FEATURES_EXT); + if (removed) + { + LAYER_LOG("Device extension masked: %s", target.c_str()); + } +} + /** See Vulkan API for documentation. */ template <> PFN_vkVoidFunction layer_vkGetInstanceProcAddr(VkInstance instance, const char* pName) @@ -621,15 +660,130 @@ VkResult layer_vkEnumerateInstanceExtensionProperties(const char* p { LAYER_TRACE(__func__); - UNUSED(pProperties); + // Query for a layer + if (pLayerName) + { + // ... but not this layer + if(strcmp(pLayerName, layerProps[0].layerName)) + { + return VK_ERROR_LAYER_NOT_PRESENT; + } + + size_t count = Instance::injectedInstanceExtensions.size(); - if (!pLayerName || strcmp(pLayerName, layerProps[0].layerName)) + // Size query + if (!pProperties) + { + *pPropertyCount = static_cast(count); + return VK_SUCCESS; + } + + // Property query, clamped to size of user array if smaller + size_t emitCount = std::min(count, static_cast(*pPropertyCount)); + for (size_t i = 0; i < emitCount; i++) + { + const auto& ref = Instance::injectedInstanceExtensions[i]; + std::strcpy(pProperties[i].extensionName, ref.first.c_str()); + pProperties[i].specVersion = ref.second; + } + + *pPropertyCount = static_cast(emitCount); + + if (count > emitCount) + { + return VK_INCOMPLETE; + } + + return VK_SUCCESS; + } + + // Note that unlike device extensions, there is no layering for this + // query, as we have no way of knowing what's beneath us + + return VK_ERROR_LAYER_NOT_PRESENT; +} + +static std::vector get_driver_device_extensions( + Instance& layer, + VkPhysicalDevice gpu +) { + uint32_t queryCount = 0; + std::vector query; + + // Query how many extensions to allocate for + auto err = layer.driver.vkEnumerateDeviceExtensionProperties(gpu, nullptr, &queryCount, nullptr); + if (err != VK_SUCCESS) { - return VK_ERROR_LAYER_NOT_PRESENT; + return {}; } - *pPropertyCount = 0; - return VK_SUCCESS; + // Allocate storage + query.resize(queryCount); + + // Query + err = layer.driver.vkEnumerateDeviceExtensionProperties(gpu, nullptr, &queryCount, query.data()); + if (err != VK_SUCCESS) + { + return {}; + } + + return query; +} + +static void get_extended_device_extensions( + Instance& layer, + std::vector& extensions +) { + std::vector passthroughExtensions; + + // For each extension in our extension list ... + for (auto& injectedExtension : layer.injectedDeviceExtensions) + { + const std::string& name = injectedExtension.first; + // Is it in the list already? + bool found = false; + for (const auto& driverExtension : extensions) + { + if (name == driverExtension.extensionName) + { + passthroughExtensions.emplace_back(name); + found = true; + break; + } + } + + // If not then add it to the list + if (!found) + { + LAYER_LOG("Injecting device extension: %s", name.c_str()); + VkExtensionProperties prop {}; + + // Populate the string, and guarantee it's NUL terminated + std::strncpy(prop.extensionName, name.c_str(), VK_MAX_EXTENSION_NAME_SIZE - 1); + prop.extensionName[VK_MAX_EXTENSION_NAME_SIZE - 1] = '\0'; + prop.specVersion = injectedExtension.second; + extensions.emplace_back(prop); + } + else + { + LAYER_LOG("Not injecting device extension: %s", name.c_str()); + } + } + + // Remove any found extensions from the injected list so that we can tell + // that the driver supports it and we didn't need to inject it + for (auto& ref : passthroughExtensions) + { + auto& tgt = layer.injectedDeviceExtensions; + for (auto it = tgt.begin(); it != tgt.end(); it++) + { + if (ref == it->first) + { + tgt.erase(it); + break; + } + } + } } /** See Vulkan API for documentation. */ @@ -641,23 +795,50 @@ VkResult layer_vkEnumerateDeviceExtensionProperties(VkPhysicalDevic { LAYER_TRACE(__func__); - UNUSED(pProperties); - - // Android layer enumeration will always pass a nullptr for the device - if (!gpu) + // Query for a layer + if (pLayerName) { - if (!pLayerName || strcmp(pLayerName, layerProps[0].layerName)) + // ... but not this layer + if(strcmp(pLayerName, layerProps[0].layerName)) { return VK_ERROR_LAYER_NOT_PRESENT; } - *pPropertyCount = 0; + size_t count = Instance::injectedDeviceExtensions.size(); + + // Size query + if (!pProperties) + { + *pPropertyCount = static_cast(count); + return VK_SUCCESS; + } + + // Property query, clamped to size of user array if smaller + size_t emitCount = std::min(count, static_cast(*pPropertyCount)); + for (size_t i = 0; i < emitCount; i++) + { + const auto& ref = Instance::injectedDeviceExtensions[i]; + std::strcpy(pProperties[i].extensionName, ref.first.c_str()); + pProperties[i].specVersion = ref.second; + } + + *pPropertyCount = static_cast(emitCount); + + if (count > emitCount) + { + return VK_INCOMPLETE; + } + return VK_SUCCESS; } + // Query for a device, but on Android discovery may pass a null device + if (!gpu) + { + return VK_ERROR_LAYER_NOT_PRESENT; + } + // For other cases forward to the driver to handle it - assert(!pLayerName); - assert(gpu); // Hold the lock to access layer-wide global store std::unique_lock lock {g_vulkanLock}; @@ -665,7 +846,38 @@ VkResult layer_vkEnumerateDeviceExtensionProperties(VkPhysicalDevic // Release the lock to call into the driver lock.unlock(); - return layer->driver.vkEnumerateDeviceExtensionProperties(gpu, pLayerName, pPropertyCount, pProperties); + auto our_extensions = get_driver_device_extensions(*layer, gpu); + + // Lock again because we patch a shared structure based on driver response + lock.lock(); + get_extended_device_extensions(*layer, our_extensions); + lock.unlock(); + + // Size query + if (!pProperties) + { + *pPropertyCount = static_cast(our_extensions.size()); + return VK_SUCCESS; + } + + // Property query, clamped to size of user array if smaller + size_t count2 = our_extensions.size(); + size_t emitCount2 = std::min(count2, static_cast(*pPropertyCount)); + for (size_t i = 0; i < emitCount2; i++) + { + const auto& ref = our_extensions[i]; + std::strcpy(pProperties[i].extensionName, ref.extensionName); + pProperties[i].specVersion = ref.specVersion; + } + + *pPropertyCount = static_cast(emitCount2); + + if (count2 > emitCount2) + { + return VK_INCOMPLETE; + } + + return VK_SUCCESS; } /** See Vulkan API for documentation. */ @@ -674,20 +886,25 @@ VkResult layer_vkEnumerateInstanceLayerProperties(uint32_t* pProper { LAYER_TRACE(__func__); - if (pProperties) - { - size_t count = std::min(layerProps.size(), static_cast(*pPropertyCount)); - if (count < layerProps.size()) - { - return VK_INCOMPLETE; - } + size_t count = layerProps.size(); - memcpy(pProperties, layerProps.data(), count * sizeof(VkLayerProperties)); - *pPropertyCount = count; + // Size query + if (!pProperties) + { + *pPropertyCount = static_cast(count); return VK_SUCCESS; } - *pPropertyCount = layerProps.size(); + // Property query, clamped to size of user array if smaller + size_t emitCount = std::min(count, static_cast(*pPropertyCount)); + std::memcpy(pProperties, layerProps.data(), emitCount * sizeof(VkLayerProperties)); + *pPropertyCount = static_cast(emitCount); + + if (count > emitCount) + { + return VK_INCOMPLETE; + } + return VK_SUCCESS; } @@ -701,20 +918,25 @@ VkResult layer_vkEnumerateDeviceLayerProperties(VkPhysicalDevice gp UNUSED(gpu); - if (pProperties) - { - size_t count = std::min(layerProps.size(), static_cast(*pPropertyCount)); - if (count < layerProps.size()) - { - return VK_INCOMPLETE; - } + size_t count = layerProps.size(); - memcpy(pProperties, layerProps.data(), count * sizeof(VkLayerProperties)); - *pPropertyCount = count; + // Size query + if (!pProperties) + { + *pPropertyCount = static_cast(count); return VK_SUCCESS; } - *pPropertyCount = layerProps.size(); + // Property query, clamped to size of user array if smaller + size_t emitCount = std::min(count, static_cast(*pPropertyCount)); + std::memcpy(pProperties, layerProps.data(), emitCount * sizeof(VkLayerProperties)); + *pPropertyCount = static_cast(emitCount); + + if (count > emitCount) + { + return VK_INCOMPLETE; + } + return VK_SUCCESS; } @@ -763,18 +985,18 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateInstance(const VkInsta } // Create modifiable structures we can patch - vku::safe_VkInstanceCreateInfo newCreateInfoSafe(pCreateInfo); - auto* newCreateInfo = reinterpret_cast(&newCreateInfoSafe); + vku::safe_VkInstanceCreateInfo safeCreateInfo(pCreateInfo); + auto* newCreateInfo = reinterpret_cast(&safeCreateInfo); // Patch updated application info - newCreateInfoSafe.pApplicationInfo->apiVersion = VK_MAKE_API_VERSION(0, newVersion.first, newVersion.second, 0); + safeCreateInfo.pApplicationInfo->apiVersion = VK_MAKE_API_VERSION(0, newVersion.first, newVersion.second, 0); // Enable extra extensions for (const auto& newExt : Instance::requiredDriverExtensions) { if (newExt == VK_EXT_DEBUG_UTILS_EXTENSION_NAME) { - enableInstanceVkExtDebugUtils(newCreateInfoSafe, supportedExtensions); + enableInstanceVkExtDebugUtils(safeCreateInfo, supportedExtensions); } else { @@ -857,13 +1079,13 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateDevice(VkPhysicalDevic LAYER_LOG("Device API version %u.%u", apiVersion.first, apiVersion.second); // Create a modifiable structure we can patch - vku::safe_VkDeviceCreateInfo newCreateInfoSafe(pCreateInfo); - auto* newCreateInfo = reinterpret_cast(&newCreateInfoSafe); + vku::safe_VkDeviceCreateInfo safeCreateInfo(pCreateInfo); + auto* newCreateInfo = reinterpret_cast(&safeCreateInfo); // Apply all required patches to the VkDeviceCreateInfo for (const auto patch : Device::createInfoPatches) { - patch(*layer, physicalDevice, newCreateInfoSafe, supportedExtensions); + patch(*layer, physicalDevice, safeCreateInfo, supportedExtensions); } // Log extensions after patching for debug purposes diff --git a/source_common/framework/manual_functions.hpp b/source_common/framework/manual_functions.hpp index 18849d83..c066f301 100644 --- a/source_common/framework/manual_functions.hpp +++ b/source_common/framework/manual_functions.hpp @@ -23,6 +23,8 @@ * ---------------------------------------------------------------------------- */ +#pragma once + /** * @file * This module exposes common functionality used by layer entrypoints, @@ -31,7 +33,7 @@ */ #include - #include "device.hpp" +#include "device.hpp" #include "framework/device_dispatch_table.hpp" #include "framework/device_functions.hpp" #include "framework/instance_functions.hpp" @@ -229,3 +231,20 @@ void enableDeviceVkExtImageCompressionControl(Instance& instance, VkPhysicalDevice physicalDevice, vku::safe_VkDeviceCreateInfo& createInfo, std::vector& supported); + +/** + * Hide VK_EXT_frame_boundary if emulated on top of the driver. + * + * If the driver supports this already we don't need to do anything, but + * if the driver does not then we need to hide the support. + * + * @param instance The layer instance we are running within. + * @param physicalDevice The physical device we are creating a device for. + * @param createInfo The createInfo we can search to find user config. + * @param supported The list of supported extensions. + */ +void emulateDeviceVkExtFrameBoundary(Instance& instance, + VkPhysicalDevice physicalDevice, + vku::safe_VkDeviceCreateInfo& createInfo, + std::vector& supported); + diff --git a/source_common/trackers/render_pass.cpp b/source_common/trackers/render_pass.cpp index 4f921236..76203490 100644 --- a/source_common/trackers/render_pass.cpp +++ b/source_common/trackers/render_pass.cpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2022-2024 Arm Limited + * Copyright (c) 2022-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to From cce33570d829ffe904d6456403dc709de17ad7c9 Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Wed, 17 Dec 2025 11:15:51 +0000 Subject: [PATCH 3/7] Whitespace --- layer_gpu_timeline/source/layer_device_functions_queue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layer_gpu_timeline/source/layer_device_functions_queue.cpp b/layer_gpu_timeline/source/layer_device_functions_queue.cpp index 5434ea17..bfb2f1f7 100644 --- a/layer_gpu_timeline/source/layer_device_functions_queue.cpp +++ b/layer_gpu_timeline/source/layer_device_functions_queue.cpp @@ -377,4 +377,4 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkQueueBindSparse( // Release the lock to call into the driver lock.unlock(); return layer->driver.vkQueueBindSparse(queue, bindInfoCount, newBindInfo, fence); -} \ No newline at end of file +} From 36a9a0339c9b73e1cba17069ff3adb0c4a86ed2b Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Wed, 17 Dec 2025 11:27:07 +0000 Subject: [PATCH 4/7] Leave extension structs in place --- docs/extension_support.md | 13 +++- .../source/layer_device_functions_queue.cpp | 69 ++----------------- .../source/layer_instance_functions.cpp | 4 -- 3 files changed, 16 insertions(+), 70 deletions(-) diff --git a/docs/extension_support.md b/docs/extension_support.md index c991a0d7..565be7c7 100644 --- a/docs/extension_support.md +++ b/docs/extension_support.md @@ -35,11 +35,22 @@ the `injectedExtensions` list, although the layer will probably want to cache this check to reduce performance overhead. If the driver beneath the layer does not support the extension, the extended -API parameters must be rewritten to remove the extension before passing down +API parameters should be rewritten to remove the extension before passing down to the driver. User structure inputs to the Vulkan API are usually marked as `const`, so we must take a safe-struct copy which we can modify and pass that copy to the driver. +Note that Vulkan specifies that components must ignore structures in the +`pNext` chain that they do not understand: + +> Any component of the implementation (the loader, any enabled layers, and +> drivers) must skip over, without processing (other than reading the `sType` +> and `pNext` members) any extending structures in the chain not defined by +> core versions or extensions supported by that component. + +Any extension structures can therefore be left in-situ when being emulated, but +any other API parameter modifications must be unpicked to hide the emulation. + ## Common extension notes This section is a set of brief notes about extensions that we have implemented, diff --git a/layer_gpu_timeline/source/layer_device_functions_queue.cpp b/layer_gpu_timeline/source/layer_device_functions_queue.cpp index bfb2f1f7..a4ba1a05 100644 --- a/layer_gpu_timeline/source/layer_device_functions_queue.cpp +++ b/layer_gpu_timeline/source/layer_device_functions_queue.cpp @@ -131,37 +131,6 @@ static void checkManualFrameBoundary( } } -/** - * @brief Remove emulated frame boundaries from the pNext chain. - * - * @param layer The layer context. - * @param pSubmits The list of user-supplied submits. - * @param submitCount The number of submits in the list. - * @param safeSubmits Storage for allocated copies if we need to patch. - * @param pSubmitsForCall Pointer passed to the API call. - */ -template -static void stripManualFrameBoundary( - Device* layer, - const T* pSubmits, - uint32_t submitCount, - std::vector& safeSubmits, - const T** pSubmitsForCall -) { - if (layer->isEmulatingExtFrameBoundary) - { - safeSubmits.reserve(submitCount); - - for (uint32_t i = 0; i < submitCount; i++) - { - safeSubmits.emplace_back(pSubmits + i); - vku::RemoveFromPnext(safeSubmits[i], VK_STRUCTURE_TYPE_FRAME_BOUNDARY_EXT); - } - - *pSubmitsForCall = reinterpret_cast(safeSubmits.data()); - } -} - /* See Vulkan API for documentation. */ template<> VKAPI_ATTR VkResult VKAPI_CALL layer_vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) @@ -228,17 +197,9 @@ VKAPI_ATTR VkResult VKAPI_CALL checkManualFrameBoundary(layer, queue, submit.pNext, isLast, workloadVisitor); } - // Remove emulated frame boundaries - const VkSubmitInfo* newSubmits = pSubmits; - std::vector safeSubmits; - - stripManualFrameBoundary( - layer, pSubmits, submitCount, - safeSubmits, &newSubmits); - // Release the lock to call into the driver lock.unlock(); - return layer->driver.vkQueueSubmit(queue, submitCount, newSubmits, fence); + return layer->driver.vkQueueSubmit(queue, submitCount, pSubmits, fence); } /* See Vulkan API for documentation. */ @@ -274,17 +235,9 @@ VKAPI_ATTR VkResult VKAPI_CALL checkManualFrameBoundary(layer, queue, submit.pNext, isLast, workloadVisitor); } - // Remove emulated frame boundaries - const VkSubmitInfo2* newSubmits = pSubmits; - std::vector safeSubmits; - - stripManualFrameBoundary( - layer, pSubmits, submitCount, - safeSubmits, &newSubmits); - // Release the lock to call into the driver lock.unlock(); - return layer->driver.vkQueueSubmit2(queue, submitCount, newSubmits, fence); + return layer->driver.vkQueueSubmit2(queue, submitCount, pSubmits, fence); } /* See Vulkan API for documentation. */ @@ -320,16 +273,9 @@ VKAPI_ATTR VkResult VKAPI_CALL checkManualFrameBoundary(layer, queue, submit.pNext, isLast, workloadVisitor); } - // Remove emulated frame boundaries - const VkSubmitInfo2* newSubmits = pSubmits; - std::vector safeSubmits; - stripManualFrameBoundary( - layer, pSubmits, submitCount, - safeSubmits, &newSubmits); - // Release the lock to call into the driver lock.unlock(); - return layer->driver.vkQueueSubmit2KHR(queue, submitCount, newSubmits, fence); + return layer->driver.vkQueueSubmit2KHR(queue, submitCount, pSubmits, fence); } /** @@ -367,14 +313,7 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkQueueBindSparse( } } - // Remove emulated frame boundaries - const VkBindSparseInfo* newBindInfo = pBindInfo; - std::vector safeInfos; - stripManualFrameBoundary( - layer, pBindInfo, bindInfoCount, - safeInfos, &newBindInfo); - // Release the lock to call into the driver lock.unlock(); - return layer->driver.vkQueueBindSparse(queue, bindInfoCount, newBindInfo, fence); + return layer->driver.vkQueueBindSparse(queue, bindInfoCount, pBindInfo, fence); } diff --git a/layer_gpu_timeline/source/layer_instance_functions.cpp b/layer_gpu_timeline/source/layer_instance_functions.cpp index 713a8e3f..0d36dfd2 100644 --- a/layer_gpu_timeline/source/layer_instance_functions.cpp +++ b/layer_gpu_timeline/source/layer_instance_functions.cpp @@ -81,8 +81,6 @@ VKAPI_ATTR void VKAPI_CALL layer_vkGetPhysicalDeviceFeatures2( layer->driver.vkGetPhysicalDeviceFeatures2(physicalDevice, pFeatures); // Patch the query response to show that it is supported - // TODO: We should hide this when calling down to the driver, and then - // copy results back to the user structure auto* ext = vku::FindStructInPNextChain(pFeatures->pNext); if (ext) { @@ -107,8 +105,6 @@ VKAPI_ATTR void VKAPI_CALL layer_vkGetPhysicalDeviceFeatures2KHR( layer->driver.vkGetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures); // Patch the query response to show that it is supported - // TODO: We should hide this when calling down to the driver, and then - // copy results back to the user structure auto* ext = vku::FindStructInPNextChain(pFeatures->pNext); if (ext) { From 833d3e1a097232d172d763e69d4fcddb4f201bc6 Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Wed, 17 Dec 2025 16:14:12 +0000 Subject: [PATCH 5/7] Update docs --- docs/extension_support.md | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/extension_support.md b/docs/extension_support.md index 565be7c7..e1b81a8f 100644 --- a/docs/extension_support.md +++ b/docs/extension_support.md @@ -5,9 +5,9 @@ It might be useful for some layers to implement an extension, such as This page explains the general approach that needs to be taken, and the specific API modifications that need to be applied for specific extensions. -Note that the core framework allows you to expose additional extensions via +The core libGPULayers framework allows you to expose additional extensions via the default `vkEnumerate*ExtensionProperties()` implementation, but per-layer -code must implement the API modifications to other functions as needed. +code must implement the API modifications in any other functions as needed. ## Exposing a new extension @@ -20,13 +20,14 @@ add the new extension information that they want to expose to either: * `Instance::injectedDeviceExtensions` for device extensions. Device extensions will be removed from this list if we can detect that the -underlying device already supports them, which means we can just pass through. +underlying device already supports them, which means we can just pass through +rather than emulating support. ### Handling extended API entry points -All entrypoints that are touched by an extension need to be intercepted with -a `user_tag` version of a function, which will implement the functionality that -the layer requires. +All entrypoints that are touched by an extension need to be intercepted with a +`user_tag` version of that function, which will implement the functionality +that the layer requires. If the driver beneath the layer actually supports the extension, the extended API parameters can be passed down to the driver without modification. This @@ -37,7 +38,7 @@ this check to reduce performance overhead. If the driver beneath the layer does not support the extension, the extended API parameters should be rewritten to remove the extension before passing down to the driver. User structure inputs to the Vulkan API are usually marked as -`const`, so we must take a safe-struct copy which we can modify and pass +`const`, so we must take a safe-struct copy which we can modify and then pass that copy to the driver. Note that Vulkan specifies that components must ignore structures in the @@ -66,16 +67,20 @@ where CPU processing for frames can overlap, and for applications which do not have frames, but that want to use tools such as RenderDoc that require them. +The `layer_gpu_timeline` layer is an example of a layer exposing this +extension using emulation on devices that do not support it. + #### Exposing extension Adding exposure handling: * Add `VK_EXT_frame_boundary` to device extension list. -* Add `VkPhysicalDeviceFrameBoundary` to `VkPhysicalDeviceFeatures2.pNext` list - returned by `vkGetPhysicalDeviceFeatures2()`, or force existing structure to - `VK_TRUE`, if not supported by driver. +* Populate the `VkPhysicalDeviceFrameBoundary` in the + `VkPhysicalDeviceFeatures2.pNext` list returned by + `vkGetPhysicalDeviceFeatures2()`, forcing the value to `VK_TRUE`, if the + extension is "supported" but feature-disabled by the driver. * Query `VkPhysicalDeviceFrameBoundary` in `VkDeviceCreateInfo.pNext` to see if - application enabled the extension. Strip if not supported by the driver. + application enabled the extension. #### Implementing extension @@ -92,8 +97,9 @@ Adding implementation handling: Most applications using this that I have seen are using it to demarcate frames when using a single submitting render thread for off-screen rendering or compute use cases that do not use `vkQueuePresent()`. In these systems just -detecting a change in frame ID is enough to indicate "frame changed", much -how we would use `vkQueuePresent()` to do the same without this extension. +detecting the frame boundary flag in the extension structure passed to a queue +submit is enough, and how we would use `vkQueuePresent()` to do the same +without this extension. It is possible for applications to have multiple concurrent frames being submitted in an overlapping manner, which can be handled by tagging work with From b3a26d551c256d70e83509582cf2de6df9fbcaab Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Wed, 17 Dec 2025 16:16:03 +0000 Subject: [PATCH 6/7] Remove injectedAPIFunctions --- generator/vk_layer/source/instance.cpp | 3 --- generator/vk_layer/source/instance.hpp | 11 ----------- layer_example/source/instance.cpp | 3 --- layer_example/source/instance.hpp | 11 ----------- layer_gpu_profile/source/instance.cpp | 3 --- layer_gpu_profile/source/instance.hpp | 11 ----------- layer_gpu_support/source/instance.cpp | 3 --- layer_gpu_support/source/instance.hpp | 11 ----------- layer_gpu_timeline/source/instance.cpp | 3 --- layer_gpu_timeline/source/instance.hpp | 11 ----------- 10 files changed, 70 deletions(-) diff --git a/generator/vk_layer/source/instance.cpp b/generator/vk_layer/source/instance.cpp index edf2443e..70e705a0 100644 --- a/generator/vk_layer/source/instance.cpp +++ b/generator/vk_layer/source/instance.cpp @@ -48,9 +48,6 @@ const std::vector> Instance::injectedInstanceEx /* See header for documentation. */ std::vector> Instance::injectedDeviceExtensions {}; -/* See header for documentation. */ -const std::vector Instance::injectedAPIFunctions {}; - /* See header for documentation. */ void Instance::store( VkInstance handle, diff --git a/generator/vk_layer/source/instance.hpp b/generator/vk_layer/source/instance.hpp index f5995336..8faee440 100644 --- a/generator/vk_layer/source/instance.hpp +++ b/generator/vk_layer/source/instance.hpp @@ -163,15 +163,4 @@ class Instance * removed from the list if the driver already exposes the extension. */ static std::vector> injectedDeviceExtensions; - - /** - * @brief Additional API functions that are injected by the layer. - * - * This list must include both instance and device functions, because - * vkGetInstanceProcAddr can (inefficiently) be used to return device - * functions. - * - * The layer will expose these even if the driver does not. - */ - static const std::vector injectedAPIFunctions; }; diff --git a/layer_example/source/instance.cpp b/layer_example/source/instance.cpp index 2daee7b4..35c6b842 100644 --- a/layer_example/source/instance.cpp +++ b/layer_example/source/instance.cpp @@ -48,9 +48,6 @@ const std::vector> Instance::injectedInstanceEx /* See header for documentation. */ std::vector> Instance::injectedDeviceExtensions {}; -/* See header for documentation. */ -const std::vector Instance::injectedAPIFunctions {}; - /* See header for documentation. */ void Instance::store(VkInstance handle, std::unique_ptr& instance) { diff --git a/layer_example/source/instance.hpp b/layer_example/source/instance.hpp index 19ee1aab..67718ae0 100644 --- a/layer_example/source/instance.hpp +++ b/layer_example/source/instance.hpp @@ -157,15 +157,4 @@ class Instance * removed from the list if the driver already exposes the extension. */ static std::vector> injectedDeviceExtensions; - - /** - * @brief Additional API functions that are injected by the layer. - * - * This list must include both instance and device functions, because - * vkGetInstanceProcAddr can (inefficiently) be used to return device - * functions. - * - * The layer will expose these even if the driver does not. - */ - static const std::vector injectedAPIFunctions; }; diff --git a/layer_gpu_profile/source/instance.cpp b/layer_gpu_profile/source/instance.cpp index a49a1610..df4f55ac 100644 --- a/layer_gpu_profile/source/instance.cpp +++ b/layer_gpu_profile/source/instance.cpp @@ -48,9 +48,6 @@ const std::vector> Instance::injectedInstanceEx /* See header for documentation. */ std::vector> Instance::injectedDeviceExtensions {}; -/* See header for documentation. */ -const std::vector Instance::injectedAPIFunctions {}; - /* See header for documentation. */ void Instance::store(VkInstance handle, std::unique_ptr& instance) { diff --git a/layer_gpu_profile/source/instance.hpp b/layer_gpu_profile/source/instance.hpp index b2b8b33e..511d9581 100644 --- a/layer_gpu_profile/source/instance.hpp +++ b/layer_gpu_profile/source/instance.hpp @@ -163,15 +163,4 @@ class Instance * removed from the list if the driver already exposes the extension. */ static std::vector> injectedDeviceExtensions; - - /** - * @brief Additional API functions that are injected by the layer. - * - * This list must include both instance and device functions, because - * vkGetInstanceProcAddr can (inefficiently) be used to return device - * functions. - * - * The layer will expose these even if the driver does not. - */ - static const std::vector injectedAPIFunctions; }; diff --git a/layer_gpu_support/source/instance.cpp b/layer_gpu_support/source/instance.cpp index a49a1610..df4f55ac 100644 --- a/layer_gpu_support/source/instance.cpp +++ b/layer_gpu_support/source/instance.cpp @@ -48,9 +48,6 @@ const std::vector> Instance::injectedInstanceEx /* See header for documentation. */ std::vector> Instance::injectedDeviceExtensions {}; -/* See header for documentation. */ -const std::vector Instance::injectedAPIFunctions {}; - /* See header for documentation. */ void Instance::store(VkInstance handle, std::unique_ptr& instance) { diff --git a/layer_gpu_support/source/instance.hpp b/layer_gpu_support/source/instance.hpp index 5f9724fa..5807d97d 100644 --- a/layer_gpu_support/source/instance.hpp +++ b/layer_gpu_support/source/instance.hpp @@ -162,15 +162,4 @@ class Instance * removed from the list if the driver already exposes the extension. */ static std::vector> injectedDeviceExtensions; - - /** - * @brief Additional API functions that are injected by the layer. - * - * This list must include both instance and device functions, because - * vkGetInstanceProcAddr can (inefficiently) be used to return device - * functions. - * - * The layer will expose these even if the driver does not. - */ - static const std::vector injectedAPIFunctions; }; diff --git a/layer_gpu_timeline/source/instance.cpp b/layer_gpu_timeline/source/instance.cpp index ceac9d3e..0aaf9b1a 100644 --- a/layer_gpu_timeline/source/instance.cpp +++ b/layer_gpu_timeline/source/instance.cpp @@ -50,9 +50,6 @@ std::vector> Instance::injectedDeviceExtensions {VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME, VK_EXT_FRAME_BOUNDARY_SPEC_VERSION} }; -/* See header for documentation. */ -const std::vector Instance::injectedAPIFunctions {}; - /* See header for documentation. */ void Instance::store(VkInstance handle, std::unique_ptr& instance) { diff --git a/layer_gpu_timeline/source/instance.hpp b/layer_gpu_timeline/source/instance.hpp index 19ee1aab..67718ae0 100644 --- a/layer_gpu_timeline/source/instance.hpp +++ b/layer_gpu_timeline/source/instance.hpp @@ -157,15 +157,4 @@ class Instance * removed from the list if the driver already exposes the extension. */ static std::vector> injectedDeviceExtensions; - - /** - * @brief Additional API functions that are injected by the layer. - * - * This list must include both instance and device functions, because - * vkGetInstanceProcAddr can (inefficiently) be used to return device - * functions. - * - * The layer will expose these even if the driver does not. - */ - static const std::vector injectedAPIFunctions; }; From 5f9c82e8e2992a8206e2682ad34a889f2440581f Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Wed, 17 Dec 2025 19:58:27 +0000 Subject: [PATCH 7/7] Whitespace and typo fixes --- generator/vk_layer/source/instance.hpp | 2 +- layer_example/source/instance.hpp | 2 +- layer_gpu_profile/source/instance.hpp | 2 +- layer_gpu_profile/source/layer_device_functions_debug.cpp | 8 ++++---- layer_gpu_support/source/device.cpp | 5 +++-- layer_gpu_support/source/instance.hpp | 2 +- layer_gpu_timeline/source/device.hpp | 2 +- layer_gpu_timeline/source/instance.hpp | 2 +- source_common/framework/manual_functions.cpp | 4 ++-- 9 files changed, 15 insertions(+), 14 deletions(-) diff --git a/generator/vk_layer/source/instance.hpp b/generator/vk_layer/source/instance.hpp index 8faee440..b2cd47b5 100644 --- a/generator/vk_layer/source/instance.hpp +++ b/generator/vk_layer/source/instance.hpp @@ -150,7 +150,7 @@ class Instance static const std::vector requiredDriverExtensions; /** - * @brief Additional instance extensions injected by the layer. + * @brief Additional instance extensions injected by the layer. * * The layer will expose these even if the driver does not. */ diff --git a/layer_example/source/instance.hpp b/layer_example/source/instance.hpp index 67718ae0..acd7c91a 100644 --- a/layer_example/source/instance.hpp +++ b/layer_example/source/instance.hpp @@ -144,7 +144,7 @@ class Instance static const std::vector requiredDriverExtensions; /** - * @brief Additional instance extensions injected by the layer. + * @brief Additional instance extensions injected by the layer. * * The layer will expose these even if the driver does not. */ diff --git a/layer_gpu_profile/source/instance.hpp b/layer_gpu_profile/source/instance.hpp index 511d9581..d2788599 100644 --- a/layer_gpu_profile/source/instance.hpp +++ b/layer_gpu_profile/source/instance.hpp @@ -150,7 +150,7 @@ class Instance static const std::vector requiredDriverExtensions; /** - * @brief Additional instance extensions injected by the layer. + * @brief Additional instance extensions injected by the layer. * * The layer will expose these even if the driver does not. */ diff --git a/layer_gpu_profile/source/layer_device_functions_debug.cpp b/layer_gpu_profile/source/layer_device_functions_debug.cpp index f975b385..74f352f5 100644 --- a/layer_gpu_profile/source/layer_device_functions_debug.cpp +++ b/layer_gpu_profile/source/layer_device_functions_debug.cpp @@ -42,7 +42,7 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdDebugMarkerBeginEXT(VkCommandBuf auto* layer = Device::retrieve(commandBuffer); // Only instrument inside active frame of interest - if(layer->isFrameOfInterest) + if (layer->isFrameOfInterest) { auto& tracker = layer->getStateTracker(); auto& cb = tracker.getCommandBuffer(commandBuffer); @@ -67,7 +67,7 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdDebugMarkerEndEXT(VkCommandBuffe auto* layer = Device::retrieve(commandBuffer); // Only instrument inside active frame of interest - if(layer->isFrameOfInterest) + if (layer->isFrameOfInterest) { auto& tracker = layer->getStateTracker(); auto& cb = tracker.getCommandBuffer(commandBuffer); @@ -93,7 +93,7 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdBeginDebugUtilsLabelEXT(VkComman auto* layer = Device::retrieve(commandBuffer); // Only instrument inside active frame of interest - if(layer->isFrameOfInterest) + if (layer->isFrameOfInterest) { auto& tracker = layer->getStateTracker(); auto& cb = tracker.getCommandBuffer(commandBuffer); @@ -118,7 +118,7 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdEndDebugUtilsLabelEXT(VkCommandB auto* layer = Device::retrieve(commandBuffer); // Only instrument inside active frame of interest - if(layer->isFrameOfInterest) + if (layer->isFrameOfInterest) { auto& tracker = layer->getStateTracker(); auto& cb = tracker.getCommandBuffer(commandBuffer); diff --git a/layer_gpu_support/source/device.cpp b/layer_gpu_support/source/device.cpp index f3cdeabb..f9c9664e 100644 --- a/layer_gpu_support/source/device.cpp +++ b/layer_gpu_support/source/device.cpp @@ -180,7 +180,7 @@ static void modifyDeviceRobustBufferAccess(Instance& instance, { if (enableRobustness) { - if(config->robustBufferAccess) + if (config->robustBufferAccess) { LAYER_LOG("Device feature already enabled: robustBufferAccess"); } @@ -190,9 +190,10 @@ static void modifyDeviceRobustBufferAccess(Instance& instance, config->robustBufferAccess = VK_TRUE; } } + if (disableRobustness) { - if(!config->robustBufferAccess) + if (!config->robustBufferAccess) { LAYER_LOG("Device feature already disabled: robustBufferAccess"); } diff --git a/layer_gpu_support/source/instance.hpp b/layer_gpu_support/source/instance.hpp index 5807d97d..d24cf593 100644 --- a/layer_gpu_support/source/instance.hpp +++ b/layer_gpu_support/source/instance.hpp @@ -149,7 +149,7 @@ class Instance static const std::vector requiredDriverExtensions; /** - * @brief Additional instance extensions injected by the layer. + * @brief Additional instance extensions injected by the layer. * * The layer will expose these even if the driver does not. */ diff --git a/layer_gpu_timeline/source/device.hpp b/layer_gpu_timeline/source/device.hpp index 4778d313..e3bb6b85 100644 --- a/layer_gpu_timeline/source/device.hpp +++ b/layer_gpu_timeline/source/device.hpp @@ -188,7 +188,7 @@ class Device /** * @brief Is this layer emulating VK_EXT_frame_boundary? * - * Set to @c true is layer is emulating on top a driver that doesn't + * Set to @c true if layer is emulating on top of a driver that doesn't * support it, @c false if layer knows driver supports it. */ bool isEmulatingExtFrameBoundary { false }; diff --git a/layer_gpu_timeline/source/instance.hpp b/layer_gpu_timeline/source/instance.hpp index 67718ae0..acd7c91a 100644 --- a/layer_gpu_timeline/source/instance.hpp +++ b/layer_gpu_timeline/source/instance.hpp @@ -144,7 +144,7 @@ class Instance static const std::vector requiredDriverExtensions; /** - * @brief Additional instance extensions injected by the layer. + * @brief Additional instance extensions injected by the layer. * * The layer will expose these even if the driver does not. */ diff --git a/source_common/framework/manual_functions.cpp b/source_common/framework/manual_functions.cpp index 3f9f07bf..a7bcbe6a 100644 --- a/source_common/framework/manual_functions.cpp +++ b/source_common/framework/manual_functions.cpp @@ -664,7 +664,7 @@ VkResult layer_vkEnumerateInstanceExtensionProperties(const char* p if (pLayerName) { // ... but not this layer - if(strcmp(pLayerName, layerProps[0].layerName)) + if (strcmp(pLayerName, layerProps[0].layerName)) { return VK_ERROR_LAYER_NOT_PRESENT; } @@ -799,7 +799,7 @@ VkResult layer_vkEnumerateDeviceExtensionProperties(VkPhysicalDevic if (pLayerName) { // ... but not this layer - if(strcmp(pLayerName, layerProps[0].layerName)) + if (strcmp(pLayerName, layerProps[0].layerName)) { return VK_ERROR_LAYER_NOT_PRESENT; }