| // Copyright (c) 2015-2016 The Khronos Group Inc. |
| // |
| // 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 <algorithm> |
| #include <string> |
| #include <vector> |
| |
| #include "source/latest_version_glsl_std_450_header.h" |
| #include "test/unit_spirv.h" |
| |
| namespace spvtools { |
| namespace { |
| |
| /// Context for an extended instruction. |
| /// |
| /// Information about a GLSL extended instruction (including its opname, return |
| /// type, etc.) and related instructions used to generate the return type and |
| /// constant as the operands. Used in generating extended instruction tests. |
| struct ExtInstContext { |
| const char* extInstOpName; |
| const char* extInstOperandVars; |
| /// The following fields are used to check the SPIR-V binary representation |
| /// of this instruction. |
| uint32_t extInstOpcode; ///< Opcode value for this extended instruction. |
| uint32_t extInstLength; ///< Wordcount of this extended instruction. |
| std::vector<uint32_t> extInstOperandIds; ///< Ids for operands. |
| }; |
| |
| using ExtInstGLSLstd450RoundTripTest = ::testing::TestWithParam<ExtInstContext>; |
| |
| TEST_P(ExtInstGLSLstd450RoundTripTest, ParameterizedExtInst) { |
| spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_0); |
| const std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical Simple |
| OpEntryPoint Vertex %2 "main" |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %2 = OpFunction %3 None %5 |
| %6 = OpLabel |
| %8 = OpExtInst %7 %1 )" + std::string(GetParam().extInstOpName) + |
| " " + GetParam().extInstOperandVars + R"( |
| OpReturn |
| OpFunctionEnd |
| )"; |
| const std::string spirv_header = |
| R"(; SPIR-V |
| ; Version: 1.0 |
| ; Generator: Khronos SPIR-V Tools Assembler; 0 |
| ; Bound: 9 |
| ; Schema: 0)"; |
| spv_binary binary = nullptr; |
| spv_diagnostic diagnostic; |
| spv_result_t error = spvTextToBinary(context, spirv.c_str(), spirv.size(), |
| &binary, &diagnostic); |
| if (error) { |
| spvDiagnosticPrint(diagnostic); |
| spvDiagnosticDestroy(diagnostic); |
| ASSERT_EQ(SPV_SUCCESS, error) |
| << "Source was: " << std::endl |
| << spirv << std::endl |
| << "Test case for : " << GetParam().extInstOpName << std::endl; |
| } |
| |
| // Check we do have the extended instruction's corresponding binary code in |
| // the generated SPIR-V binary. |
| std::vector<uint32_t> expected_contains( |
| {12 /*OpExtInst*/ | GetParam().extInstLength << 16, 7 /*return type*/, |
| 8 /*result id*/, 1 /*glsl450 import*/, GetParam().extInstOpcode}); |
| for (uint32_t operand : GetParam().extInstOperandIds) { |
| expected_contains.push_back(operand); |
| } |
| EXPECT_NE(binary->code + binary->wordCount, |
| std::search(binary->code, binary->code + binary->wordCount, |
| expected_contains.begin(), expected_contains.end())) |
| << "Cannot find\n" |
| << spvtest::WordVector(expected_contains).str() << "in\n" |
| << spvtest::WordVector(*binary).str(); |
| |
| // Check round trip gives the same text. |
| spv_text output_text = nullptr; |
| error = spvBinaryToText(context, binary->code, binary->wordCount, |
| SPV_BINARY_TO_TEXT_OPTION_NONE, &output_text, |
| &diagnostic); |
| |
| if (error) { |
| spvDiagnosticPrint(diagnostic); |
| spvDiagnosticDestroy(diagnostic); |
| ASSERT_EQ(SPV_SUCCESS, error); |
| } |
| EXPECT_EQ(spirv_header + spirv, output_text->str); |
| spvTextDestroy(output_text); |
| spvBinaryDestroy(binary); |
| spvContextDestroy(context); |
| } |
| |
| INSTANTIATE_TEST_CASE_P( |
| ExtInstParameters, ExtInstGLSLstd450RoundTripTest, |
| ::testing::ValuesIn(std::vector<ExtInstContext>({ |
| // We are only testing the correctness of encoding and decoding here. |
| // Semantic correctness should be the responsibility of validator. So |
| // some of the instructions below have incorrect operand and/or return |
| // types, e.g, Modf, ModfStruct, etc. |
| {"Round", "%5", 1, 6, {5}}, |
| {"RoundEven", "%5", 2, 6, {5}}, |
| {"Trunc", "%5", 3, 6, {5}}, |
| {"FAbs", "%5", 4, 6, {5}}, |
| {"SAbs", "%5", 5, 6, {5}}, |
| {"FSign", "%5", 6, 6, {5}}, |
| {"SSign", "%5", 7, 6, {5}}, |
| {"Floor", "%5", 8, 6, {5}}, |
| {"Ceil", "%5", 9, 6, {5}}, |
| {"Fract", "%5", 10, 6, {5}}, |
| {"Radians", "%5", 11, 6, {5}}, |
| {"Degrees", "%5", 12, 6, {5}}, |
| {"Sin", "%5", 13, 6, {5}}, |
| {"Cos", "%5", 14, 6, {5}}, |
| {"Tan", "%5", 15, 6, {5}}, |
| {"Asin", "%5", 16, 6, {5}}, |
| {"Acos", "%5", 17, 6, {5}}, |
| {"Atan", "%5", 18, 6, {5}}, |
| {"Sinh", "%5", 19, 6, {5}}, |
| {"Cosh", "%5", 20, 6, {5}}, |
| {"Tanh", "%5", 21, 6, {5}}, |
| {"Asinh", "%5", 22, 6, {5}}, |
| {"Acosh", "%5", 23, 6, {5}}, |
| {"Atanh", "%5", 24, 6, {5}}, |
| {"Atan2", "%5 %5", 25, 7, {5, 5}}, |
| {"Pow", "%5 %5", 26, 7, {5, 5}}, |
| {"Exp", "%5", 27, 6, {5}}, |
| {"Log", "%5", 28, 6, {5}}, |
| {"Exp2", "%5", 29, 6, {5}}, |
| {"Log2", "%5", 30, 6, {5}}, |
| {"Sqrt", "%5", 31, 6, {5}}, |
| {"InverseSqrt", "%5", 32, 6, {5}}, |
| {"Determinant", "%5", 33, 6, {5}}, |
| {"MatrixInverse", "%5", 34, 6, {5}}, |
| {"Modf", "%5 %5", 35, 7, {5, 5}}, |
| {"ModfStruct", "%5", 36, 6, {5}}, |
| {"FMin", "%5 %5", 37, 7, {5, 5}}, |
| {"UMin", "%5 %5", 38, 7, {5, 5}}, |
| {"SMin", "%5 %5", 39, 7, {5, 5}}, |
| {"FMax", "%5 %5", 40, 7, {5, 5}}, |
| {"UMax", "%5 %5", 41, 7, {5, 5}}, |
| {"SMax", "%5 %5", 42, 7, {5, 5}}, |
| {"FClamp", "%5 %5 %5", 43, 8, {5, 5, 5}}, |
| {"UClamp", "%5 %5 %5", 44, 8, {5, 5, 5}}, |
| {"SClamp", "%5 %5 %5", 45, 8, {5, 5, 5}}, |
| {"FMix", "%5 %5 %5", 46, 8, {5, 5, 5}}, |
| {"IMix", "%5 %5 %5", 47, 8, {5, 5, 5}}, // Bug 15452. Reserved. |
| {"Step", "%5 %5", 48, 7, {5, 5}}, |
| {"SmoothStep", "%5 %5 %5", 49, 8, {5, 5, 5}}, |
| {"Fma", "%5 %5 %5", 50, 8, {5, 5, 5}}, |
| {"Frexp", "%5 %5", 51, 7, {5, 5}}, |
| {"FrexpStruct", "%5", 52, 6, {5}}, |
| {"Ldexp", "%5 %5", 53, 7, {5, 5}}, |
| {"PackSnorm4x8", "%5", 54, 6, {5}}, |
| {"PackUnorm4x8", "%5", 55, 6, {5}}, |
| {"PackSnorm2x16", "%5", 56, 6, {5}}, |
| {"PackUnorm2x16", "%5", 57, 6, {5}}, |
| {"PackHalf2x16", "%5", 58, 6, {5}}, |
| {"PackDouble2x32", "%5", 59, 6, {5}}, |
| {"UnpackSnorm2x16", "%5", 60, 6, {5}}, |
| {"UnpackUnorm2x16", "%5", 61, 6, {5}}, |
| {"UnpackHalf2x16", "%5", 62, 6, {5}}, |
| {"UnpackSnorm4x8", "%5", 63, 6, {5}}, |
| {"UnpackUnorm4x8", "%5", 64, 6, {5}}, |
| {"UnpackDouble2x32", "%5", 65, 6, {5}}, |
| {"Length", "%5", 66, 6, {5}}, |
| {"Distance", "%5 %5", 67, 7, {5, 5}}, |
| {"Cross", "%5 %5", 68, 7, {5, 5}}, |
| {"Normalize", "%5", 69, 6, {5}}, |
| // clang-format off |
| {"FaceForward", "%5 %5 %5", 70, 8, {5, 5, 5}}, |
| // clang-format on |
| {"Reflect", "%5 %5", 71, 7, {5, 5}}, |
| {"Refract", "%5 %5 %5", 72, 8, {5, 5, 5}}, |
| {"FindILsb", "%5", 73, 6, {5}}, |
| {"FindSMsb", "%5", 74, 6, {5}}, |
| {"FindUMsb", "%5", 75, 6, {5}}, |
| {"InterpolateAtCentroid", "%5", 76, 6, {5}}, |
| // clang-format off |
| {"InterpolateAtSample", "%5 %5", 77, 7, {5, 5}}, |
| {"InterpolateAtOffset", "%5 %5", 78, 7, {5, 5}}, |
| // clang-format on |
| {"NMin", "%5 %5", 79, 7, {5, 5}}, |
| {"NMax", "%5 %5", 80, 7, {5, 5}}, |
| {"NClamp", "%5 %5 %5", 81, 8, {5, 5, 5}}, |
| })), ); |
| |
| } // namespace |
| } // namespace spvtools |