Skip to content

Fix layoutDescriptorStride bitfield truncation for large stride values#4184

Merged
jeremy-lunarg merged 3 commits intoKhronosGroup:mainfrom
Guang-035:descriptorStride
Mar 11, 2026
Merged

Fix layoutDescriptorStride bitfield truncation for large stride values#4184
jeremy-lunarg merged 3 commits intoKhronosGroup:mainfrom
Guang-035:descriptorStride

Conversation

@Guang-035
Copy link
Copy Markdown
Contributor

Issue: #4178

RootCause:
layoutDescriptorStride was declared as a 4-bit bitfield, which can only values 0-15. Since descriptor_stride must be a power of two, any >= 16 (e.g. 512) was silently truncated to 0 — the "not set" sentinel — causing the SPIR-V backend to fall back to instead of emitting the user-specified stride.

Fix:
Widen the field to a full unsigned int to support the complete range allowed by the GL_EXT_descriptor_heap and SPV_EXT_descriptor_heap specs.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 10, 2026

CLA assistant check
All committers have signed the CLA.

@Guang-035
Copy link
Copy Markdown
Contributor Author

Hello @Tobski and @spencer-lunarg , Here is the fix for #4178, please help me review.

@Guang-035
Copy link
Copy Markdown
Contributor Author

SPIR-V generated with this fix:

; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 11
; Bound: 89
; Schema: 0
               OpCapability Shader
               OpCapability UntypedPointersKHR
               OpCapability DescriptorHeapEXT
               OpExtension "SPV_EXT_descriptor_heap"
               OpExtension "SPV_KHR_untyped_pointers"
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Vertex %main "main" %outNormal %inNormal %outColor %inColor %outUV %inUV %_ %gl_InstanceIndex %inPos %outInstanceIndex
               OpSource GLSL 450
               OpSourceExtension "GL_EXT_descriptor_heap"
               OpName %main "main"
               OpName %outNormal "outNormal"
               OpName %inNormal "inNormal"
               OpName %outColor "outColor"
               OpName %inColor "inColor"
               OpName %outUV "outUV"
               OpName %inUV "inUV"
               OpName %gl_PerVertex "gl_PerVertex"
               OpMemberName %gl_PerVertex 0 "gl_Position"
               OpMemberName %gl_PerVertex 1 "gl_PointSize"
               OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
               OpMemberName %gl_PerVertex 3 "gl_CullDistance"
               OpName %_ ""
               OpName %resource_heap "resource_heap"
               OpName %PushConsts "PushConsts"
               OpMemberName %PushConsts 0 "samplerIndex"
               OpMemberName %PushConsts 1 "frameIndex"
               OpName %pushConsts "pushConsts"
               OpName %UBO "UBO"
               OpMemberName %UBO 0 "projection"
               OpMemberName %UBO 1 "view"
               OpMemberName %UBO 2 "model"
               OpName %UBO_0 "UBO"
               OpMemberName %UBO_0 0 "projection"
               OpMemberName %UBO_0 1 "view"
               OpMemberName %UBO_0 2 "model"
               OpName %UBO_1 "UBO"
               OpMemberName %UBO_1 0 "projection"
               OpMemberName %UBO_1 1 "view"
               OpMemberName %UBO_1 2 "model"
               OpName %gl_InstanceIndex "gl_InstanceIndex"
               OpName %inPos "inPos"
               OpName %outInstanceIndex "outInstanceIndex"
               OpDecorate %outNormal Location 0
               OpDecorate %inNormal Location 1
               OpDecorate %outColor Location 1
               OpDecorate %inColor Location 3
               OpDecorate %outUV Location 2
               OpDecorate %inUV Location 2
               OpDecorate %gl_PerVertex Block
               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
               OpDecorate %PushConsts Block
               OpMemberDecorate %PushConsts 0 Offset 0
               OpMemberDecorate %PushConsts 1 Offset 4
               OpDecorate %_arr_mat4v4float_uint_2 ArrayStride 64
               OpDecorate %UBO BufferBlock
               OpMemberDecorate %UBO 0 ColMajor
               OpMemberDecorate %UBO 0 MatrixStride 16
               OpMemberDecorate %UBO 0 Offset 0
               OpMemberDecorate %UBO 1 ColMajor
               OpMemberDecorate %UBO 1 MatrixStride 16
               OpMemberDecorate %UBO 1 Offset 64
               OpMemberDecorate %UBO 2 ColMajor
               OpMemberDecorate %UBO 2 MatrixStride 16
               OpMemberDecorate %UBO 2 Offset 128
               OpDecorateId %_runtimearr_46 ArrayStrideIdEXT %uint_512
               OpDecorate %_arr_mat4v4float_uint_2_0 ArrayStride 64
               OpDecorate %UBO_0 BufferBlock
               OpMemberDecorate %UBO_0 0 ColMajor
               OpMemberDecorate %UBO_0 0 MatrixStride 16
               OpMemberDecorate %UBO_0 0 Offset 0
               OpMemberDecorate %UBO_0 1 ColMajor
               OpMemberDecorate %UBO_0 1 MatrixStride 16
               OpMemberDecorate %UBO_0 1 Offset 64
               OpMemberDecorate %UBO_0 2 ColMajor
               OpMemberDecorate %UBO_0 2 MatrixStride 16
               OpMemberDecorate %UBO_0 2 Offset 128
               OpDecorateId %_runtimearr_46_0 ArrayStrideIdEXT %uint_512
               OpDecorate %_arr_mat4v4float_uint_2_1 ArrayStride 64
               OpDecorate %UBO_1 BufferBlock
               OpMemberDecorate %UBO_1 0 ColMajor
               OpMemberDecorate %UBO_1 0 MatrixStride 16
               OpMemberDecorate %UBO_1 0 Offset 0
               OpMemberDecorate %UBO_1 1 ColMajor
               OpMemberDecorate %UBO_1 1 MatrixStride 16
               OpMemberDecorate %UBO_1 1 Offset 64
               OpMemberDecorate %UBO_1 2 ColMajor
               OpMemberDecorate %UBO_1 2 MatrixStride 16
               OpMemberDecorate %UBO_1 2 Offset 128
               OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
               OpDecorateId %_runtimearr_46_1 ArrayStrideIdEXT %uint_512
               OpDecorate %inPos Location 0
               OpDecorate %outInstanceIndex Flat
               OpDecorate %outInstanceIndex Location 3
       %void = OpTypeVoid
          %3 = OpTypeFunction %void
      %float = OpTypeFloat 32
    %v3float = OpTypeVector %float 3
%_ptr_Output_v3float = OpTypePointer Output %v3float
  %outNormal = OpVariable %_ptr_Output_v3float Output
%_ptr_Input_v3float = OpTypePointer Input %v3float
   %inNormal = OpVariable %_ptr_Input_v3float Input
   %outColor = OpVariable %_ptr_Output_v3float Output
    %inColor = OpVariable %_ptr_Input_v3float Input
    %v2float = OpTypeVector %float 2
%_ptr_Output_v2float = OpTypePointer Output %v2float
      %outUV = OpVariable %_ptr_Output_v2float Output
%_ptr_Input_v2float = OpTypePointer Input %v2float
       %inUV = OpVariable %_ptr_Input_v2float Input
    %v4float = OpTypeVector %float 4
       %uint = OpTypeInt 32 0
     %uint_1 = OpConstant %uint 1
%_arr_float_uint_1 = OpTypeArray %float %uint_1
%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
        %int = OpTypeInt 32 1
      %int_0 = OpConstant %int 0
%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
 %PushConsts = OpTypeStruct %int %int
%_ptr_PushConstant_PushConsts = OpTypePointer PushConstant %PushConsts
 %pushConsts = OpVariable %_ptr_PushConstant_PushConsts PushConstant
      %int_1 = OpConstant %int 1
%_ptr_PushConstant_int = OpTypePointer PushConstant %int
%mat4v4float = OpTypeMatrix %v4float 4
     %uint_2 = OpConstant %uint 2
%_arr_mat4v4float_uint_2 = OpTypeArray %mat4v4float %uint_2
        %UBO = OpTypeStruct %mat4v4float %mat4v4float %_arr_mat4v4float_uint_2
%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform
         %46 = OpTypeBufferEXT Uniform
   %uint_512 = OpConstant %uint 512
%_runtimearr_46 = OpTypeRuntimeArray %46
%_arr_mat4v4float_uint_2_0 = OpTypeArray %mat4v4float %uint_2
      %UBO_0 = OpTypeStruct %mat4v4float %mat4v4float %_arr_mat4v4float_uint_2_0
%_runtimearr_46_0 = OpTypeRuntimeArray %46
%_arr_mat4v4float_uint_2_1 = OpTypeArray %mat4v4float %uint_2
      %UBO_1 = OpTypeStruct %mat4v4float %mat4v4float %_arr_mat4v4float_uint_2_1
      %int_2 = OpConstant %int 2
%_ptr_Input_int = OpTypePointer Input %int
%gl_InstanceIndex = OpVariable %_ptr_Input_int Input
%_runtimearr_46_1 = OpTypeRuntimeArray %46
      %inPos = OpVariable %_ptr_Input_v3float Input
    %float_1 = OpConstant %float 1
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Output_int = OpTypePointer Output %int
%outInstanceIndex = OpVariable %_ptr_Output_int Output
       %main = OpFunction %void None %3
          %5 = OpLabel
         %12 = OpLoad %v3float %inNormal
               OpStore %outNormal %12
         %15 = OpLoad %v3float %inColor
               OpStore %outColor %15
         %21 = OpLoad %v2float %inUV
               OpStore %outUV %21
         %38 = OpAccessChain %_ptr_PushConstant_int %pushConsts %int_1
         %39 = OpLoad %int %38
         %45 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_46 %resource_heap %39
         %49 = OpBufferPointerEXT %_ptr_Uniform %45
         %50 = OpUntypedAccessChainKHR %_ptr_Uniform %UBO %49 %int_0
         %51 = OpLoad %mat4v4float %50
         %52 = OpAccessChain %_ptr_PushConstant_int %pushConsts %int_1
         %53 = OpLoad %int %52
         %56 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_46_0 %resource_heap %53
         %58 = OpBufferPointerEXT %_ptr_Uniform %56
         %59 = OpUntypedAccessChainKHR %_ptr_Uniform %UBO_0 %58 %int_1
         %60 = OpLoad %mat4v4float %59
         %61 = OpMatrixTimesMatrix %mat4v4float %51 %60
         %62 = OpAccessChain %_ptr_PushConstant_int %pushConsts %int_1
         %63 = OpLoad %int %62
         %69 = OpLoad %int %gl_InstanceIndex
         %70 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_46_1 %resource_heap %63
         %72 = OpBufferPointerEXT %_ptr_Uniform %70
         %73 = OpUntypedAccessChainKHR %_ptr_Uniform %UBO_1 %72 %int_2 %69
         %74 = OpLoad %mat4v4float %73
         %75 = OpMatrixTimesMatrix %mat4v4float %61 %74
         %77 = OpLoad %v3float %inPos
         %79 = OpCompositeExtract %float %77 0
         %80 = OpCompositeExtract %float %77 1
         %81 = OpCompositeExtract %float %77 2
         %82 = OpCompositeConstruct %v4float %79 %80 %81 %float_1
         %83 = OpMatrixTimesVector %v4float %75 %82
         %85 = OpAccessChain %_ptr_Output_v4float %_ %int_0
               OpStore %85 %83
         %88 = OpLoad %int %gl_InstanceIndex
               OpStore %outInstanceIndex %88
               OpReturn
               OpFunctionEnd

@spencer-lunarg
Copy link
Copy Markdown
Contributor

@Guang-035 can we add this as a test (or preferably a test of various structs using descriptor_stride)

@Guang-035
Copy link
Copy Markdown
Contributor Author

@Guang-035 can we add this as a test (or preferably a test of various structs using descriptor_stride)

Hello @spencer-lunarg , I've added a test with descriptor_stride=512 using a buffer block with mat4 members. Could you clarify what you mean by "various structs"? A GLSL example would be helpful.

@spencer-lunarg
Copy link
Copy Markdown
Contributor

various structs

Basically test that setting descriptor_stride= is doing what is should be, glad this is working for this specific case, but clearly we were lacking testing for it from the beginning

@Tobski
Copy link
Copy Markdown
Contributor

Tobski commented Mar 10, 2026

Yea I don't think we need to test different structs; the struct definition for the UBO has no interaction with the stride. What might be worth adding a test for is the same but for the stride of an image descriptor. E.g.

layout(descriptor_heap, descriptor_stride = 1024) image2D image;

@Tobski
Copy link
Copy Markdown
Contributor

Tobski commented Mar 10, 2026

(This is more for regression testing - I don't think there's any reason it's necessary to test it for this particular change)

@spencer-lunarg
Copy link
Copy Markdown
Contributor

@Guang-035 we can't merge this until you sign the CLA

@arcady-lunarg
Copy link
Copy Markdown
Contributor

@Guang-035 You will need to sign the CLA, then I will be able to merge this.

Add an image2D declaration with descriptor_stride=1024 alongside the
existing buffer test with descriptor_stride=512, covering both buffer
and image descriptor types as suggested in code review.
@Guang-035
Copy link
Copy Markdown
Contributor Author

Guang-035 commented Mar 11, 2026

Add an image2D declaration with descriptor_stride=1024 alongside the buffer test with descriptor_stride=512, covering both buffer image descriptor types as suggested by @Tobski .

@jeremy-lunarg jeremy-lunarg added the kokoro:run Trigger Google bot runs label Mar 11, 2026
@kokoro-team kokoro-team removed the kokoro:run Trigger Google bot runs label Mar 11, 2026
@jeremy-lunarg jeremy-lunarg merged commit 959ff4a into KhronosGroup:main Mar 11, 2026
26 checks passed
@SaschaWillems
Copy link
Copy Markdown

Can you provide a pre-compiled binary so I can test if this fixes the issues I've been having?

@spencer-lunarg
Copy link
Copy Markdown
Contributor

@SaschaWillems https://github.com/KhronosGroup/glslang/releases/tag/main-tot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants