Hi,
I recently came across an odd phenomenon when using specialization constants as array sizes.
See this GLSL example for a compute shader:
#version 460
layout(constant_id=0) const uint size=16;
const uint broken_size = size;
layout (set = 0, binding = 0) buffer StorageBuffer
{
int arr[broken_size];
int val;
};
void main()
{
val = 42;
}
In the SPIR-V code that is generated from this code, the offset of arr
and var
are specified via the OpMemberDecorate
instruction.
Depending on whether broken_size
or size
are used, the shader compiler (glslc) produces an offset of 4
or 64
, respectively.
OpMemberDecorate %StorageBuffer 1 Offset 4 // when `broken_size` was used.
OpMemberDecorate %StorageBuffer 1 Offset 64 // when `size` was used.
Initially I thought this is a bug in the shader compiler, which it is, but the issue is much more extensive.
I coud fix the shader compiler, to produce the correct offset for var
for the given default value for the specialization constant, but what if I modify the specialization constant at application runtime?
The offset used for var
is independent of the specified value for size
, and I don’t see a way in which to specify this in SPIRV…
I am missing the ability to express someting like this in SPIR-V:
%uint = OpTypeInt 32 0
%int_0 = OpConstant %uint 0
%size = OpSpecConstant %uint 16
OpMemberDecorate %StorageBuffer 0 Offset %int_0
OpMemberDecorate %StorageBuffer 1 Offset %size
Is there some way of working around this issue other than actually modifying the SPIR-V code when modifying the specialization constant value?
Regards,
Tom
EDIT:
Generated SPIR-V code:
; SPIR-V
; Version: 1.0
; Generator: Google Shaderc over Glslang; 10
; Bound: 17
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 460
OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
OpSourceExtension "GL_GOOGLE_include_directive"
OpName %main "main"
OpName %size "size"
OpName %size "broken_size"
OpName %StorageBuffer "StorageBuffer"
OpMemberName %StorageBuffer 0 "arr"
OpMemberName %StorageBuffer 1 "val"
OpName %_ ""
OpDecorate %size SpecId 0
OpDecorate %_arr_int_size ArrayStride 4
OpMemberDecorate %StorageBuffer 0 Offset 0
OpMemberDecorate %StorageBuffer 1 Offset 4
OpDecorate %StorageBuffer BufferBlock
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%size = OpSpecConstant %uint 16
%_arr_int_size = OpTypeArray %int %size
%StorageBuffer = OpTypeStruct %_arr_int_size %int
%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
%_ = OpVariable %_ptr_Uniform_StorageBuffer Uniform
%int_1 = OpConstant %int 1
%int_42 = OpConstant %int 42
%_ptr_Uniform_int = OpTypePointer Uniform %int
%main = OpFunction %void None %3
%5 = OpLabel
%16 = OpAccessChain %_ptr_Uniform_int %_ %int_1
OpStore %16 %int_42
OpReturn
OpFunctionEnd