Skip to content

Commit 32abeef

Browse files
committed
Expose extension strings
1 parent cde0782 commit 32abeef

File tree

21 files changed

+760
-70
lines changed

21 files changed

+760
-70
lines changed

docs/extension_support.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Extension support in a layer
2+
3+
It might be useful for some layers to implement an extension, such as
4+
`VK_EXT_frame_boundary`, even if the underlying driver does not support it.
5+
This page explains the general approach that needs to be taken, and the
6+
specific API modifications that need to be applied for specific extensions.
7+
8+
Note that the core framework allows you to expose additional extensions via
9+
the default `vkEnumerate*ExtensionProperties()` implementation, but per-layer
10+
code must implement the API modifications to other functions as needed.
11+
12+
## Exposing a new extension
13+
14+
New extensions are advertised to applications by adding the extension string to
15+
the list returned by `vkEnumerate*ExtensionProperties()`. This functionality
16+
is provided in the common framework default functions. Layer implementations
17+
add the new extension information that they want to expose to either:
18+
19+
* `Instance::injectedInstanceExtensions` for instance extensions.
20+
* `Instance::injectedDeviceExtensions` for device extensions.
21+
22+
Device extensions will be removed from this list if we can detect that the
23+
underlying device already supports them, which means we can just pass through.
24+
25+
### Handling extended API entry points
26+
27+
All entrypoints that are touched by an extension need to be intercepted with
28+
a `user_tag` version of a function, which will implement the functionality that
29+
the layer requires.
30+
31+
If the driver beneath the layer actually supports the extension, the extended
32+
API parameters can be passed down to the driver without modification. This
33+
scenario can be detected by checking that the extension name is no longer in
34+
the `injectedExtensions` list, although the layer will probably want to cache
35+
this check to reduce performance overhead.
36+
37+
If the driver beneath the layer does not support the extension, the extended
38+
API parameters must be rewritten to remove the extension before passing down
39+
to the driver. User structure inputs to the Vulkan API are usually marked as
40+
`const`, so we must take a safe-struct copy which we can modify and pass
41+
that copy to the driver.
42+
43+
## Common extension notes
44+
45+
This section is a set of brief notes about extensions that we have implemented,
46+
summarizing the changes needed and referencing where you can find an example
47+
of the changes if you need something similar.
48+
49+
### VK_EXT_frame_boundary
50+
51+
This extension allows applications to annotate arbitrary submit calls to
52+
indicate which frame the submitted work belongs to, instead of relying on
53+
`vkQueuePresent()`. This can be useful for multi-threaded applications,
54+
where CPU processing for frames can overlap, and for applications which
55+
do not have frames, but that want to use tools such as RenderDoc that
56+
require them.
57+
58+
#### Exposing extension
59+
60+
Adding exposure handling:
61+
62+
* Add `VK_EXT_frame_boundary` to device extension list.
63+
* Add `VkPhysicalDeviceFrameBoundary` to `VkPhysicalDeviceFeatures2.pNext` list
64+
returned by `vkGetPhysicalDeviceFeatures2()`, or force existing structure to
65+
`VK_TRUE`, if not supported by driver.
66+
* Query `VkPhysicalDeviceFrameBoundary` in `VkDeviceCreateInfo.pNext` to see if
67+
application enabled the extension. Strip if not supported by the driver.
68+
69+
#### Implementing extension
70+
71+
Adding implementation handling:
72+
73+
* Add `VkFrameBoundaryEXT` extension struct handling to:
74+
* `vkQueueSubmit()`
75+
* `vkQueueSubmit2()`
76+
* `vkQueuePresent()`
77+
* `vkQueueBindSparse()`
78+
79+
#### Implementation notes
80+
81+
Most applications using this that I have seen are using it to demarcate frames
82+
when using a single submitting render thread for off-screen rendering or
83+
compute use cases that do not use `vkQueuePresent()`. In these systems just
84+
detecting a change in frame ID is enough to indicate "frame changed", much
85+
how we would use `vkQueuePresent()` to do the same without this extension.
86+
87+
It is possible for applications to have multiple concurrent frames being
88+
submitted in an overlapping manner, which can be handled by tagging work with
89+
the frame ID found in the extension structure for each `vkQueue*()` call. This
90+
will require downstream data handling to cope with overlapping frame
91+
submissions, which most of our layers do not handle, as it is rarely
92+
encountered.
93+
94+
- - -
95+
96+
_Copyright © 2025, Arm Limited and contributors._

generator/vk_layer/source/instance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const std::vector<std::string> Instance::requiredDriverExtensions {
4646
const std::vector<std::pair<std::string, uint32_t>> Instance::injectedInstanceExtensions {};
4747

4848
/* See header for documentation. */
49-
const std::vector<std::pair<std::string, uint32_t>> Instance::injectedDeviceExtensions {};
49+
std::vector<std::pair<std::string, uint32_t>> Instance::injectedDeviceExtensions {};
5050

5151
/* See header for documentation. */
5252
const std::vector<std::string> Instance::injectedAPIFunctions {};

generator/vk_layer/source/instance.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,10 @@ class Instance
159159
/**
160160
* @brief Additional device extensions injected by the layer.
161161
*
162-
* The layer will expose these even if the driver does not.
162+
* The layer will expose these even if the driver does not. Items are
163+
* removed from the list if the driver already exposes the extension.
163164
*/
164-
static const std::vector<std::pair<std::string, uint32_t>> injectedDeviceExtensions;
165+
static std::vector<std::pair<std::string, uint32_t>> injectedDeviceExtensions;
165166

166167
/**
167168
* @brief Additional API functions that are injected by the layer.

layer_example/source/instance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const std::vector<std::string> Instance::requiredDriverExtensions {
4646
const std::vector<std::pair<std::string, uint32_t>> Instance::injectedInstanceExtensions {};
4747

4848
/* See header for documentation. */
49-
const std::vector<std::pair<std::string, uint32_t>> Instance::injectedDeviceExtensions {};
49+
std::vector<std::pair<std::string, uint32_t>> Instance::injectedDeviceExtensions {};
5050

5151
/* See header for documentation. */
5252
const std::vector<std::string> Instance::injectedAPIFunctions {};

layer_example/source/instance.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,10 @@ class Instance
153153
/**
154154
* @brief Additional device extensions injected by the layer.
155155
*
156-
* The layer will expose these even if the driver does not.
156+
* The layer will expose these even if the driver does not. Items are
157+
* removed from the list if the driver already exposes the extension.
157158
*/
158-
static const std::vector<std::pair<std::string, uint32_t>> injectedDeviceExtensions;
159+
static std::vector<std::pair<std::string, uint32_t>> injectedDeviceExtensions;
159160

160161
/**
161162
* @brief Additional API functions that are injected by the layer.

layer_gpu_profile/source/instance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const std::vector<std::string> Instance::requiredDriverExtensions {
4646
const std::vector<std::pair<std::string, uint32_t>> Instance::injectedInstanceExtensions {};
4747

4848
/* See header for documentation. */
49-
const std::vector<std::pair<std::string, uint32_t>> Instance::injectedDeviceExtensions {};
49+
std::vector<std::pair<std::string, uint32_t>> Instance::injectedDeviceExtensions {};
5050

5151
/* See header for documentation. */
5252
const std::vector<std::string> Instance::injectedAPIFunctions {};

layer_gpu_profile/source/instance.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,10 @@ class Instance
159159
/**
160160
* @brief Additional device extensions injected by the layer.
161161
*
162-
* The layer will expose these even if the driver does not.
162+
* The layer will expose these even if the driver does not. Items are
163+
* removed from the list if the driver already exposes the extension.
163164
*/
164-
static const std::vector<std::pair<std::string, uint32_t>> injectedDeviceExtensions;
165+
static std::vector<std::pair<std::string, uint32_t>> injectedDeviceExtensions;
165166

166167
/**
167168
* @brief Additional API functions that are injected by the layer.

layer_gpu_support/source/instance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const std::vector<std::string> Instance::requiredDriverExtensions {
4646
const std::vector<std::pair<std::string, uint32_t>> Instance::injectedInstanceExtensions {};
4747

4848
/* See header for documentation. */
49-
const std::vector<std::pair<std::string, uint32_t>> Instance::injectedDeviceExtensions {};
49+
std::vector<std::pair<std::string, uint32_t>> Instance::injectedDeviceExtensions {};
5050

5151
/* See header for documentation. */
5252
const std::vector<std::string> Instance::injectedAPIFunctions {};

layer_gpu_support/source/instance.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,10 @@ class Instance
158158
/**
159159
* @brief Additional device extensions injected by the layer.
160160
*
161-
* The layer will expose these even if the driver does not.
161+
* The layer will expose these even if the driver does not. Items are
162+
* removed from the list if the driver already exposes the extension.
162163
*/
163-
static const std::vector<std::pair<std::string, uint32_t>> injectedDeviceExtensions;
164+
static std::vector<std::pair<std::string, uint32_t>> injectedDeviceExtensions;
164165

165166
/**
166167
* @brief Additional API functions that are injected by the layer.

layer_gpu_support/source/layer_device_functions_image.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,10 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateImage<user_tag>(VkDevice device,
122122
}
123123

124124
// Create modifiable structures we can patch
125-
vku::safe_VkImageCreateInfo newCreateInfoSafe(pCreateInfo);
126-
auto* newCreateInfo = reinterpret_cast<VkImageCreateInfo*>(&newCreateInfoSafe);
125+
vku::safe_VkImageCreateInfo safeCreateInfo(pCreateInfo);
126+
auto* newCreateInfo = reinterpret_cast<VkImageCreateInfo*>(&safeCreateInfo);
127127
// We know we can const-cast here because this is a safe-struct clone
128-
void* pNextBase = const_cast<void*>(newCreateInfoSafe.pNext);
128+
void* pNextBase = const_cast<void*>(safeCreateInfo.pNext);
129129

130130
// Create extra structures we can patch in
131131
VkImageCompressionControlEXT newCompressionControl = vku::InitStructHelper();
@@ -165,7 +165,7 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateImage<user_tag>(VkDevice device,
165165
// Add a config if not already configured by the application
166166
if (patchNeeded)
167167
{
168-
vku::AddToPnext(newCreateInfoSafe, *compressionControl);
168+
vku::AddToPnext(safeCreateInfo, *compressionControl);
169169
}
170170

171171
return layer->driver.vkCreateImage(device, newCreateInfo, pAllocator, pImage);

0 commit comments

Comments
 (0)