KhronosGroup / SPIRV-Cross

SPIRV-Cross is a practical tool and library for performing reflection on SPIR-V and disassembling SPIR-V back to high level languages.
Apache License 2.0
1.96k stars 549 forks source link

Decompile glsl and get unexpected buffer size #2316

Closed CheapMeow closed 2 months ago

CheapMeow commented 2 months ago

I use spirv_cross to decompile glsl. But get unexpected buffer size.

It is how I use spirv_cross.

void Shader::GetUniformBuffersMeta(spirv_cross::Compiler&        compiler,
                                   spirv_cross::ShaderResources& resources,
                                   vk::ShaderStageFlags          stageFlags)
{
    for (int32_t i = 0; i < resources.uniform_buffers.size(); ++i)
    {
        spirv_cross::Resource& res                        = resources.uniform_buffers[i];
        spirv_cross::SPIRType  type                       = compiler.get_type(res.type_id);
        spirv_cross::SPIRType  base_type                  = compiler.get_type(res.base_type_id);
        const std::string&     var_name                   = compiler.get_name(res.id);
        const std::string&     type_name                  = compiler.get_name(res.base_type_id);
        uint32_t               uniform_buffer_struct_size = (uint32_t)compiler.get_declared_struct_size(type);

        ...
    }
}

It is the frag shader that is decomiled.

#version 450

layout (input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput inputColor;
layout (input_attachment_index = 1, set = 0, binding = 1) uniform subpassInput inputNormal;
layout (input_attachment_index = 2, set = 0, binding = 2) uniform subpassInput inputDepth;

layout (binding = 3) uniform ParamBlock
{
    int attachmentIndex;
    vec3 padding;
} param;

layout (location = 0) in vec2 inUV0;

layout (location = 0) out vec4 outFragColor;

void main() 
{
    if (param.attachmentIndex == 0) {
        outFragColor = subpassLoad(inputColor);
    } 
    else if (param.attachmentIndex == 1) {
        outFragColor = vec4(subpassLoad(inputDepth).r);
    } 
    else if (param.attachmentIndex == 2) {
        outFragColor = subpassLoad(inputNormal);
    } else {
        outFragColor = vec4(inUV0, 0.0, 1.0);
    }
}

The size of param should be 16, but spriv_cross return 28.

Is anything wrong? Or the memory alignment has some special rule I don't know? Thank you for your reply.

Try commented 2 months ago

Is anything wrong?

Default buffer block alignment in GLSL is std140, resulting in following compilation: https://shader-playground.timjones.io/758f913c2dba4afb747ba19854ccbef5

OpMemberDecorate %ParamBlock 0 Offset 0   // 'attachmentIndex' has offset=0
OpMemberDecorate %ParamBlock 1 Offset 16  // 'padding' has offset=16
// `16` + sizeof(vec3) = 28

Basically you have double padding: one from GLSL-compiler, one from your code.

CheapMeow commented 2 months ago

Is anything wrong?

Default buffer block alignment in GLSL is std140, resulting in following compilation: https://shader-playground.timjones.io/758f913c2dba4afb747ba19854ccbef5

OpMemberDecorate %ParamBlock 0 Offset 0   // 'attachmentIndex' has offset=0
OpMemberDecorate %ParamBlock 1 Offset 16  // 'padding' has offset=16
// `16` + sizeof(vec3) = 28

Basically you have double padding: one from GLSL-compiler, one from your code.

You are right, i don't fully understand std140. Because vec3 is vector, so its offset is n times of vec4.Thank you!