Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Alignment error compiling HLSL to SPIR-V. #334

Answered by TheMostDiligent
bill-call asked this question in Q&A
Discussion options

I am a long-time programmer, mostly back-end-stuff, but new to Vulkan and Diligent. I created a fairly simple app to generate and dispaly a Fibonacci Sphere with a compute shader, and it worked fine. Now, I am trying something more ambitious.
I have a HLSL compute shader that I am cross-compiling using:

Diligent::IRenderDevice::CreateShader(ShaderCreateInfo, RefCntAutoPtr)

This shader has multiple entry points. When I invoke CreateShader, I get an error about structure alignment:

Diligent Engine: ERROR: Spirv optimizer error: Structure id 390 decorated as BufferBlock for variable in Uniform storage class must follow standard storage buffer layout rules: member 1 at offset 20 overlaps previous member ending at offset 31 %Cell = OpTypeStruct %_arr_uint_uint_8 %_arr_uint_uint_4

The ShaderCreateInfo is configured as follows:

ShaderCreateInfo shaderCI;
shaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL;
shaderCI.ShaderCompiler = SHADER_COMPILER_DEFAULT;
shaderCI.EntryPoint = entryPoints[stageIdx];
shaderCI.Source = shaderSource.c_str();
shaderCI.Desc.ShaderType = SHADER_TYPE_COMPUTE;
shaderCI.Desc.Name = (std::string("Shader CS - ") + entryPoints[stageIdx]).c_str();

And the problem structure is:

struct Cell {
uint ids[8]; // Store up to 8 different IDs per cell
uint count[4]; // Number IDs in this cell
};

I have no idea how this manages to violate SPIR-V alignment rules, and even less idea why the offset of member 1 would be 20, as opposed to 31. Can anybody explain this to me?

You must be logged in to vote

First of all, in this line:

shaderCI.Desc.Name = (std::string("Shader CS - ") + entryPoints[stageIdx]).c_str();

You create a temporary string on stack, which is immediately destroyed, so shaderCI.Desc.Name is a pointer to dead memory.
Note that Core Diligent API is c-compatible, so shaderCI.Desc.Name is a pointer, not std::string
There is GraphicsTypesX.hpp header that contains C++ wrappers, which are easier and safer to use.

As to the topic, this appears to be an issue in GLSLang - it generates the code that does not pass validation by SPIRV-Tools.
Note that indexing in Shaders can only be done on 4-vectors (e.g. float4, uint4). As a rule of thumb, always align you struct members by 4-ve...

Replies: 3 comments 3 replies

Comment options

First of all, in this line:

shaderCI.Desc.Name = (std::string("Shader CS - ") + entryPoints[stageIdx]).c_str();

You create a temporary string on stack, which is immediately destroyed, so shaderCI.Desc.Name is a pointer to dead memory.
Note that Core Diligent API is c-compatible, so shaderCI.Desc.Name is a pointer, not std::string
There is GraphicsTypesX.hpp header that contains C++ wrappers, which are easier and safer to use.

As to the topic, this appears to be an issue in GLSLang - it generates the code that does not pass validation by SPIRV-Tools.
Note that indexing in Shaders can only be done on 4-vectors (e.g. float4, uint4). As a rule of thumb, always align you struct members by 4-vectors to avoid hard-to-debug problems caused by mismatch between CPU and GPU layouts.
For example, struct like this will cause a lot of pain:

struct Bad {
float3 f3;
float4 f4;
};

As the shader will add padding between f3 and f4. Also, never use float3 in constant buffers.

Going back to your question, try

struct Cell {
uint4 ids[2]; // Store up to 8 different IDs per cell
uint4 count; // Number IDs in this cell
};

If the problem was indeed in this struct, this most likely should fix it.

You must be logged in to vote
0 replies
Answer selected by bill-call
Comment options

Thanks very much for your reply. The automatic-scope string is SMH; thanks for pointing it out. The information about alignment is gold!
...
On Tuesday, March 25th, 2025 at 7:00 PM, Assiduous ***@***.***> wrote: First of all, in this line: shaderCI.Desc.Name = (std::string( " Shader CS - " ) + entryPoints[stageIdx]).c_str(); You create a temporary string on stack, which is immediately destroyed, so shaderCI.Desc.Name is a pointer to dead memory. Note that Core Diligent API is c-compatible, so shaderCI.Desc.Name is a pointer, not std::string There is GraphicsTypesX.hpp header that contains C++ wrappers, which are easier and safer to use. As to the topic, this appears to be an issue in GLSLang - it generates the code that does not pass validation by SPIRV-Tools. Note that indexing in Shaders can only be done on 4-vectors (e.g. float4, uint4). As a rule of thumb, always align you struct members by 4-vectors to avoid hard-to-debug problems caused by mismatch between CPU and GPU layouts. For example, struct like this will cause a lot of pain: struct Bad { float3 f3; float4 f4; }; As the shader will add padding between f3 and f4. Also, never use float3 in constant buffers. Going back to your question, try struct Cell { uint4 ids[ 2 ]; // Store up to 8 different IDs per cell uint4 count; // Number IDs in this cell }; If the problem was indeed in this struct, this most likely should fix it. — Reply to this email directly, [view it on GitHub](#334 (comment)), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/AG7INTF6RAOPEG6KXITXCPT2WHUYVAVCNFSM6AAAAABZY5BWGWVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTENRSGE2TAMI). You are receiving this because you authored the thread.Message ID: ***@***.***>
You must be logged in to vote
0 replies
Comment options

Did you manage to fix the shader issue?

You must be logged in to vote
3 replies
Comment options

Yes, Assiduous' suggestion did resolve the problem. Indexing into the resulting array is awkward, but it does work.

I still don't understand why this is necessary, but it appears to be some quirk of GLSLang?

Comment options

This may be a bug in GLSLang. The SPIRV must be valid, or the shader should not compile.

It should be possible to use some math tricks to get the index from the vector, for example, use dot with mask vector (such as uint4(0, 0, 1, 0))

Comment options

The term "mask vector" is new to me. I will do some research.

Update:

I am currently using:

int vecIdx = idIdx / 4;
uint compIdx = idIdx % 4;
vec[cellIdx].ids[vecIdx][compIdx];

Which at least compiles. You are suggesting something like

dot(vec[vecIdx], mask[compIdx])

where

uint4 mask[4] = {{1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1}};

?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /