1 // Copyright (c) 2017 LunarG Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <sstream>
16 #include <string>
17
18 #include "gmock/gmock.h"
19 #include "test/unit_spirv.h"
20 #include "test/val/val_fixtures.h"
21
22 namespace spvtools {
23 namespace val {
24 namespace {
25
26 using ::testing::HasSubstr;
27 using ::testing::Not;
28
29 using ValidatePrimitives = spvtest::ValidateBase<bool>;
30
GenerateShaderCode(const std::string & body,const std::string & capabilities_and_extensions="OpCapability GeometryStreams",const std::string & execution_model="Geometry")31 std::string GenerateShaderCode(
32 const std::string& body,
33 const std::string& capabilities_and_extensions =
34 "OpCapability GeometryStreams",
35 const std::string& execution_model = "Geometry") {
36 std::ostringstream ss;
37 ss << capabilities_and_extensions << "\n";
38 ss << "OpMemoryModel Logical GLSL450\n";
39 ss << "OpEntryPoint " << execution_model << " %main \"main\"\n";
40 if (execution_model == "Geometry") {
41 ss << "OpExecutionMode %main InputPoints\n";
42 ss << "OpExecutionMode %main OutputPoints\n";
43 }
44
45 ss << R"(
46 %void = OpTypeVoid
47 %func = OpTypeFunction %void
48 %f32 = OpTypeFloat 32
49 %u32 = OpTypeInt 32 0
50 %u32vec4 = OpTypeVector %u32 4
51
52 %f32_0 = OpConstant %f32 0
53 %u32_0 = OpConstant %u32 0
54 %u32_1 = OpConstant %u32 1
55 %u32_2 = OpConstant %u32 2
56 %u32_3 = OpConstant %u32 3
57 %u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
58
59 %main = OpFunction %void None %func
60 %main_entry = OpLabel
61 )";
62
63 ss << body;
64
65 ss << R"(
66 OpReturn
67 OpFunctionEnd)";
68
69 return ss.str();
70 }
71
72 // Returns SPIR-V assembly fragment representing a function call,
73 // the end of the callee body, and the preamble and body of the called
74 // function with the given body, but missing the final return and
75 // function-end. The result is of the form where it can be used in the
76 // |body| argument to GenerateShaderCode.
CallAndCallee(const std::string & body)77 std::string CallAndCallee(const std::string& body) {
78 std::ostringstream ss;
79 ss << R"(
80 %placeholder = OpFunctionCall %void %foo
81 OpReturn
82 OpFunctionEnd
83
84 %foo = OpFunction %void None %func
85 %foo_entry = OpLabel
86 )";
87
88 ss << body;
89
90 return ss.str();
91 }
92
93 // OpEmitVertex doesn't have any parameters, so other validation
94 // is handled by the binary parser, and generic dominance checks.
TEST_F(ValidatePrimitives,EmitVertexSuccess)95 TEST_F(ValidatePrimitives, EmitVertexSuccess) {
96 CompileSuccessfully(
97 GenerateShaderCode("OpEmitVertex", "OpCapability Geometry"));
98 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
99 }
100
TEST_F(ValidatePrimitives,EmitVertexFailMissingCapability)101 TEST_F(ValidatePrimitives, EmitVertexFailMissingCapability) {
102 CompileSuccessfully(
103 GenerateShaderCode("OpEmitVertex", "OpCapability Shader", "Vertex"));
104 EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
105 EXPECT_THAT(
106 getDiagnosticString(),
107 HasSubstr(
108 "Opcode EmitVertex requires one of these capabilities: Geometry"));
109 }
110
TEST_F(ValidatePrimitives,EmitVertexFailWrongExecutionMode)111 TEST_F(ValidatePrimitives, EmitVertexFailWrongExecutionMode) {
112 CompileSuccessfully(
113 GenerateShaderCode("OpEmitVertex", "OpCapability Geometry", "Vertex"));
114 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
115 EXPECT_THAT(
116 getDiagnosticString(),
117 HasSubstr("EmitVertex instructions require Geometry execution model"));
118 }
119
TEST_F(ValidatePrimitives,EmitVertexFailWrongExecutionModeNestedFunction)120 TEST_F(ValidatePrimitives, EmitVertexFailWrongExecutionModeNestedFunction) {
121 CompileSuccessfully(GenerateShaderCode(CallAndCallee("OpEmitVertex"),
122 "OpCapability Geometry", "Vertex"));
123 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
124 EXPECT_THAT(
125 getDiagnosticString(),
126 HasSubstr("EmitVertex instructions require Geometry execution model"));
127 }
128
129 // OpEndPrimitive doesn't have any parameters, so other validation
130 // is handled by the binary parser, and generic dominance checks.
TEST_F(ValidatePrimitives,EndPrimitiveSuccess)131 TEST_F(ValidatePrimitives, EndPrimitiveSuccess) {
132 CompileSuccessfully(
133 GenerateShaderCode("OpEndPrimitive", "OpCapability Geometry"));
134 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
135 }
136
TEST_F(ValidatePrimitives,EndPrimitiveFailMissingCapability)137 TEST_F(ValidatePrimitives, EndPrimitiveFailMissingCapability) {
138 CompileSuccessfully(
139 GenerateShaderCode("OpEndPrimitive", "OpCapability Shader", "Vertex"));
140 EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
141 EXPECT_THAT(
142 getDiagnosticString(),
143 HasSubstr(
144 "Opcode EndPrimitive requires one of these capabilities: Geometry"));
145 }
146
TEST_F(ValidatePrimitives,EndPrimitiveFailWrongExecutionMode)147 TEST_F(ValidatePrimitives, EndPrimitiveFailWrongExecutionMode) {
148 CompileSuccessfully(
149 GenerateShaderCode("OpEndPrimitive", "OpCapability Geometry", "Vertex"));
150 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
151 EXPECT_THAT(
152 getDiagnosticString(),
153 HasSubstr("EndPrimitive instructions require Geometry execution model"));
154 }
155
TEST_F(ValidatePrimitives,EndPrimitiveFailWrongExecutionModeNestedFunction)156 TEST_F(ValidatePrimitives, EndPrimitiveFailWrongExecutionModeNestedFunction) {
157 CompileSuccessfully(GenerateShaderCode(CallAndCallee("OpEndPrimitive"),
158 "OpCapability Geometry", "Vertex"));
159 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
160 EXPECT_THAT(
161 getDiagnosticString(),
162 HasSubstr("EndPrimitive instructions require Geometry execution model"));
163 }
164
TEST_F(ValidatePrimitives,EmitStreamVertexSuccess)165 TEST_F(ValidatePrimitives, EmitStreamVertexSuccess) {
166 const std::string body = R"(
167 OpEmitStreamVertex %u32_0
168 )";
169
170 CompileSuccessfully(GenerateShaderCode(body));
171 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
172 }
173
TEST_F(ValidatePrimitives,EmitStreamVertexFailMissingCapability)174 TEST_F(ValidatePrimitives, EmitStreamVertexFailMissingCapability) {
175 CompileSuccessfully(GenerateShaderCode("OpEmitStreamVertex %u32_0",
176 "OpCapability Shader", "Vertex"));
177 EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
178 EXPECT_THAT(getDiagnosticString(),
179 HasSubstr("Opcode EmitStreamVertex requires one of these "
180 "capabilities: GeometryStreams"));
181 }
182
TEST_F(ValidatePrimitives,EmitStreamVertexFailWrongExecutionMode)183 TEST_F(ValidatePrimitives, EmitStreamVertexFailWrongExecutionMode) {
184 CompileSuccessfully(GenerateShaderCode(
185 "OpEmitStreamVertex %u32_0", "OpCapability GeometryStreams", "Vertex"));
186 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
187 EXPECT_THAT(
188 getDiagnosticString(),
189 HasSubstr(
190 "EmitStreamVertex instructions require Geometry execution model"));
191 }
192
TEST_F(ValidatePrimitives,EmitStreamVertexFailWrongExecutionModeNestedFunction)193 TEST_F(ValidatePrimitives,
194 EmitStreamVertexFailWrongExecutionModeNestedFunction) {
195 CompileSuccessfully(
196 GenerateShaderCode(CallAndCallee("OpEmitStreamVertex %u32_0"),
197 "OpCapability GeometryStreams", "Vertex"));
198 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
199 EXPECT_THAT(
200 getDiagnosticString(),
201 HasSubstr(
202 "EmitStreamVertex instructions require Geometry execution model"));
203 }
204
TEST_F(ValidatePrimitives,EmitStreamVertexNonInt)205 TEST_F(ValidatePrimitives, EmitStreamVertexNonInt) {
206 const std::string body = R"(
207 OpEmitStreamVertex %f32_0
208 )";
209
210 CompileSuccessfully(GenerateShaderCode(body));
211 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
212 EXPECT_THAT(getDiagnosticString(),
213 HasSubstr("EmitStreamVertex: "
214 "expected Stream to be int scalar"));
215 }
216
TEST_F(ValidatePrimitives,EmitStreamVertexNonScalar)217 TEST_F(ValidatePrimitives, EmitStreamVertexNonScalar) {
218 const std::string body = R"(
219 OpEmitStreamVertex %u32vec4_0123
220 )";
221
222 CompileSuccessfully(GenerateShaderCode(body));
223 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
224 EXPECT_THAT(getDiagnosticString(),
225 HasSubstr("EmitStreamVertex: "
226 "expected Stream to be int scalar"));
227 }
228
TEST_F(ValidatePrimitives,EmitStreamVertexNonConstant)229 TEST_F(ValidatePrimitives, EmitStreamVertexNonConstant) {
230 const std::string body = R"(
231 %val1 = OpIAdd %u32 %u32_0 %u32_1
232 OpEmitStreamVertex %val1
233 )";
234
235 CompileSuccessfully(GenerateShaderCode(body));
236 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
237 EXPECT_THAT(getDiagnosticString(),
238 HasSubstr("EmitStreamVertex: "
239 "expected Stream to be constant instruction"));
240 }
241
TEST_F(ValidatePrimitives,EndStreamPrimitiveSuccess)242 TEST_F(ValidatePrimitives, EndStreamPrimitiveSuccess) {
243 const std::string body = R"(
244 OpEndStreamPrimitive %u32_0
245 )";
246
247 CompileSuccessfully(GenerateShaderCode(body));
248 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
249 }
250
TEST_F(ValidatePrimitives,EndStreamPrimitiveFailMissingCapability)251 TEST_F(ValidatePrimitives, EndStreamPrimitiveFailMissingCapability) {
252 CompileSuccessfully(GenerateShaderCode("OpEndStreamPrimitive %u32_0",
253 "OpCapability Shader", "Vertex"));
254 EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
255 EXPECT_THAT(getDiagnosticString(),
256 HasSubstr("Opcode EndStreamPrimitive requires one of these "
257 "capabilities: GeometryStreams"));
258 }
259
TEST_F(ValidatePrimitives,EndStreamPrimitiveFailWrongExecutionMode)260 TEST_F(ValidatePrimitives, EndStreamPrimitiveFailWrongExecutionMode) {
261 CompileSuccessfully(GenerateShaderCode(
262 "OpEndStreamPrimitive %u32_0", "OpCapability GeometryStreams", "Vertex"));
263 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
264 EXPECT_THAT(
265 getDiagnosticString(),
266 HasSubstr(
267 "EndStreamPrimitive instructions require Geometry execution model"));
268 }
269
TEST_F(ValidatePrimitives,EndStreamPrimitiveFailWrongExecutionModeNestedFunction)270 TEST_F(ValidatePrimitives,
271 EndStreamPrimitiveFailWrongExecutionModeNestedFunction) {
272 CompileSuccessfully(
273 GenerateShaderCode(CallAndCallee("OpEndStreamPrimitive %u32_0"),
274 "OpCapability GeometryStreams", "Vertex"));
275 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
276 EXPECT_THAT(
277 getDiagnosticString(),
278 HasSubstr(
279 "EndStreamPrimitive instructions require Geometry execution model"));
280 }
281
TEST_F(ValidatePrimitives,EndStreamPrimitiveNonInt)282 TEST_F(ValidatePrimitives, EndStreamPrimitiveNonInt) {
283 const std::string body = R"(
284 OpEndStreamPrimitive %f32_0
285 )";
286
287 CompileSuccessfully(GenerateShaderCode(body));
288 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
289 EXPECT_THAT(getDiagnosticString(),
290 HasSubstr("EndStreamPrimitive: "
291 "expected Stream to be int scalar"));
292 }
293
TEST_F(ValidatePrimitives,EndStreamPrimitiveNonScalar)294 TEST_F(ValidatePrimitives, EndStreamPrimitiveNonScalar) {
295 const std::string body = R"(
296 OpEndStreamPrimitive %u32vec4_0123
297 )";
298
299 CompileSuccessfully(GenerateShaderCode(body));
300 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
301 EXPECT_THAT(getDiagnosticString(),
302 HasSubstr("EndStreamPrimitive: "
303 "expected Stream to be int scalar"));
304 }
305
TEST_F(ValidatePrimitives,EndStreamPrimitiveNonConstant)306 TEST_F(ValidatePrimitives, EndStreamPrimitiveNonConstant) {
307 const std::string body = R"(
308 %val1 = OpIAdd %u32 %u32_0 %u32_1
309 OpEndStreamPrimitive %val1
310 )";
311
312 CompileSuccessfully(GenerateShaderCode(body));
313 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
314 EXPECT_THAT(getDiagnosticString(),
315 HasSubstr("EndStreamPrimitive: "
316 "expected Stream to be constant instruction"));
317 }
318
319 } // namespace
320 } // namespace val
321 } // namespace spvtools
322