着色器内存布局
当实现从接口访问内存时,它需要知道内存布局。这包括诸如偏移量、跨度和对齐方式等内容。虽然 Vulkan 规范有一个专门针对此的章节,但由于各种扩展为规范语言增加了额外的复杂性,因此很难解析。本章旨在帮助解释 Vulkan 使用的所有内存布局概念,并提供一些高级着色语言 (GLSL) 示例。
对齐要求
Vulkan 具有 3 个对齐要求,接口对象可以在其中进行布局。
-
扩展对齐(也称为
std140
) -
基本对齐(也称为
std430
) -
标量对齐
对齐的规范语言分解了以下每个块成员类型的规则。
-
标量 (
float
、int
、char
等) -
向量 (
float2
、vec3
、uvec4
等) -
矩阵
-
数组
-
结构体
VK_KHR_uniform_buffer_standard_layout
在 Vulkan 1.2 中晋升为核心 |
此扩展允许在 UBO 中使用 std430
内存布局。Vulkan 标准缓冲区布局接口可以在本指南之外找到。这些内存布局更改仅应用于 Uniforms
,因为其他存储项目(例如推送常量和 SSBO)已经允许 std430 样式的布局。
当应用程序不希望 UBO 的数组步幅被限制为 extended alignment
时,需要 uniformBufferStandardLayout
功能的一个示例。
(在线尝试)
layout(std140, binding = 0) uniform ubo140 {
float array140[8];
};
layout(std430, binding = 1) uniform ubo430 {
float array430[8];
};
在 SPIR-V 中转换为
// extended alignment for array is rounded up to multiple of 16
OpDecorate %array140 ArrayStride 16
// base alignment is 4 bytes (OpTypeFloat 32)
// only valid with uniformBufferStandardLayout feature enabled
OpDecorate %array430 ArrayStride 4
运行 SPIR-V 验证器时,请确保设置 --uniform-buffer-standard-layout
。
VK_KHR_relaxed_block_layout
在 Vulkan 1.1 中晋升为核心 从未为此扩展添加功能位,因此所有 Vulkan 1.1+ 设备都支持宽松的块布局。 |
此扩展允许实现指示它们可以支持块 Offset
修饰符的更多变化。这在使用 std430
内存布局时出现,其中 vec3
(12 个字节)仍然定义为 16 字节对齐。使用宽松的块布局,应用程序可以将 float
放置在 vec3
的任一侧,并保持它们之间的 16 字节对齐。
// SPIR-V offsets WITHOUT relaxed block layout
layout (set = 0, binding = 0) buffer block {
float b; // Offset: 0
vec3 a; // Offset: 16
} ssbo;
// SPIR-V offsets WITH relaxed block layout
layout (set = 0, binding = 0) buffer block {
float b; // Offset: 0
vec3 a; // Offset: 4
} ssbo;
VK_KHR_relaxed_block_layout
也可以看作是 VK_EXT_scalar_block_layout
的子集
在运行 SPIR-V 验证器并使用 Vulkan 1.0 环境时,请确保设置 |
目前在 GLSL 中没有办法合法地表达宽松的块布局,但开发人员可以使用带有 |
VK_EXT_scalar_block_layout
在 Vulkan 1.2 中晋升为核心 |
此扩展允许大多数存储类型以 scalar alignment
对齐。一个很大的区别是能够跨越 16 字节的边界。
在 GLSL 中,这可以与 scalar
关键字和扩展一起使用
#extension GL_EXT_scalar_block_layout : enable
layout (scalar, binding = 0) buffer block { }
运行 SPIR-V 验证器时,请确保设置 |
|
对齐示例
以下是一些 GLSL 到 SPIR-V 的示例,以帮助更好地理解支持的对齐方式之间的差异。
对齐示例 1
(在线尝试)
layout(binding = 0) buffer block {
vec2 a[4];
vec4 b;
};
在 SPIR-V 中转换为
// extended alignment (std140)
OpDecorate %vec2array ArrayStride 16
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 1 Offset 64
// scalar alignment and base alignment (std430)
OpDecorate %vec2array ArrayStride 8
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 1 Offset 32
对齐示例 2
(在线尝试)
layout(binding = 0) buffer block {
float a;
vec2 b;
vec2 c;
};
在 SPIR-V 中转换为
// extended alignment (std140) and base alignment (std430)
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 1 Offset 8
OpMemberDecorate %block 2 Offset 16
// scalar alignment
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 1 Offset 4
OpMemberDecorate %block 2 Offset 12
对齐示例 3
(在线尝试)
layout(binding = 0) buffer block {
vec3 a;
vec2 b;
vec4 c;
};
在 SPIR-V 中转换为
// extended alignment (std140) and base alignment (std430)
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 1 Offset 16
OpMemberDecorate %block 2 Offset 32
// scalar alignment
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 1 Offset 12
OpMemberDecorate %block 2 Offset 20
对齐示例 4
(在线尝试)
layout (binding = 0) buffer block {
vec3 a;
vec2 b;
vec2 c;
vec3 d;
};
在 SPIR-V 中转换为
// extended alignment (std140) and base alignment (std430)
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 1 Offset 16
OpMemberDecorate %block 2 Offset 24
OpMemberDecorate %block 3 Offset 32
// scalar alignment
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 1 Offset 12
OpMemberDecorate %block 2 Offset 20
OpMemberDecorate %block 3 Offset 28