着色器内存布局

当实现从接口访问内存时,它需要知道内存布局。这包括诸如偏移量跨度对齐方式等内容。虽然 Vulkan 规范有一个专门针对此的章节,但由于各种扩展为规范语言增加了额外的复杂性,因此很难解析。本章旨在帮助解释 Vulkan 使用的所有内存布局概念,并提供一些高级着色语言 (GLSL) 示例。

对齐要求

Vulkan 具有 3 个对齐要求,接口对象可以在其中进行布局。

  • 扩展对齐(也称为 std140

  • 基本对齐(也称为 std430

  • 标量对齐

对齐的规范语言分解了以下每个块成员类型的规则。

  • 标量 (floatintchar 等)

  • 向量 (float2vec3uvec4 等)

  • 矩阵

  • 数组

  • 结构体

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 环境时,请确保设置 --relax-block-layout

目前在 GLSL 中没有办法合法地表达宽松的块布局,但开发人员可以使用带有 glslang--hlsl-offsets 来生成所需的偏移量。

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 验证器时,请确保设置 --scalar-block-layout

Workgroup 存储类不支持 VK_EXT_scalar_block_layout,并且需要 VK_KHR_workgroup_memory_explicit_layout 中的 workgroupMemoryExplicitLayoutScalarBlockLayout 来启用标量支持。

对齐示例

以下是一些 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