PENGUINLIONG/spirq-rs

Reflection does not include built-in variables with DXC

lopea opened this issue · 4 comments

Hello! Thank you so much for making this crate! I currently found an issue while generating reflection data from SPIR-V code compiled in DXC (v1.7.2212).

I have been using this (simple) HLSL code for testing:

struct ia2v {
    float4 pos : POSITION;
};

struct v2f {
    float4 pos : SV_POSITION;
};

v2f vert(ia2v input) {
    v2f o;
    o.pos = input.pos;
    return o;
}
    
float4 frag(v2f input) : SV_Target0 {
    return input.pos;
}

with the following command to compile in DXC:

$ dxc.exe -T vs_6_0  -E vert -spirv  -O3 -fspv-reflect 

and get the following SPIRV result (also visible here: https://godbolt.org/z/jfoM7h6ja)

               OpCapability Shader
               OpExtension "SPV_GOOGLE_hlsl_functionality1"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Vertex %vert "vert" %in_var_POSITION %gl_Position
               OpDecorateString %in_var_POSITION UserSemantic "POSITION"
               OpDecorate %gl_Position BuiltIn Position
               OpDecorateString %gl_Position UserSemantic "SV_POSITION"
               OpDecorate %in_var_POSITION Location 0
      %float = OpTypeFloat 32
    %v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%_ptr_Output_v4float = OpTypePointer Output %v4float
       %void = OpTypeVoid
         %10 = OpTypeFunction %void
%in_var_POSITION = OpVariable %_ptr_Input_v4float Input
%gl_Position = OpVariable %_ptr_Output_v4float Output
       %vert = OpFunction %void None %10
         %11 = OpLabel
         %12 = OpLoad %v4float %in_var_POSITION
               OpStore %gl_Position %12
               OpReturn
               OpFunctionEnd

Everything looks OK so far, but once i try to reflect I get this:

vert {
    exec_model: Vertex,
    name: "vert",
    vars: [
        Input {
            name: Some(
                "in.var.POSITION",
            ),
            location: (loc=0, comp=0),
            ty: Vector(
                VectorType {
                    scalar_ty: Float {
                        bits: 32,
                    },
                    nscalar: 4,
                },
            ),
        },
    ],
    exec_modes: [],
}

It seems to completely skip the output variable for this vertex shader, which looks like it represents the built-in gl_Position from glsl. If I change the semantics of the output variable to POSITION instead of SV_POSITION we get the following output (which is intended):

vert {
    exec_model: Vertex,
    name: "vert",
    vars: [
        Input {
            name: Some(
                "in.var.POSITION",
            ),
            location: (loc=0, comp=0),
            ty: Vector(
                VectorType {
                    scalar_ty: Float {
                        bits: 32,
                    },
                    nscalar: 4,
                },
            ),
        },
        Output {
            name: Some(
                "out.var.POSITION",
            ),
            location: (loc=0, comp=0),
            ty: Vector(
                VectorType {
                    scalar_ty: Float {
                        bits: 32,
                    },
                    nscalar: 4,
                },
            ),
        },
    ],
    exec_modes: [],
}

This also happens in fragment shaders under the same code under the same semantics (SV_POSITION). The built in variable is equivalent to gl_FragCoord under the fragment shader however. Note that I have toggled ref_all_rscs() with no change whatsoever.

Hmm, I will look into that.

Sorry for the late reply and I'm glad you like the crate. :)

Well, there is a different between POSITION and SV_POSITION. I'm not sure if you have any experience with GLSL but I think it's easier to understand in that language system.

The System Value (SV) SV_POSITION in HLSL is basically the Built-In variable gl_Position in GLSL. It's not a pipeline output and is only visible to the hardware rasterizer. You can't load from such built-in variable in the fragment shader (or, if you prefer, the pixel shader). As if you might noticed, there is too no programming interface in the Vulkan API for you to manually connect gl_Position from vert to frag. Everything about it is handled internally by the driver.

On the other hand, to pass a value from stage to stage, you list it as a Pipeline Interface Variable, or non-SV variables in HLSL. The rasterizer will produce a new value interpolated from the three contributing vertices of the triangle for each fragment/pixel.

What you attempted are basically these in GLSL. This is your SV_POSITION version:

#version 450
layout(location=0) in vec4 i_pos;
void main() {
    gl_Position = i_pos;
}

And this is the POSITION version:

#version 450
layout(location=0) in vec4 i_pos;
layout(location=0) out vec4 o_pos;
void main() {
    o_pos = i_pos;
}

Neither of them works. In the SV_POSITION version, you don't have any value to interpolate. In the POSITION version, you don't know how to interpolate. and that program won't compile in GLSL because it enforces the assignment to gl_Position in vert shader. After all, a blend them is what you need:

#version 450
layout(location=0) in vec4 i_pos;
layout(location=0) out vec4 o_pos;
void main() {
    o_pos = i_pos;
    gl_Position = o_pos;
}

So IIUC this is not a bug, I'm gonna close the issue first. Feel free to reopen it if you have further concerns.

Thanks for the info! I understood so much about the difference between SV_POSITION and POSITION but it was weird that I could see other values within the shader the vertex output (e.g NORMAL or TEXCOORD0) but not see the SV_POSITION when reflecting (I guess i should of used another semantic as an example, sorry about that). Your explanation regarding why makes more sense. I was using the reflection data to check if all the inputs and outputs line up properly for each shader module as a post-compile stage ,but I guess since Vulkan already supplies those built-in variables I wouldn't have to worry about them showing up in reflection data. I would agree that it is not a bug.