Skip to content

Commit 8135dd6

Browse files
committed
More validation on primitive instructions
- Test validation success for OpEmitVertex OpEndPrimitive - Test missing capabilities for primitive instructions - Primitive instructions require Geometry execution model
1 parent 4dbcef6 commit 8135dd6

File tree

2 files changed

+190
-23
lines changed

2 files changed

+190
-23
lines changed

source/validate_primitives.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#include "validate.h"
1818

19+
#include <string>
20+
1921
#include "diagnostic.h"
2022
#include "opcode.h"
2123
#include "val/instruction.h"
@@ -28,6 +30,20 @@ spv_result_t PrimitivesPass(ValidationState_t& _,
2830
const spv_parsed_instruction_t* inst) {
2931
const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
3032

33+
switch (opcode) {
34+
case SpvOpEmitVertex:
35+
case SpvOpEndPrimitive:
36+
case SpvOpEmitStreamVertex:
37+
case SpvOpEndStreamPrimitive:
38+
_.current_function().RegisterExecutionModelLimitation(
39+
SpvExecutionModelGeometry,
40+
std::string(spvOpcodeString(opcode)) +
41+
" instructions require Geometry execution model");
42+
break;
43+
default:
44+
break;
45+
}
46+
3147
switch (opcode) {
3248
case SpvOpEmitStreamVertex:
3349
case SpvOpEndStreamPrimitive: {

test/val/val_primitives_test.cpp

Lines changed: 174 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,11 @@ using ValidatePrimitives = spvtest::ValidateBase<bool>;
2828

2929
std::string GenerateShaderCode(
3030
const std::string& body,
31-
const std::string& capabilities_and_extensions = "",
31+
const std::string& capabilities_and_extensions =
32+
"OpCapability GeometryStreams",
3233
const std::string& execution_model = "Geometry") {
3334
std::ostringstream ss;
34-
ss << R"(
35-
OpCapability Geometry
36-
OpCapability GeometryStreams
37-
)";
38-
39-
ss << capabilities_and_extensions;
35+
ss << capabilities_and_extensions << "\n";
4036
ss << "OpMemoryModel Logical GLSL450\n";
4137
ss << "OpEntryPoint " << execution_model << " %main \"main\"\n";
4238

@@ -67,22 +63,146 @@ OpFunctionEnd)";
6763
return ss.str();
6864
}
6965

66+
// Returns SPIR-V assembly fragment representing a function call,
67+
// the end of the callee body, and the preamble and body of the called
68+
// function with the given body, but missing the final return and
69+
// function-end. The result is of the form where it can be used in the
70+
// |body| argument to GenerateShaderCode.
71+
std::string CallAndCallee(std::string body) {
72+
std::ostringstream ss;
73+
ss << R"(
74+
%dummy = OpFunctionCall %void %foo
75+
OpReturn
76+
OpFunctionEnd
77+
78+
%foo = OpFunction %void None %func
79+
%foo_entry = OpLabel
80+
)";
81+
82+
ss << body;
83+
84+
return ss.str();
85+
}
86+
87+
// OpEmitVertex doesn't have any parameters, so other validation
88+
// is handled by the binary parser, and generic dominance checks.
89+
TEST_F(ValidatePrimitives, EmitVertexSuccess) {
90+
CompileSuccessfully(
91+
GenerateShaderCode("OpEmitVertex", "OpCapability Geometry"));
92+
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
93+
}
94+
95+
TEST_F(ValidatePrimitives, EmitVertexFailMissingCapability) {
96+
CompileSuccessfully(
97+
GenerateShaderCode("OpEmitVertex", "OpCapability Shader", "Vertex"));
98+
EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
99+
EXPECT_THAT(
100+
getDiagnosticString(),
101+
HasSubstr(
102+
"Opcode EmitVertex requires one of these capabilities: Geometry"));
103+
}
104+
105+
TEST_F(ValidatePrimitives, EmitVertexFailWrongExecutionMode) {
106+
CompileSuccessfully(
107+
GenerateShaderCode("OpEmitVertex", "OpCapability Geometry", "Vertex"));
108+
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
109+
EXPECT_THAT(
110+
getDiagnosticString(),
111+
HasSubstr("EmitVertex instructions require Geometry execution model"));
112+
}
113+
114+
TEST_F(ValidatePrimitives, EmitVertexFailWrongExecutionModeNestedFunction) {
115+
CompileSuccessfully(GenerateShaderCode(CallAndCallee("OpEmitVertex"),
116+
"OpCapability Geometry", "Vertex"));
117+
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
118+
EXPECT_THAT(
119+
getDiagnosticString(),
120+
HasSubstr("EmitVertex instructions require Geometry execution model"));
121+
}
122+
123+
// OpEndPrimitive doesn't have any parameters, so other validation
124+
// is handled by the binary parser, and generic dominance checks.
125+
TEST_F(ValidatePrimitives, EndPrimitiveSuccess) {
126+
CompileSuccessfully(
127+
GenerateShaderCode("OpEndPrimitive", "OpCapability Geometry"));
128+
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
129+
}
130+
131+
TEST_F(ValidatePrimitives, EndPrimitiveFailMissingCapability) {
132+
CompileSuccessfully(
133+
GenerateShaderCode("OpEndPrimitive", "OpCapability Shader", "Vertex"));
134+
EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
135+
EXPECT_THAT(
136+
getDiagnosticString(),
137+
HasSubstr(
138+
"Opcode EndPrimitive requires one of these capabilities: Geometry"));
139+
}
140+
141+
TEST_F(ValidatePrimitives, EndPrimitiveFailWrongExecutionMode) {
142+
CompileSuccessfully(
143+
GenerateShaderCode("OpEndPrimitive", "OpCapability Geometry", "Vertex"));
144+
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
145+
EXPECT_THAT(
146+
getDiagnosticString(),
147+
HasSubstr("EndPrimitive instructions require Geometry execution model"));
148+
}
149+
150+
TEST_F(ValidatePrimitives, EndPrimitiveFailWrongExecutionModeNestedFunction) {
151+
CompileSuccessfully(GenerateShaderCode(CallAndCallee("OpEndPrimitive"),
152+
"OpCapability Geometry", "Vertex"));
153+
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
154+
EXPECT_THAT(
155+
getDiagnosticString(),
156+
HasSubstr("EndPrimitive instructions require Geometry execution model"));
157+
}
158+
70159
TEST_F(ValidatePrimitives, EmitStreamVertexSuccess) {
71160
const std::string body = R"(
72161
OpEmitStreamVertex %u32_0
73162
)";
74163

75-
CompileSuccessfully(GenerateShaderCode(body).c_str());
76-
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
164+
CompileSuccessfully(GenerateShaderCode(body));
165+
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
166+
}
167+
168+
TEST_F(ValidatePrimitives, EmitStreamVertexFailMissingCapability) {
169+
CompileSuccessfully(GenerateShaderCode("OpEmitStreamVertex %u32_0",
170+
"OpCapability Shader", "Vertex"));
171+
EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
172+
EXPECT_THAT(getDiagnosticString(),
173+
HasSubstr("Opcode EmitStreamVertex requires one of these "
174+
"capabilities: GeometryStreams"));
175+
}
176+
177+
TEST_F(ValidatePrimitives, EmitStreamVertexFailWrongExecutionMode) {
178+
CompileSuccessfully(GenerateShaderCode(
179+
"OpEmitStreamVertex %u32_0", "OpCapability GeometryStreams", "Vertex"));
180+
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
181+
EXPECT_THAT(
182+
getDiagnosticString(),
183+
HasSubstr(
184+
"EmitStreamVertex instructions require Geometry execution model"));
185+
}
186+
187+
TEST_F(ValidatePrimitives,
188+
EmitStreamVertexFailWrongExecutionModeNestedFunction) {
189+
CompileSuccessfully(
190+
GenerateShaderCode(CallAndCallee("OpEmitStreamVertex %u32_0"),
191+
"OpCapability GeometryStreams", "Vertex"));
192+
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
193+
EXPECT_THAT(
194+
getDiagnosticString(),
195+
HasSubstr(
196+
"EmitStreamVertex instructions require Geometry execution model"));
77197
}
78198

79199
TEST_F(ValidatePrimitives, EmitStreamVertexNonInt) {
80200
const std::string body = R"(
81201
OpEmitStreamVertex %f32_0
82202
)";
83203

84-
CompileSuccessfully(GenerateShaderCode(body).c_str());
85-
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
204+
CompileSuccessfully(GenerateShaderCode(body));
205+
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
86206
EXPECT_THAT(getDiagnosticString(),
87207
HasSubstr("EmitStreamVertex: "
88208
"expected Stream to be int scalar"));
@@ -93,8 +213,8 @@ TEST_F(ValidatePrimitives, EmitStreamVertexNonScalar) {
93213
OpEmitStreamVertex %u32vec4_0123
94214
)";
95215

96-
CompileSuccessfully(GenerateShaderCode(body).c_str());
97-
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
216+
CompileSuccessfully(GenerateShaderCode(body));
217+
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
98218
EXPECT_THAT(getDiagnosticString(),
99219
HasSubstr("EmitStreamVertex: "
100220
"expected Stream to be int scalar"));
@@ -106,8 +226,8 @@ TEST_F(ValidatePrimitives, EmitStreamVertexNonConstant) {
106226
OpEmitStreamVertex %val1
107227
)";
108228

109-
CompileSuccessfully(GenerateShaderCode(body).c_str());
110-
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
229+
CompileSuccessfully(GenerateShaderCode(body));
230+
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
111231
EXPECT_THAT(getDiagnosticString(),
112232
HasSubstr("EmitStreamVertex: "
113233
"expected Stream to be constant instruction"));
@@ -118,17 +238,48 @@ TEST_F(ValidatePrimitives, EndStreamPrimitiveSuccess) {
118238
OpEndStreamPrimitive %u32_0
119239
)";
120240

121-
CompileSuccessfully(GenerateShaderCode(body).c_str());
122-
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
241+
CompileSuccessfully(GenerateShaderCode(body));
242+
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
243+
}
244+
245+
TEST_F(ValidatePrimitives, EndStreamPrimitiveFailMissingCapability) {
246+
CompileSuccessfully(GenerateShaderCode("OpEndStreamPrimitive %u32_0",
247+
"OpCapability Shader", "Vertex"));
248+
EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
249+
EXPECT_THAT(getDiagnosticString(),
250+
HasSubstr("Opcode EndStreamPrimitive requires one of these "
251+
"capabilities: GeometryStreams"));
252+
}
253+
254+
TEST_F(ValidatePrimitives, EndStreamPrimitiveFailWrongExecutionMode) {
255+
CompileSuccessfully(GenerateShaderCode(
256+
"OpEndStreamPrimitive %u32_0", "OpCapability GeometryStreams", "Vertex"));
257+
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
258+
EXPECT_THAT(
259+
getDiagnosticString(),
260+
HasSubstr(
261+
"EndStreamPrimitive instructions require Geometry execution model"));
262+
}
263+
264+
TEST_F(ValidatePrimitives,
265+
EndStreamPrimitiveFailWrongExecutionModeNestedFunction) {
266+
CompileSuccessfully(
267+
GenerateShaderCode(CallAndCallee("OpEndStreamPrimitive %u32_0"),
268+
"OpCapability GeometryStreams", "Vertex"));
269+
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
270+
EXPECT_THAT(
271+
getDiagnosticString(),
272+
HasSubstr(
273+
"EndStreamPrimitive instructions require Geometry execution model"));
123274
}
124275

125276
TEST_F(ValidatePrimitives, EndStreamPrimitiveNonInt) {
126277
const std::string body = R"(
127278
OpEndStreamPrimitive %f32_0
128279
)";
129280

130-
CompileSuccessfully(GenerateShaderCode(body).c_str());
131-
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
281+
CompileSuccessfully(GenerateShaderCode(body));
282+
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
132283
EXPECT_THAT(getDiagnosticString(),
133284
HasSubstr("EndStreamPrimitive: "
134285
"expected Stream to be int scalar"));
@@ -139,8 +290,8 @@ TEST_F(ValidatePrimitives, EndStreamPrimitiveNonScalar) {
139290
OpEndStreamPrimitive %u32vec4_0123
140291
)";
141292

142-
CompileSuccessfully(GenerateShaderCode(body).c_str());
143-
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
293+
CompileSuccessfully(GenerateShaderCode(body));
294+
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
144295
EXPECT_THAT(getDiagnosticString(),
145296
HasSubstr("EndStreamPrimitive: "
146297
"expected Stream to be int scalar"));
@@ -152,8 +303,8 @@ TEST_F(ValidatePrimitives, EndStreamPrimitiveNonConstant) {
152303
OpEndStreamPrimitive %val1
153304
)";
154305

155-
CompileSuccessfully(GenerateShaderCode(body).c_str());
156-
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
306+
CompileSuccessfully(GenerateShaderCode(body));
307+
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
157308
EXPECT_THAT(getDiagnosticString(),
158309
HasSubstr("EndStreamPrimitive: "
159310
"expected Stream to be constant instruction"));

0 commit comments

Comments
 (0)