跳到主要内容

Shader内存布局

当驱动实现访问 memory 时,它需要知道 memory 的布局是什么,包括偏移步长对齐等信息。 Vulkan Spec有一个章节专门介绍这块内容,由于增加了各种扩展,因此有的内容可能会有些复杂。本章旨在通过一些高级着色语言 (GLSL) 示例来帮助理解 Vulkan 使用的内存布局概念。

对齐要求

Vulkan 有 3 个对齐要求,接口对象可以按照这些要求进行布局:

  • 扩展对齐(也称为std140)
  • 基础对齐(也称为std430)
  • 标量对齐

对齐规范可细分成以下每种块类型的规则:

  • 标量 (floatintchar等)
  • 向量(float2vec3uvec4等)
  • 矩阵
  • 数组
  • 结构

VK_KHR_uniform_buffer_standard_layout

提示

在 Vulkan 1.2 中提升为核心功扩展。

此扩展允许 UBO 使用std430内存布局,更详细的内容可阅读Vulkan 标准缓冲区布局接口。这些内存布局的变动仅对Uniforms生效,其他存储项(如 Push ConstantsSSBO)已经允许使用 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 字节对齐。使用宽松的块布局,应用程序可以对floatvec3使用更合适的对齐长度:

// 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的子集。

提示

确保在 Vulkan 1.0 环境运行 SPIR-V 验证器时设置--relax-block-layout

提示

目前GLSL 规范还没有明确地表达宽松的块布局细节,开发人员可以使用glslang--hlsl-offsets参数来产生所需的偏移量。

VK_EXT_scalar_block_layout

提示

在 Vulkan 1.2 中提升为核心扩展GLSL - GL_EXT_scalar_block_layout

此扩展允许大多数存储类型按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,支持scalar功能需要在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