Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 81a0d9c

Browse files
committed
Add explanation of uniform buffer alignment requirements
1 parent d878474 commit 81a0d9c

9 files changed

Lines changed: 144 additions & 24 deletions

05_Uniform_buffers/01_Descriptor_pool_and_sets.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,126 @@ corrects for aspect ratio. The `updateUniformBuffer` takes care of screen
251251
resizing, so we don't need to recreate the descriptor set in
252252
`recreateSwapChain`.
253253

254+
## Alignment requirements
255+
256+
One thing we've glossed over so far is how exactly the data in the C++ structure should match with the uniform definition in the shader. It seems obvious enough to simply use the same types in both:
257+
258+
```c++
259+
struct UniformBufferObject {
260+
glm::mat4 model;
261+
glm::mat4 view;
262+
glm::mat4 proj;
263+
};
264+
265+
layout(binding = 0) uniform UniformBufferObject {
266+
mat4 model;
267+
mat4 view;
268+
mat4 proj;
269+
} ubo;
270+
```
271+
272+
However, that's not all there is to it. For example, try modifying the struct and shader to look like this:
273+
274+
```c++
275+
struct UniformBufferObject {
276+
glm::vec2 foo;
277+
glm::mat4 model;
278+
glm::mat4 view;
279+
glm::mat4 proj;
280+
};
281+
282+
layout(binding = 0) uniform UniformBufferObject {
283+
vec2 foo;
284+
mat4 model;
285+
mat4 view;
286+
mat4 proj;
287+
} ubo;
288+
```
289+
290+
Recompile your shader and your program and run it and you'll find that the colorful square you worked so far has disappeared! That's because we haven't taken into account the *alignment requirements*.
291+
292+
Vulkan expects the data in your structure to be aligned in memory in a specific way, for example:
293+
294+
* Scalars have to be aligned by N (= 4 bytes given 32 bit floats).
295+
* A `vec2` must be aligned by 2N (= 8 bytes)
296+
* A `vec3` or `vec4` must be aligned by 4N (= 16 bytes)
297+
* A nested structure must be aligned by the base alignment of its members rounded up to a multiple of 16.
298+
* A `mat4` matrix must have the same alignment as a `vec4`.
299+
300+
You can find the full list of alignment requirements in [the specification](https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/chap14.html#interfaces-resources-layout).
301+
302+
Our original shader with just three `mat4` fields already met the alignment requirements. As each `mat4` is 4 x 4 x 4 = 64 bytes in size, `model` has an offset of `0`, `view` has an offset of 64 and `proj` has an offset of 128. All of these are multiples of 16 and that's why it worked fine.
303+
304+
The new structure starts with a `vec2` which is only 8 bytes in size and therefore throws off all of the offsets. Now `model` has an offset of `8`, `view` an offset of `72` and `proj` an offset of `136`, none of which are multiples of 16. To fix this problem we can use the [`alignas`](https://en.cppreference.com/w/cpp/language/alignas) specifier introduced in C++11:
305+
306+
```c++
307+
struct UniformBufferObject {
308+
glm::vec2 foo;
309+
alignas(16) glm::mat4 model;
310+
glm::mat4 view;
311+
glm::mat4 proj;
312+
};
313+
```
314+
315+
If you now compile and run your program again you should see that the shader correctly receives its matrix values once again.
316+
317+
Luckily there is a way to not have to think about these alignment requirements *most* of the time. We can define `GLM_FORCE_DEFAULT_ALIGNED_GENTYPES` right before including GLM:
318+
319+
```c++
320+
#define GLM_FORCE_RADIANS
321+
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES
322+
#include <glm/glm.hpp>
323+
```
324+
325+
This will force GLM to use a version of `vec2` and `mat4` that has the alignment requirements already specified for us. If you add this definition then you can remove the `alignas` specifier and your program should still work.
326+
327+
Unfortunately this method can break down if you start using nested structures. Consider the following definition in the C++ code:
328+
329+
```c++
330+
struct Foo {
331+
glm::vec2 v;
332+
};
333+
334+
struct UniformBufferObject {
335+
Foo f1;
336+
Foo f2;
337+
};
338+
```
339+
340+
And the following shader definition:
341+
342+
```c++
343+
struct Foo {
344+
vec2 v;
345+
};
346+
347+
layout(binding = 0) uniform UniformBufferObject {
348+
Foo f1;
349+
Foo f2;
350+
} ubo;
351+
```
352+
353+
In this case `f2` will have an offset of `8` whereas it should have an offset of `16` since it is a nested structure. In this case you must specify the alignment yourself:
354+
355+
```c++
356+
struct UniformBufferObject {
357+
Foo f1;
358+
alignas(16) Foo f2;
359+
};
360+
```
361+
362+
These gotchas are a good reason to always be explicit about alignment. That way you won't be caught offguard by the strange symptoms of alignment errors.
363+
364+
```c++
365+
struct UniformBufferObject {
366+
alignas(16) glm::mat4 model;
367+
alignas(16) glm::mat4 view;
368+
alignas(16) glm::mat4 proj;
369+
};
370+
```
371+
372+
Don't forget to recompile your shader after removing the `foo` field.
373+
254374
## Multiple descriptor sets
255375

256376
As some of the structures and function calls hinted at, it is actually possible

code/22_descriptor_sets.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ struct Vertex {
9898
};
9999

100100
struct UniformBufferObject {
101-
glm::mat4 model;
102-
glm::mat4 view;
103-
glm::mat4 proj;
101+
alignas(16) glm::mat4 model;
102+
alignas(16) glm::mat4 view;
103+
alignas(16) glm::mat4 proj;
104104
};
105105

106106
const std::vector<Vertex> vertices = {

code/23_texture_image.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ struct Vertex {
101101
};
102102

103103
struct UniformBufferObject {
104-
glm::mat4 model;
105-
glm::mat4 view;
106-
glm::mat4 proj;
104+
alignas(16) glm::mat4 model;
105+
alignas(16) glm::mat4 view;
106+
alignas(16) glm::mat4 proj;
107107
};
108108

109109
const std::vector<Vertex> vertices = {

code/24_sampler.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ struct Vertex {
101101
};
102102

103103
struct UniformBufferObject {
104-
glm::mat4 model;
105-
glm::mat4 view;
106-
glm::mat4 proj;
104+
alignas(16) glm::mat4 model;
105+
alignas(16) glm::mat4 view;
106+
alignas(16) glm::mat4 proj;
107107
};
108108

109109
const std::vector<Vertex> vertices = {

code/25_texture_mapping.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ struct Vertex {
107107
};
108108

109109
struct UniformBufferObject {
110-
glm::mat4 model;
111-
glm::mat4 view;
112-
glm::mat4 proj;
110+
alignas(16) glm::mat4 model;
111+
alignas(16) glm::mat4 view;
112+
alignas(16) glm::mat4 proj;
113113
};
114114

115115
const std::vector<Vertex> vertices = {

code/26_depth_buffering.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ struct Vertex {
108108
};
109109

110110
struct UniformBufferObject {
111-
glm::mat4 model;
112-
glm::mat4 view;
113-
glm::mat4 proj;
111+
alignas(16) glm::mat4 model;
112+
alignas(16) glm::mat4 view;
113+
alignas(16) glm::mat4 proj;
114114
};
115115

116116
const std::vector<Vertex> vertices = {

code/27_model_loading.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ namespace std {
129129
}
130130

131131
struct UniformBufferObject {
132-
glm::mat4 model;
133-
glm::mat4 view;
134-
glm::mat4 proj;
132+
alignas(16) glm::mat4 model;
133+
alignas(16) glm::mat4 view;
134+
alignas(16) glm::mat4 proj;
135135
};
136136

137137
class HelloTriangleApplication {

code/28_mipmapping.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ namespace std {
129129
}
130130

131131
struct UniformBufferObject {
132-
glm::mat4 model;
133-
glm::mat4 view;
134-
glm::mat4 proj;
132+
alignas(16) glm::mat4 model;
133+
alignas(16) glm::mat4 view;
134+
alignas(16) glm::mat4 proj;
135135
};
136136

137137
class HelloTriangleApplication {

code/29_multisampling.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ namespace std {
129129
}
130130

131131
struct UniformBufferObject {
132-
glm::mat4 model;
133-
glm::mat4 view;
134-
glm::mat4 proj;
132+
alignas(16) glm::mat4 model;
133+
alignas(16) glm::mat4 view;
134+
alignas(16) glm::mat4 proj;
135135
};
136136

137137
class HelloTriangleApplication {

0 commit comments

Comments
 (0)