| // 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 <tuple> | |
| #include "gmock/gmock.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; | |
| std::string GenerateShaderCode( | |
| const std::string& body, | |
| const std::string& capabilities_and_extensions = "", | |
| const std::string& execution_model = "GLCompute") { | |
| std::ostringstream ss; | |
| ss << R"( | |
| OpCapability Shader | |
| OpCapability GroupNonUniform | |
| OpCapability GroupNonUniformVote | |
| OpCapability GroupNonUniformBallot | |
| OpCapability GroupNonUniformShuffle | |
| OpCapability GroupNonUniformShuffleRelative | |
| OpCapability GroupNonUniformArithmetic | |
| OpCapability GroupNonUniformClustered | |
| OpCapability GroupNonUniformQuad | |
| )"; | |
| ss << capabilities_and_extensions; | |
| ss << "OpMemoryModel Logical GLSL450\n"; | |
| ss << "OpEntryPoint " << execution_model << " %main \"main\"\n"; | |
| if (execution_model == "GLCompute") { | |
| ss << "OpExecutionMode %main LocalSize 1 1 1\n"; | |
| } | |
| ss << R"( | |
| %void = OpTypeVoid | |
| %func = OpTypeFunction %void | |
| %bool = OpTypeBool | |
| %u32 = OpTypeInt 32 0 | |
| %int = OpTypeInt 32 1 | |
| %float = OpTypeFloat 32 | |
| %u32vec4 = OpTypeVector %u32 4 | |
| %u32vec3 = OpTypeVector %u32 3 | |
| %true = OpConstantTrue %bool | |
| %false = OpConstantFalse %bool | |
| %u32_0 = OpConstant %u32 0 | |
| %float_0 = OpConstant %float 0 | |
| %u32vec4_null = OpConstantComposite %u32vec4 %u32_0 %u32_0 %u32_0 %u32_0 | |
| %u32vec3_null = OpConstantComposite %u32vec3 %u32_0 %u32_0 %u32_0 | |
| %cross_device = OpConstant %u32 0 | |
| %device = OpConstant %u32 1 | |
| %workgroup = OpConstant %u32 2 | |
| %subgroup = OpConstant %u32 3 | |
| %invocation = OpConstant %u32 4 | |
| %reduce = OpConstant %u32 0 | |
| %inclusive_scan = OpConstant %u32 1 | |
| %exclusive_scan = OpConstant %u32 2 | |
| %clustered_reduce = OpConstant %u32 3 | |
| %main = OpFunction %void None %func | |
| %main_entry = OpLabel | |
| )"; | |
| ss << body; | |
| ss << R"( | |
| OpReturn | |
| OpFunctionEnd)"; | |
| return ss.str(); | |
| } | |
| spv::Scope scopes[] = {spv::Scope::CrossDevice, spv::Scope::Device, | |
| spv::Scope::Workgroup, spv::Scope::Subgroup, | |
| spv::Scope::Invocation}; | |
| using ValidateGroupNonUniform = spvtest::ValidateBase<bool>; | |
| using GroupNonUniform = spvtest::ValidateBase< | |
| std::tuple<std::string, std::string, spv::Scope, std::string, std::string>>; | |
| std::string ConvertScope(spv::Scope scope) { | |
| switch (scope) { | |
| case spv::Scope::CrossDevice: | |
| return "%cross_device"; | |
| case spv::Scope::Device: | |
| return "%device"; | |
| case spv::Scope::Workgroup: | |
| return "%workgroup"; | |
| case spv::Scope::Subgroup: | |
| return "%subgroup"; | |
| case spv::Scope::Invocation: | |
| return "%invocation"; | |
| default: | |
| return ""; | |
| } | |
| } | |
| TEST_P(GroupNonUniform, Vulkan1p1) { | |
| std::string opcode = std::get<0>(GetParam()); | |
| std::string type = std::get<1>(GetParam()); | |
| spv::Scope execution_scope = std::get<2>(GetParam()); | |
| std::string args = std::get<3>(GetParam()); | |
| std::string error = std::get<4>(GetParam()); | |
| std::ostringstream sstr; | |
| sstr << "%result = " << opcode << " "; | |
| sstr << type << " "; | |
| sstr << ConvertScope(execution_scope) << " "; | |
| sstr << args << "\n"; | |
| CompileSuccessfully(GenerateShaderCode(sstr.str()), SPV_ENV_VULKAN_1_1); | |
| spv_result_t result = ValidateInstructions(SPV_ENV_VULKAN_1_1); | |
| if (error == "") { | |
| if (execution_scope == spv::Scope::Subgroup) { | |
| EXPECT_EQ(SPV_SUCCESS, result); | |
| } else { | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, result); | |
| EXPECT_THAT(getDiagnosticString(), | |
| AnyVUID("VUID-StandaloneSpirv-None-04642")); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr( | |
| "in Vulkan environment Execution scope is limited to Subgroup")); | |
| } | |
| } else { | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, result); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(error)); | |
| } | |
| } | |
| TEST_P(GroupNonUniform, Spirv1p3) { | |
| std::string opcode = std::get<0>(GetParam()); | |
| std::string type = std::get<1>(GetParam()); | |
| spv::Scope execution_scope = std::get<2>(GetParam()); | |
| std::string args = std::get<3>(GetParam()); | |
| std::string error = std::get<4>(GetParam()); | |
| std::ostringstream sstr; | |
| sstr << "%result = " << opcode << " "; | |
| sstr << type << " "; | |
| sstr << ConvertScope(execution_scope) << " "; | |
| sstr << args << "\n"; | |
| CompileSuccessfully(GenerateShaderCode(sstr.str()), SPV_ENV_UNIVERSAL_1_3); | |
| spv_result_t result = ValidateInstructions(SPV_ENV_UNIVERSAL_1_3); | |
| if (error == "") { | |
| if (execution_scope == spv::Scope::Subgroup || | |
| execution_scope == spv::Scope::Workgroup) { | |
| EXPECT_EQ(SPV_SUCCESS, result); | |
| } else { | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, result); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr("Execution scope is limited to Subgroup or Workgroup")); | |
| } | |
| } else { | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, result); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(error)); | |
| } | |
| } | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformElect, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformElect"), | |
| Values("%bool"), ValuesIn(scopes), Values(""), | |
| Values(""))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformVote, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformAll", | |
| "OpGroupNonUniformAny", | |
| "OpGroupNonUniformAllEqual"), | |
| Values("%bool"), ValuesIn(scopes), | |
| Values("%true"), Values(""))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformBroadcast, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformBroadcast"), | |
| Values("%bool"), ValuesIn(scopes), | |
| Values("%true %u32_0"), Values(""))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformBroadcastFirst, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformBroadcastFirst"), | |
| Values("%bool"), ValuesIn(scopes), | |
| Values("%true"), Values(""))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallot, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformBallot"), | |
| Values("%u32vec4"), ValuesIn(scopes), | |
| Values("%true"), Values(""))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformInverseBallot, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformInverseBallot"), | |
| Values("%bool"), ValuesIn(scopes), | |
| Values("%u32vec4_null"), Values(""))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBitExtract, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformBallotBitExtract"), | |
| Values("%bool"), ValuesIn(scopes), | |
| Values("%u32vec4_null %u32_0"), Values(""))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBitCount, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformBallotBitCount"), | |
| Values("%u32"), ValuesIn(scopes), | |
| Values("Reduce %u32vec4_null"), Values(""))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotFind, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformBallotFindLSB", | |
| "OpGroupNonUniformBallotFindMSB"), | |
| Values("%u32"), ValuesIn(scopes), | |
| Values("%u32vec4_null"), Values(""))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformShuffle, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformShuffle", | |
| "OpGroupNonUniformShuffleXor", | |
| "OpGroupNonUniformShuffleUp", | |
| "OpGroupNonUniformShuffleDown"), | |
| Values("%u32"), ValuesIn(scopes), | |
| Values("%u32_0 %u32_0"), Values(""))); | |
| INSTANTIATE_TEST_SUITE_P( | |
| GroupNonUniformIntegerArithmetic, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul", | |
| "OpGroupNonUniformSMin", "OpGroupNonUniformUMin", | |
| "OpGroupNonUniformSMax", "OpGroupNonUniformUMax", | |
| "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr", | |
| "OpGroupNonUniformBitwiseXor"), | |
| Values("%u32"), ValuesIn(scopes), Values("Reduce %u32_0"), | |
| Values(""))); | |
| INSTANTIATE_TEST_SUITE_P( | |
| GroupNonUniformFloatArithmetic, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul", | |
| "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"), | |
| Values("%float"), ValuesIn(scopes), Values("Reduce %float_0"), | |
| Values(""))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformLogicalArithmetic, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformLogicalAnd", | |
| "OpGroupNonUniformLogicalOr", | |
| "OpGroupNonUniformLogicalXor"), | |
| Values("%bool"), ValuesIn(scopes), | |
| Values("Reduce %true"), Values(""))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformQuad, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformQuadBroadcast", | |
| "OpGroupNonUniformQuadSwap"), | |
| Values("%u32"), ValuesIn(scopes), | |
| Values("%u32_0 %u32_0"), Values(""))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBitCountScope, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformBallotBitCount"), | |
| Values("%u32"), ValuesIn(scopes), | |
| Values("Reduce %u32vec4_null"), Values(""))); | |
| INSTANTIATE_TEST_SUITE_P( | |
| GroupNonUniformBallotBitCountBadResultType, GroupNonUniform, | |
| Combine( | |
| Values("OpGroupNonUniformBallotBitCount"), Values("%float", "%int"), | |
| Values(spv::Scope::Subgroup), Values("Reduce %u32vec4_null"), | |
| Values("Expected Result Type to be an unsigned integer type scalar."))); | |
| INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBitCountBadValue, GroupNonUniform, | |
| Combine(Values("OpGroupNonUniformBallotBitCount"), | |
| Values("%u32"), Values(spv::Scope::Subgroup), | |
| Values("Reduce %u32vec3_null", "Reduce %u32_0", | |
| "Reduce %float_0"), | |
| Values("Expected Value to be a vector of four " | |
| "components of integer type scalar"))); | |
| TEST_F(ValidateGroupNonUniform, VulkanGroupNonUniformBallotBitCountOperation) { | |
| std::string test = R"( | |
| OpCapability Shader | |
| OpCapability GroupNonUniform | |
| OpCapability GroupNonUniformBallot | |
| OpCapability GroupNonUniformClustered | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint GLCompute %main "main" | |
| OpExecutionMode %main LocalSize 1 1 1 | |
| %void = OpTypeVoid | |
| %func = OpTypeFunction %void | |
| %u32 = OpTypeInt 32 0 | |
| %u32vec4 = OpTypeVector %u32 4 | |
| %u32_0 = OpConstant %u32 0 | |
| %u32vec4_null = OpConstantComposite %u32vec4 %u32_0 %u32_0 %u32_0 %u32_0 | |
| %subgroup = OpConstant %u32 3 | |
| %main = OpFunction %void None %func | |
| %main_entry = OpLabel | |
| %result = OpGroupNonUniformBallotBitCount %u32 %subgroup ClusteredReduce %u32vec4_null | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(test, SPV_ENV_VULKAN_1_1); | |
| ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| AnyVUID("VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685")); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr( | |
| "In Vulkan: The OpGroupNonUniformBallotBitCount group operation must " | |
| "be only: Reduce, InclusiveScan, or ExclusiveScan.")); | |
| } | |
| } // namespace | |
| } // namespace val | |
| } // namespace spvtools |