Skip to content

Commit 940d485

Browse files
spirv-val: Validate Mesh Shader output are an array (#6449)
from https://gitlab.khronos.org/vulkan/vulkan/-/issues/4559
1 parent 8c1e6ca commit 940d485

File tree

2 files changed

+191
-4
lines changed

2 files changed

+191
-4
lines changed

source/val/validate_mesh_shading.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,9 @@ spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst) {
132132
}
133133
case spv::Op::OpVariable: {
134134
if (_.HasCapability(spv::Capability::MeshShadingEXT)) {
135-
bool meshInterfaceVar =
135+
bool is_mesh_interface_var =
136136
IsInterfaceVariable(_, inst, spv::ExecutionModel::MeshEXT);
137-
bool fragInterfaceVar =
137+
bool is_frag_interface_var =
138138
IsInterfaceVariable(_, inst, spv::ExecutionModel::Fragment);
139139

140140
const spv::StorageClass storage_class =
@@ -143,21 +143,35 @@ spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst) {
143143
bool storage_input = (storage_class == spv::StorageClass::Input);
144144

145145
if (_.HasDecoration(inst->id(), spv::Decoration::PerPrimitiveEXT)) {
146-
if (fragInterfaceVar && !storage_input) {
146+
if (is_frag_interface_var && !storage_input) {
147147
return _.diag(SPV_ERROR_INVALID_DATA, inst)
148148
<< "PerPrimitiveEXT decoration must be applied only to "
149149
"variables in the Input Storage Class in the Fragment "
150150
"Execution Model.";
151151
}
152152

153-
if (meshInterfaceVar && !storage_output) {
153+
if (is_mesh_interface_var && !storage_output) {
154154
return _.diag(SPV_ERROR_INVALID_DATA, inst)
155155
<< _.VkErrorID(4336)
156156
<< "PerPrimitiveEXT decoration must be applied only to "
157157
"variables in the Output Storage Class in the "
158158
"Storage Class in the MeshEXT Execution Model.";
159159
}
160160
}
161+
162+
// This only applies to user interface variables, not built-ins (they
163+
// are validated with the rest of the builtin)
164+
if (is_mesh_interface_var && storage_output &&
165+
!_.HasDecoration(inst->id(), spv::Decoration::BuiltIn)) {
166+
const Instruction* pointer_inst = _.FindDef(inst->type_id());
167+
if (pointer_inst->opcode() == spv::Op::OpTypePointer) {
168+
if (!_.IsArrayType(pointer_inst->word(3))) {
169+
return _.diag(SPV_ERROR_INVALID_DATA, inst)
170+
<< "In the MeshEXT Execution Mode, all Output Variables "
171+
"must contain an Array.";
172+
}
173+
}
174+
}
161175
}
162176
break;
163177
}

test/val/val_mesh_shading_test.cpp

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,104 @@ TEST_F(ValidateMeshShading, BasicMeshSuccess) {
128128
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
129129
}
130130

131+
// https://godbolt.org/z/Kvb1rsceP
132+
TEST_F(ValidateMeshShading, BasicMeshBuiltinSuccess) {
133+
const std::string body = R"(
134+
OpCapability MeshShadingEXT
135+
OpExtension "SPV_EXT_mesh_shader"
136+
OpMemoryModel Logical GLSL450
137+
OpEntryPoint MeshEXT %main "main" %gl_MeshVerticesEXT %vertexOutput %gl_MeshPrimitivesEXT %gl_LocalInvocationIndex %gl_PrimitiveTriangleIndicesEXT
138+
OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
139+
OpExecutionMode %main OutputVertices 3
140+
OpExecutionMode %main OutputPrimitivesEXT 1
141+
OpExecutionMode %main OutputTrianglesEXT
142+
OpDecorate %gl_MeshPerVertexEXT Block
143+
OpMemberDecorate %gl_MeshPerVertexEXT 0 BuiltIn Position
144+
OpMemberDecorate %gl_MeshPerVertexEXT 1 BuiltIn PointSize
145+
OpMemberDecorate %gl_MeshPerVertexEXT 2 BuiltIn ClipDistance
146+
OpMemberDecorate %gl_MeshPerVertexEXT 3 BuiltIn CullDistance
147+
OpDecorate %VertexOutput Block
148+
OpDecorate %vertexOutput Location 0
149+
OpDecorate %gl_MeshPerPrimitiveEXT Block
150+
OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveId
151+
OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
152+
OpMemberDecorate %gl_MeshPerPrimitiveEXT 1 BuiltIn Layer
153+
OpMemberDecorate %gl_MeshPerPrimitiveEXT 1 PerPrimitiveEXT
154+
OpMemberDecorate %gl_MeshPerPrimitiveEXT 2 BuiltIn ViewportIndex
155+
OpMemberDecorate %gl_MeshPerPrimitiveEXT 2 PerPrimitiveEXT
156+
OpMemberDecorate %gl_MeshPerPrimitiveEXT 3 BuiltIn CullPrimitiveEXT
157+
OpMemberDecorate %gl_MeshPerPrimitiveEXT 3 PerPrimitiveEXT
158+
OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
159+
OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT
160+
%void = OpTypeVoid
161+
%4 = OpTypeFunction %void
162+
%uint = OpTypeInt 32 0
163+
%uint_1 = OpConstant %uint 1
164+
%uint_3 = OpConstant %uint 3
165+
%float = OpTypeFloat 32
166+
%v4float = OpTypeVector %float 4
167+
%_arr_float_uint_1 = OpTypeArray %float %uint_1
168+
%gl_MeshPerVertexEXT = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
169+
%_arr_gl_MeshPerVertexEXT_uint_3 = OpTypeArray %gl_MeshPerVertexEXT %uint_3
170+
%_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 = OpTypePointer Output %_arr_gl_MeshPerVertexEXT_uint_3
171+
%gl_MeshVerticesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 Output
172+
%int = OpTypeInt 32 1
173+
%int_0 = OpConstant %int 0
174+
%float_0 = OpConstant %float 0
175+
%20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
176+
%_ptr_Output_v4float = OpTypePointer Output %v4float
177+
%int_1 = OpConstant %int 1
178+
%int_2 = OpConstant %int 2
179+
%VertexOutput = OpTypeStruct %v4float
180+
%_arr_VertexOutput_uint_3 = OpTypeArray %VertexOutput %uint_3
181+
%_ptr_Output__arr_VertexOutput_uint_3 = OpTypePointer Output %_arr_VertexOutput_uint_3
182+
%vertexOutput = OpVariable %_ptr_Output__arr_VertexOutput_uint_3 Output
183+
%bool = OpTypeBool
184+
%gl_MeshPerPrimitiveEXT = OpTypeStruct %int %int %int %bool
185+
%_arr_gl_MeshPerPrimitiveEXT_uint_1 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_1
186+
%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_1 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_1
187+
%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_1 Output
188+
%_ptr_Input_uint = OpTypePointer Input %uint
189+
%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
190+
%int_3 = OpConstant %int 3
191+
%false = OpConstantFalse %bool
192+
%_ptr_Output_bool = OpTypePointer Output %bool
193+
%v3uint = OpTypeVector %uint 3
194+
%_arr_v3uint_uint_1 = OpTypeArray %v3uint %uint_1
195+
%_ptr_Output__arr_v3uint_uint_1 = OpTypePointer Output %_arr_v3uint_uint_1
196+
%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_1 Output
197+
%uint_0 = OpConstant %uint 0
198+
%52 = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0
199+
%_ptr_Output_v3uint = OpTypePointer Output %v3uint
200+
%main = OpFunction %void None %4
201+
%6 = OpLabel
202+
OpSetMeshOutputsEXT %uint_3 %uint_1
203+
%22 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_0 %int_0
204+
OpStore %22 %20
205+
%24 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_1 %int_0
206+
OpStore %24 %20
207+
%26 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_2 %int_0
208+
OpStore %26 %20
209+
%31 = OpAccessChain %_ptr_Output_v4float %vertexOutput %int_0 %int_0
210+
OpStore %31 %20
211+
%32 = OpAccessChain %_ptr_Output_v4float %vertexOutput %int_1 %int_0
212+
OpStore %32 %20
213+
%33 = OpAccessChain %_ptr_Output_v4float %vertexOutput %int_2 %int_0
214+
OpStore %33 %20
215+
%41 = OpLoad %uint %gl_LocalInvocationIndex
216+
%45 = OpAccessChain %_ptr_Output_bool %gl_MeshPrimitivesEXT %41 %int_3
217+
OpStore %45 %false
218+
%50 = OpLoad %uint %gl_LocalInvocationIndex
219+
%54 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %50
220+
OpStore %54 %52
221+
OpReturn
222+
OpFunctionEnd
223+
)";
224+
225+
CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
226+
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
227+
}
228+
131229
TEST_F(ValidateMeshShading, VulkanBasicMeshAndTaskSuccess) {
132230
const std::string body = R"(
133231
OpCapability MeshShadingEXT
@@ -672,6 +770,81 @@ TEST_F(ValidateMeshShading, OpEmitMeshTasksZeroSuccess) {
672770
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
673771
}
674772

773+
TEST_F(ValidateMeshShading, MeshOutputScalar) {
774+
const std::string body = R"(
775+
OpCapability MeshShadingEXT
776+
OpExtension "SPV_EXT_mesh_shader"
777+
OpMemoryModel Logical GLSL450
778+
OpEntryPoint MeshEXT %main "main" %x
779+
OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
780+
OpExecutionMode %main OutputVertices 3
781+
OpExecutionMode %main OutputPrimitivesEXT 1
782+
OpExecutionMode %main OutputTrianglesEXT
783+
OpDecorate %x Location 0
784+
%void = OpTypeVoid
785+
%4 = OpTypeFunction %void
786+
%uint = OpTypeInt 32 0
787+
%uint_1 = OpConstant %uint 1
788+
%uint_3 = OpConstant %uint 3
789+
%o_ptr = OpTypePointer Output %uint
790+
%x = OpVariable %o_ptr Output
791+
%main = OpFunction %void None %4
792+
%6 = OpLabel
793+
OpSetMeshOutputsEXT %uint_3 %uint_1
794+
OpReturn
795+
OpFunctionEnd
796+
)";
797+
798+
CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
799+
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
800+
EXPECT_THAT(getDiagnosticString(),
801+
HasSubstr("In the MeshEXT Execution Mode, all Output Variables "
802+
"must contain an Array."));
803+
}
804+
805+
TEST_F(ValidateMeshShading, MeshOutputScalarStruct) {
806+
const std::string body = R"(
807+
OpCapability MeshShadingEXT
808+
OpExtension "SPV_EXT_mesh_shader"
809+
OpMemoryModel Logical GLSL450
810+
OpEntryPoint MeshEXT %main "main" %vertexOutput
811+
OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
812+
OpExecutionMode %main OutputVertices 3
813+
OpExecutionMode %main OutputPrimitivesEXT 1
814+
OpExecutionMode %main OutputTrianglesEXT
815+
OpDecorate %VertexOutput Block
816+
OpMemberDecorate %VertexOutput 0 Location 0
817+
%void = OpTypeVoid
818+
%4 = OpTypeFunction %void
819+
%uint = OpTypeInt 32 0
820+
%uint_1 = OpConstant %uint 1
821+
%uint_3 = OpConstant %uint 3
822+
%float = OpTypeFloat 32
823+
%v4float = OpTypeVector %float 4
824+
%VertexOutput = OpTypeStruct %v4float
825+
%_ptr_Output_uint_3 = OpTypePointer Output %VertexOutput
826+
%vertexOutput = OpVariable %_ptr_Output_uint_3 Output
827+
%int = OpTypeInt 32 1
828+
%int_0 = OpConstant %int 0
829+
%float_0 = OpConstant %float 0
830+
%19 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
831+
%_ptr_Output_v4float = OpTypePointer Output %v4float
832+
%main = OpFunction %void None %4
833+
%6 = OpLabel
834+
OpSetMeshOutputsEXT %uint_3 %uint_1
835+
%21 = OpAccessChain %_ptr_Output_v4float %vertexOutput %int_0
836+
OpStore %21 %19
837+
OpReturn
838+
OpFunctionEnd
839+
)";
840+
841+
CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
842+
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
843+
EXPECT_THAT(getDiagnosticString(),
844+
HasSubstr("In the MeshEXT Execution Mode, all Output Variables "
845+
"must contain an Array."));
846+
}
847+
675848
TEST_F(ValidateMeshShading, BadPerPrimitiveEXTStorageClassInMeshEXT) {
676849
const std::string body = R"(
677850
OpCapability MeshShadingEXT

0 commit comments

Comments
 (0)