Description
According to
VUID-StandaloneSpirv-Flat-04744:
"Any interface variable of integer or double-precision type, or a structure
type containing an integer or double-precision type, that is in the Input
storage class of a fragment shader, must be decorated with Flat."
It does not say that structures containing integers must be decorated with the
flat decoration (e.g., struct T { struct S { int i; float f; } s; }). This
seems like an inconsistency. Even though I assume i should be flat because all
integers have to be flat while the float f is interpolated. Making i flat is not explict as in other cases.
Note that we cannot apply the flat decoration to i itself because the SPIR-V Specification
(Section 2.16.2 "Validation Rules for Shader Capabilities") forbids decorating nested structure type
members:
"If applied to structure-type members, the decorations Noperspective, Flat,
Patch, Centroid, and Sample must be applied only to the top-level members of
the structure type. (Nested objects' types must not be structures whose
members are decorated with these decorations.)"
I believe there should be a clarification in the spec that says that i is
implicitly flat in this case, or make this case invalid because the member s in
T must also be declared as flat.
Note that GLSL is much less general, and that this example cannot be generated
from GLSL (https://godbolt.org/z/9qc36dena).
This SPIR-V cannot be generated from HLSL either.
HLSL allows applying nointerpolation to individual struct
members, and in fact implicitly infers it for all integer interface members
if omitted:
struct S {
int i; // nointerpolation is implicit here in HLSL
float f;
};
struct T { S s; };
However, when compiling this HLSL to
SPIR-V using DXC (with or without explicit nointerpolation), the compiler
avoids the nested struct limitation by completely flattening the interface
structure into individual variables at the SPIR-V level (e.g., generating two
separate Input variables: a flat int at Location 0, and a float at
Location 1). See https://godbolt.org/z/916rvE9M7.
Steps to reproduce
Run the following amber script using amber:
amber nested_struct_without_flat.amber
#!amber
SHADER vertex vert_shader SPIRV-ASM
; SPIR-V
; Version: 1.0
; Bound: 40
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %position %out_var %gl_Position
OpName %main "main"
OpName %position "position"
OpName %S "S"
OpMemberName %S 0 "i"
OpMemberName %S 1 "f"
OpName %T "T"
OpMemberName %T 0 "s"
OpName %out_var "out_var"
OpName %gl_PerVertex "gl_PerVertex"
OpMemberName %gl_PerVertex 0 "gl_Position"
OpName %gl_Position "gl_Position"
OpDecorate %position Location 0
OpDecorate %out_var Location 0
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpDecorate %gl_PerVertex Block
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%position = OpVariable %_ptr_Input_v4float Input
%int = OpTypeInt 32 1
%S = OpTypeStruct %int %float
%T = OpTypeStruct %S
%_ptr_Output_T = OpTypePointer Output %T
%out_var = OpVariable %_ptr_Output_T Output
%gl_PerVertex = OpTypeStruct %v4float
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
%gl_Position = OpVariable %_ptr_Output_gl_PerVertex Output
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Output_int = OpTypePointer Output %int
%_ptr_Output_float = OpTypePointer Output %float
%main = OpFunction %void None %3
%5 = OpLabel
%15 = OpLoad %v4float %position
%17 = OpAccessChain %_ptr_Output_v4float %gl_Position %int_0
OpStore %17 %15
%18 = OpCompositeExtract %float %15 0 ; Extract position.x
%19 = OpConvertFToS %int %18 ; Convert to int
%20 = OpAccessChain %_ptr_Output_int %out_var %int_0 %int_0
OpStore %20 %19
%22 = OpAccessChain %_ptr_Output_float %out_var %int_0 %int_1
OpStore %22 %18
OpReturn
OpFunctionEnd
END
SHADER fragment frag_shader SPIRV-ASM
; SPIR-V
; Version: 1.0
; Bound: 25
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %in_var %out_var_int %out_var_float
OpExecutionMode %main OriginUpperLeft
OpName %main "main"
OpName %S "S"
OpMemberName %S 0 "i"
OpMemberName %S 1 "f"
OpName %T "T"
OpMemberName %T 0 "s"
OpName %in_var "in_var"
OpName %out_var_int "out_var_int"
OpName %out_var_float "out_var_float"
OpDecorate %in_var Location 0
; OpDecorate %in_var Flat <-- Removed
OpDecorate %out_var_int Location 0
OpDecorate %out_var_float Location 1
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%float = OpTypeFloat 32
%S = OpTypeStruct %int %float
%T = OpTypeStruct %S
%_ptr_Input_T = OpTypePointer Input %T
%in_var = OpVariable %_ptr_Input_T Input
%_ptr_Output_int = OpTypePointer Output %int
%out_var_int = OpVariable %_ptr_Output_int Output
%_ptr_Output_float = OpTypePointer Output %float
%out_var_float = OpVariable %_ptr_Output_float Output
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%_ptr_Input_int = OpTypePointer Input %int
%_ptr_Input_float = OpTypePointer Input %float
%main = OpFunction %void None %3
%5 = OpLabel
%15 = OpAccessChain %_ptr_Input_int %in_var %int_0 %int_0
%16 = OpLoad %int %15
OpStore %out_var_int %16
%17 = OpAccessChain %_ptr_Input_float %in_var %int_0 %int_1
%18 = OpLoad %float %17
OpStore %out_var_float %18
OpReturn
OpFunctionEnd
END
BUFFER position_buf DATA_TYPE vec4<float> DATA
-1.0 -1.0 0.0 1.0
1.0 -1.0 0.0 1.0
-1.0 1.0 0.0 1.0
1.0 1.0 0.0 1.0
END
BUFFER out_int_buf FORMAT R32_SINT
BUFFER out_float_buf FORMAT R32_SFLOAT
PIPELINE graphics pipeline
ATTACH vert_shader
ATTACH frag_shader
VERTEX_DATA position_buf LOCATION 0
BIND BUFFER out_int_buf AS color LOCATION 0
BIND BUFFER out_float_buf AS color LOCATION 1
FRAMEBUFFER_SIZE 3 3
END
CLEAR pipeline
RUN pipeline DRAW_ARRAY AS TRIANGLE_STRIP START_IDX 0 COUNT 4
# Without flat, the center pixel should be 0 (for both, but int might be 0 due to failure or interpolation).
EXPECT out_int_buf IDX 4 EQ -1
EXPECT out_float_buf IDX 4 TOLERANCE 0.001 EQ 0.0
Description
According to
VUID-StandaloneSpirv-Flat-04744:
It does not say that structures containing integers must be decorated with the
flat decoration (e.g.,
struct T { struct S { int i; float f; } s; }). Thisseems like an inconsistency. Even though I assume
ishould be flat because allintegers have to be flat while the float
fis interpolated. Makingiflat is not explict as in other cases.Note that we cannot apply the flat decoration to
iitself because the SPIR-V Specification(Section 2.16.2 "Validation Rules for Shader Capabilities") forbids decorating nested structure type
members:
I believe there should be a clarification in the spec that says that
iisimplicitly flat in this case, or make this case invalid because the member
sinTmust also be declared as flat.Note that GLSL is much less general, and that this example cannot be generated
from GLSL (https://godbolt.org/z/9qc36dena).
This SPIR-V cannot be generated from HLSL either.
HLSL allows applying
nointerpolationto individual structmembers, and in fact implicitly infers it for all integer interface members
if omitted:
However, when compiling this HLSL to
SPIR-V using DXC (with or without explicit
nointerpolation), the compileravoids the nested struct limitation by completely flattening the interface
structure into individual variables at the SPIR-V level (e.g., generating two
separate
Inputvariables: aflat intat Location 0, and afloatatLocation 1). See https://godbolt.org/z/916rvE9M7.
Steps to reproduce
Run the following amber script using amber: