KhronosGroup / SPIRV-Registry

SPIR-V specs
109 stars 72 forks source link

Specialization constants break structure offsets when used for fixed-size arrays #99

Open Hugobros3 opened 3 years ago

Hugobros3 commented 3 years ago

Such code:

layout(constant_id = 0) const int N_bins = 42;
layout(set = 0, binding = 2) buffer Buf {
    uint arr[N_bins];
    float f;
};

Cannot be translated correctly. This "works" in glslang but actually will break as soon as we specialize N_bins to be anything different than 42. The issue is the Offset decoration on struct type members takes a literal, instead of a constant instruction like OpTypeArray does for its length parameter, meaning we can't express the relation with the spec constant.

Generated code for reference:

OpDecorate %N_bins SpecId 0
OpDecorate %_arr_uint_N_bins ArrayStride 4
OpMemberDecorate %Buf 0 Offset 0
OpMemberDecorate %Buf 1 Offset 168 ; problem area, this needs to depend on %N_bins
OpDecorate %Buf BufferBlock
OpDecorate %__0 DescriptorSet 0
OpDecorate %__0 Binding 2

%N_bins = OpSpecConstant %int 42
%uint = OpTypeInt 32 0
%_arr_uint_N_bins = OpTypeArray %uint %N_bins
%Buf = OpTypeStruct %_arr_uint_N_bins %float
%_ptr_Uniform_Buf = OpTypePointer Uniform %Buf
%__0 = OpVariable %_ptr_Uniform_Buf Uniform
Hugobros3 commented 3 years ago

It's being pointed out to me GLSL mandates specialization constants to not alter struct layouts:

Arrays inside a block may be sized with a specialization constant, but the block will have a static layout. Changing the specialized size will not re-layout the block. In the absence of explicit offsets, the layout will be based on the default size of the array.

This just reads like careful wording around this limitation, in practice I don't see anyone wanting this behavior as the default, let alone the only option. I hope this is reconsidered for future SPIR-V revisions.