blob: c5b1a378bac68b65f303616173776db663d00e0d [file] [log] [blame]
// 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(),
HasSubstr("In the Vulkan environment, GLCompute execution model entry "
"points require either the LocalSize 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, 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(),
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(),
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";
if (!spvIsWebGPUEnv(env)) {
sstr << "OpCapability Geometry\n";
sstr << "OpCapability Tessellation\n";
sstr << "OpCapability TransformFeedback\n";
}
if (!spvIsVulkanOrWebGPUEnv(env)) {
sstr << "OpCapability Kernel\n";
if (env == SPV_ENV_UNIVERSAL_1_3) {
sstr << "OpCapability SubgroupDispatch\n";
}
}
if (spvIsWebGPUEnv(env)) {
sstr << "OpCapability VulkanMemoryModelKHR\n";
sstr << "OpExtension \"SPV_KHR_vulkan_memory_model\"\n";
sstr << "OpMemoryModel Logical VulkanKHR\n";
} else {
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"),
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"),
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)));
INSTANTIATE_TEST_SUITE_P(ValidateModeGLComputeWebGPUWhitelistGood,
ValidateModeExecution,
Combine(Values(SPV_SUCCESS), Values(""),
Values("GLCompute"), Values("LocalSize 1 1 1"),
Values(SPV_ENV_WEBGPU_0)));
INSTANTIATE_TEST_SUITE_P(
ValidateModeGLComputeWebGPUWhitelistBad, ValidateModeExecution,
Combine(Values(SPV_ERROR_INVALID_DATA),
Values("Execution mode must be one of OriginUpperLeft, "
"DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, "
"LocalSize, or LocalSizeHint for WebGPU environment"),
Values("GLCompute"), Values("LocalSizeId %int1 %int1 %int1"),
Values(SPV_ENV_WEBGPU_0)));
INSTANTIATE_TEST_SUITE_P(
ValidateModeFragmentWebGPUWhitelistGood, ValidateModeExecution,
Combine(Values(SPV_SUCCESS), Values(""), Values("Fragment"),
Values("OriginUpperLeft", "DepthReplacing", "DepthGreater",
"DepthLess", "DepthUnchanged"),
Values(SPV_ENV_WEBGPU_0)));
INSTANTIATE_TEST_SUITE_P(
ValidateModeFragmentWebGPUWhitelistBad, ValidateModeExecution,
Combine(Values(SPV_ERROR_INVALID_DATA),
Values("Execution mode must be one of OriginUpperLeft, "
"DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, "
"LocalSize, or LocalSizeHint for WebGPU environment"),
Values("Fragment"),
Values("PixelCenterInteger", "OriginLowerLeft",
"EarlyFragmentTests"),
Values(SPV_ENV_WEBGPU_0)));
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 = 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 = 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 = 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());
}
} // namespace
} // namespace val
} // namespace spvtools