OpenGL Shading Language Specification -- Non-Normative SPIR-V Mappings

低功耗蓝牙项目,需要一块懂省电的板

思澈 SF32LB52 芯片,BLE 协议栈深度优化,上手即开发

非规范性 SPIR‑V 映射

本附录包含内容:

  • 对比 Vulkan 与 OpenGL 在使用 SPIR‑V 和不使用 SPIR‑V 时的功能差异
  • 讨论 GLSL 功能如何逻辑映射到 SPIR‑V 功能

功能对比

以下功能在 OpenGL 和 Vulkan 中均被移除:

  • 子程序(subroutines)
  • shared 与 packed 块布局
  • 已弃用的纹理采样函数(如 texture2D()
  • 已弃用的噪声函数(如 noise1()
  • 兼容模式(compatibility‑profile)特性
  • gl_DepthRangeParametersgl_NumSamples

Vulkan 移除但 OpenGL 仍保留的功能:

  • 非透明类型的默认 Uniform:可在全局作用域对单个变量使用 UniformConstant 存储类(即 Uniform 不必放在块中,GLSL 4.5 及以上版本中属于块的内置成员除外)
  • GLSL 原子计数器绑定的 offset 布局限定符 → SPIR‑V AtomicCounter 存储类并使用 Offset 装饰
  • GLSL origin_lower_left → SPIR‑V OriginLowerLeft
  • 顶点着色器中双精度输入的位置(location)特殊规则
  • gl_VertexIDgl_InstanceID(详见下文)

以下功能在 OpenGL 和 Vulkan 中均被添加:

  • 专用常量(specialization constants)
  • offset 可按与声明顺序不同的顺序组织成员
  • 对原本不支持的 GLSL 版本,为 Uniform / 缓冲块提供 offsetalign 布局限定符

仅 Vulkan 添加的功能:

  • 推送常量缓冲区(push‑constant buffers)
  • 着色器中独立纹理与采样器的组合(SPIR‑V OpTypeSampler
  • 描述符集(DescriptorSet,若存在则必须为 0)
  • gl_VertexIndexgl_InstanceIndex
  • 子 pass 输入目标与输入附件(input_attachment_index

以下功能在 OpenGL 和 Vulkan 中均被修改:

  • gl_FragColor 不再表示隐式广播

仅 Vulkan 修改的功能:

  • 精度限定符(mediumplowp)在所有版本中均生效,桌面版不再忽略(桌面版所有类型默认精度为 highp
  • Uniform 数组与缓冲块数组对整个对象只占用一个绑定号,而非每个数组元素一个
  • 默认坐标系原点为 origin_upper_left,而非 origin_lower_left

Vulkan 的 SPIR‑V 环境规范不允许 UBO、SSBO 这类资源使用多维数组。SPIR‑V 本身支持该特性,且 OpenGL GLSL 着色器与面向 OpenGL 的 SPIR‑V 均允许使用。


GLSL 到 SPIR‑V 的映射

专用常量

SPIR‑V 专用常量可由上层 API 后续设置,在 GLSL 中通过 layout(constant_id=…) 声明。例如,声明一个默认值为 12 的专用常量:

layout(constant_id = 17) const int arraySize = 12;

其中 17 是 API 或工具后续引用该专用常量的 ID。在着色器最终编译为可执行代码前,API 或中间工具可修改其值;若未修改,则保留默认值 12。

专用常量具有 const 语义,但不会被常量折叠。因此可用于声明数组:

vec4 data[arraySize];  // 合法,即使 arraySize 可能被修改

专用常量可用于表达式:

vec4 data2[arraySize + 2];

data2 的大小将在着色器编译阶段由 arraySize 的最终值加 2 决定。

由专用常量构成的表达式在着色器中仍视为专用常量,而非普通常量:

arraySize + 2       // 专用常量(无 constant_id)

这类表达式可使用在普通常量允许的任何位置。

constant_id 仅可用于标量整型、标量浮点型或标量布尔型。

仅基础运算符与构造函数作用于专用常量后,结果仍为专用常量:

layout(constant_id = 17) const int arraySize = 12;
sin(float(arraySize));    // 结果不再是专用常量

SPIR‑V 专用常量仅支持标量,但可通过标量运算构造向量:

layout(constant_id = 18) const int scX = 1;
layout(constant_id = 19) const int scZ = 1;
const ivec3 scVec = ivec3(scX, 1, scZ);  // 部分专用化向量

内置变量可附加 constant_id

layout(constant_id = 18) gl_MaxImageUnits;

这使其行为等同于专用常量,并非完整重声明;原内置声明的其他特性保持不变。

内置向量 gl_WorkGroupSize 可通过对 in 限定符使用专用 local_size_{xyz}_id 布局实现专用化:

layout(local_size_x_id = 18, local_size_z_id = 19) in;

gl_WorkGroupSize.y 仍为非专用常量,gl_WorkGroupSize 成为部分专用化向量,其 x、z 分量可后续通过 ID 18、19 专用化。


仅 Vulkan:推送常量

推送常量位于使用新布局限定符 push_constant 声明的 Uniform 块中。API 将一组常量写入推送常量缓冲区,着色器从 push_constant 块读取:

layout(push_constant) uniform BlockName {
    int member1;
    float member2;
    ...
} InstanceName; // 实例名可选
... = InstanceName.member2; // 读取推送常量

push_constant Uniform 块的内存管理与其他 Uniform 块不同:它必须适配一块独立的小容量内存池。默认情况下,推送常量缓冲区遵循 std430 打包规则。


仅 Vulkan:描述符集

描述符集中的每个着色器资源被分配一个三元组:(set 编号, binding 编号, 数组元素),用于定义其在描述符集布局中的位置。在 GLSL 中,set 编号与 binding 编号分别通过 setbinding 布局限定符指定;数组元素从 0 开始隐式连续分配(非数组变量为 0)。

// set=M, binding=N, array element=0
layout (set=M, binding=N) uniform sampler2D variableName;

// 所有数组元素共享 set=M, binding=N;第 i 个元素对应 array element=i
layout (set=M, binding=N) uniform sampler2D variableNameArray[I];

示例:将两个纹理采样器组合对象声明在不同描述符集中:

layout(set = 0, binding = 0) uniform sampler2D ts3;
layout(set = 1, binding = 0) uniform sampler2D ts4;

描述符集的运行模型详见 API 文档。


仅 Vulkan:采样器、图像、纹理与缓冲

存储图像

存储图像在 GLSL 中使用对应维度的 Uniform 图像变量声明,必要时附加格式布局限定符:

layout (set=m, binding=n, r32f) uniform image2D myStorageImage;

映射到 SPIR‑V:

...
%1 = OpExtInstImport "GLSL.std.450"
...
OpName %9 "myStorageImage"
OpDecorate %9 DescriptorSet m
OpDecorate %9 Binding n
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeImage %6 2D 0 0 0 2 R32f
%8 = OpTypePointer UniformConstant %7
%9 = OpVariable %8 UniformConstant
...
采样器

SPIR‑V 采样器在 GLSL 中使用 samplersamplerShadow 类型声明:

layout (set=m, binding=n) uniform sampler mySampler;

映射到 SPIR‑V:

...
%1 = OpExtInstImport "GLSL.std.450"
...
OpName %8 "mySampler"
OpDecorate %8 DescriptorSet m
OpDecorate %8 Binding n
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeSampler
%7 = OpTypePointer UniformConstant %6
%8 = OpVariable %7 UniformConstant
...
纹理(采样图像)

纹理在 GLSL 中使用对应维度的 Uniform 纹理变量声明:

layout (set=m, binding=n) uniform texture2D mySampledImage;

映射到 SPIR‑V:

...
%1 = OpExtInstImport "GLSL.std.450"
...
OpName %9 "mySampledImage"
OpDecorate %9 DescriptorSet m
OpDecorate %9 Binding n
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeImage %6 2D 0 0 0 1 Unknown
%8 = OpTypePointer UniformConstant %7
%9 = OpVariable %8 UniformConstant
...
纹理采样器组合

纹理采样器组合对象在 GLSL 中使用对应维度的组合类型声明:

layout (set=m, binding=n) uniform sampler2D myCombinedImageSampler;

映射到 SPIR‑V:

...
%1 = OpExtInstImport "GLSL.std.450"
...
OpName %10 "myCombinedImageSampler"
OpDecorate %10 DescriptorSet m
OpDecorate %10 Binding n
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeImage %6 2D 0 0 0 1 Unknown
%8 = OpTypeSampledImage %7
%9 = OpTypePointer UniformConstant %8
%10 = OpVariable %9 UniformConstant
...

注意:组合图像采样器描述符可在着色器中仅当作图像或采样器使用。

独立采样器与纹理组合

使用 sampler 声明的采样器仅包含过滤信息,不包含纹理 / 图像:

uniform sampler s;    // 仅过滤信息句柄

使用 texture2D 等声明的纹理仅包含图像信息,不包含过滤信息:

uniform texture2D t;  // 纹理句柄(SPIR‑V 中为图像)

在纹理采样调用处可使用构造函数组合采样器与纹理:

texture(sampler2D(t, s), ...);

上述代码省略 layout() 以突出功能。

纹理缓冲(Uniform 纹素缓冲)

纹理缓冲在 GLSL 中使用 textureBuffer 声明:

layout (set=m, binding=n) uniform textureBuffer myUniformTexelBuffer;

映射到 SPIR‑V:

...
%1 = OpExtInstImport "GLSL.std.450"
...
OpName %9 "myUniformTexelBuffer"
OpDecorate %9 DescriptorSet m
OpDecorate %9 Binding n
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeImage %6 Buffer 0 0 0 1 Unknown
%8 = OpTypePointer UniformConstant %7
%9 = OpVariable %8 UniformConstant
...
图像缓冲(存储纹素缓冲)

图像缓冲在 GLSL 中使用 imageBuffer 声明:

layout (set=m, binding=n, r32f) uniform imageBuffer myStorageTexelBuffer;

映射到 SPIR‑V:

...
%1 = OpExtInstImport "GLSL.std.450"
...
OpName %9 "myStorageTexelBuffer"
OpDecorate %9 DescriptorSet m
OpDecorate %9 Binding n
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeImage %6 Buffer 0 0 0 2 R32f
%8 = OpTypePointer UniformConstant %7
%9 = OpVariable %8 UniformConstant
...
存储缓冲

存储缓冲在 GLSL 中使用 buffer 存储限定符与块语法声明:

layout (set=m, binding=n) buffer myStorageBuffer
{
    vec4 myElement[];
};

映射到 SPIR‑V:

...
%1 = OpExtInstImport "GLSL.std.450"
...
OpName %9 "myStorageBuffer"
OpMemberName %9 0 "myElement"
OpName %11 ""
OpDecorate %8 ArrayStride 16
OpMemberDecorate %9 0 Offset 0
OpDecorate %9 BufferBlock
OpDecorate %11 DescriptorSet m
OpDecorate %11 Binding n
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypeRuntimeArray %7
%9 = OpTypeStruct %8
%10 = OpTypePointer Uniform %9
%11 = OpVariable %10 Uniform
...
Uniform 缓冲

Uniform 缓冲在 GLSL 中使用 uniform 存储限定符与块语法声明:

layout (set=m, binding=n) uniform myUniformBuffer
{
    vec4 myElement[32];
};

映射到 SPIR‑V:

...
%1 = OpExtInstImport "GLSL.std.450"
...
OpName %11 "myUniformBuffer"
OpMemberName %11 0 "myElement"
OpName %13 ""
OpDecorate %10 ArrayStride 16
OpMemberDecorate %11 0 Offset 0
OpDecorate %11 Block
OpDecorate %13 DescriptorSet m
OpDecorate %13 Binding n
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypeInt 32 0
%9 = OpConstant %8 32
%10 = OpTypeArray %7 %9
%11 = OpTypeStruct %10
%12 = OpTypePointer Uniform %11
%13 = OpVariable %12 Uniform
...
子 pass 输入

在渲染流程中,一个子 pass 可将结果写入输出目标,供后续子 pass 作为输入读取。子 pass 输入即读取这类输出目标的能力。

子 pass 输入通过一组仅片段着色器可用的新类型读取:

  • subpassInput
  • subpassInputMS
  • isubpassInput
  • isubpassInputMS
  • usubpassInput
  • usubpassInputMS

与采样器、图像对象不同,子 pass 输入通过片段的 (x, y, layer) 坐标隐式寻址。

输入附件除描述符集与绑定号外,还需使用 input_attachment_index 装饰:

layout (input_attachment_index=i, set=m, binding=n) uniform subpassInput myInputAttachment;

映射到 SPIR‑V:

...
%1 = OpExtInstImport "GLSL.std.450"
...
OpName %9 "myInputAttachment"
OpDecorate %9 DescriptorSet m
OpDecorate %9 Binding n
OpDecorate %9 InputAttachmentIndex i
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeImage %6 SubpassData 0 0 0 2 Unknown
%8 = OpTypePointer UniformConstant %7
%9 = OpVariable %8 UniformConstant
...

input_attachment_index = i 选择输入流程列表中的第 i 项(详见 API 规范)。

这类对象支持通过以下函数读取子 pass 输入:

gvec4 subpassLoad(gsubpassInput   subpass);
gvec4 subpassLoad(gsubpassInputMS subpass, int sample);

变量映射

gl_FragColor

片段阶段内置变量 gl_FragColor 原本表示向所有输出广播,在 SPIR‑V 中不存在。允许写入 gl_FragColor 的着色器仍可写入,但仅表示写入满足以下条件的输出:

  • 类型与 gl_FragColor 相同
  • location = 0 装饰
  • 未被装饰为内置变量

无隐式广播行为。


Vulkan:gl_VertexIndex 与 gl_InstanceIndex

新增两个内置变量 gl_VertexIndexgl_InstanceIndex,替换原有 gl_VertexIDgl_InstanceID

在索引相对于某个基偏移的场景中,Vulkan 下其值定义如下:

gl_VertexIndex             base, base+1, base+2, ...
gl_InstanceIndex           base, base+1, base+2, ...

基值 base 由具体场景决定。


存储类映射

uniform sampler2D...;        -> UniformConstant
uniform blockN { ... } ...;  -> Uniform,带 Block 装饰
in / out variable            -> Input/Output,可能带块(见下文)
in / out block...            -> Input/Output,带 Block 装饰
buffer  blockN { ... } ...;  -> Uniform,带 BufferBlock 装饰
shared                       -> Workgroup
<普通全局变量>               -> Private
  • 仅 Vulkanbuffer blockN { ... } ...; → 按需映射为 StorageBuffer
  • 仅 OpenGL:非块 Uniform 变量 → UniformConstant
  • 仅 OpenGL... uniform atomic_uint ...AtomicCounter

输入 / 输出映射

所有 GLSL/ESSL 版本中,输入 / 输出块或变量的映射规则一致。变量或成员在对应版本中可用时,其位置映射如下:

以下变量映射为 SPIR‑V 独立变量,并使用拼写相近的内置装饰(特别说明除外):

所有阶段

in gl_VertexIndex          (仅 Vulkan)
in gl_VertexID             (仅 OpenGL)
in gl_InstanceIndex        (仅 Vulkan)
in gl_InstanceID           (仅 OpenGL)
in gl_InvocationID
in gl_PatchVerticesIn      (PatchVertices)
in gl_PrimitiveIDIn        (PrimitiveID)
in/out gl_PrimitiveID      (仅由存储类决定输入/输出)
in gl_TessCoord

in/out gl_Layer
in/out gl_ViewportIndex

patch in/out gl_TessLevelOuter  (使用 Patch 装饰)
patch in/out gl_TessLevelInner  (使用 Patch 装饰)

仅计算阶段

in gl_NumWorkGroups
in gl_WorkGroupSize
in gl_WorkGroupID
in gl_LocalInvocationID
in gl_GlobalInvocationID
in gl_LocalInvocationIndex

仅片段阶段

in gl_FragCoord
in gl_FrontFacing
in gl_ClipDistance
in gl_CullDistance
in gl_PointCoord
in gl_SampleID
in gl_SamplePosition
in gl_HelperInvocation
out gl_FragDepth
in gl_SampleMaskIn        (SampleMask)
out gl_SampleMask         (仅由存储类决定输入/输出)

以下变量映射为 SPIR‑V 块(如伪代码所示),成员使用拼写相近的内置装饰:

非片段阶段

in/out gl_PerVertex {   // 仅使用成员子集
    gl_Position
    gl_PointSize
    gl_ClipDistance
    gl_CullDistance
}                       // 块名仅用于调试

SPIR‑V 中每个阶段最多一个输入块与一个输出块。共享接口的阶段之间,成员子集与顺序保持一致。


仅 Vulkan:精度限定符映射

lowp     -> RelaxedPrecision,修饰存储变量与操作
mediump  -> RelaxedPrecision,修饰存储变量与操作
highp    -> 32‑bit,与 int/float 一致

可移植工具 / 模式 → OpQuantizeToF16

precise 映射 → NoContraction


仅 OpenGL:atomic_uint offset 布局限定符映射

offset         -> Offset 装饰

图像操作映射

imageLoad()   -> OpImageRead
imageStore()  -> OpImageWrite
texelFetch()  -> OpImageFetch
subpassInput  -> OpTypeImage,维度为 SubpassData(仅 Vulkan)
subpassLoad() -> OpImageRead(仅 Vulkan)

imageAtomicXXX(params, data)  -> %ptr = OpImageTexelPointer params
                                        OpAtomicXXX %ptr, data

XXXQueryXXX(combined) -> %image = OpImage combined
                                OpXXXQueryXXX %image

布局映射

std140/std430  ->  结构体成员使用显式 Offset、ArrayStride、MatrixStride 装饰
shared/packed  ->  不允许
<default>      ->  非 shared,使用 std140 或 std430
xfb_offset     ->  对象或结构体成员使用 Offset 装饰
xfb_buffer     ->  对象使用 XfbBuffer 装饰
xfb_stride     ->  对象使用 XfbStride 装饰
any xfb_*      ->  设置 Xfb Execution Mode
captured XFB   ->  同时拥有 XfbBuffer 与 Offset
non-captured   ->  缺少 XfbBuffer 或 Offset

max_vertices   ->  OutputVertices

屏障映射

// 计算着色器 barrier()
barrier() -> OpControlBarrier(/*Execution*/Workgroup,
                              /*Memory*/Workgroup,
                              /*Semantics*/AcquireRelease |
                                          WorkgroupMemory)

// 曲面细分控制着色器 barrier()
barrier() -> OpControlBarrier(/*Execution*/Workgroup,
                              /*Memory*/Invocation,
                              /*Semantics*/None)

memoryBarrier() -> OpMemoryBarrier(/*Memory*/Device,
                                    /*Semantics*/AcquireRelease |
                                                UniformMemory |
                                                WorkgroupMemory |
                                                ImageMemory)

memoryBarrierBuffer() -> OpMemoryBarrier(/*Memory*/Device,
                                        /*Semantics*/AcquireRelease |
                                                    UniformMemory)

memoryBarrierShared() -> OpMemoryBarrier(/*Memory*/Device,
                                        /*Semantics*/AcquireRelease |
                                                    WorkgroupMemory)

memoryBarrierImage() -> OpMemoryBarrier(/*Memory*/Device,
                                        /*Semantics*/AcquireRelease |
                                                    ImageMemory)

groupMemoryBarrier() -> OpMemoryBarrier(/*Memory*/Workgroup,
                                        /*Semantics*/AcquireRelease |
                                                    UniformMemory |
                                                    WorkgroupMemory |
                                                    ImageMemory)

原子操作映射

所有原子内置函数 → 语义 None(Relaxed)

atomicExchange()      -> OpAtomicExchange
imageAtomicExchange() -> OpAtomicExchange
atomicCompSwap()      -> OpAtomicCompareExchange
imageAtomicCompSwap() -> OpAtomicCompareExchange
N/A                   -> OpAtomicCompareExchangeWeak

仅 OpenGL:原子计数器映射

atomicCounterIncrement -> OpAtomicIIncrement
atomicCounterDecrement -> OpAtomicIDecrement
atomicCounter          -> OpAtomicLoad

其他指令映射

%     -> OpUMod/OpSMod
mod() -> OpFMod
N/A   -> OpSRem/OpFRem

pack/unpack(类型转换)   -> GLSL 扩展指令中的 pack/unpack
pack/unpack(无类型转换) -> OpBitcast

低功耗蓝牙项目,需要一块懂省电的板

思澈 SF32LB52 芯片,BLE 协议栈深度优化,上手即开发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值