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 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. 77 std::string CallAndCallee(const std::string& body) { 78 std::ostringstream ss; 79 ss << R"( 80 %dummy = 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. 95 TEST_F(ValidatePrimitives, EmitVertexSuccess) { 96 CompileSuccessfully( 97 GenerateShaderCode("OpEmitVertex", "OpCapability Geometry")); 98 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); 99 } 100 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 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 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. 131 TEST_F(ValidatePrimitives, EndPrimitiveSuccess) { 132 CompileSuccessfully( 133 GenerateShaderCode("OpEndPrimitive", "OpCapability Geometry")); 134 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); 135 } 136 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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