| // Copyright (c) 2018 Google LLC. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "source/spirv_target_env.h" |
| #include "test/test_fixture.h" |
| #include "test/unit_spirv.h" |
| #include "test/val/val_fixtures.h" |
| |
| namespace spvtools { |
| namespace val { |
| namespace { |
| |
| using ::testing::Combine; |
| using ::testing::HasSubstr; |
| using ::testing::Values; |
| using ::testing::ValuesIn; |
| |
| using ValidateMode = spvtest::ValidateBase<bool>; |
| |
| const std::string kVoidFunction = R"(%void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| TEST_F(ValidateMode, GLComputeNoMode) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateMode, GLComputeNoModeVulkan) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_VULKAN_1_0; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-LocalSize-06426")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "In the Vulkan environment, GLCompute execution model entry " |
| "points require either the LocalSize or LocalSizeId execution mode " |
| "or an object decorated with WorkgroupSize must be specified.")); |
| } |
| |
| TEST_F(ValidateMode, GLComputeNoModeVulkanWorkgroupSize) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpDecorate %int3_1 BuiltIn WorkgroupSize |
| %int = OpTypeInt 32 0 |
| %int3 = OpTypeVector %int 3 |
| %int_1 = OpConstant %int 1 |
| %int3_1 = OpConstantComposite %int3 %int_1 %int_1 %int_1 |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_VULKAN_1_0; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); |
| } |
| |
| TEST_F(ValidateMode, GLComputeVulkanLocalSize) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_VULKAN_1_0; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); |
| } |
| |
| TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdBad) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 |
| %int = OpTypeInt 32 0 |
| %int_1 = OpConstant %int 1 |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_VULKAN_1_1; // need SPIR-V 1.2 |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("LocalSizeId mode is not allowed by the current environment.")); |
| } |
| |
| TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdGood) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 |
| %int = OpTypeInt 32 0 |
| %int_1 = OpConstant %int 1 |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_VULKAN_1_1; // need SPIR-V 1.2 |
| CompileSuccessfully(spirv, env); |
| spvValidatorOptionsSetAllowLocalSizeId(getValidatorOptions(), true); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); |
| } |
| |
| TEST_F(ValidateMode, FragmentOriginLowerLeftVulkan) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginLowerLeft |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_VULKAN_1_0; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-OriginLowerLeft-04653")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("In the Vulkan environment, the OriginLowerLeft " |
| "execution mode must not be used.")); |
| } |
| |
| TEST_F(ValidateMode, FragmentPixelCenterIntegerVulkan) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpExecutionMode %main PixelCenterInteger |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_VULKAN_1_0; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-PixelCenterInteger-04654")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("In the Vulkan environment, the PixelCenterInteger " |
| "execution mode must not be used.")); |
| } |
| |
| TEST_F(ValidateMode, GeometryNoOutputMode) { |
| const std::string spirv = R"( |
| OpCapability Geometry |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Geometry %main "main" |
| OpExecutionMode %main InputPoints |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Geometry execution model entry points must specify " |
| "exactly one of OutputPoints, OutputLineStrip or " |
| "OutputTriangleStrip execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, GeometryNoInputMode) { |
| const std::string spirv = R"( |
| OpCapability Geometry |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Geometry %main "main" |
| OpExecutionMode %main OutputPoints |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Geometry execution model entry points must specify exactly " |
| "one of InputPoints, InputLines, InputLinesAdjacency, " |
| "Triangles or InputTrianglesAdjacency execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, FragmentNoOrigin) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Fragment execution model entry points require either an " |
| "OriginUpperLeft or OriginLowerLeft execution mode.")); |
| } |
| |
| TEST_F(ValidateMode, FragmentBothOrigins) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpExecutionMode %main OriginLowerLeft |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Fragment execution model entry points can only specify one of " |
| "OriginUpperLeft or OriginLowerLeft execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, FragmentDepthGreaterAndLess) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpExecutionMode %main DepthGreater |
| OpExecutionMode %main DepthLess |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Fragment execution model entry points can specify at " |
| "most one of DepthGreater, DepthLess or DepthUnchanged " |
| "execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, FragmentDepthGreaterAndUnchanged) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpExecutionMode %main DepthGreater |
| OpExecutionMode %main DepthUnchanged |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Fragment execution model entry points can specify at " |
| "most one of DepthGreater, DepthLess or DepthUnchanged " |
| "execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, FragmentDepthLessAndUnchanged) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpExecutionMode %main DepthLess |
| OpExecutionMode %main DepthUnchanged |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Fragment execution model entry points can specify at " |
| "most one of DepthGreater, DepthLess or DepthUnchanged " |
| "execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, FragmentAllDepths) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpExecutionMode %main DepthGreater |
| OpExecutionMode %main DepthLess |
| OpExecutionMode %main DepthUnchanged |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Fragment execution model entry points can specify at " |
| "most one of DepthGreater, DepthLess or DepthUnchanged " |
| "execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, TessellationControlSpacingEqualAndFractionalOdd) { |
| const std::string spirv = R"( |
| OpCapability Tessellation |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint TessellationControl %main "main" |
| OpExecutionMode %main SpacingEqual |
| OpExecutionMode %main SpacingFractionalOdd |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Tessellation execution model entry points can specify " |
| "at most one of SpacingEqual, SpacingFractionalOdd or " |
| "SpacingFractionalEven execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, TessellationControlSpacingEqualAndSpacingFractionalEven) { |
| const std::string spirv = R"( |
| OpCapability Tessellation |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint TessellationControl %main "main" |
| OpExecutionMode %main SpacingEqual |
| OpExecutionMode %main SpacingFractionalEven |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Tessellation execution model entry points can specify " |
| "at most one of SpacingEqual, SpacingFractionalOdd or " |
| "SpacingFractionalEven execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, |
| TessellationControlSpacingFractionalOddAndSpacingFractionalEven) { |
| const std::string spirv = R"( |
| OpCapability Tessellation |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint TessellationControl %main "main" |
| OpExecutionMode %main SpacingFractionalOdd |
| OpExecutionMode %main SpacingFractionalEven |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Tessellation execution model entry points can specify " |
| "at most one of SpacingEqual, SpacingFractionalOdd or " |
| "SpacingFractionalEven execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, TessellationControlAllSpacing) { |
| const std::string spirv = R"( |
| OpCapability Tessellation |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint TessellationControl %main "main" |
| OpExecutionMode %main SpacingEqual |
| OpExecutionMode %main SpacingFractionalOdd |
| OpExecutionMode %main SpacingFractionalEven |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Tessellation execution model entry points can specify " |
| "at most one of SpacingEqual, SpacingFractionalOdd or " |
| "SpacingFractionalEven execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, |
| TessellationEvaluationSpacingEqualAndSpacingFractionalOdd) { |
| const std::string spirv = R"( |
| OpCapability Tessellation |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint TessellationEvaluation %main "main" |
| OpExecutionMode %main SpacingEqual |
| OpExecutionMode %main SpacingFractionalOdd |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Tessellation execution model entry points can specify " |
| "at most one of SpacingEqual, SpacingFractionalOdd or " |
| "SpacingFractionalEven execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, |
| TessellationEvaluationSpacingEqualAndSpacingFractionalEven) { |
| const std::string spirv = R"( |
| OpCapability Tessellation |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint TessellationEvaluation %main "main" |
| OpExecutionMode %main SpacingEqual |
| OpExecutionMode %main SpacingFractionalEven |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Tessellation execution model entry points can specify " |
| "at most one of SpacingEqual, SpacingFractionalOdd or " |
| "SpacingFractionalEven execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, |
| TessellationEvaluationSpacingFractionalOddAndSpacingFractionalEven) { |
| const std::string spirv = R"( |
| OpCapability Tessellation |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint TessellationEvaluation %main "main" |
| OpExecutionMode %main SpacingFractionalOdd |
| OpExecutionMode %main SpacingFractionalEven |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Tessellation execution model entry points can specify " |
| "at most one of SpacingEqual, SpacingFractionalOdd or " |
| "SpacingFractionalEven execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, TessellationEvaluationAllSpacing) { |
| const std::string spirv = R"( |
| OpCapability Tessellation |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint TessellationEvaluation %main "main" |
| OpExecutionMode %main SpacingEqual |
| OpExecutionMode %main SpacingFractionalOdd |
| OpExecutionMode %main SpacingFractionalEven |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Tessellation execution model entry points can specify " |
| "at most one of SpacingEqual, SpacingFractionalOdd or " |
| "SpacingFractionalEven execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, TessellationControlBothVertex) { |
| const std::string spirv = R"( |
| OpCapability Tessellation |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint TessellationControl %main "main" |
| OpExecutionMode %main VertexOrderCw |
| OpExecutionMode %main VertexOrderCcw |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Tessellation execution model entry points can specify at most " |
| "one of VertexOrderCw or VertexOrderCcw execution modes.")); |
| } |
| |
| TEST_F(ValidateMode, TessellationEvaluationBothVertex) { |
| const std::string spirv = R"( |
| OpCapability Tessellation |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint TessellationEvaluation %main "main" |
| OpExecutionMode %main VertexOrderCw |
| OpExecutionMode %main VertexOrderCcw |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Tessellation execution model entry points can specify at most " |
| "one of VertexOrderCw or VertexOrderCcw execution modes.")); |
| } |
| |
| using ValidateModeGeometry = spvtest::ValidateBase<std::tuple< |
| std::tuple<std::string, std::string, std::string, std::string, std::string>, |
| std::tuple<std::string, std::string, std::string>>>; |
| |
| TEST_P(ValidateModeGeometry, ExecutionMode) { |
| std::vector<std::string> input_modes; |
| std::vector<std::string> output_modes; |
| input_modes.push_back(std::get<0>(std::get<0>(GetParam()))); |
| input_modes.push_back(std::get<1>(std::get<0>(GetParam()))); |
| input_modes.push_back(std::get<2>(std::get<0>(GetParam()))); |
| input_modes.push_back(std::get<3>(std::get<0>(GetParam()))); |
| input_modes.push_back(std::get<4>(std::get<0>(GetParam()))); |
| output_modes.push_back(std::get<0>(std::get<1>(GetParam()))); |
| output_modes.push_back(std::get<1>(std::get<1>(GetParam()))); |
| output_modes.push_back(std::get<2>(std::get<1>(GetParam()))); |
| |
| std::ostringstream sstr; |
| sstr << "OpCapability Geometry\n"; |
| sstr << "OpMemoryModel Logical GLSL450\n"; |
| sstr << "OpEntryPoint Geometry %main \"main\"\n"; |
| size_t num_input_modes = 0; |
| for (auto input : input_modes) { |
| if (!input.empty()) { |
| num_input_modes++; |
| sstr << "OpExecutionMode %main " << input << "\n"; |
| } |
| } |
| size_t num_output_modes = 0; |
| for (auto output : output_modes) { |
| if (!output.empty()) { |
| num_output_modes++; |
| sstr << "OpExecutionMode %main " << output << "\n"; |
| } |
| } |
| sstr << "%void = OpTypeVoid\n"; |
| sstr << "%void_fn = OpTypeFunction %void\n"; |
| sstr << "%int = OpTypeInt 32 0\n"; |
| sstr << "%int1 = OpConstant %int 1\n"; |
| sstr << "%main = OpFunction %void None %void_fn\n"; |
| sstr << "%entry = OpLabel\n"; |
| sstr << "OpReturn\n"; |
| sstr << "OpFunctionEnd\n"; |
| |
| CompileSuccessfully(sstr.str()); |
| if (num_input_modes == 1 && num_output_modes == 1) { |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); |
| } else { |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| if (num_input_modes != 1) { |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Geometry execution model entry points must " |
| "specify exactly one of InputPoints, InputLines, " |
| "InputLinesAdjacency, Triangles or " |
| "InputTrianglesAdjacency execution modes.")); |
| } else { |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Geometry execution model entry points must specify " |
| "exactly one of OutputPoints, OutputLineStrip or " |
| "OutputTriangleStrip execution modes.")); |
| } |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| GeometryRequiredModes, ValidateModeGeometry, |
| Combine(Combine(Values("InputPoints", ""), Values("InputLines", ""), |
| Values("InputLinesAdjacency", ""), Values("Triangles", ""), |
| Values("InputTrianglesAdjacency", "")), |
| Combine(Values("OutputPoints", ""), Values("OutputLineStrip", ""), |
| Values("OutputTriangleStrip", "")))); |
| |
| using ValidateModeExecution = |
| spvtest::ValidateBase<std::tuple<spv_result_t, std::string, std::string, |
| std::string, spv_target_env>>; |
| |
| TEST_P(ValidateModeExecution, ExecutionMode) { |
| const spv_result_t expectation = std::get<0>(GetParam()); |
| const std::string error = std::get<1>(GetParam()); |
| const std::string model = std::get<2>(GetParam()); |
| const std::string mode = std::get<3>(GetParam()); |
| const spv_target_env env = std::get<4>(GetParam()); |
| |
| std::ostringstream sstr; |
| sstr << "OpCapability Shader\n"; |
| sstr << "OpCapability Geometry\n"; |
| sstr << "OpCapability Tessellation\n"; |
| sstr << "OpCapability TransformFeedback\n"; |
| if (!spvIsVulkanEnv(env)) { |
| sstr << "OpCapability Kernel\n"; |
| if (env == SPV_ENV_UNIVERSAL_1_3) { |
| sstr << "OpCapability SubgroupDispatch\n"; |
| } |
| } |
| sstr << "OpMemoryModel Logical GLSL450\n"; |
| sstr << "OpEntryPoint " << model << " %main \"main\"\n"; |
| if (mode.find("LocalSizeId") == 0 || mode.find("LocalSizeHintId") == 0 || |
| mode.find("SubgroupsPerWorkgroupId") == 0) { |
| sstr << "OpExecutionModeId %main " << mode << "\n"; |
| } else { |
| sstr << "OpExecutionMode %main " << mode << "\n"; |
| } |
| if (model == "Geometry") { |
| if (!(mode.find("InputPoints") == 0 || mode.find("InputLines") == 0 || |
| mode.find("InputLinesAdjacency") == 0 || |
| mode.find("Triangles") == 0 || |
| mode.find("InputTrianglesAdjacency") == 0)) { |
| // Exactly one of the above modes is required for Geometry shaders. |
| sstr << "OpExecutionMode %main InputPoints\n"; |
| } |
| if (!(mode.find("OutputPoints") == 0 || mode.find("OutputLineStrip") == 0 || |
| mode.find("OutputTriangleStrip") == 0)) { |
| // Exactly one of the above modes is required for Geometry shaders. |
| sstr << "OpExecutionMode %main OutputPoints\n"; |
| } |
| } else if (model == "Fragment") { |
| if (!(mode.find("OriginUpperLeft") == 0 || |
| mode.find("OriginLowerLeft") == 0)) { |
| // Exactly one of the above modes is required for Fragment shaders. |
| sstr << "OpExecutionMode %main OriginUpperLeft\n"; |
| } |
| } |
| sstr << "%void = OpTypeVoid\n"; |
| sstr << "%void_fn = OpTypeFunction %void\n"; |
| sstr << "%int = OpTypeInt 32 0\n"; |
| sstr << "%int1 = OpConstant %int 1\n"; |
| sstr << "%main = OpFunction %void None %void_fn\n"; |
| sstr << "%entry = OpLabel\n"; |
| sstr << "OpReturn\n"; |
| sstr << "OpFunctionEnd\n"; |
| |
| CompileSuccessfully(sstr.str(), env); |
| EXPECT_THAT(expectation, ValidateInstructions(env)); |
| if (expectation != SPV_SUCCESS) { |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(error)); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ValidateModeGeometryOnlyGoodSpv10, ValidateModeExecution, |
| Combine(Values(SPV_SUCCESS), Values(""), Values("Geometry"), |
| Values("Invocations 3", "InputPoints", "InputLines", |
| "InputLinesAdjacency", "InputTrianglesAdjacency", |
| "OutputPoints", "OutputLineStrip", "OutputTriangleStrip"), |
| Values(SPV_ENV_UNIVERSAL_1_0))); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ValidateModeGeometryOnlyBadSpv10, ValidateModeExecution, |
| Combine(Values(SPV_ERROR_INVALID_DATA), |
| Values("Execution mode can only be used with the Geometry " |
| "execution model."), |
| Values("Fragment", "TessellationEvaluation", "TessellationControl", |
| "GLCompute", "Vertex", "Kernel"), |
| Values("Invocations 3", "InputPoints", "InputLines", |
| "InputLinesAdjacency", "InputTrianglesAdjacency", |
| "OutputPoints", "OutputLineStrip", "OutputTriangleStrip"), |
| Values(SPV_ENV_UNIVERSAL_1_0))); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ValidateModeTessellationOnlyGoodSpv10, ValidateModeExecution, |
| Combine(Values(SPV_SUCCESS), Values(""), |
| Values("TessellationControl", "TessellationEvaluation"), |
| Values("SpacingEqual", "SpacingFractionalEven", |
| "SpacingFractionalOdd", "VertexOrderCw", "VertexOrderCcw", |
| "PointMode", "Quads", "Isolines"), |
| Values(SPV_ENV_UNIVERSAL_1_0))); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ValidateModeTessellationOnlyBadSpv10, ValidateModeExecution, |
| Combine(Values(SPV_ERROR_INVALID_DATA), |
| Values("Execution mode can only be used with a tessellation " |
| "execution model."), |
| Values("Fragment", "Geometry", "GLCompute", "Vertex", "Kernel"), |
| Values("SpacingEqual", "SpacingFractionalEven", |
| "SpacingFractionalOdd", "VertexOrderCw", "VertexOrderCcw", |
| "PointMode", "Quads", "Isolines"), |
| Values(SPV_ENV_UNIVERSAL_1_0))); |
| |
| INSTANTIATE_TEST_SUITE_P(ValidateModeGeometryAndTessellationGoodSpv10, |
| ValidateModeExecution, |
| Combine(Values(SPV_SUCCESS), Values(""), |
| Values("TessellationControl", |
| "TessellationEvaluation", "Geometry"), |
| Values("Triangles", "OutputVertices 3"), |
| Values(SPV_ENV_UNIVERSAL_1_0))); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ValidateModeGeometryAndTessellationBadSpv10, ValidateModeExecution, |
| Combine(Values(SPV_ERROR_INVALID_DATA), |
| Values("Execution mode can only be used with a Geometry or " |
| "tessellation execution model."), |
| Values("Fragment", "GLCompute", "Vertex", "Kernel"), |
| Values("Triangles", "OutputVertices 3"), |
| Values(SPV_ENV_UNIVERSAL_1_0))); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ValidateModeFragmentOnlyGoodSpv10, ValidateModeExecution, |
| Combine(Values(SPV_SUCCESS), Values(""), Values("Fragment"), |
| Values("PixelCenterInteger", "OriginUpperLeft", "OriginLowerLeft", |
| "EarlyFragmentTests", "DepthReplacing", "DepthLess", |
| "DepthUnchanged"), |
| Values(SPV_ENV_UNIVERSAL_1_0))); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ValidateModeFragmentOnlyBadSpv10, ValidateModeExecution, |
| Combine(Values(SPV_ERROR_INVALID_DATA), |
| Values("Execution mode can only be used with the Fragment " |
| "execution model."), |
| Values("Geometry", "TessellationControl", "TessellationEvaluation", |
| "GLCompute", "Vertex", "Kernel"), |
| Values("PixelCenterInteger", "OriginUpperLeft", "OriginLowerLeft", |
| "EarlyFragmentTests", "DepthReplacing", "DepthGreater", |
| "DepthLess", "DepthUnchanged"), |
| Values(SPV_ENV_UNIVERSAL_1_0))); |
| |
| INSTANTIATE_TEST_SUITE_P(ValidateModeKernelOnlyGoodSpv13, ValidateModeExecution, |
| Combine(Values(SPV_SUCCESS), Values(""), |
| Values("Kernel"), |
| Values("LocalSizeHint 1 1 1", "VecTypeHint 4", |
| "ContractionOff", |
| "LocalSizeHintId %int1 %int1 %int1"), |
| Values(SPV_ENV_UNIVERSAL_1_3))); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ValidateModeKernelOnlyBadSpv13, ValidateModeExecution, |
| Combine( |
| Values(SPV_ERROR_INVALID_DATA), |
| Values( |
| "Execution mode can only be used with the Kernel execution model."), |
| Values("Geometry", "TessellationControl", "TessellationEvaluation", |
| "GLCompute", "Vertex", "Fragment"), |
| Values("LocalSizeHint 1 1 1", "VecTypeHint 4", "ContractionOff", |
| "LocalSizeHintId %int1 %int1 %int1"), |
| Values(SPV_ENV_UNIVERSAL_1_3))); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ValidateModeGLComputeAndKernelGoodSpv13, ValidateModeExecution, |
| Combine(Values(SPV_SUCCESS), Values(""), Values("Kernel", "GLCompute"), |
| Values("LocalSize 1 1 1", "LocalSizeId %int1 %int1 %int1"), |
| Values(SPV_ENV_UNIVERSAL_1_3))); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ValidateModeGLComputeAndKernelBadSpv13, ValidateModeExecution, |
| Combine(Values(SPV_ERROR_INVALID_DATA), |
| Values("Execution mode can only be used with a Kernel or GLCompute " |
| "execution model."), |
| Values("Geometry", "TessellationControl", "TessellationEvaluation", |
| "Fragment", "Vertex"), |
| Values("LocalSize 1 1 1", "LocalSizeId %int1 %int1 %int1"), |
| Values(SPV_ENV_UNIVERSAL_1_3))); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ValidateModeAllGoodSpv13, ValidateModeExecution, |
| Combine(Values(SPV_SUCCESS), Values(""), |
| Values("Kernel", "GLCompute", "Geometry", "TessellationControl", |
| "TessellationEvaluation", "Fragment", "Vertex"), |
| Values("Xfb", "Initializer", "Finalizer", "SubgroupSize 1", |
| "SubgroupsPerWorkgroup 1", "SubgroupsPerWorkgroupId %int1"), |
| Values(SPV_ENV_UNIVERSAL_1_3))); |
| |
| TEST_F(ValidateModeExecution, MeshNVLocalSize) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability MeshShadingNV |
| OpExtension "SPV_NV_mesh_shader" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint MeshNV %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateModeExecution, TaskNVLocalSize) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability MeshShadingNV |
| OpExtension "SPV_NV_mesh_shader" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint TaskNV %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateModeExecution, MeshNVOutputPoints) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability MeshShadingNV |
| OpExtension "SPV_NV_mesh_shader" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint MeshNV %main "main" |
| OpExecutionMode %main OutputPoints |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateModeExecution, MeshNVOutputVertices) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability MeshShadingNV |
| OpExtension "SPV_NV_mesh_shader" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint MeshNV %main "main" |
| OpExecutionMode %main OutputVertices 42 |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateModeExecution, MeshNVLocalSizeId) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability MeshShadingNV |
| OpExtension "SPV_NV_mesh_shader" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint MeshNV %main "main" |
| OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 |
| %int = OpTypeInt 32 0 |
| %int_1 = OpConstant %int 1 |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_UNIVERSAL_1_3; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); |
| } |
| |
| TEST_F(ValidateModeExecution, TaskNVLocalSizeId) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability MeshShadingNV |
| OpExtension "SPV_NV_mesh_shader" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint TaskNV %main "main" |
| OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 |
| %int = OpTypeInt 32 0 |
| %int_1 = OpConstant %int 1 |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_UNIVERSAL_1_3; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); |
| } |
| |
| TEST_F(ValidateModeExecution, ExecModeSubgroupsPerWorkgroupIdBad) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability SubgroupDispatch |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpExecutionMode %main SubgroupsPerWorkgroupId %int_1 |
| %int = OpTypeInt 32 0 |
| %int_1 = OpConstant %int 1 |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_UNIVERSAL_1_3; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("OpExecutionMode is only valid when the Mode operand " |
| "is an execution mode that takes no Extra Operands")); |
| } |
| |
| TEST_F(ValidateModeExecution, ExecModeIdSubgroupsPerWorkgroupIdGood) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability SubgroupDispatch |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpExecutionModeId %main SubgroupsPerWorkgroupId %int_1 |
| %int = OpTypeInt 32 0 |
| %int_1 = OpConstant %int 1 |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_UNIVERSAL_1_3; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); |
| } |
| |
| TEST_F(ValidateModeExecution, ExecModeIdSubgroupsPerWorkgroupIdNonConstantBad) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability SubgroupDispatch |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpExecutionModeId %main SubgroupsPerWorkgroupId %int_1 |
| %int = OpTypeInt 32 0 |
| %int_ptr = OpTypePointer Private %int |
| %int_1 = OpVariable %int_ptr Private |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_UNIVERSAL_1_3; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("For OpExecutionModeId all Extra Operand ids must be " |
| "constant instructions.")); |
| } |
| |
| TEST_F(ValidateModeExecution, ExecModeLocalSizeHintIdBad) { |
| const std::string spirv = R"( |
| OpCapability Kernel |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Kernel %main "main" |
| OpExecutionMode %main LocalSizeHintId %int_1 %int_1 %int_1 |
| %int = OpTypeInt 32 0 |
| %int_1 = OpConstant %int 1 |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_UNIVERSAL_1_3; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("OpExecutionMode is only valid when the Mode operand " |
| "is an execution mode that takes no Extra Operands")); |
| } |
| |
| TEST_F(ValidateModeExecution, ExecModeIdLocalSizeHintIdGood) { |
| const std::string spirv = R"( |
| OpCapability Kernel |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Kernel %main "main" |
| OpExecutionModeId %main LocalSizeHintId %int_1 %int_1 %int_1 |
| %int = OpTypeInt 32 0 |
| %int_1 = OpConstant %int 1 |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_UNIVERSAL_1_3; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); |
| } |
| |
| TEST_F(ValidateModeExecution, ExecModeIdLocalSizeHintIdNonConstantBad) { |
| const std::string spirv = R"( |
| OpCapability Kernel |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpExecutionModeId %main LocalSizeHintId %int_1 %int_1 %int_1 |
| %int = OpTypeInt 32 0 |
| %int_ptr = OpTypePointer Private %int |
| %int_1 = OpVariable %int_ptr Private |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_UNIVERSAL_1_3; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("For OpExecutionModeId all Extra Operand ids must be " |
| "constant instructions.")); |
| } |
| |
| TEST_F(ValidateModeExecution, ExecModeLocalSizeIdBad) { |
| const std::string spirv = R"( |
| OpCapability Kernel |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Kernel %main "main" |
| OpExecutionMode %main LocalSizeId %int_1 %int_1 %int_1 |
| %int = OpTypeInt 32 0 |
| %int_1 = OpConstant %int 1 |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_UNIVERSAL_1_3; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("OpExecutionMode is only valid when the Mode operand " |
| "is an execution mode that takes no Extra Operands")); |
| } |
| |
| TEST_F(ValidateModeExecution, ExecModeIdLocalSizeIdGood) { |
| const std::string spirv = R"( |
| OpCapability Kernel |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Kernel %main "main" |
| OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 |
| %int = OpTypeInt 32 0 |
| %int_1 = OpConstant %int 1 |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_UNIVERSAL_1_3; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); |
| } |
| |
| TEST_F(ValidateModeExecution, ExecModeIdLocalSizeIdNonConstantBad) { |
| const std::string spirv = R"( |
| OpCapability Kernel |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 |
| %int = OpTypeInt 32 0 |
| %int_ptr = OpTypePointer Private %int |
| %int_1 = OpVariable %int_ptr Private |
| )" + kVoidFunction; |
| |
| spv_target_env env = SPV_ENV_UNIVERSAL_1_3; |
| CompileSuccessfully(spirv, env); |
| EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("For OpExecutionModeId all Extra Operand ids must be " |
| "constant instructions.")); |
| } |
| |
| TEST_F(ValidateMode, FragmentShaderInterlockVertexBad) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability FragmentShaderPixelInterlockEXT |
| OpExtension "SPV_EXT_fragment_shader_interlock" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpExecutionMode %main PixelInterlockOrderedEXT |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Execution mode can only be used with the Fragment execution model")); |
| } |
| |
| TEST_F(ValidateMode, FragmentShaderInterlockTooManyModesBad) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability FragmentShaderPixelInterlockEXT |
| OpCapability FragmentShaderSampleInterlockEXT |
| OpExtension "SPV_EXT_fragment_shader_interlock" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpExecutionMode %main PixelInterlockOrderedEXT |
| OpExecutionMode %main SampleInterlockOrderedEXT |
| )" + kVoidFunction; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Fragment execution model entry points can specify at most " |
| "one fragment shader interlock execution mode")); |
| } |
| |
| TEST_F(ValidateMode, FragmentShaderInterlockNoModeBad) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability FragmentShaderPixelInterlockEXT |
| OpExtension "SPV_EXT_fragment_shader_interlock" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %func = OpFunction %void None %void_fn |
| %entryf = OpLabel |
| OpBeginInvocationInterlockEXT |
| OpEndInvocationInterlockEXT |
| OpReturn |
| OpFunctionEnd |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| %1 = OpFunctionCall %void %func |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "OpBeginInvocationInterlockEXT/OpEndInvocationInterlockEXT require a " |
| "fragment shader interlock execution mode")); |
| } |
| |
| TEST_F(ValidateMode, FragmentShaderInterlockGood) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability FragmentShaderPixelInterlockEXT |
| OpExtension "SPV_EXT_fragment_shader_interlock" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpExecutionMode %main PixelInterlockOrderedEXT |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %func = OpFunction %void None %void_fn |
| %entryf = OpLabel |
| OpBeginInvocationInterlockEXT |
| OpEndInvocationInterlockEXT |
| OpReturn |
| OpFunctionEnd |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| %1 = OpFunctionCall %void %func |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateMode, FragmentShaderDemoteVertexBad) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability DemoteToHelperInvocationEXT |
| OpExtension "SPV_EXT_demote_to_helper_invocation" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| %bool = OpTypeBool |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpDemoteToHelperInvocationEXT |
| %1 = OpIsHelperInvocationEXT %bool |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "OpDemoteToHelperInvocationEXT requires Fragment execution model")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("OpIsHelperInvocationEXT requires Fragment execution model")); |
| } |
| |
| TEST_F(ValidateMode, FragmentShaderDemoteGood) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability DemoteToHelperInvocationEXT |
| OpExtension "SPV_EXT_demote_to_helper_invocation" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| %bool = OpTypeBool |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpDemoteToHelperInvocationEXT |
| %1 = OpIsHelperInvocationEXT %bool |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateMode, FragmentShaderDemoteBadType) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability DemoteToHelperInvocationEXT |
| OpExtension "SPV_EXT_demote_to_helper_invocation" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| %u32 = OpTypeInt 32 0 |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpDemoteToHelperInvocationEXT |
| %1 = OpIsHelperInvocationEXT %u32 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Expected bool scalar type as Result Type")); |
| } |
| |
| } // namespace |
| } // namespace val |
| } // namespace spvtools |