Home | History | Annotate | Download | only in val
      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