Skip to content

Commit 69f1fd3

Browse files
Add Buffer Device Address chapter (#275)
1 parent 3aac4ef commit 69f1fd3

File tree

10 files changed

+236
-0
lines changed

10 files changed

+236
-0
lines changed

README.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ The Vulkan Guide can be built as a single page using `asciidoctor guide.adoc`
158158

159159
// include::{chapters}protected.adoc[]
160160

161+
=== xref:{chapters}buffer_device_address.adoc[Buffer Device Address]
162+
163+
// include::{chapters}buffer_device_address.adoc[]
164+
165+
* `VK_KHR_buffer_device_address`, `VK_EXT_buffer_device_address`
166+
161167
== xref:{chapters}pipeline_cache.adoc[Pipeline Caching/Derivatives]
162168

163169
// include::{chapters}pipeline_cache.adoc[]
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Copyright 2024 The Khronos Group, Inc.
2+
// SPDX-License-Identifier: CC-BY-4.0
3+
4+
// Required for both single-page and combined guide xrefs to work
5+
ifndef::chapters[:chapters:]
6+
ifndef::images[:images: images/]
7+
8+
[[buffer-device-address]]
9+
= Buffer Device Address
10+
11+
Buffer Device Address allows you to have a pointer to the `VkBuffer` in your shaders. There are many other usages such as Ray Tracing, GPU Side tooling, etc.
12+
13+
[NOTE]
14+
====
15+
This will be more of a technical breakdown of how it works, if you looking for a simple "how do I just use this", then please take a look at the link:https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/extensions/buffer_device_address[Vulkan Samples].
16+
====
17+
18+
== Extension
19+
20+
The original proposal was done with the `VK_EXT_buffer_device_address` extension. Shortly afterwards, `VK_KHR_buffer_device_address` was added, which had some minor feature differences. Starting in Vulkan 1.2 this has become core and it is very widely available on GPUs on every platform. Starting in Vulkan 1.3 it became required, so if you are using Vulkan 1.3, you are guaranteed support.
21+
22+
=== Name alias
23+
24+
The naming of this `Buffer Device Address` feature will be different depending on where you look.
25+
26+
For GLSL, it is `buffer reference` (see link:https://github.com/KhronosGroup/GLSL/blob/main/extensions/ext/GLSL_EXT_buffer_reference.txt[GL_EXT_buffer_reference]). This is because syntactically it's used more like a C++ reference than a pointer.
27+
28+
For SPIR-V, it is `PhysicalStorageBuffer` (see link:https://htmlpreview.github.io/?https://github.com/KhronosGroup/SPIRV-Registry/blob/main/extensions/KHR/SPV_KHR_physical_storage_buffer.html[SPV_KHR_physical_storage_buffer]). The name comes from the fact there was a `StorageBuffer` and now it is "Physical". (`Physical` is from "physical addressing" which is a concept in `Kernel` SPIR-V)
29+
30+
If you are coming from DirectX, you will know the feature as `GPU virtual address`.
31+
32+
== Vulkan Side
33+
34+
From the Vulkan code, the only 3 things you need to do is
35+
36+
1. Enable the `bufferDeviceAddress` feature. (Also the extension if using Vulkan 1.0 or 1.1)
37+
2. Add `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR` when creating your `VkBuffer`.
38+
3. Add `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR` when allocating your `VkDeviceMemory`.
39+
40+
From here you can use the `vkGetBufferDeviceAddress` call and it will return a `VkDeviceAddress`. This is now your 64-bit pointer to that `VkBuffer` that can be handed down into your shader.
41+
42+
[NOTE]
43+
====
44+
For tools, the `vkGetBufferDeviceAddress` function might break assumptions that Vulkan function only return `void` or `VkResult`
45+
====
46+
47+
== SPIR-V Side
48+
49+
[NOTE]
50+
====
51+
If you are using GLSL/HLSL/Slang/etc then the following is all taken care for you!
52+
====
53+
54+
=== Capability
55+
56+
The SPIR-V will contain the `OpCapability PhysicalStorageBufferAddresses` instruction that will match up with `VkPhysicalDeviceVulkan12Features::bufferDeviceAddress` (or `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`) to let everyone know the device does support this.
57+
58+
=== Addressing Model
59+
60+
SPIR-V has an link:https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#Addressing_Model[Addressing Model] that has 3 group of options
61+
62+
1. `Logical` - this is what Vulkan 1.0 uses, it has no concept of pointers
63+
2. `Physical32`/`Physical64` - this for OpenCL
64+
3. `PhysicalStorageBuffer64` - this is what you will use if you are making use of Buffer Device Address
65+
66+
=== shaderInt64
67+
68+
Since these `VkDeviceAddress` are represented as 64-bit integer pointers in your shader, you will likely want `shaderInt64` support for your device.
69+
70+
Some device migth support `bufferDeviceAddress`, but not `shaderInt64`. The way around this situation is to make everything an `uvec2` (see link:https://github.com/KhronosGroup/GLSL/blob/main/extensions/ext/GLSL_EXT_buffer_reference_uvec2.txt[GL_EXT_buffer_reference_uvec2]).
71+
72+
=== Alignment
73+
74+
All variables accessed with `PhysicalStorageBuffer` must have an `Aligned` memory operand to it.
75+
76+
[source,swift]
77+
----
78+
%x = OpLoad %type %ptr Aligned 16
79+
OpStore %ptr %obj Aligned 16
80+
----
81+
82+
Shading languages will have a default, but can allow you to align it explicitly (ex `buffer_reference_alignment`).
83+
84+
The goal of this alignment is this is a promise for how aligned this specific pointer is. The user is responsible to confirm the address they use is aligned to it.
85+
86+
=== Nullptr
87+
88+
SPIR-V has a `OpConstantNull`, but that can't be used with `PhysicalStorageBuffer`. The way around this is to either convert the pointer to an integer with `OpConvertPtrToU` or to a `uvec2` with `OpBitcast`.
89+
90+
Vulkan defines the integer value of `0` to be null (as everyone would hopefully expect!).
91+
92+
=== Cross stage variables
93+
94+
There is still on-going discussion to clarify if/how passing pointers from two stages works (ex. vertex to fragment).
95+
96+
The main issue is you may get validation layers errors with the `Location` matching (see https://github.com/KhronosGroup/Vulkan-ValidationLayers/pull/5349).
97+
98+
The suggestion is to just pass a `uvec2` or `int64` across stages and re-cast it in your consumer stage.
99+
100+
=== OpTypeForwardPointer and infinite loops
101+
102+
The `OpTypeForwardPointer` is used to forward reference the type of the pointer. This is useful if the app wants to do something like a linked-list
103+
104+
[source,glsl]
105+
----
106+
layout(buffer_reference) buffer Node;
107+
108+
layout(buffer_reference, std430) buffer Node {
109+
Node next_node;
110+
int payload;
111+
};
112+
113+
layout(set = 0, binding = 0, std430) buffer SSBO {
114+
Node start;
115+
};
116+
----
117+
118+
You will see the following SPIR-V
119+
120+
[source,swift]
121+
----
122+
OpTypeForwardPointer %Node_ptr PhysicalStorageBuffer
123+
%SSBO = OpTypeStruct %Node_ptr
124+
%int = OpTypeInt 32 1
125+
%Node = OpTypeStruct %Node_ptr %int
126+
%Node_ptr = OpTypePointer PhysicalStorageBuffer %Node
127+
%SSBO_ptr = OpTypePointer StorageBuffer %SSBO
128+
%var = OpVariable %SSBO_ptr StorageBuffer
129+
----
130+
131+
When parsing this SPIR-V to do reflection, it is very easy to get into an infinite loop, so be careful.
132+
133+
[NOTE]
134+
====
135+
If you want some SPIR-V to test this, look at the `buffer_handle_*.spv` tests in link:https://github.com/KhronosGroup/SPIRV-Reflect/blob/main/tests/glsl[SPIR-V Reflect Tests].
136+
====
137+
138+
=== Accesses
139+
140+
If you take the following simple GLSL example
141+
142+
[source,glsl]
143+
----
144+
#version 450
145+
#extension GL_EXT_buffer_reference : enable
146+
147+
layout(buffer_reference) buffer BDA {
148+
int a;
149+
};
150+
151+
layout(set=0, binding=0) uniform InData {
152+
BDA b;
153+
};
154+
155+
void main() {
156+
b.a = 0;
157+
}
158+
----
159+
160+
You will see the following SPIR-V
161+
162+
[source,swift]
163+
----
164+
%bda_ptr = OpTypePointer PhysicalStorageBuffer %bda_struct
165+
%ubo_ptr = OpTypePointer Uniform %bda_ptr
166+
%int_ptr = OpTypePointer PhysicalStorageBuffer %type_int
167+
168+
%1 = OpAccessChain %ubo_ptr %3 %int_0
169+
%2 = OpLoad %bda_ptr %1
170+
%3 = OpAccessChain %int_ptr %2 %int_0
171+
OpStore %3 %int_0 Aligned 16
172+
----
173+
174+
It is easy to think that this `OpLoad` here is dereferencing the pointer before we store into it.
175+
176+
This assumption is wrong, and instead the `OpLoad` is loading the logical pointer from the `ubo`. The access chain is computing an offset from that to the physical pointer. From here, the `OpStore` access the right location in memory through the pointer.
177+
178+
Therefore in the above example, there is only a write access to that memory.
179+
180+
== Overview
181+
182+
The following diagram tries to capture visually how Buffer Device Address works
183+
184+
image::{images}buffer_device_address_overview.png[buffer_device_address_overview.png]
115 KB
Loading

guide.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ include::{chapters}sparse_resources.adoc[]
157157

158158
include::{chapters}protected.adoc[]
159159

160+
// === Buffer Device Address
161+
162+
include::{chapters}buffer_device_address.adoc[]
163+
160164
// == Pipeline Caching/Derivatives
161165

162166
include::{chapters}pipeline_cache.adoc[]

lang/jp/README-jp.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ Vulkan Guide は、`asciidoctor guide.adoc` を使って1つのページとし
144144

145145
// include::{chapters}protected.adoc[]
146146

147+
=== xref:{chapters}buffer_device_address.adoc[Buffer Device Address]
148+
149+
// include::{chapters}buffer_device_address.adoc[]
150+
151+
* `VK_KHR_buffer_device_address`, `VK_EXT_buffer_device_address`
152+
147153
== xref:{chapters}pipeline_cache.adoc[パイプライン キャッシング/派生]
148154

149155
// include::{chapters}pipeline_cache.adoc[]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2024 The Khronos Group, Inc.
2+
// SPDX-License-Identifier: CC-BY-4.0
3+
4+
// Required for both single-page and combined guide xrefs to work
5+
ifndef::chapters[:chapters:]
6+
ifndef::images[:images: images/]
7+
8+
[[buffer-device-address]]
9+
= Buffer Device Address
10+
11+
すみません!まだまだ翻訳が必要!

lang/jp/guide.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ include::{chapters}sparse_resources.adoc[]
150150

151151
include::{chapters}protected.adoc[]
152152

153+
// === Buffer Device Address
154+
155+
include::{chapters}buffer_device_address.adoc[]
156+
153157
// == Pipeline Caching/Derivatives
154158

155159
include::{chapters}pipeline_cache.adoc[]

lang/kor/README-kor.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ Vulkan에서 사용하는 특수한 용어에 대한 혼란을 막기 위해 명
159159

160160
// include::{chapters}protected.adoc[]
161161

162+
=== xref:{chapters}buffer_device_address.adoc[Buffer Device Address]
163+
164+
// include::{chapters}buffer_device_address.adoc[]
165+
166+
* `VK_KHR_buffer_device_address`, `VK_EXT_buffer_device_address`
167+
162168
== xref:{chapters}pipeline_cache.adoc[파이프라인 캐싱/파생]
163169

164170
// include::{chapters}pipeline_cache.adoc[]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2024 The Khronos Group, Inc.
2+
// SPDX-License-Identifier: CC-BY-4.0
3+
4+
// Required for both single-page and combined guide xrefs to work
5+
ifndef::chapters[:chapters:]
6+
ifndef::images[:images: images/]
7+
8+
[[buffer-device-address]]
9+
= Buffer Device Address
10+
11+
죄송합니다, 아직 번역이 필요합니다.

lang/kor/guide.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ include::{chapters}sparse_resources.adoc[]
153153

154154
include::{chapters}protected.adoc[]
155155

156+
// === Buffer Device Address
157+
158+
include::{chapters}buffer_device_address.adoc[]
159+
156160
// == Pipeline Caching/Derivatives
157161

158162
include::{chapters}pipeline_cache.adoc[]

0 commit comments

Comments
 (0)