Search code examples
openglglslshaderspir-v

Why won't my GLSL program link when one shader is optimized and the other is not?


I'm using a shader toolchain where I start with GLSL, compile it to SPIRV, optimize the SPIRV, and then use spirv-cross to generate optimized GLSL. This is working well for the most part.

However, I have a mechanism whereby user generate snippets can be injected into some fragment shaders via text replacement. When this happens, the original un-optimized GLSL is used since the markers for text replacement can't survive the passage through SPIRV.

However, I've discovered that in some cases the optimized vertex shader and the customized fragment shader will both compile, but the program won't link, instead giving the following error:

WARNING: warning(#276) Symbol "_normal" usage doesn't match between two stages
ERROR: error(#277) Symbol "_16" usage doesn't match between two stages

The optimized vertex shader and the optimized fragment shader link. The unoptimized versions of both links. Even the unoptimized vertex shader and the optimized fragment shader links.

I've narrowed down the issue to the following declaration, which appears in the fragment shader

struct TransformCamera {
    mat4 _view;
    mat4 _viewInverse;
    mat4 _projectionViewUntranslated;
    mat4 _projection;
    mat4 _projectionInverse;
    vec4 _viewport;
    vec4 _stereoInfo;
};

layout(std140, binding=15) uniform transformCameraBuffer {
    TransformCamera _camera;
};

In the optimized versions of shaders where the entire UBO isn't optimized away as unused, the declaration becomes

layout(binding = 15, std140) uniform transformCameraBuffer
{
    TransformCamera _camera;
} _16;

If I manually modify the unoptimized fragment shader to use a similar mechanism to name the UBO, the link error goes away (regardless of what I name the UBO) so for instance, the following change to the fragment shader compiles and links successfully.

layout(std140, binding=15) uniform transformCameraBuffer {
    TransformCamera _camera;
} _foo;

I can obviously work around the issue, but I don't understand how the different syntax for the UBO declaration completely breaks the link phase of my program. Can anyone provide some insight?

Also, if something in the glslangValidator -> spirv-opt -> spirv-cross is changing the link interface for my shader, should I consider that a bug and report it?


Solution

  • I can obviously work around the issue, but I don't understand how the different syntax for the UBO declaration completely breaks the link phase of my program

    Because the spec says so. GLSL 4.60 specification, section 4.3.9. "Interface Blocks" states (emphasis mine):

    Matched block names within a shader interface (as defined above) must match in terms of having the same number of declarations with the same sequence of types and the same sequence of member names, as well as having matching member-wise layout qualification (see next section). Matched uniform or shader storage block names (but not input or output block names) must also either all be lacking an instance name or all having an instance name, putting their members at the same scoping level. When instance names are present on matched block names, it is allowed for the instance names to differ; they need not match for the blocks to match. [...].

    So if your vertex shader uses an instance name, the fragment shader must use one, too.

    Also, if something in the glslangValidator -> spirv-opt -> spirv-cross is changing the link interface for my shader, should I consider that a bug and report it?

    I don't know what guarantees they do make on the individual parts of a program. I would not intuitively expect that your workflow of combining unoptimized and optimized parts is guaranteed to work, at least.