| // Copyright (c) 2017 Google 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. |
| |
| // Validation tests for decorations |
| |
| #include <string> |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "source/val/decoration.h" |
| #include "test/test_fixture.h" |
| #include "test/unit_spirv.h" |
| #include "test/val/val_code_generator.h" |
| #include "test/val/val_fixtures.h" |
| |
| namespace spvtools { |
| namespace val { |
| namespace { |
| |
| using ::testing::Combine; |
| using ::testing::Eq; |
| using ::testing::HasSubstr; |
| using ::testing::Values; |
| |
| struct TestResult { |
| TestResult(spv_result_t in_validation_result = SPV_SUCCESS, |
| const std::string& in_error_str = "") |
| : validation_result(in_validation_result), error_str(in_error_str) {} |
| spv_result_t validation_result; |
| const std::string error_str; |
| }; |
| |
| using ValidateDecorations = spvtest::ValidateBase<bool>; |
| using ValidateDecorationString = spvtest::ValidateBase<std::string>; |
| using ValidateVulkanCombineDecorationResult = |
| spvtest::ValidateBase<std::tuple<const char*, const char*, TestResult>>; |
| |
| TEST_F(ValidateDecorations, ValidateOpDecorateRegistration) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %1 Location 4 |
| OpDecorate %1 Centroid |
| %2 = OpTypeFloat 32 |
| %3 = OpTypePointer Output %2 |
| %1 = OpVariable %3 Output |
| ; Since %1 is used first in Decoration, it gets id 1. |
| )"; |
| const uint32_t id = 1; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| // Must have 2 decorations. |
| EXPECT_THAT( |
| vstate_->id_decorations(id), |
| Eq(std::set<Decoration>{Decoration(spv::Decoration::Location, {4}), |
| Decoration(spv::Decoration::Centroid)})); |
| } |
| |
| TEST_F(ValidateDecorations, ValidateOpMemberDecorateRegistration) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %_arr_double_uint_6 ArrayStride 4 |
| OpMemberDecorate %_struct_115 2 NonReadable |
| OpMemberDecorate %_struct_115 2 Offset 2 |
| OpDecorate %_struct_115 BufferBlock |
| %float = OpTypeFloat 32 |
| %uint = OpTypeInt 32 0 |
| %uint_6 = OpConstant %uint 6 |
| %_arr_double_uint_6 = OpTypeArray %float %uint_6 |
| %_struct_115 = OpTypeStruct %float %float %_arr_double_uint_6 |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| |
| // The array must have 1 decoration. |
| const uint32_t arr_id = 1; |
| EXPECT_THAT( |
| vstate_->id_decorations(arr_id), |
| Eq(std::set<Decoration>{Decoration(spv::Decoration::ArrayStride, {4})})); |
| |
| // The struct must have 3 decorations. |
| const uint32_t struct_id = 2; |
| EXPECT_THAT( |
| vstate_->id_decorations(struct_id), |
| Eq(std::set<Decoration>{Decoration(spv::Decoration::NonReadable, {}, 2), |
| Decoration(spv::Decoration::Offset, {2}, 2), |
| Decoration(spv::Decoration::BufferBlock)})); |
| } |
| |
| TEST_F(ValidateDecorations, ValidateOpMemberDecorateOutOfBound) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "Main" |
| OpExecutionMode %1 OriginUpperLeft |
| OpMemberDecorate %_struct_2 1 RelaxedPrecision |
| %void = OpTypeVoid |
| %4 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %_struct_2 = OpTypeStruct %float |
| %1 = OpFunction %void None %4 |
| %6 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Index 1 provided in OpMemberDecorate for struct <id> " |
| "'2[%_struct_2]' is out of bounds. The structure has 1 " |
| "members. Largest valid index is 0.")); |
| } |
| |
| TEST_F(ValidateDecorations, ValidateGroupDecorateRegistration) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %1 DescriptorSet 0 |
| OpDecorate %1 RelaxedPrecision |
| OpDecorate %1 Restrict |
| %1 = OpDecorationGroup |
| OpGroupDecorate %1 %2 %3 |
| OpGroupDecorate %1 %4 |
| %float = OpTypeFloat 32 |
| %_runtimearr_float = OpTypeRuntimeArray %float |
| %_struct_9 = OpTypeStruct %_runtimearr_float |
| %_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9 |
| %2 = OpVariable %_ptr_Uniform__struct_9 Uniform |
| %_struct_10 = OpTypeStruct %_runtimearr_float |
| %_ptr_Uniform__struct_10 = OpTypePointer Uniform %_struct_10 |
| %3 = OpVariable %_ptr_Uniform__struct_10 Uniform |
| %_struct_11 = OpTypeStruct %_runtimearr_float |
| %_ptr_Uniform__struct_11 = OpTypePointer Uniform %_struct_11 |
| %4 = OpVariable %_ptr_Uniform__struct_11 Uniform |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| |
| // Decoration group has 3 decorations. |
| auto expected_decorations = |
| std::set<Decoration>{Decoration(spv::Decoration::DescriptorSet, {0}), |
| Decoration(spv::Decoration::RelaxedPrecision), |
| Decoration(spv::Decoration::Restrict)}; |
| |
| // Decoration group is applied to id 1, 2, 3, and 4. Note that id 1 (which is |
| // the decoration group id) also has all the decorations. |
| EXPECT_THAT(vstate_->id_decorations(1), Eq(expected_decorations)); |
| EXPECT_THAT(vstate_->id_decorations(2), Eq(expected_decorations)); |
| EXPECT_THAT(vstate_->id_decorations(3), Eq(expected_decorations)); |
| EXPECT_THAT(vstate_->id_decorations(4), Eq(expected_decorations)); |
| } |
| |
| TEST_F(ValidateDecorations, ValidateGroupMemberDecorateRegistration) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %1 Offset 3 |
| %1 = OpDecorationGroup |
| OpGroupMemberDecorate %1 %_struct_1 3 %_struct_2 3 %_struct_3 3 |
| %float = OpTypeFloat 32 |
| %_runtimearr = OpTypeRuntimeArray %float |
| %_struct_1 = OpTypeStruct %float %float %float %_runtimearr |
| %_struct_2 = OpTypeStruct %float %float %float %_runtimearr |
| %_struct_3 = OpTypeStruct %float %float %float %_runtimearr |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| // Decoration group has 1 decoration. |
| auto expected_decorations = |
| std::set<Decoration>{Decoration(spv::Decoration::Offset, {3}, 3)}; |
| |
| // Decoration group is applied to id 2, 3, and 4. |
| EXPECT_THAT(vstate_->id_decorations(2), Eq(expected_decorations)); |
| EXPECT_THAT(vstate_->id_decorations(3), Eq(expected_decorations)); |
| EXPECT_THAT(vstate_->id_decorations(4), Eq(expected_decorations)); |
| } |
| |
| TEST_F(ValidateDecorations, LinkageImportUsedForInitializedVariableBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %target LinkageAttributes "link_ptr" Import |
| %float = OpTypeFloat 32 |
| %_ptr_float = OpTypePointer Uniform %float |
| %zero = OpConstantNull %float |
| %target = OpVariable %_ptr_float Uniform %zero |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("A module-scope OpVariable with initialization value " |
| "cannot be marked with the Import Linkage Type.")); |
| } |
| TEST_F(ValidateDecorations, LinkageExportUsedForInitializedVariableGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %target LinkageAttributes "link_ptr" Export |
| %float = OpTypeFloat 32 |
| %_ptr_float = OpTypePointer Uniform %float |
| %zero = OpConstantNull %float |
| %target = OpVariable %_ptr_float Uniform %zero |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, StructAllMembersHaveBuiltInDecorationsGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %_struct_1 Block |
| OpMemberDecorate %_struct_1 0 BuiltIn Position |
| OpMemberDecorate %_struct_1 1 BuiltIn Position |
| OpMemberDecorate %_struct_1 2 BuiltIn Position |
| OpMemberDecorate %_struct_1 3 BuiltIn Position |
| %float = OpTypeFloat 32 |
| %_runtimearr = OpTypeRuntimeArray %float |
| %_struct_1 = OpTypeStruct %float %float %float %_runtimearr |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, MixedBuiltInDecorationsBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %_struct_1 Block |
| OpMemberDecorate %_struct_1 0 BuiltIn Position |
| OpMemberDecorate %_struct_1 1 BuiltIn Position |
| %float = OpTypeFloat 32 |
| %_runtimearr = OpTypeRuntimeArray %float |
| %_struct_1 = OpTypeStruct %float %float %float %_runtimearr |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("When BuiltIn decoration is applied to a structure-type " |
| "member, all members of that structure type must also be " |
| "decorated with BuiltIn (No allowed mixing of built-in " |
| "variables and non-built-in variables within a single " |
| "structure). Structure id 1 does not meet this requirement.")); |
| } |
| |
| TEST_F(ValidateDecorations, StructContainsBuiltInStructBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %_struct_1 Block |
| OpMemberDecorate %_struct_1 0 BuiltIn Position |
| OpMemberDecorate %_struct_1 1 BuiltIn Position |
| OpMemberDecorate %_struct_1 2 BuiltIn Position |
| OpMemberDecorate %_struct_1 3 BuiltIn Position |
| %float = OpTypeFloat 32 |
| %_runtimearr = OpTypeRuntimeArray %float |
| %_struct_1 = OpTypeStruct %float %float %float %_runtimearr |
| %_struct_2 = OpTypeStruct %_struct_1 |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Structure <id> '1[%_struct_1]' contains members with " |
| "BuiltIn decoration. Therefore this structure may not " |
| "be contained as a member of another structure type. " |
| "Structure <id> '4[%_struct_4]' contains structure <id> " |
| "'1[%_struct_1]'.")); |
| } |
| |
| TEST_F(ValidateDecorations, StructContainsNonBuiltInStructGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| %float = OpTypeFloat 32 |
| %_struct_1 = OpTypeStruct %float |
| %_struct_2 = OpTypeStruct %_struct_1 |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, MultipleBuiltInObjectsConsumedByOpEntryPointBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Geometry |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Geometry %main "main" %in_1 %in_2 |
| OpExecutionMode %main InputPoints |
| OpExecutionMode %main OutputPoints |
| OpDecorate %struct_1 Block |
| OpDecorate %struct_2 Block |
| OpMemberDecorate %struct_1 0 BuiltIn InvocationId |
| OpMemberDecorate %struct_2 0 BuiltIn Position |
| %int = OpTypeInt 32 1 |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct_1 = OpTypeStruct %int |
| %struct_2 = OpTypeStruct %float |
| %ptr_builtin_1 = OpTypePointer Input %struct_1 |
| %ptr_builtin_2 = OpTypePointer Input %struct_2 |
| %in_1 = OpVariable %ptr_builtin_1 Input |
| %in_2 = OpVariable %ptr_builtin_2 Input |
| %main = OpFunction %void None %func |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("There must be at most one object per Storage Class " |
| "that can contain a structure type containing members " |
| "decorated with BuiltIn, consumed per entry-point.")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| OneBuiltInObjectPerStorageClassConsumedByOpEntryPointGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Geometry |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Geometry %main "main" %in_1 %out_1 |
| OpExecutionMode %main InputPoints |
| OpExecutionMode %main OutputPoints |
| OpDecorate %struct_1 Block |
| OpDecorate %struct_2 Block |
| OpMemberDecorate %struct_1 0 BuiltIn InvocationId |
| OpMemberDecorate %struct_2 0 BuiltIn Position |
| %int = OpTypeInt 32 1 |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct_1 = OpTypeStruct %int |
| %struct_2 = OpTypeStruct %float |
| %ptr_builtin_1 = OpTypePointer Input %struct_1 |
| %ptr_builtin_2 = OpTypePointer Output %struct_2 |
| %in_1 = OpVariable %ptr_builtin_1 Input |
| %out_1 = OpVariable %ptr_builtin_2 Output |
| %main = OpFunction %void None %func |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, NoBuiltInObjectsConsumedByOpEntryPointGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Geometry |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Geometry %main "main" %in_1 %out_1 |
| OpExecutionMode %main InputPoints |
| OpExecutionMode %main OutputPoints |
| %int = OpTypeInt 32 1 |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct_1 = OpTypeStruct %int |
| %struct_2 = OpTypeStruct %float |
| %ptr_builtin_1 = OpTypePointer Input %struct_1 |
| %ptr_builtin_2 = OpTypePointer Output %struct_2 |
| %in_1 = OpVariable %ptr_builtin_1 Input |
| %out_1 = OpVariable %ptr_builtin_2 Output |
| %main = OpFunction %void None %func |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, EntryPointFunctionHasLinkageAttributeBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpDecorate %main LinkageAttributes "import_main" Import |
| %1 = OpTypeVoid |
| %2 = OpTypeFunction %1 |
| %main = OpFunction %1 None %2 |
| %4 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv.c_str()); |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("The LinkageAttributes Decoration (Linkage name: import_main) " |
| "cannot be applied to function id 1 because it is targeted by " |
| "an OpEntryPoint instruction.")); |
| } |
| |
| TEST_F(ValidateDecorations, FunctionDeclarationWithoutImportLinkageBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Function declaration (id 3) must have a LinkageAttributes " |
| "decoration with the Import Linkage type.")); |
| } |
| |
| TEST_F(ValidateDecorations, FunctionDeclarationWithImportLinkageGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %main LinkageAttributes "link_fn" Import |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, FunctionDeclarationWithExportLinkageBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %main LinkageAttributes "link_fn" Export |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Function declaration (id 1) must have a LinkageAttributes " |
| "decoration with the Import Linkage type.")); |
| } |
| |
| TEST_F(ValidateDecorations, FunctionDefinitionWithImportLinkageBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %main LinkageAttributes "link_fn" Import |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Function definition (id 1) may not be decorated with " |
| "Import Linkage type.")); |
| } |
| |
| TEST_F(ValidateDecorations, FunctionDefinitionWithoutImportLinkageGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, BuiltinVariablesGoodVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %gl_FragCoord %_entryPointOutput |
| OpExecutionMode %main OriginUpperLeft |
| OpSource HLSL 500 |
| OpDecorate %gl_FragCoord BuiltIn FragCoord |
| OpDecorate %_entryPointOutput Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %float_0 = OpConstant %float 0 |
| %14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 |
| %_ptr_Input_v4float = OpTypePointer Input %v4float |
| %gl_FragCoord = OpVariable %_ptr_Input_v4float Input |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %_entryPointOutput = OpVariable %_ptr_Output_v4float Output |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpStore %_entryPointOutput %14 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| } |
| |
| TEST_F(ValidateDecorations, BuiltinVariablesWithLocationDecorationVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %gl_FragCoord %_entryPointOutput |
| OpExecutionMode %main OriginUpperLeft |
| OpSource HLSL 500 |
| OpDecorate %gl_FragCoord BuiltIn FragCoord |
| OpDecorate %gl_FragCoord Location 0 |
| OpDecorate %_entryPointOutput Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %float_0 = OpConstant %float 0 |
| %14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 |
| %_ptr_Input_v4float = OpTypePointer Input %v4float |
| %gl_FragCoord = OpVariable %_ptr_Input_v4float Input |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %_entryPointOutput = OpVariable %_ptr_Output_v4float Output |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpStore %_entryPointOutput %14 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Location-04915")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("A BuiltIn variable (id 2) cannot have any Location or " |
| "Component decorations")); |
| } |
| TEST_F(ValidateDecorations, BuiltinVariablesWithComponentDecorationVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %gl_FragCoord %_entryPointOutput |
| OpExecutionMode %main OriginUpperLeft |
| OpSource HLSL 500 |
| OpDecorate %gl_FragCoord BuiltIn FragCoord |
| OpDecorate %gl_FragCoord Component 0 |
| OpDecorate %_entryPointOutput Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %float_0 = OpConstant %float 0 |
| %14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 |
| %_ptr_Input_v4float = OpTypePointer Input %v4float |
| %gl_FragCoord = OpVariable %_ptr_Input_v4float Input |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %_entryPointOutput = OpVariable %_ptr_Output_v4float Output |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpStore %_entryPointOutput %14 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Location-04915")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("A BuiltIn variable (id 2) cannot have any Location or " |
| "Component decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, LocationDecorationOnNumericTypeBad) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %fragCoord |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %fragCoord Location 0 |
| OpDecorate %v4float Location 1 |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %ptr_v4float = OpTypePointer Output %v4float |
| %fragCoord = OpVariable %ptr_v4float Output |
| %non_interface = OpVariable %ptr_v4float Output |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Location decoration on target <id> '3[%v4float]' must " |
| "be a variable")); |
| } |
| |
| TEST_F(ValidateDecorations, LocationDecorationOnStructBad) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %fragCoord |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %fragCoord Location 0 |
| OpDecorate %struct Location 1 |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %v4float = OpTypeVector %float 4 |
| %ptr_v4float = OpTypePointer Output %v4float |
| %fragCoord = OpVariable %ptr_v4float Output |
| %non_interface = OpVariable %ptr_v4float Output |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Location decoration on target <id> '3[%_struct_3]' " |
| "must be a variable")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| LocationDecorationUnusedNonInterfaceVariableVulkan_Ignored) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %fragCoord |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %fragCoord Location 0 |
| OpDecorate %non_interface Location 1 |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %ptr_v4float = OpTypePointer Output %v4float |
| %fragCoord = OpVariable %ptr_v4float Output |
| %non_interface = OpVariable %ptr_v4float Output |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| EXPECT_EQ(getDiagnosticString(), ""); |
| } |
| |
| TEST_F(ValidateDecorations, |
| LocationDecorationNonInterfaceStructVulkan_Ignored) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %fragCoord |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %fragCoord Location 0 |
| OpMemberDecorate %block 0 Location 2 |
| OpMemberDecorate %block 0 Component 1 |
| OpDecorate %block Block |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %vec3 = OpTypeVector %float 3 |
| %outvar_ptr = OpTypePointer Output %vec3 |
| %fragCoord = OpVariable %outvar_ptr Output |
| %block = OpTypeStruct %vec3 |
| %invar_ptr = OpTypePointer Input %block |
| %non_interface = OpVariable %invar_ptr Input |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| EXPECT_EQ(getDiagnosticString(), ""); |
| } |
| |
| TEST_F(ValidateDecorations, LocationDecorationNonInterfaceStructVulkanGood) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %fragCoord %interface |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %fragCoord Location 0 |
| OpMemberDecorate %block 0 Location 2 |
| OpMemberDecorate %block 0 Component 1 |
| OpDecorate %block Block |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %vec3 = OpTypeVector %float 3 |
| %outvar_ptr = OpTypePointer Output %vec3 |
| %fragCoord = OpVariable %outvar_ptr Output |
| %block = OpTypeStruct %vec3 |
| %invar_ptr = OpTypePointer Input %block |
| %interface = OpVariable %invar_ptr Input ;; this variable is unused. Ignore it |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| } |
| |
| TEST_F(ValidateDecorations, LocationDecorationVariableNonStructVulkanBad) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %fragCoord %nonblock_var |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %fragCoord Location 0 |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %ptr_v4float = OpTypePointer Output %v4float |
| %fragCoord = OpVariable %ptr_v4float Output |
| %nonblock_var = OpVariable %ptr_v4float Output |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Location-04916")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Variable must be decorated with a location")); |
| } |
| |
| TEST_F(ValidateDecorations, LocationDecorationVariableStructNoBlockVulkanBad) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %fragCoord %block_var |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %fragCoord Location 0 |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %ptr_v4float = OpTypePointer Output %v4float |
| %fragCoord = OpVariable %ptr_v4float Output |
| %block = OpTypeStruct %v4float |
| %block_ptr = OpTypePointer Output %block |
| %block_var = OpVariable %block_ptr Output |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Location-04917")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Variable must be decorated with a location")); |
| } |
| |
| TEST_F(ValidateDecorations, LocationDecorationVariableNoBlockVulkanGood) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %fragCoord %block_var |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %fragCoord Location 0 |
| OpDecorate %block_var Location 1 |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %ptr_v4float = OpTypePointer Output %v4float |
| %fragCoord = OpVariable %ptr_v4float Output |
| %block = OpTypeStruct %v4float |
| %block_ptr = OpTypePointer Output %block |
| %block_var = OpVariable %block_ptr Output |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, LocationDecorationVariableExtraMemeberVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %fragCoord %block_var |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %fragCoord Location 0 |
| OpDecorate %block Block |
| OpDecorate %block_var Location 1 |
| OpMemberDecorate %block 0 Location 1 |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %ptr_v4float = OpTypePointer Output %v4float |
| %fragCoord = OpVariable %ptr_v4float Output |
| %block = OpTypeStruct %v4float |
| %block_ptr = OpTypePointer Output %block |
| %block_var = OpVariable %block_ptr Output |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Location-04918")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Members cannot be assigned a location")); |
| } |
| |
| TEST_F(ValidateDecorations, LocationDecorationVariableMissingMemeberVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %fragCoord %block_var |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %fragCoord Location 0 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Location 1 |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %ptr_v4float = OpTypePointer Output %v4float |
| %fragCoord = OpVariable %ptr_v4float Output |
| %block = OpTypeStruct %v4float %v4float |
| %block_ptr = OpTypePointer Output %block |
| %block_var = OpVariable %block_ptr Output |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Location-04919")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Member index 1 is missing a location assignment")); |
| } |
| |
| TEST_F(ValidateDecorations, LocationDecorationVariableOnlyMemeberVulkanGood) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %fragCoord %block_var |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %fragCoord Location 0 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Location 1 |
| OpMemberDecorate %block 1 Location 4 |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %ptr_v4float = OpTypePointer Output %v4float |
| %fragCoord = OpVariable %ptr_v4float Output |
| %block = OpTypeStruct %v4float %v4float |
| %block_ptr = OpTypePointer Output %block |
| %block_var = OpVariable %block_ptr Output |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| // #version 440 |
| // #extension GL_EXT_nonuniform_qualifier : enable |
| // layout(binding = 1) uniform sampler2D s2d[]; |
| // layout(location = 0) in nonuniformEXT int i; |
| // void main() |
| // { |
| // vec4 v = texture(s2d[i], vec2(0.3)); |
| // } |
| TEST_F(ValidateDecorations, RuntimeArrayOfDescriptorSetsIsAllowed) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability ShaderNonUniformEXT |
| OpCapability RuntimeDescriptorArrayEXT |
| OpCapability SampledImageArrayNonUniformIndexingEXT |
| OpExtension "SPV_EXT_descriptor_indexing" |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" %i |
| OpSource GLSL 440 |
| OpSourceExtension "GL_EXT_nonuniform_qualifier" |
| OpName %main "main" |
| OpName %v "v" |
| OpName %s2d "s2d" |
| OpName %i "i" |
| OpDecorate %s2d DescriptorSet 0 |
| OpDecorate %s2d Binding 1 |
| OpDecorate %i Location 0 |
| OpDecorate %i NonUniformEXT |
| OpDecorate %18 NonUniformEXT |
| OpDecorate %21 NonUniformEXT |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %_ptr_Function_v4float = OpTypePointer Function %v4float |
| %10 = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %11 = OpTypeSampledImage %10 |
| %_runtimearr_11 = OpTypeRuntimeArray %11 |
| %_ptr_Uniform__runtimearr_11 = OpTypePointer Uniform %_runtimearr_11 |
| %s2d = OpVariable %_ptr_Uniform__runtimearr_11 Uniform |
| %int = OpTypeInt 32 1 |
| %_ptr_Input_int = OpTypePointer Input %int |
| %i = OpVariable %_ptr_Input_int Input |
| %_ptr_Uniform_11 = OpTypePointer Uniform %11 |
| %v2float = OpTypeVector %float 2 |
| %float_0_300000012 = OpConstant %float 0.300000012 |
| %24 = OpConstantComposite %v2float %float_0_300000012 %float_0_300000012 |
| %float_0 = OpConstant %float 0 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %v = OpVariable %_ptr_Function_v4float Function |
| %18 = OpLoad %int %i |
| %20 = OpAccessChain %_ptr_Uniform_11 %s2d %18 |
| %21 = OpLoad %11 %20 |
| %26 = OpImageSampleExplicitLod %v4float %21 %24 Lod %float_0 |
| OpStore %v %26 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, BlockDecoratingArrayBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %int = OpTypeInt 32 1 |
| %int_3 = OpConstant %int 3 |
| %Output = OpTypeArray %float %int_3 |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockDecoratingIntBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %Output = OpTypeInt 32 1 |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockMissingOffsetBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %Output = OpTypeStruct %float |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with Offset decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockMissingOffsetBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output BufferBlock |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %Output = OpTypeStruct %float |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with Offset decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockNestedStructMissingOffsetBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 16 |
| OpMemberDecorate %Output 2 Offset 32 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %v3float = OpTypeVector %float 3 |
| %int = OpTypeInt 32 1 |
| %S = OpTypeStruct %v3float %int |
| %Output = OpTypeStruct %float %v4float %S |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with Offset decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockNestedStructMissingOffsetBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 16 |
| OpMemberDecorate %Output 2 Offset 32 |
| OpDecorate %Output BufferBlock |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %v3float = OpTypeVector %float 3 |
| %int = OpTypeInt 32 1 |
| %S = OpTypeStruct %v3float %int |
| %Output = OpTypeStruct %float %v4float %S |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with Offset decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockGLSLSharedBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output Block |
| OpDecorate %Output GLSLShared |
| OpMemberDecorate %Output 0 Offset 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %Output = OpTypeStruct %float |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "'GLSLShared' is not valid for the Vulkan execution environment")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockGLSLSharedBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output BufferBlock |
| OpDecorate %Output GLSLShared |
| OpMemberDecorate %Output 0 Offset 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %Output = OpTypeStruct %float |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "'GLSLShared' is not valid for the Vulkan execution environment")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockNestedStructGLSLSharedBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %S 0 Offset 0 |
| OpDecorate %S GLSLShared |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 16 |
| OpMemberDecorate %Output 2 Offset 32 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %int = OpTypeInt 32 1 |
| %S = OpTypeStruct %int |
| %Output = OpTypeStruct %float %v4float %S |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "'GLSLShared' is not valid for the Vulkan execution environment")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockNestedStructGLSLSharedBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %S 0 Offset 0 |
| OpDecorate %S GLSLShared |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 16 |
| OpMemberDecorate %Output 2 Offset 32 |
| OpDecorate %Output BufferBlock |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %int = OpTypeInt 32 1 |
| %S = OpTypeStruct %int |
| %Output = OpTypeStruct %float %v4float %S |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "'GLSLShared' is not valid for the Vulkan execution environment")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockGLSLPackedBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output Block |
| OpDecorate %Output GLSLPacked |
| OpMemberDecorate %Output 0 Offset 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %Output = OpTypeStruct %float |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "'GLSLPacked' is not valid for the Vulkan execution environment")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockGLSLPackedBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output BufferBlock |
| OpDecorate %Output GLSLPacked |
| OpMemberDecorate %Output 0 Offset 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %Output = OpTypeStruct %float |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "'GLSLPacked' is not valid for the Vulkan execution environment")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockNestedStructGLSLPackedBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %S 0 Offset 0 |
| OpDecorate %S GLSLPacked |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 16 |
| OpMemberDecorate %Output 2 Offset 32 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %int = OpTypeInt 32 1 |
| %S = OpTypeStruct %int |
| %Output = OpTypeStruct %float %v4float %S |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "'GLSLPacked' is not valid for the Vulkan execution environment")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockNestedStructGLSLPackedBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %S 0 Offset 0 |
| OpDecorate %S GLSLPacked |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 16 |
| OpMemberDecorate %Output 2 Offset 32 |
| OpDecorate %Output BufferBlock |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %int = OpTypeInt 32 1 |
| %S = OpTypeStruct %int |
| %Output = OpTypeStruct %float %v4float %S |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "'GLSLPacked' is not valid for the Vulkan execution environment")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockMissingArrayStrideBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output Block |
| OpMemberDecorate %Output 0 Offset 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %int = OpTypeInt 32 1 |
| %int_3 = OpConstant %int 3 |
| %array = OpTypeArray %float %int_3 |
| %Output = OpTypeStruct %array |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with ArrayStride decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockMissingArrayStrideBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output BufferBlock |
| OpMemberDecorate %Output 0 Offset 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %int = OpTypeInt 32 1 |
| %int_3 = OpConstant %int 3 |
| %array = OpTypeArray %float %int_3 |
| %Output = OpTypeStruct %array |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with ArrayStride decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockNestedStructMissingArrayStrideBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 16 |
| OpMemberDecorate %Output 2 Offset 32 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %int = OpTypeInt 32 1 |
| %int_3 = OpConstant %int 3 |
| %array = OpTypeArray %float %int_3 |
| %S = OpTypeStruct %array |
| %Output = OpTypeStruct %float %v4float %S |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with ArrayStride decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockNestedStructMissingArrayStrideBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 16 |
| OpMemberDecorate %Output 2 Offset 32 |
| OpDecorate %Output BufferBlock |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %int = OpTypeInt 32 1 |
| %int_3 = OpConstant %int 3 |
| %array = OpTypeArray %float %int_3 |
| %S = OpTypeStruct %array |
| %Output = OpTypeStruct %float %v4float %S |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with ArrayStride decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockMissingMatrixStrideBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output Block |
| OpMemberDecorate %Output 0 Offset 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %matrix = OpTypeMatrix %v3float 4 |
| %Output = OpTypeStruct %matrix |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with MatrixStride decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockMissingMatrixStrideBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output BufferBlock |
| OpMemberDecorate %Output 0 Offset 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %matrix = OpTypeMatrix %v3float 4 |
| %Output = OpTypeStruct %matrix |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with MatrixStride decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockMissingMatrixStrideArrayBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output Block |
| OpMemberDecorate %Output 0 Offset 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %matrix = OpTypeMatrix %v3float 4 |
| %int = OpTypeInt 32 1 |
| %int_3 = OpConstant %int 3 |
| %array = OpTypeArray %matrix %int_3 |
| %Output = OpTypeStruct %matrix |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with MatrixStride decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockMissingMatrixStrideArrayBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %Output BufferBlock |
| OpMemberDecorate %Output 0 Offset 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %matrix = OpTypeMatrix %v3float 4 |
| %int = OpTypeInt 32 1 |
| %int_3 = OpConstant %int 3 |
| %array = OpTypeArray %matrix %int_3 |
| %Output = OpTypeStruct %matrix |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with MatrixStride decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockNestedStructMissingMatrixStrideBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 16 |
| OpMemberDecorate %Output 2 Offset 32 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %v4float = OpTypeVector %float 4 |
| %matrix = OpTypeMatrix %v3float 4 |
| %S = OpTypeStruct %matrix |
| %Output = OpTypeStruct %float %v4float %S |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with MatrixStride decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockNestedStructMissingMatrixStrideBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 16 |
| OpMemberDecorate %Output 2 Offset 32 |
| OpDecorate %Output BufferBlock |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %v4float = OpTypeVector %float 4 |
| %matrix = OpTypeMatrix %v3float 4 |
| %S = OpTypeStruct %matrix |
| %Output = OpTypeStruct %float %v4float %S |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("must be explicitly laid out with MatrixStride decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockStandardUniformBufferLayout) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %F 0 Offset 0 |
| OpMemberDecorate %F 1 Offset 8 |
| OpDecorate %_arr_float_uint_2 ArrayStride 16 |
| OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48 |
| OpMemberDecorate %O 0 Offset 0 |
| OpMemberDecorate %O 1 Offset 16 |
| OpMemberDecorate %O 2 Offset 32 |
| OpMemberDecorate %O 3 Offset 64 |
| OpMemberDecorate %O 4 ColMajor |
| OpMemberDecorate %O 4 Offset 80 |
| OpMemberDecorate %O 4 MatrixStride 16 |
| OpDecorate %_arr_O_uint_2 ArrayStride 176 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 8 |
| OpMemberDecorate %Output 2 Offset 16 |
| OpMemberDecorate %Output 3 Offset 32 |
| OpMemberDecorate %Output 4 Offset 48 |
| OpMemberDecorate %Output 5 Offset 64 |
| OpMemberDecorate %Output 6 ColMajor |
| OpMemberDecorate %Output 6 Offset 96 |
| OpMemberDecorate %Output 6 MatrixStride 16 |
| OpMemberDecorate %Output 7 Offset 128 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %v3float = OpTypeVector %float 3 |
| %int = OpTypeInt 32 1 |
| %uint = OpTypeInt 32 0 |
| %v2uint = OpTypeVector %uint 2 |
| %F = OpTypeStruct %int %v2uint |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %mat2v3float = OpTypeMatrix %v3float 2 |
| %v3uint = OpTypeVector %uint 3 |
| %mat3v3float = OpTypeMatrix %v3float 3 |
| %_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2 |
| %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2 |
| %_arr_O_uint_2 = OpTypeArray %O %uint_2 |
| %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2 |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, BlockLayoutPermitsTightVec3ScalarPackingGood) { |
| // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 12 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %v3float %float |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, BlockCantAppearWithinABlockBad) { |
| // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1587 |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 16 |
| OpMemberDecorate %S2 0 Offset 0 |
| OpMemberDecorate %S2 1 Offset 12 |
| OpDecorate %S Block |
| OpDecorate %S2 Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %S2 = OpTypeStruct %float %float |
| %S = OpTypeStruct %float %S2 |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("rules: A Block or BufferBlock cannot be nested within " |
| "another Block or BufferBlock.")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferblockCantAppearWithinABufferblockBad) { |
| // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1587 |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 16 |
| OpMemberDecorate %S2 0 Offset 0 |
| OpMemberDecorate %S2 1 Offset 16 |
| OpMemberDecorate %S3 0 Offset 0 |
| OpMemberDecorate %S3 1 Offset 12 |
| OpDecorate %S BufferBlock |
| OpDecorate %S3 BufferBlock |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %S3 = OpTypeStruct %float %float |
| %S2 = OpTypeStruct %float %S3 |
| %S = OpTypeStruct %float %S2 |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("rules: A Block or BufferBlock cannot be nested within " |
| "another Block or BufferBlock.")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferblockCantAppearWithinABlockBad) { |
| // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1587 |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 16 |
| OpMemberDecorate %S2 0 Offset 0 |
| OpMemberDecorate %S2 1 Offset 16 |
| OpMemberDecorate %S3 0 Offset 0 |
| OpMemberDecorate %S3 1 Offset 12 |
| OpDecorate %S Block |
| OpDecorate %S3 BufferBlock |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %S3 = OpTypeStruct %float %float |
| %S2 = OpTypeStruct %float %S3 |
| %S = OpTypeStruct %float %S2 |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("rules: A Block or BufferBlock cannot be nested within " |
| "another Block or BufferBlock.")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockCantAppearWithinABufferblockBad) { |
| // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1587 |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 16 |
| OpMemberDecorate %S2 0 Offset 0 |
| OpMemberDecorate %S2 1 Offset 16 |
| OpMemberDecorate %S3 0 Offset 0 |
| OpMemberDecorate %S3 1 Offset 16 |
| OpMemberDecorate %S4 0 Offset 0 |
| OpMemberDecorate %S4 1 Offset 12 |
| OpDecorate %S BufferBlock |
| OpDecorate %S4 Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %S4 = OpTypeStruct %float %float |
| %S3 = OpTypeStruct %float %S4 |
| %S2 = OpTypeStruct %float %S3 |
| %S = OpTypeStruct %float %S2 |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("rules: A Block or BufferBlock cannot be nested within " |
| "another Block or BufferBlock.")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockLayoutForbidsTightScalarVec3PackingBad) { |
| // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %float %v3float |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Structure id 2 decorated as Block for variable in Uniform " |
| "storage class must follow standard uniform buffer layout " |
| "rules: member 1 at offset 4 is not aligned to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BlockLayoutPermitsTightScalarVec3PackingWithRelaxedLayoutGood) { |
| // Same as previous test, but with explicit option to relax block layout. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %float %v3float |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BlockLayoutPermitsTightScalarVec3PackingBadOffsetWithRelaxedLayoutBad) { |
| // Same as previous test, but with the vector not aligned to its scalar |
| // element. Use offset 5 instead of a multiple of 4. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 5 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %float %v3float |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 2 decorated as Block for variable in Uniform storage " |
| "class must follow relaxed uniform buffer layout rules: member 1 at " |
| "offset 5 is not aligned to scalar element size 4")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BlockLayoutPermitsTightScalarVec3PackingWithVulkan1_1Good) { |
| // Same as previous test, but with Vulkan 1.1. Vulkan 1.1 included |
| // VK_KHR_relaxed_block_layout in core. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %float %v3float |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BlockLayoutPermitsTightScalarVec3PackingWithScalarLayoutGood) { |
| // Same as previous test, but with scalar block layout. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %float %v3float |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BlockLayoutPermitsScalarAlignedArrayWithScalarLayoutGood) { |
| // The array at offset 4 is ok with scalar block layout. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| OpDecorate %arr_float ArrayStride 4 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %uint = OpTypeInt 32 0 |
| %uint_3 = OpConstant %uint 3 |
| %float = OpTypeFloat 32 |
| %arr_float = OpTypeArray %float %uint_3 |
| %S = OpTypeStruct %float %arr_float |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BlockLayoutPermitsScalarAlignedArrayOfVec3WithScalarLayoutGood) { |
| // The array at offset 4 is ok with scalar block layout, even though |
| // its elements are vec3. |
| // This is the same as the previous case, but the array elements are vec3 |
| // instead of float. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| OpDecorate %arr_vec3 ArrayStride 12 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %uint = OpTypeInt 32 0 |
| %uint_3 = OpConstant %uint 3 |
| %float = OpTypeFloat 32 |
| %vec3 = OpTypeVector %float 3 |
| %arr_vec3 = OpTypeArray %vec3 %uint_3 |
| %S = OpTypeStruct %float %arr_vec3 |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BlockLayoutPermitsScalarAlignedStructWithScalarLayoutGood) { |
| // Scalar block layout permits the struct at offset 4, even though |
| // it contains a vector with base alignment 8 and scalar alignment 4. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpMemberDecorate %st 0 Offset 0 |
| OpMemberDecorate %st 1 Offset 8 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %vec2 = OpTypeVector %float 2 |
| %st = OpTypeStruct %vec2 %float |
| %S = OpTypeStruct %float %st |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F( |
| ValidateDecorations, |
| BlockLayoutPermitsFieldsInBaseAlignmentPaddingAtEndOfStructWithScalarLayoutGood) { |
| // Scalar block layout permits fields in what would normally be the padding at |
| // the end of a struct. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Float64 |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %st 0 Offset 0 |
| OpMemberDecorate %st 1 Offset 8 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 12 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %double = OpTypeFloat 64 |
| %st = OpTypeStruct %double %float |
| %S = OpTypeStruct %st %float |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F( |
| ValidateDecorations, |
| BlockLayoutPermitsStraddlingVectorWithScalarLayoutOverrideRelaxBlockLayoutGood) { |
| // Same as previous, but set relaxed block layout first. Scalar layout always |
| // wins. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %vec4 = OpTypeVector %float 4 |
| %S = OpTypeStruct %float %vec4 |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true); |
| spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F( |
| ValidateDecorations, |
| BlockLayoutPermitsStraddlingVectorWithRelaxedLayoutOverridenByScalarBlockLayoutGood) { |
| // Same as previous, but set scalar block layout first. Scalar layout always |
| // wins. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %vec4 = OpTypeVector %float 4 |
| %S = OpTypeStruct %float %vec4 |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); |
| spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlock16bitStandardStorageBufferLayout) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability StorageUniform16 |
| OpExtension "SPV_KHR_16bit_storage" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %f32arr ArrayStride 4 |
| OpDecorate %f16arr ArrayStride 2 |
| OpMemberDecorate %SSBO32 0 Offset 0 |
| OpMemberDecorate %SSBO16 0 Offset 0 |
| OpDecorate %SSBO32 BufferBlock |
| OpDecorate %SSBO16 BufferBlock |
| %void = OpTypeVoid |
| %voidf = OpTypeFunction %void |
| %u32 = OpTypeInt 32 0 |
| %i32 = OpTypeInt 32 1 |
| %f32 = OpTypeFloat 32 |
| %uvec3 = OpTypeVector %u32 3 |
| %c_i32_32 = OpConstant %i32 32 |
| %c_i32_128 = OpConstant %i32 128 |
| %f32arr = OpTypeArray %f32 %c_i32_128 |
| %f16 = OpTypeFloat 16 |
| %f16arr = OpTypeArray %f16 %c_i32_128 |
| %SSBO32 = OpTypeStruct %f32arr |
| %SSBO16 = OpTypeStruct %f16arr |
| %_ptr_Uniform_SSBO32 = OpTypePointer Uniform %SSBO32 |
| %varSSBO32 = OpVariable %_ptr_Uniform_SSBO32 Uniform |
| %_ptr_Uniform_SSBO16 = OpTypePointer Uniform %SSBO16 |
| %varSSBO16 = OpVariable %_ptr_Uniform_SSBO16 Uniform |
| %main = OpFunction %void None %voidf |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, BlockArrayExtendedAlignmentGood) { |
| // For uniform buffer, Array base alignment is 16, and ArrayStride |
| // must be a multiple of 16. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpDecorate %_arr_float_uint_2 ArrayStride 16 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 16 |
| OpDecorate %S Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %uint = OpTypeInt 32 0 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %S = OpTypeStruct %v2float %_arr_float_uint_2 |
| %_ptr_PushConstant_S = OpTypePointer PushConstant %S |
| %u = OpVariable %_ptr_PushConstant_S PushConstant |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, BlockArrayBaseAlignmentBad) { |
| // For uniform buffer, Array base alignment is 16. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpDecorate %_arr_float_uint_2 ArrayStride 16 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 8 |
| OpDecorate %S Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %uint = OpTypeInt 32 0 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %S = OpTypeStruct %v2float %_arr_float_uint_2 |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %u = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 3 decorated as Block for variable in Uniform " |
| "storage class must follow standard uniform buffer layout rules: " |
| "member 1 at offset 8 is not aligned to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockArrayBaseAlignmentWithRelaxedLayoutStillBad) { |
| // For uniform buffer, Array base alignment is 16, and ArrayStride |
| // must be a multiple of 16. This case uses relaxed block layout. Relaxed |
| // layout only relaxes rules for vector alignment, not array alignment. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpDecorate %_arr_float_uint_2 ArrayStride 16 |
| OpDecorate %u DescriptorSet 0 |
| OpDecorate %u Binding 0 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 8 |
| OpDecorate %S Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %uint = OpTypeInt 32 0 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %S = OpTypeStruct %v2float %_arr_float_uint_2 |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %u = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 4 decorated as Block for variable in Uniform " |
| "storage class must follow standard uniform buffer layout rules: " |
| "member 1 at offset 8 is not aligned to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockArrayBaseAlignmentWithVulkan1_1StillBad) { |
| // Same as previous test, but with Vulkan 1.1, which includes |
| // VK_KHR_relaxed_block_layout in core. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpDecorate %_arr_float_uint_2 ArrayStride 16 |
| OpDecorate %u DescriptorSet 0 |
| OpDecorate %u Binding 0 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 8 |
| OpDecorate %S Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %uint = OpTypeInt 32 0 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %S = OpTypeStruct %v2float %_arr_float_uint_2 |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %u = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 4 decorated as Block for variable in Uniform " |
| "storage class must follow relaxed uniform buffer layout rules: " |
| "member 1 at offset 8 is not aligned to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BlockArrayBaseAlignmentWithBlockStandardLayoutGood) { |
| // Same as previous test, but with VK_KHR_uniform_buffer_standard_layout |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpDecorate %_arr_float_uint_2 ArrayStride 16 |
| OpDecorate %u DescriptorSet 0 |
| OpDecorate %u Binding 0 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 8 |
| OpDecorate %S Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %uint = OpTypeInt 32 0 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %S = OpTypeStruct %v2float %_arr_float_uint_2 |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %u = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| spvValidatorOptionsSetUniformBufferStandardLayout(getValidatorOptions(), |
| true); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanBufferBlockOnStorageBufferBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct BufferBlock |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr StorageBuffer |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("In Vulkan, BufferBlock is disallowed on variables in " |
| "the StorageBuffer storage class")); |
| } |
| |
| TEST_F(ValidateDecorations, PushConstantArrayBaseAlignmentGood) { |
| // Tests https://github.com/KhronosGroup/SPIRV-Tools/issues/1664 |
| // From GLSL vertex shader: |
| // #version 450 |
| // layout(push_constant) uniform S { vec2 v; float arr[2]; } u; |
| // void main() { } |
| |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpDecorate %_arr_float_uint_2 ArrayStride 4 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 8 |
| OpDecorate %S Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %uint = OpTypeInt 32 0 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %S = OpTypeStruct %v2float %_arr_float_uint_2 |
| %_ptr_PushConstant_S = OpTypePointer PushConstant %S |
| %u = OpVariable %_ptr_PushConstant_S PushConstant |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, PushConstantArrayBadAlignmentBad) { |
| // Like the previous test, but with offset 7 instead of 8. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpDecorate %_arr_float_uint_2 ArrayStride 4 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 7 |
| OpDecorate %S Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %uint = OpTypeInt 32 0 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %S = OpTypeStruct %v2float %_arr_float_uint_2 |
| %_ptr_PushConstant_S = OpTypePointer PushConstant %S |
| %u = OpVariable %_ptr_PushConstant_S PushConstant |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 3 decorated as Block for variable in PushConstant " |
| "storage class must follow standard storage buffer layout rules: " |
| "member 1 at offset 7 is not aligned to 4")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| PushConstantLayoutPermitsTightVec3ScalarPackingGood) { |
| // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 12 |
| OpDecorate %S Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %v3float %float |
| %_ptr_PushConstant_S = OpTypePointer PushConstant %S |
| %B = OpVariable %_ptr_PushConstant_S PushConstant |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, |
| PushConstantLayoutForbidsTightScalarVec3PackingBad) { |
| // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpDecorate %S Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %float %v3float |
| %_ptr_Uniform_S = OpTypePointer PushConstant %S |
| %B = OpVariable %_ptr_Uniform_S PushConstant |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 2 decorated as Block for variable in PushConstant " |
| "storage class must follow standard storage buffer layout " |
| "rules: member 1 at offset 4 is not aligned to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, PushConstantMissingBlockGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpMemberDecorate %struct 0 Offset 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer PushConstant %struct |
| %pc = OpVariable %ptr PushConstant |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanPushConstantMissingBlockBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpMemberDecorate %struct 0 Offset 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer PushConstant %struct |
| %pc = OpVariable %ptr PushConstant |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("PushConstant id '2' is missing Block decoration.\n" |
| "From Vulkan spec:\n" |
| "Such variables must be identified with a Block " |
| "decoration")); |
| } |
| |
| TEST_F(ValidateDecorations, MultiplePushConstantsSingleEntryPointGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %int = OpTypeInt 32 0 |
| %int_0 = OpConstant %int 0 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer PushConstant %struct |
| %ptr_float = OpTypePointer PushConstant %float |
| %pc1 = OpVariable %ptr PushConstant |
| %pc2 = OpVariable %ptr PushConstant |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| %2 = OpAccessChain %ptr_float %pc1 %int_0 |
| %3 = OpLoad %float %2 |
| %4 = OpAccessChain %ptr_float %pc2 %int_0 |
| %5 = OpLoad %float %4 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, |
| VulkanMultiplePushConstantsDifferentEntryPointGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %1 "func1" |
| OpEntryPoint Fragment %2 "func2" |
| OpExecutionMode %2 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %int = OpTypeInt 32 0 |
| %int_0 = OpConstant %int 0 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer PushConstant %struct |
| %ptr_float = OpTypePointer PushConstant %float |
| %pc1 = OpVariable %ptr PushConstant |
| %pc2 = OpVariable %ptr PushConstant |
| |
| %1 = OpFunction %void None %voidfn |
| %label1 = OpLabel |
| %3 = OpAccessChain %ptr_float %pc1 %int_0 |
| %4 = OpLoad %float %3 |
| OpReturn |
| OpFunctionEnd |
| |
| %2 = OpFunction %void None %voidfn |
| %label2 = OpLabel |
| %5 = OpAccessChain %ptr_float %pc2 %int_0 |
| %6 = OpLoad %float %5 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, |
| VulkanMultiplePushConstantsUnusedSingleEntryPointGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %int = OpTypeInt 32 0 |
| %int_0 = OpConstant %int 0 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer PushConstant %struct |
| %ptr_float = OpTypePointer PushConstant %float |
| %pc1 = OpVariable %ptr PushConstant |
| %pc2 = OpVariable %ptr PushConstant |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanMultiplePushConstantsSingleEntryPointBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %int = OpTypeInt 32 0 |
| %int_0 = OpConstant %int 0 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer PushConstant %struct |
| %ptr_float = OpTypePointer PushConstant %float |
| %pc1 = OpVariable %ptr PushConstant |
| %pc2 = OpVariable %ptr PushConstant |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| %2 = OpAccessChain %ptr_float %pc1 %int_0 |
| %3 = OpLoad %float %2 |
| %4 = OpAccessChain %ptr_float %pc2 %int_0 |
| %5 = OpLoad %float %4 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-06674")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Entry point id '1' uses more than one PushConstant interface.\n" |
| "From Vulkan spec:\n" |
| "There must be no more than one push constant block " |
| "statically used per shader entry point.")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| VulkanMultiplePushConstantsDifferentEntryPointSubFunctionGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %1 "func1" |
| OpEntryPoint Fragment %2 "func2" |
| OpExecutionMode %2 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %int = OpTypeInt 32 0 |
| %int_0 = OpConstant %int 0 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer PushConstant %struct |
| %ptr_float = OpTypePointer PushConstant %float |
| %pc1 = OpVariable %ptr PushConstant |
| %pc2 = OpVariable %ptr PushConstant |
| |
| %sub1 = OpFunction %void None %voidfn |
| %label_sub1 = OpLabel |
| %3 = OpAccessChain %ptr_float %pc1 %int_0 |
| %4 = OpLoad %float %3 |
| OpReturn |
| OpFunctionEnd |
| |
| %sub2 = OpFunction %void None %voidfn |
| %label_sub2 = OpLabel |
| %5 = OpAccessChain %ptr_float %pc2 %int_0 |
| %6 = OpLoad %float %5 |
| OpReturn |
| OpFunctionEnd |
| |
| %1 = OpFunction %void None %voidfn |
| %label1 = OpLabel |
| %call1 = OpFunctionCall %void %sub1 |
| OpReturn |
| OpFunctionEnd |
| |
| %2 = OpFunction %void None %voidfn |
| %label2 = OpLabel |
| %call2 = OpFunctionCall %void %sub2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, |
| VulkanMultiplePushConstantsSingleEntryPointSubFunctionBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %int = OpTypeInt 32 0 |
| %int_0 = OpConstant %int 0 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer PushConstant %struct |
| %ptr_float = OpTypePointer PushConstant %float |
| %pc1 = OpVariable %ptr PushConstant |
| %pc2 = OpVariable %ptr PushConstant |
| |
| %sub1 = OpFunction %void None %voidfn |
| %label_sub1 = OpLabel |
| %3 = OpAccessChain %ptr_float %pc1 %int_0 |
| %4 = OpLoad %float %3 |
| OpReturn |
| OpFunctionEnd |
| |
| %sub2 = OpFunction %void None %voidfn |
| %label_sub2 = OpLabel |
| %5 = OpAccessChain %ptr_float %pc2 %int_0 |
| %6 = OpLoad %float %5 |
| OpReturn |
| OpFunctionEnd |
| |
| %1 = OpFunction %void None %voidfn |
| %label1 = OpLabel |
| %call1 = OpFunctionCall %void %sub1 |
| %call2 = OpFunctionCall %void %sub2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-06674")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Entry point id '1' uses more than one PushConstant interface.\n" |
| "From Vulkan spec:\n" |
| "There must be no more than one push constant block " |
| "statically used per shader entry point.")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanUniformMissingDescriptorSetBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| OpDecorate %var Binding 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer Uniform %struct |
| %ptr_float = OpTypePointer Uniform %float |
| %var = OpVariable %ptr Uniform |
| %int = OpTypeInt 32 0 |
| %int_0 = OpConstant %int 0 |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| %2 = OpAccessChain %ptr_float %var %int_0 |
| %3 = OpLoad %float %2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Uniform id '3' is missing DescriptorSet decoration.\n" |
| "From Vulkan spec:\n" |
| "These variables must have DescriptorSet and Binding " |
| "decorations specified")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanUniformMissingBindingBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| OpDecorate %var DescriptorSet 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer Uniform %struct |
| %ptr_float = OpTypePointer Uniform %float |
| %var = OpVariable %ptr Uniform |
| %int = OpTypeInt 32 0 |
| %int_0 = OpConstant %int 0 |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| %2 = OpAccessChain %ptr_float %var %int_0 |
| %3 = OpLoad %float %2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Uniform id '3' is missing Binding decoration.\n" |
| "From Vulkan spec:\n" |
| "These variables must have DescriptorSet and Binding " |
| "decorations specified")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanUniformConstantMissingDescriptorSetBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %var Binding 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %sampler = OpTypeSampler |
| %ptr = OpTypePointer UniformConstant %sampler |
| %var = OpVariable %ptr UniformConstant |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| %2 = OpLoad %sampler %var |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("UniformConstant id '2' is missing DescriptorSet decoration.\n" |
| "From Vulkan spec:\n" |
| "These variables must have DescriptorSet and Binding " |
| "decorations specified")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanUniformConstantMissingBindingBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %var DescriptorSet 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %sampler = OpTypeSampler |
| %ptr = OpTypePointer UniformConstant %sampler |
| %var = OpVariable %ptr UniformConstant |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| %2 = OpLoad %sampler %var |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("UniformConstant id '2' is missing Binding decoration.\n" |
| "From Vulkan spec:\n" |
| "These variables must have DescriptorSet and Binding " |
| "decorations specified")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanStorageBufferMissingDescriptorSetBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpDecorate %var Binding 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr StorageBuffer |
| %ptr_float = OpTypePointer StorageBuffer %float |
| %int = OpTypeInt 32 0 |
| %int_0 = OpConstant %int 0 |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| %2 = OpAccessChain %ptr_float %var %int_0 |
| %3 = OpLoad %float %2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n" |
| "From Vulkan spec:\n" |
| "These variables must have DescriptorSet and Binding " |
| "decorations specified")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanStorageBufferMissingBindingBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpDecorate %var DescriptorSet 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr StorageBuffer |
| %ptr_float = OpTypePointer StorageBuffer %float |
| %int = OpTypeInt 32 0 |
| %int_0 = OpConstant %int 0 |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| %2 = OpAccessChain %ptr_float %var %int_0 |
| %3 = OpLoad %float %2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("StorageBuffer id '3' is missing Binding decoration.\n" |
| "From Vulkan spec:\n" |
| "These variables must have DescriptorSet and Binding " |
| "decorations specified")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| VulkanStorageBufferMissingDescriptorSetSubFunctionBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpDecorate %var Binding 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr StorageBuffer |
| %ptr_float = OpTypePointer StorageBuffer %float |
| %int = OpTypeInt 32 0 |
| %int_0 = OpConstant %int 0 |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| %call = OpFunctionCall %void %2 |
| OpReturn |
| OpFunctionEnd |
| %2 = OpFunction %void None %voidfn |
| %label2 = OpLabel |
| %3 = OpAccessChain %ptr_float %var %int_0 |
| %4 = OpLoad %float %3 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n" |
| "From Vulkan spec:\n" |
| "These variables must have DescriptorSet and Binding " |
| "decorations specified")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| VulkanStorageBufferMissingDescriptorAndBindingUnusedGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr StorageBuffer |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); |
| } |
| |
| TEST_F(ValidateDecorations, UniformMissingDescriptorSetGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| OpDecorate %var Binding 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer Uniform %struct |
| %var = OpVariable %ptr Uniform |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, UniformMissingBindingGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| OpDecorate %var DescriptorSet 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer Uniform %struct |
| %var = OpVariable %ptr Uniform |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, UniformConstantMissingDescriptorSetGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %var Binding 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %sampler = OpTypeSampler |
| %ptr = OpTypePointer UniformConstant %sampler |
| %var = OpVariable %ptr UniformConstant |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, UniformConstantMissingBindingGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %var DescriptorSet 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %sampler = OpTypeSampler |
| %ptr = OpTypePointer UniformConstant %sampler |
| %var = OpVariable %ptr UniformConstant |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, StorageBufferMissingDescriptorSetGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct BufferBlock |
| OpDecorate %var Binding 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr StorageBuffer |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, StorageBufferMissingBindingGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct BufferBlock |
| OpDecorate %var DescriptorSet 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %ptr = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr StorageBuffer |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, StorageBufferStorageClassArrayBaseAlignmentGood) { |
| // Spot check buffer rules when using StorageBuffer storage class with Block |
| // decoration. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpDecorate %_arr_float_uint_2 ArrayStride 4 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 8 |
| OpDecorate %S Block |
| OpDecorate %u DescriptorSet 0 |
| OpDecorate %u Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %uint = OpTypeInt 32 0 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %S = OpTypeStruct %v2float %_arr_float_uint_2 |
| %_ptr_Uniform_S = OpTypePointer StorageBuffer %S |
| %u = OpVariable %_ptr_Uniform_S StorageBuffer |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, StorageBufferStorageClassArrayBadAlignmentBad) { |
| // Like the previous test, but with offset 7. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpDecorate %_arr_float_uint_2 ArrayStride 4 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 7 |
| OpDecorate %S Block |
| OpDecorate %u DescriptorSet 0 |
| OpDecorate %u Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %uint = OpTypeInt 32 0 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %S = OpTypeStruct %v2float %_arr_float_uint_2 |
| %_ptr_Uniform_S = OpTypePointer StorageBuffer %S |
| %u = OpVariable %_ptr_Uniform_S StorageBuffer |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 3 decorated as Block for variable in StorageBuffer " |
| "storage class must follow standard storage buffer layout rules: " |
| "member 1 at offset 7 is not aligned to 4")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockStandardStorageBufferLayout) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %F 0 Offset 0 |
| OpMemberDecorate %F 1 Offset 8 |
| OpDecorate %_arr_float_uint_2 ArrayStride 4 |
| OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48 |
| OpMemberDecorate %O 0 Offset 0 |
| OpMemberDecorate %O 1 Offset 16 |
| OpMemberDecorate %O 2 Offset 24 |
| OpMemberDecorate %O 3 Offset 32 |
| OpMemberDecorate %O 4 ColMajor |
| OpMemberDecorate %O 4 Offset 48 |
| OpMemberDecorate %O 4 MatrixStride 16 |
| OpDecorate %_arr_O_uint_2 ArrayStride 144 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 8 |
| OpMemberDecorate %Output 2 Offset 16 |
| OpMemberDecorate %Output 3 Offset 32 |
| OpMemberDecorate %Output 4 Offset 48 |
| OpMemberDecorate %Output 5 Offset 52 |
| OpMemberDecorate %Output 6 ColMajor |
| OpMemberDecorate %Output 6 Offset 64 |
| OpMemberDecorate %Output 6 MatrixStride 16 |
| OpMemberDecorate %Output 7 Offset 96 |
| OpDecorate %Output BufferBlock |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %v3float = OpTypeVector %float 3 |
| %int = OpTypeInt 32 1 |
| %uint = OpTypeInt 32 0 |
| %v2uint = OpTypeVector %uint 2 |
| %F = OpTypeStruct %int %v2uint |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %mat2v3float = OpTypeMatrix %v3float 2 |
| %v3uint = OpTypeVector %uint 3 |
| %mat3v3float = OpTypeMatrix %v3float 3 |
| %_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2 |
| %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2 |
| %_arr_O_uint_2 = OpTypeArray %O %uint_2 |
| %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2 |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, |
| StorageBufferLayoutPermitsTightVec3ScalarPackingGood) { |
| // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 |
| std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 12 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %v3float %float |
| %_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S |
| %B = OpVariable %_ptr_StorageBuffer_S StorageBuffer |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, |
| StorageBufferLayoutForbidsTightScalarVec3PackingBad) { |
| // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 |
| std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %float %v3float |
| %_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S |
| %B = OpVariable %_ptr_StorageBuffer_S StorageBuffer |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 2 decorated as Block for variable in StorageBuffer " |
| "storage class must follow standard storage buffer layout " |
| "rules: member 1 at offset 4 is not aligned to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BlockStandardUniformBufferLayoutIncorrectOffset0Bad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %F 0 Offset 0 |
| OpMemberDecorate %F 1 Offset 8 |
| OpDecorate %_arr_float_uint_2 ArrayStride 16 |
| OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48 |
| OpMemberDecorate %O 0 Offset 0 |
| OpMemberDecorate %O 1 Offset 16 |
| OpMemberDecorate %O 2 Offset 24 |
| OpMemberDecorate %O 3 Offset 33 |
| OpMemberDecorate %O 4 ColMajor |
| OpMemberDecorate %O 4 Offset 80 |
| OpMemberDecorate %O 4 MatrixStride 16 |
| OpDecorate %_arr_O_uint_2 ArrayStride 176 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 8 |
| OpMemberDecorate %Output 2 Offset 16 |
| OpMemberDecorate %Output 3 Offset 32 |
| OpMemberDecorate %Output 4 Offset 48 |
| OpMemberDecorate %Output 5 Offset 64 |
| OpMemberDecorate %Output 6 ColMajor |
| OpMemberDecorate %Output 6 Offset 96 |
| OpMemberDecorate %Output 6 MatrixStride 16 |
| OpMemberDecorate %Output 7 Offset 128 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %v3float = OpTypeVector %float 3 |
| %int = OpTypeInt 32 1 |
| %uint = OpTypeInt 32 0 |
| %v2uint = OpTypeVector %uint 2 |
| %F = OpTypeStruct %int %v2uint |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %mat2v3float = OpTypeMatrix %v3float 2 |
| %v3uint = OpTypeVector %uint 3 |
| %mat3v3float = OpTypeMatrix %v3float 3 |
| %_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2 |
| %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2 |
| %_arr_O_uint_2 = OpTypeArray %O %uint_2 |
| %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2 |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Structure id 6 decorated as Block for variable in Uniform " |
| "storage class must follow standard uniform buffer layout " |
| "rules: member 2 at offset 152 is not aligned to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BlockStandardUniformBufferLayoutIncorrectOffset1Bad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %F 0 Offset 0 |
| OpMemberDecorate %F 1 Offset 8 |
| OpDecorate %_arr_float_uint_2 ArrayStride 16 |
| OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48 |
| OpMemberDecorate %O 0 Offset 0 |
| OpMemberDecorate %O 1 Offset 16 |
| OpMemberDecorate %O 2 Offset 32 |
| OpMemberDecorate %O 3 Offset 64 |
| OpMemberDecorate %O 4 ColMajor |
| OpMemberDecorate %O 4 Offset 80 |
| OpMemberDecorate %O 4 MatrixStride 16 |
| OpDecorate %_arr_O_uint_2 ArrayStride 176 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 8 |
| OpMemberDecorate %Output 2 Offset 16 |
| OpMemberDecorate %Output 3 Offset 32 |
| OpMemberDecorate %Output 4 Offset 48 |
| OpMemberDecorate %Output 5 Offset 71 |
| OpMemberDecorate %Output 6 ColMajor |
| OpMemberDecorate %Output 6 Offset 96 |
| OpMemberDecorate %Output 6 MatrixStride 16 |
| OpMemberDecorate %Output 7 Offset 128 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %v3float = OpTypeVector %float 3 |
| %int = OpTypeInt 32 1 |
| %uint = OpTypeInt 32 0 |
| %v2uint = OpTypeVector %uint 2 |
| %F = OpTypeStruct %int %v2uint |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %mat2v3float = OpTypeMatrix %v3float 2 |
| %v3uint = OpTypeVector %uint 3 |
| %mat3v3float = OpTypeMatrix %v3float 3 |
| %_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2 |
| %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2 |
| %_arr_O_uint_2 = OpTypeArray %O %uint_2 |
| %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2 |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Structure id 8 decorated as Block for variable in Uniform " |
| "storage class must follow standard uniform buffer layout " |
| "rules: member 5 at offset 71 is not aligned to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockUniformBufferLayoutIncorrectArrayStrideBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %F 0 Offset 0 |
| OpMemberDecorate %F 1 Offset 8 |
| OpDecorate %_arr_float_uint_2 ArrayStride 16 |
| OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 49 |
| OpMemberDecorate %O 0 Offset 0 |
| OpMemberDecorate %O 1 Offset 16 |
| OpMemberDecorate %O 2 Offset 32 |
| OpMemberDecorate %O 3 Offset 64 |
| OpMemberDecorate %O 4 ColMajor |
| OpMemberDecorate %O 4 Offset 80 |
| OpMemberDecorate %O 4 MatrixStride 16 |
| OpDecorate %_arr_O_uint_2 ArrayStride 176 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 8 |
| OpMemberDecorate %Output 2 Offset 16 |
| OpMemberDecorate %Output 3 Offset 32 |
| OpMemberDecorate %Output 4 Offset 48 |
| OpMemberDecorate %Output 5 Offset 64 |
| OpMemberDecorate %Output 6 ColMajor |
| OpMemberDecorate %Output 6 Offset 96 |
| OpMemberDecorate %Output 6 MatrixStride 16 |
| OpMemberDecorate %Output 7 Offset 128 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v2float = OpTypeVector %float 2 |
| %v3float = OpTypeVector %float 3 |
| %int = OpTypeInt 32 1 |
| %uint = OpTypeInt 32 0 |
| %v2uint = OpTypeVector %uint 2 |
| %F = OpTypeStruct %int %v2uint |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %mat2v3float = OpTypeMatrix %v3float 2 |
| %v3uint = OpTypeVector %uint 3 |
| %mat3v3float = OpTypeMatrix %v3float 3 |
| %_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2 |
| %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2 |
| %_arr_O_uint_2 = OpTypeArray %O %uint_2 |
| %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2 |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 6 decorated as Block for variable in Uniform storage " |
| "class must follow standard uniform buffer layout rules: member 4 " |
| "contains " |
| "an array with stride 49 not satisfying alignment to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BufferBlockStandardStorageBufferLayoutImproperStraddleBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 8 |
| OpDecorate %Output BufferBlock |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %Output = OpTypeStruct %float %v3float |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Structure id 3 decorated as BufferBlock for variable in " |
| "Uniform storage class must follow standard storage buffer " |
| "layout rules: member 1 at offset 8 is not aligned to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BlockUniformBufferLayoutOffsetInsideArrayPaddingBad) { |
| // In this case the 2nd member fits entirely within the padding. |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpDecorate %_arr_float_uint_2 ArrayStride 16 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpMemberDecorate %Output 1 Offset 20 |
| OpDecorate %Output Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %uint = OpTypeInt 32 0 |
| %v2uint = OpTypeVector %uint 2 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %Output = OpTypeStruct %_arr_float_uint_2 %float |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 4 decorated as Block for variable in Uniform storage " |
| "class must follow standard uniform buffer layout rules: member 1 at " |
| "offset 20 overlaps previous member ending at offset 31")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| BlockUniformBufferLayoutOffsetInsideStructPaddingBad) { |
| // In this case the 2nd member fits entirely within the padding. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %1 "main" |
| OpExecutionMode %1 LocalSize 1 1 1 |
| OpMemberDecorate %_struct_6 0 Offset 0 |
| OpMemberDecorate %_struct_2 0 Offset 0 |
| OpMemberDecorate %_struct_2 1 Offset 4 |
| OpDecorate %_struct_2 Block |
| %void = OpTypeVoid |
| %4 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %_struct_6 = OpTypeStruct %float |
| %_struct_2 = OpTypeStruct %_struct_6 %float |
| %_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2 |
| %8 = OpVariable %_ptr_Uniform__struct_2 Uniform |
| %1 = OpFunction %void None %4 |
| %9 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 3 decorated as Block for variable in Uniform storage " |
| "class must follow standard uniform buffer layout rules: member 1 at " |
| "offset 4 overlaps previous member ending at offset 15")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderGoodUniversal1_0) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpMemberDecorate %Outer 0 Offset 4 |
| OpMemberDecorate %Outer 1 Offset 0 |
| OpDecorate %Outer Block |
| OpDecorate %O DescriptorSet 0 |
| OpDecorate %O Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %uint = OpTypeInt 32 0 |
| %Outer = OpTypeStruct %uint %uint |
| %_ptr_Uniform_Outer = OpTypePointer Uniform %Outer |
| %O = OpVariable %_ptr_Uniform_Outer Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderGoodOpenGL4_5) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpMemberDecorate %Outer 0 Offset 4 |
| OpMemberDecorate %Outer 1 Offset 0 |
| OpDecorate %Outer Block |
| OpDecorate %O DescriptorSet 0 |
| OpDecorate %O Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %uint = OpTypeInt 32 0 |
| %Outer = OpTypeStruct %uint %uint |
| %_ptr_Uniform_Outer = OpTypePointer Uniform %Outer |
| %O = OpVariable %_ptr_Uniform_Outer Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_OPENGL_4_5)); |
| } |
| |
| TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderGoodVulkan1_1) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpMemberDecorate %Outer 0 Offset 4 |
| OpMemberDecorate %Outer 1 Offset 0 |
| OpDecorate %Outer Block |
| OpDecorate %O DescriptorSet 0 |
| OpDecorate %O Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %uint = OpTypeInt 32 0 |
| %Outer = OpTypeStruct %uint %uint |
| %_ptr_Uniform_Outer = OpTypePointer Uniform %Outer |
| %O = OpVariable %_ptr_Uniform_Outer Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)) |
| << getDiagnosticString(); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockLayoutOffsetOverlapBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpMemberDecorate %Outer 0 Offset 0 |
| OpMemberDecorate %Outer 1 Offset 16 |
| OpMemberDecorate %Inner 0 Offset 0 |
| OpMemberDecorate %Inner 1 Offset 16 |
| OpDecorate %Outer Block |
| OpDecorate %O DescriptorSet 0 |
| OpDecorate %O Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %uint = OpTypeInt 32 0 |
| %Inner = OpTypeStruct %uint %uint |
| %Outer = OpTypeStruct %Inner %uint |
| %_ptr_Uniform_Outer = OpTypePointer Uniform %Outer |
| %O = OpVariable %_ptr_Uniform_Outer Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 3 decorated as Block for variable in Uniform storage " |
| "class must follow standard uniform buffer layout rules: member 1 at " |
| "offset 16 overlaps previous member ending at offset 31")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockEmptyStruct) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpDecorate %Output BufferBlock |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %S = OpTypeStruct |
| %Output = OpTypeStruct %S |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, RowMajorMatrixTightPackingGood) { |
| // Row major matrix rule: |
| // A row-major matrix of C columns has a base alignment equal to |
| // the base alignment of a vector of C matrix components. |
| // Note: The "matrix component" is the scalar element type. |
| |
| // The matrix has 3 columns and 2 rows (C=3, R=2). |
| // So the base alignment of b is the same as a vector of 3 floats, which is 16 |
| // bytes. The matrix consists of two of these, and therefore occupies 2 x 16 |
| // bytes, or 32 bytes. |
| // |
| // So the offsets can be: |
| // a -> 0 |
| // b -> 16 |
| // c -> 48 |
| // d -> 60 ; d fits at bytes 12-15 after offset of c. Tight (vec3;float) |
| // packing |
| |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %1 "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %_struct_2 0 Offset 0 |
| OpMemberDecorate %_struct_2 1 RowMajor |
| OpMemberDecorate %_struct_2 1 Offset 16 |
| OpMemberDecorate %_struct_2 1 MatrixStride 16 |
| OpMemberDecorate %_struct_2 2 Offset 48 |
| OpMemberDecorate %_struct_2 3 Offset 60 |
| OpDecorate %_struct_2 Block |
| OpDecorate %3 DescriptorSet 0 |
| OpDecorate %3 Binding 0 |
| %void = OpTypeVoid |
| %5 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %v2float = OpTypeVector %float 2 |
| %mat3v2float = OpTypeMatrix %v2float 3 |
| %v3float = OpTypeVector %float 3 |
| %_struct_2 = OpTypeStruct %v4float %mat3v2float %v3float %float |
| %_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2 |
| %3 = OpVariable %_ptr_Uniform__struct_2 Uniform |
| %1 = OpFunction %void None %5 |
| %12 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, ArrayArrayRowMajorMatrixTightPackingGood) { |
| // Like the previous case, but we have an array of arrays of matrices. |
| // The RowMajor decoration goes on the struct member (surprisingly). |
| |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %1 "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %_struct_2 0 Offset 0 |
| OpMemberDecorate %_struct_2 1 RowMajor |
| OpMemberDecorate %_struct_2 1 Offset 16 |
| OpMemberDecorate %_struct_2 1 MatrixStride 16 |
| OpMemberDecorate %_struct_2 2 Offset 80 |
| OpMemberDecorate %_struct_2 3 Offset 92 |
| OpDecorate %arr_mat ArrayStride 32 |
| OpDecorate %arr_arr_mat ArrayStride 32 |
| OpDecorate %_struct_2 Block |
| OpDecorate %3 DescriptorSet 0 |
| OpDecorate %3 Binding 0 |
| %void = OpTypeVoid |
| %5 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %v2float = OpTypeVector %float 2 |
| %mat3v2float = OpTypeMatrix %v2float 3 |
| %uint = OpTypeInt 32 0 |
| %uint_1 = OpConstant %uint 1 |
| %uint_2 = OpConstant %uint 2 |
| %arr_mat = OpTypeArray %mat3v2float %uint_1 |
| %arr_arr_mat = OpTypeArray %arr_mat %uint_2 |
| %v3float = OpTypeVector %float 3 |
| %_struct_2 = OpTypeStruct %v4float %arr_arr_mat %v3float %float |
| %_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2 |
| %3 = OpVariable %_ptr_Uniform__struct_2 Uniform |
| %1 = OpFunction %void None %5 |
| %12 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, ArrayArrayRowMajorMatrixNextMemberOverlapsBad) { |
| // Like the previous case, but the offset of member 2 overlaps the matrix. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %1 "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %_struct_2 0 Offset 0 |
| OpMemberDecorate %_struct_2 1 RowMajor |
| OpMemberDecorate %_struct_2 1 Offset 16 |
| OpMemberDecorate %_struct_2 1 MatrixStride 16 |
| OpMemberDecorate %_struct_2 2 Offset 64 |
| OpMemberDecorate %_struct_2 3 Offset 92 |
| OpDecorate %arr_mat ArrayStride 32 |
| OpDecorate %arr_arr_mat ArrayStride 32 |
| OpDecorate %_struct_2 Block |
| OpDecorate %3 DescriptorSet 0 |
| OpDecorate %3 Binding 0 |
| %void = OpTypeVoid |
| %5 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %v2float = OpTypeVector %float 2 |
| %mat3v2float = OpTypeMatrix %v2float 3 |
| %uint = OpTypeInt 32 0 |
| %uint_1 = OpConstant %uint 1 |
| %uint_2 = OpConstant %uint 2 |
| %arr_mat = OpTypeArray %mat3v2float %uint_1 |
| %arr_arr_mat = OpTypeArray %arr_mat %uint_2 |
| %v3float = OpTypeVector %float 3 |
| %_struct_2 = OpTypeStruct %v4float %arr_arr_mat %v3float %float |
| %_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2 |
| %3 = OpVariable %_ptr_Uniform__struct_2 Uniform |
| %1 = OpFunction %void None %5 |
| %12 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 2 decorated as Block for variable in Uniform storage " |
| "class must follow standard uniform buffer layout rules: member 2 at " |
| "offset 64 overlaps previous member ending at offset 79")); |
| } |
| |
| TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackGood) { |
| // Original GLSL |
| |
| // #version 450 |
| // layout (set=0,binding=0) buffer S { |
| // uvec3 arr[2][2]; // first 3 elements are 16 bytes, last is 12 |
| // uint i; // Can't have offset 60 = 3x16 + 12 |
| // } B; |
| // void main() {} |
| |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %1 "main" |
| OpDecorate %_arr_v3uint_uint_2 ArrayStride 16 |
| OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32 |
| OpMemberDecorate %_struct_4 0 Offset 0 |
| OpMemberDecorate %_struct_4 1 Offset 64 |
| OpDecorate %_struct_4 BufferBlock |
| OpDecorate %5 DescriptorSet 0 |
| OpDecorate %5 Binding 0 |
| %void = OpTypeVoid |
| %7 = OpTypeFunction %void |
| %uint = OpTypeInt 32 0 |
| %v3uint = OpTypeVector %uint 3 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 |
| %_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2 |
| %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint |
| %_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 |
| %5 = OpVariable %_ptr_Uniform__struct_4 Uniform |
| %1 = OpFunction %void None %7 |
| %12 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackGoodScalar) { |
| // Original GLSL |
| |
| // #version 450 |
| // layout (set=0,binding=0) buffer S { |
| // uvec3 arr[2][2]; // first 3 elements are 16 bytes, last is 12 |
| // uint i; // Can have offset 60 = 3x16 + 12 |
| // } B; |
| // void main() {} |
| |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %1 "main" |
| OpDecorate %_arr_v3uint_uint_2 ArrayStride 16 |
| OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32 |
| OpMemberDecorate %_struct_4 0 Offset 0 |
| OpMemberDecorate %_struct_4 1 Offset 60 |
| OpDecorate %_struct_4 BufferBlock |
| OpDecorate %5 DescriptorSet 0 |
| OpDecorate %5 Binding 0 |
| %void = OpTypeVoid |
| %7 = OpTypeFunction %void |
| %uint = OpTypeInt 32 0 |
| %v3uint = OpTypeVector %uint 3 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 |
| %_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2 |
| %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint |
| %_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 |
| %5 = OpVariable %_ptr_Uniform__struct_4 Uniform |
| %1 = OpFunction %void None %7 |
| %12 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| options_->scalar_block_layout = true; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackBad) { |
| // Like previous but, the offset of the second member is too small. |
| |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %1 "main" |
| OpDecorate %_arr_v3uint_uint_2 ArrayStride 16 |
| OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32 |
| OpMemberDecorate %_struct_4 0 Offset 0 |
| OpMemberDecorate %_struct_4 1 Offset 60 |
| OpDecorate %_struct_4 BufferBlock |
| OpDecorate %5 DescriptorSet 0 |
| OpDecorate %5 Binding 0 |
| %void = OpTypeVoid |
| %7 = OpTypeFunction %void |
| %uint = OpTypeInt 32 0 |
| %v3uint = OpTypeVector %uint 3 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 |
| %_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2 |
| %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint |
| %_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 |
| %5 = OpVariable %_ptr_Uniform__struct_4 Uniform |
| %1 = OpFunction %void None %7 |
| %12 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Structure id 4 decorated as BufferBlock for variable " |
| "in Uniform storage class must follow standard storage " |
| "buffer layout rules: member 1 at offset 60 overlaps " |
| "previous member ending at offset 63")); |
| } |
| |
| TEST_F(ValidateDecorations, UniformBufferArraySizeCalculationPackGood) { |
| // Like the corresponding buffer block case, but the array padding must |
| // count for the last element as well, and so the offset of the second |
| // member must be at least 64. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %1 "main" |
| OpDecorate %_arr_v3uint_uint_2 ArrayStride 16 |
| OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32 |
| OpMemberDecorate %_struct_4 0 Offset 0 |
| OpMemberDecorate %_struct_4 1 Offset 64 |
| OpDecorate %_struct_4 Block |
| OpDecorate %5 DescriptorSet 0 |
| OpDecorate %5 Binding 0 |
| %void = OpTypeVoid |
| %7 = OpTypeFunction %void |
| %uint = OpTypeInt 32 0 |
| %v3uint = OpTypeVector %uint 3 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 |
| %_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2 |
| %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint |
| %_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 |
| %5 = OpVariable %_ptr_Uniform__struct_4 Uniform |
| %1 = OpFunction %void None %7 |
| %12 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, UniformBufferArraySizeCalculationPackBad) { |
| // Like previous but, the offset of the second member is too small. |
| |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %1 "main" |
| OpDecorate %_arr_v3uint_uint_2 ArrayStride 16 |
| OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32 |
| OpMemberDecorate %_struct_4 0 Offset 0 |
| OpMemberDecorate %_struct_4 1 Offset 60 |
| OpDecorate %_struct_4 Block |
| OpDecorate %5 DescriptorSet 0 |
| OpDecorate %5 Binding 0 |
| %void = OpTypeVoid |
| %7 = OpTypeFunction %void |
| %uint = OpTypeInt 32 0 |
| %v3uint = OpTypeVector %uint 3 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 |
| %_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2 |
| %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint |
| %_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 |
| %5 = OpVariable %_ptr_Uniform__struct_4 Uniform |
| %1 = OpFunction %void None %7 |
| %12 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 4 decorated as Block for variable in Uniform storage " |
| "class must follow standard uniform buffer layout rules: member 1 at " |
| "offset 60 overlaps previous member ending at offset 63")); |
| } |
| |
| TEST_F(ValidateDecorations, LayoutNotCheckedWhenSkipBlockLayout) { |
| // Checks that block layout is not verified in skipping block layout mode. |
| // Even for obviously wrong layout. |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 3 ; wrong alignment |
| OpMemberDecorate %S 1 Offset 3 ; same offset as before! |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %float %v3float |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| spvValidatorOptionsSetSkipBlockLayout(getValidatorOptions(), true); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, EntryPointVariableWrongStorageClass) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "func" %var |
| OpExecutionMode %1 OriginUpperLeft |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 0 |
| %ptr_int_Workgroup = OpTypePointer Workgroup %int |
| %var = OpVariable %ptr_int_Workgroup Workgroup |
| %func_ty = OpTypeFunction %void |
| %1 = OpFunction %void None %func_ty |
| %2 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("OpEntryPoint interfaces must be OpVariables with " |
| "Storage Class of Input(1) or Output(3). Found Storage " |
| "Class 4 for Entry Point id 1.")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanMemoryModelNonCoherent) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability VulkanMemoryModelKHR |
| OpCapability Linkage |
| OpExtension "SPV_KHR_vulkan_memory_model" |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical VulkanKHR |
| OpDecorate %1 Coherent |
| %2 = OpTypeInt 32 0 |
| %3 = OpTypePointer StorageBuffer %2 |
| %1 = OpVariable %3 StorageBuffer |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Coherent decoration targeting '1[%1]' is " |
| "banned when using the Vulkan memory model.")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanMemoryModelNoCoherentMember) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability VulkanMemoryModelKHR |
| OpCapability Linkage |
| OpExtension "SPV_KHR_vulkan_memory_model" |
| OpMemoryModel Logical VulkanKHR |
| OpMemberDecorate %1 0 Coherent |
| %2 = OpTypeInt 32 0 |
| %1 = OpTypeStruct %2 %2 |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Coherent decoration targeting '1[%_struct_1]' (member index 0) " |
| "is banned when using the Vulkan memory model.")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanMemoryModelNoVolatile) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability VulkanMemoryModelKHR |
| OpCapability Linkage |
| OpExtension "SPV_KHR_vulkan_memory_model" |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical VulkanKHR |
| OpDecorate %1 Volatile |
| %2 = OpTypeInt 32 0 |
| %3 = OpTypePointer StorageBuffer %2 |
| %1 = OpVariable %3 StorageBuffer |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Volatile decoration targeting '1[%1]' is banned when " |
| "using the Vulkan memory model.")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanMemoryModelNoVolatileMember) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability VulkanMemoryModelKHR |
| OpCapability Linkage |
| OpExtension "SPV_KHR_vulkan_memory_model" |
| OpMemoryModel Logical VulkanKHR |
| OpMemberDecorate %1 1 Volatile |
| %2 = OpTypeInt 32 0 |
| %1 = OpTypeStruct %2 %2 |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Volatile decoration targeting '1[%_struct_1]' (member " |
| "index 1) is banned when using the Vulkan memory " |
| "model.")); |
| } |
| |
| TEST_F(ValidateDecorations, FPRoundingModeGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpCapability StorageBuffer16BitAccess |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpExtension "SPV_KHR_variable_pointers" |
| OpExtension "SPV_KHR_16bit_storage" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpDecorate %_ FPRoundingMode RTE |
| %half = OpTypeFloat 16 |
| %float = OpTypeFloat 32 |
| %float_1_25 = OpConstant %float 1.25 |
| %half_ptr = OpTypePointer StorageBuffer %half |
| %half_ptr_var = OpVariable %half_ptr StorageBuffer |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| %main_entry = OpLabel |
| %_ = OpFConvert %half %float_1_25 |
| OpStore %half_ptr_var %_ |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, FPRoundingModeVectorGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpCapability StorageBuffer16BitAccess |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpExtension "SPV_KHR_variable_pointers" |
| OpExtension "SPV_KHR_16bit_storage" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpDecorate %_ FPRoundingMode RTE |
| %half = OpTypeFloat 16 |
| %float = OpTypeFloat 32 |
| %v2half = OpTypeVector %half 2 |
| %v2float = OpTypeVector %float 2 |
| %float_1_25 = OpConstant %float 1.25 |
| %floats = OpConstantComposite %v2float %float_1_25 %float_1_25 |
| %halfs_ptr = OpTypePointer StorageBuffer %v2half |
| %halfs_ptr_var = OpVariable %halfs_ptr StorageBuffer |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| %main_entry = OpLabel |
| %_ = OpFConvert %v2half %floats |
| OpStore %halfs_ptr_var %_ |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, FPRoundingModeNotOpFConvert) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpCapability StorageBuffer16BitAccess |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpExtension "SPV_KHR_variable_pointers" |
| OpExtension "SPV_KHR_16bit_storage" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpDecorate %_ FPRoundingMode RTE |
| %short = OpTypeInt 16 1 |
| %int = OpTypeInt 32 1 |
| %int_17 = OpConstant %int 17 |
| %short_ptr = OpTypePointer StorageBuffer %short |
| %short_ptr_var = OpVariable %short_ptr StorageBuffer |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| %main_entry = OpLabel |
| %_ = OpSConvert %short %int_17 |
| OpStore %short_ptr_var %_ |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("FPRoundingMode decoration can be applied only to a " |
| "width-only conversion instruction for floating-point " |
| "object.")); |
| } |
| |
| TEST_F(ValidateDecorations, FPRoundingModeNoOpStoreGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpCapability StorageBuffer16BitAccess |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpExtension "SPV_KHR_variable_pointers" |
| OpExtension "SPV_KHR_16bit_storage" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpDecorate %_ FPRoundingMode RTE |
| %half = OpTypeFloat 16 |
| %float = OpTypeFloat 32 |
| %float_1_25 = OpConstant %float 1.25 |
| %half_ptr = OpTypePointer StorageBuffer %half |
| %half_ptr_var = OpVariable %half_ptr StorageBuffer |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| %main_entry = OpLabel |
| %_ = OpFConvert %half %float_1_25 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, FPRoundingModeFConvert64to16Good) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpCapability StorageBuffer16BitAccess |
| OpCapability Float64 |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpExtension "SPV_KHR_variable_pointers" |
| OpExtension "SPV_KHR_16bit_storage" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpDecorate %_ FPRoundingMode RTE |
| %half = OpTypeFloat 16 |
| %double = OpTypeFloat 64 |
| %double_1_25 = OpConstant %double 1.25 |
| %half_ptr = OpTypePointer StorageBuffer %half |
| %half_ptr_var = OpVariable %half_ptr StorageBuffer |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| %main_entry = OpLabel |
| %_ = OpFConvert %half %double_1_25 |
| OpStore %half_ptr_var %_ |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, FPRoundingModeNotStoreInFloat16) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpCapability StorageBuffer16BitAccess |
| OpCapability Float64 |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpExtension "SPV_KHR_variable_pointers" |
| OpExtension "SPV_KHR_16bit_storage" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpDecorate %_ FPRoundingMode RTE |
| %float = OpTypeFloat 32 |
| %double = OpTypeFloat 64 |
| %double_1_25 = OpConstant %double 1.25 |
| %float_ptr = OpTypePointer StorageBuffer %float |
| %float_ptr_var = OpVariable %float_ptr StorageBuffer |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| %main_entry = OpLabel |
| %_ = OpFConvert %float %double_1_25 |
| OpStore %float_ptr_var %_ |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("FPRoundingMode decoration can be applied only to the " |
| "Object operand of an OpStore storing through a " |
| "pointer to a 16-bit floating-point scalar or vector object.")); |
| } |
| |
| TEST_F(ValidateDecorations, FPRoundingModeMultipleOpStoreGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpCapability StorageBuffer16BitAccess |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpExtension "SPV_KHR_variable_pointers" |
| OpExtension "SPV_KHR_16bit_storage" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpDecorate %_ FPRoundingMode RTE |
| %half = OpTypeFloat 16 |
| %float = OpTypeFloat 32 |
| %float_1_25 = OpConstant %float 1.25 |
| %half_ptr = OpTypePointer StorageBuffer %half |
| %half_ptr_var_0 = OpVariable %half_ptr StorageBuffer |
| %half_ptr_var_1 = OpVariable %half_ptr StorageBuffer |
| %half_ptr_var_2 = OpVariable %half_ptr StorageBuffer |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| %main_entry = OpLabel |
| %_ = OpFConvert %half %float_1_25 |
| OpStore %half_ptr_var_0 %_ |
| OpStore %half_ptr_var_1 %_ |
| OpStore %half_ptr_var_2 %_ |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, FPRoundingModeMultipleUsesBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpCapability StorageBuffer16BitAccess |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpExtension "SPV_KHR_variable_pointers" |
| OpExtension "SPV_KHR_16bit_storage" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpDecorate %_ FPRoundingMode RTE |
| %half = OpTypeFloat 16 |
| %float = OpTypeFloat 32 |
| %float_1_25 = OpConstant %float 1.25 |
| %half_ptr = OpTypePointer StorageBuffer %half |
| %half_ptr_var_0 = OpVariable %half_ptr StorageBuffer |
| %half_ptr_var_1 = OpVariable %half_ptr StorageBuffer |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| %main_entry = OpLabel |
| %_ = OpFConvert %half %float_1_25 |
| OpStore %half_ptr_var_0 %_ |
| %result = OpFAdd %half %_ %_ |
| OpStore %half_ptr_var_1 %_ |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("FPRoundingMode decoration can be applied only to the " |
| "Object operand of an OpStore.")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanFPRoundingModeGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability StorageBuffer16BitAccess |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %_ |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpMemberDecorate %ssbo 0 Offset 0 |
| OpDecorate %ssbo Block |
| OpDecorate %_ DescriptorSet 0 |
| OpDecorate %_ Binding 0 |
| OpDecorate %17 FPRoundingMode RTE |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %_ptr_Function_float = OpTypePointer Function %float |
| %float_1 = OpConstant %float 1 |
| %half = OpTypeFloat 16 |
| %ssbo = OpTypeStruct %half |
| %_ptr_StorageBuffer_ssbo = OpTypePointer StorageBuffer %ssbo |
| %_ = OpVariable %_ptr_StorageBuffer_ssbo StorageBuffer |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %_ptr_StorageBuffer_half = OpTypePointer StorageBuffer %half |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %b = OpVariable %_ptr_Function_float Function |
| OpStore %b %float_1 |
| %16 = OpLoad %float %b |
| %17 = OpFConvert %half %16 |
| %19 = OpAccessChain %_ptr_StorageBuffer_half %_ %int_0 |
| OpStore %19 %17 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_2)); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanFPRoundingModeBadMode) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability StorageBuffer16BitAccess |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %_ |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpMemberDecorate %ssbo 0 Offset 0 |
| OpDecorate %ssbo Block |
| OpDecorate %_ DescriptorSet 0 |
| OpDecorate %_ Binding 0 |
| OpDecorate %17 FPRoundingMode RTP |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %_ptr_Function_float = OpTypePointer Function %float |
| %float_1 = OpConstant %float 1 |
| %half = OpTypeFloat 16 |
| %ssbo = OpTypeStruct %half |
| %_ptr_StorageBuffer_ssbo = OpTypePointer StorageBuffer %ssbo |
| %_ = OpVariable %_ptr_StorageBuffer_ssbo StorageBuffer |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %_ptr_StorageBuffer_half = OpTypePointer StorageBuffer %half |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %b = OpVariable %_ptr_Function_float Function |
| OpStore %b %float_1 |
| %16 = OpLoad %float %b |
| %17 = OpFConvert %half %16 |
| %19 = OpAccessChain %_ptr_StorageBuffer_half %_ %int_0 |
| OpStore %19 %17 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_2)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-FPRoundingMode-04675")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("In Vulkan, the FPRoundingMode mode must only by RTE or RTZ.")); |
| } |
| |
| TEST_F(ValidateDecorations, GroupDecorateTargetsDecorationGroup) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| %1 = OpDecorationGroup |
| OpGroupDecorate %1 %1 |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("OpGroupDecorate may not target OpDecorationGroup <id> " |
| "'1[%1]'")); |
| } |
| |
| TEST_F(ValidateDecorations, GroupDecorateTargetsDecorationGroup2) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| %1 = OpDecorationGroup |
| OpGroupDecorate %1 %2 %1 |
| %2 = OpTypeVoid |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("OpGroupDecorate may not target OpDecorationGroup <id> " |
| "'1[%1]'")); |
| } |
| |
| TEST_F(ValidateDecorations, RecurseThroughRuntimeArray) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %outer Block |
| OpMemberDecorate %inner 0 Offset 0 |
| OpMemberDecorate %inner 1 Offset 1 |
| OpDecorate %runtime ArrayStride 16 |
| OpMemberDecorate %outer 0 Offset 0 |
| %int = OpTypeInt 32 0 |
| %inner = OpTypeStruct %int %int |
| %runtime = OpTypeRuntimeArray %inner |
| %outer = OpTypeStruct %runtime |
| %outer_ptr = OpTypePointer StorageBuffer %outer |
| %var = OpVariable %outer_ptr StorageBuffer |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 3 decorated as Block for variable in StorageBuffer " |
| "storage class must follow standard storage buffer layout " |
| "rules: member 1 at offset 1 is not aligned to 4")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanStructWithoutDecorationWithRuntimeArray) { |
| std::string str = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" |
| OpExecutionMode %func OriginUpperLeft |
| OpDecorate %array_t ArrayStride 4 |
| OpMemberDecorate %struct_t 0 Offset 0 |
| OpMemberDecorate %struct_t 1 Offset 4 |
| %uint_t = OpTypeInt 32 0 |
| %array_t = OpTypeRuntimeArray %uint_t |
| %struct_t = OpTypeStruct %uint_t %array_t |
| %struct_ptr = OpTypePointer StorageBuffer %struct_t |
| %2 = OpVariable %struct_ptr StorageBuffer |
| %void = OpTypeVoid |
| %func_t = OpTypeFunction %void |
| %func = OpFunction %void None %func_t |
| %1 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_1); |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Vulkan, OpTypeStruct containing an OpTypeRuntimeArray " |
| "must be decorated with Block or BufferBlock.")); |
| } |
| |
| TEST_F(ValidateDecorations, EmptyStructAtNonZeroOffsetGood) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| OpMemberDecorate %struct 1 Offset 16 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %float = OpTypeFloat 32 |
| %empty = OpTypeStruct |
| %struct = OpTypeStruct %float %empty |
| %ptr_struct_ubo = OpTypePointer Uniform %struct |
| %var = OpVariable %ptr_struct_ubo Uniform |
| %voidfn = OpTypeFunction %void |
| %main = OpFunction %void None %voidfn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| // Uniform and UniformId decorations |
| |
| TEST_F(ValidateDecorations, UniformDecorationGood) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical Simple |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %int0 Uniform |
| OpDecorate %var Uniform |
| OpDecorate %val Uniform |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 1 |
| %int0 = OpConstantNull %int |
| %intptr = OpTypePointer Private %int |
| %var = OpVariable %intptr Private |
| %fn = OpTypeFunction %void |
| %main = OpFunction %void None %fn |
| %entry = OpLabel |
| %val = OpLoad %int %var |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| // Returns SPIR-V assembly for a shader that uses a given decoration |
| // instruction. |
| std::string ShaderWithUniformLikeDecoration(const std::string& inst) { |
| return std::string(R"( |
| OpCapability Shader |
| OpMemoryModel Logical Simple |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpName %subgroupscope "subgroupscope" |
| OpName %call "call" |
| OpName %myfunc "myfunc" |
| OpName %int0 "int0" |
| OpName %float0 "float0" |
| OpName %fn "fn" |
| )") + inst + |
| R"( |
| %void = OpTypeVoid |
| %float = OpTypeFloat 32 |
| %int = OpTypeInt 32 1 |
| %int0 = OpConstantNull %int |
| %int_99 = OpConstant %int 99 |
| %subgroupscope = OpConstant %int 3 |
| %float0 = OpConstantNull %float |
| %fn = OpTypeFunction %void |
| %myfunc = OpFunction %void None %fn |
| %myfuncentry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %main = OpFunction %void None %fn |
| %entry = OpLabel |
| %call = OpFunctionCall %void %myfunc |
| OpReturn |
| OpFunctionEnd |
| )"; |
| } |
| |
| TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV13Bad) { |
| const std::string spirv = ShaderWithUniformLikeDecoration( |
| "OpDecorateId %int0 UniformId %subgroupscope"); |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); |
| EXPECT_EQ(SPV_ERROR_WRONG_VERSION, |
| ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("requires SPIR-V version 1.4 or later\n" |
| " OpDecorateId %int0 UniformId %subgroupscope")) |
| << spirv; |
| } |
| |
| TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV13BadTargetV14) { |
| const std::string spirv = ShaderWithUniformLikeDecoration( |
| "OpDecorateId %int0 UniformId %subgroupscope"); |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); |
| EXPECT_EQ(SPV_ERROR_WRONG_VERSION, |
| ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("requires SPIR-V version 1.4 or later")); |
| } |
| |
| TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV14Good) { |
| const std::string spirv = ShaderWithUniformLikeDecoration( |
| "OpDecorateId %int0 UniformId %subgroupscope"); |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, UniformDecorationTargetsTypeBad) { |
| const std::string spirv = |
| ShaderWithUniformLikeDecoration("OpDecorate %fn Uniform"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Uniform decoration applied to a non-object")); |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("%fn = OpTypeFunction %void")); |
| } |
| |
| TEST_F(ValidateDecorations, UniformIdDecorationTargetsTypeBad) { |
| const std::string spirv = ShaderWithUniformLikeDecoration( |
| "OpDecorateId %fn UniformId %subgroupscope"); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("UniformId decoration applied to a non-object")); |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("%fn = OpTypeFunction %void")); |
| } |
| |
| TEST_F(ValidateDecorations, UniformDecorationTargetsVoidValueBad) { |
| const std::string spirv = |
| ShaderWithUniformLikeDecoration("OpDecorate %call Uniform"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Uniform decoration applied to a value with void type\n" |
| " %call = OpFunctionCall %void %myfunc")); |
| } |
| |
| TEST_F(ValidateDecorations, UniformIdDecorationTargetsVoidValueBad) { |
| const std::string spirv = ShaderWithUniformLikeDecoration( |
| "OpDecorateId %call UniformId %subgroupscope"); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)) |
| << spirv; |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("UniformId decoration applied to a value with void type\n" |
| " %call = OpFunctionCall %void %myfunc")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| UniformDecorationWithScopeIdV14IdIsFloatValueIsBad) { |
| const std::string spirv = |
| ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %float0"); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, |
| ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("ConstantNull: expected scope to be a 32-bit int")); |
| } |
| |
| TEST_F(ValidateDecorations, |
| UniformDecorationWithScopeIdV14IdIsInvalidIntValueBad) { |
| const std::string spirv = |
| ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %int_99"); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, |
| ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Invalid scope value:\n %int_99 = OpConstant %int 99\n")); |
| } |
| |
| TEST_F(ValidateDecorations, UniformDecorationWithScopeIdV14VulkanEnv) { |
| const std::string spirv = |
| ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %int0"); |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1_SPIRV_1_4); |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, |
| ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-None-04636")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr(": in Vulkan environment Execution Scope is limited to " |
| "Workgroup and Subgroup")); |
| } |
| |
| TEST_F(ValidateDecorations, UniformDecorationWithWrongInstructionBad) { |
| const std::string spirv = |
| ShaderWithUniformLikeDecoration("OpDecorateId %int0 Uniform"); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_2); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Decorations that don't take ID parameters may not be " |
| "used with OpDecorateId\n" |
| " OpDecorateId %int0 Uniform")); |
| } |
| |
| TEST_F(ValidateDecorations, UniformIdDecorationWithWrongInstructionBad) { |
| const std::string spirv = ShaderWithUniformLikeDecoration( |
| "OpDecorate %int0 UniformId %subgroupscope"); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Decorations taking ID parameters may not be used with OpDecorateId\n" |
| " OpDecorate %int0 UniformId %subgroupscope")); |
| } |
| |
| TEST_F(ValidateDecorations, MultipleOffsetDecorationsOnSameID) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpMemberDecorate %struct 0 Offset 0 |
| OpMemberDecorate %struct 0 Offset 0 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("ID '2', member '0' decorated with Offset multiple " |
| "times is not allowed.")); |
| } |
| |
| TEST_F(ValidateDecorations, MultipleArrayStrideDecorationsOnSameID) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %array ArrayStride 4 |
| OpDecorate %array ArrayStride 4 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %uint = OpTypeInt 32 0 |
| %uint_4 = OpConstant %uint 4 |
| %array = OpTypeArray %float %uint_4 |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("ID '2' decorated with ArrayStride multiple " |
| "times is not allowed.")); |
| } |
| |
| TEST_F(ValidateDecorations, MultipleMatrixStrideDecorationsOnSameID) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpMemberDecorate %struct 0 Offset 0 |
| OpMemberDecorate %struct 0 ColMajor |
| OpMemberDecorate %struct 0 MatrixStride 16 |
| OpMemberDecorate %struct 0 MatrixStride 16 |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %fvec4 = OpTypeVector %float 4 |
| %fmat4 = OpTypeMatrix %fvec4 4 |
| %struct = OpTypeStruct %fmat4 |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("ID '2', member '0' decorated with MatrixStride " |
| "multiple times is not allowed.")); |
| } |
| |
| TEST_F(ValidateDecorations, MultipleRowMajorDecorationsOnSameID) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpMemberDecorate %struct 0 Offset 0 |
| OpMemberDecorate %struct 0 MatrixStride 16 |
| OpMemberDecorate %struct 0 RowMajor |
| OpMemberDecorate %struct 0 RowMajor |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %fvec4 = OpTypeVector %float 4 |
| %fmat4 = OpTypeMatrix %fvec4 4 |
| %struct = OpTypeStruct %fmat4 |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("ID '2', member '0' decorated with RowMajor multiple " |
| "times is not allowed.")); |
| } |
| |
| TEST_F(ValidateDecorations, MultipleColMajorDecorationsOnSameID) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpMemberDecorate %struct 0 Offset 0 |
| OpMemberDecorate %struct 0 MatrixStride 16 |
| OpMemberDecorate %struct 0 ColMajor |
| OpMemberDecorate %struct 0 ColMajor |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %fvec4 = OpTypeVector %float 4 |
| %fmat4 = OpTypeMatrix %fvec4 4 |
| %struct = OpTypeStruct %fmat4 |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("ID '2', member '0' decorated with ColMajor multiple " |
| "times is not allowed.")); |
| } |
| |
| TEST_F(ValidateDecorations, RowMajorAndColMajorDecorationsOnSameID) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpMemberDecorate %struct 0 Offset 0 |
| OpMemberDecorate %struct 0 MatrixStride 16 |
| OpMemberDecorate %struct 0 ColMajor |
| OpMemberDecorate %struct 0 RowMajor |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %fvec4 = OpTypeVector %float 4 |
| %fmat4 = OpTypeMatrix %fvec4 4 |
| %struct = OpTypeStruct %fmat4 |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("ID '2', member '0' decorated with both RowMajor and " |
| "ColMajor is not allowed.")); |
| } |
| |
| TEST_F(ValidateDecorations, BlockAndBufferBlockDecorationsOnSameID) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| |
| OpDecorate %struct Block |
| OpDecorate %struct BufferBlock |
| OpMemberDecorate %struct 0 Offset 0 |
| OpMemberDecorate %struct 0 MatrixStride 16 |
| OpMemberDecorate %struct 0 RowMajor |
| |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %fvec4 = OpTypeVector %float 4 |
| %fmat4 = OpTypeMatrix %fvec4 4 |
| %struct = OpTypeStruct %fmat4 |
| |
| %1 = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "ID '2' decorated with both BufferBlock and Block is not allowed.")); |
| } |
| |
| std::string MakeIntegerShader( |
| const std::string& decoration, const std::string& inst, |
| const std::string& extension = |
| "OpExtension \"SPV_KHR_no_integer_wrap_decoration\"") { |
| return R"( |
| OpCapability Shader |
| OpCapability Linkage |
| )" + extension + |
| R"( |
| %glsl = OpExtInstImport "GLSL.std.450" |
| %opencl = OpExtInstImport "OpenCL.std" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpName %entry "entry" |
| )" + decoration + |
| R"( |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %zero = OpConstantNull %int |
| %float = OpTypeFloat 32 |
| %float0 = OpConstantNull %float |
| %main = OpFunction %void None %voidfn |
| %entry = OpLabel |
| )" + inst + |
| R"( |
| OpReturn |
| OpFunctionEnd)"; |
| } |
| |
| // NoSignedWrap |
| |
| TEST_F(ValidateDecorations, NoSignedWrapOnTypeBad) { |
| std::string spirv = MakeIntegerShader("OpDecorate %void NoSignedWrap", ""); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("NoSignedWrap decoration may not be applied to TypeVoid")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapOnLabelBad) { |
| std::string spirv = MakeIntegerShader("OpDecorate %entry NoSignedWrap", ""); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("NoSignedWrap decoration may not be applied to Label")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapRequiresExtensionBad) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", |
| "%val = OpIAdd %int %zero %zero", ""); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("requires one of these extensions: " |
| "SPV_KHR_no_integer_wrap_decoration")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapRequiresExtensionV13Bad) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", |
| "%val = OpIAdd %int %zero %zero", ""); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("requires one of these extensions: " |
| "SPV_KHR_no_integer_wrap_decoration")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapOkInSPV14Good) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", |
| "%val = OpIAdd %int %zero %zero", ""); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapIAddGood) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", |
| "%val = OpIAdd %int %zero %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapISubGood) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", |
| "%val = OpISub %int %zero %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapIMulGood) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", |
| "%val = OpIMul %int %zero %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapShiftLeftLogicalGood) { |
| std::string spirv = |
| MakeIntegerShader("OpDecorate %val NoSignedWrap", |
| "%val = OpShiftLeftLogical %int %zero %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapSNegateGood) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", |
| "%val = OpSNegate %int %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapSRemBad) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", |
| "%val = OpSRem %int %zero %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("NoSignedWrap decoration may not be applied to SRem")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapFAddBad) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", |
| "%val = OpFAdd %float %float0 %float0"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("NoSignedWrap decoration may not be applied to FAdd")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapExtInstOpenCLGood) { |
| std::string spirv = |
| MakeIntegerShader("OpDecorate %val NoSignedWrap", |
| "%val = OpExtInst %int %opencl s_abs %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoSignedWrapExtInstGLSLGood) { |
| std::string spirv = MakeIntegerShader( |
| "OpDecorate %val NoSignedWrap", "%val = OpExtInst %int %glsl SAbs %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| // TODO(dneto): For NoSignedWrap and NoUnsignedWrap, permit |
| // "OpExtInst for instruction numbers specified in the extended |
| // instruction-set specifications as accepting this decoration." |
| |
| // NoUnignedWrap |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapOnTypeBad) { |
| std::string spirv = MakeIntegerShader("OpDecorate %void NoUnsignedWrap", ""); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("NoUnsignedWrap decoration may not be applied to TypeVoid")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapOnLabelBad) { |
| std::string spirv = MakeIntegerShader("OpDecorate %entry NoUnsignedWrap", ""); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("NoUnsignedWrap decoration may not be applied to Label")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapRequiresExtensionBad) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", |
| "%val = OpIAdd %int %zero %zero", ""); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("requires one of these extensions: " |
| "SPV_KHR_no_integer_wrap_decoration")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapRequiresExtensionV13Bad) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", |
| "%val = OpIAdd %int %zero %zero", ""); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("requires one of these extensions: " |
| "SPV_KHR_no_integer_wrap_decoration")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapOkInSPV14Good) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", |
| "%val = OpIAdd %int %zero %zero", ""); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapIAddGood) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", |
| "%val = OpIAdd %int %zero %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapISubGood) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", |
| "%val = OpISub %int %zero %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapIMulGood) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", |
| "%val = OpIMul %int %zero %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapShiftLeftLogicalGood) { |
| std::string spirv = |
| MakeIntegerShader("OpDecorate %val NoUnsignedWrap", |
| "%val = OpShiftLeftLogical %int %zero %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapSNegateGood) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", |
| "%val = OpSNegate %int %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapSRemBad) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", |
| "%val = OpSRem %int %zero %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("NoUnsignedWrap decoration may not be applied to SRem")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapFAddBad) { |
| std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", |
| "%val = OpFAdd %float %float0 %float0"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("NoUnsignedWrap decoration may not be applied to FAdd")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapExtInstOpenCLGood) { |
| std::string spirv = |
| MakeIntegerShader("OpDecorate %val NoUnsignedWrap", |
| "%val = OpExtInst %int %opencl s_abs %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NoUnsignedWrapExtInstGLSLGood) { |
| std::string spirv = |
| MakeIntegerShader("OpDecorate %val NoUnsignedWrap", |
| "%val = OpExtInst %int %glsl SAbs %zero"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, AliasedandRestrictBad) { |
| const std::string body = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 430 |
| OpMemberDecorate %Output 0 Offset 0 |
| OpDecorate %Output BufferBlock |
| OpDecorate %dataOutput Restrict |
| OpDecorate %dataOutput Aliased |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %Output = OpTypeStruct %float |
| %_ptr_Uniform_Output = OpTypePointer Uniform %Output |
| %dataOutput = OpVariable %_ptr_Uniform_Output Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(body.c_str()); |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("decorated with both Aliased and Restrict is not allowed")); |
| } |
| |
| // TODO(dneto): For NoUnsignedWrap and NoUnsignedWrap, permit |
| // "OpExtInst for instruction numbers specified in the extended |
| // instruction-set specifications as accepting this decoration." |
| |
| TEST_F(ValidateDecorations, PSBAliasedRestrictPointerSuccess) { |
| const std::string body = R"( |
| OpCapability PhysicalStorageBufferAddresses |
| OpCapability Int64 |
| OpCapability Shader |
| OpExtension "SPV_EXT_physical_storage_buffer" |
| OpMemoryModel PhysicalStorageBuffer64 GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %val1 RestrictPointer |
| %uint64 = OpTypeInt 64 0 |
| %ptr = OpTypePointer PhysicalStorageBuffer %uint64 |
| %pptr_f = OpTypePointer Function %ptr |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %main = OpFunction %void None %voidfn |
| %entry = OpLabel |
| %val1 = OpVariable %pptr_f Function |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(body.c_str()); |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateDecorations, PSBAliasedRestrictPointerMissing) { |
| const std::string body = R"( |
| OpCapability PhysicalStorageBufferAddresses |
| OpCapability Int64 |
| OpCapability Shader |
| OpExtension "SPV_EXT_physical_storage_buffer" |
| OpMemoryModel PhysicalStorageBuffer64 GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| %uint64 = OpTypeInt 64 0 |
| %ptr = OpTypePointer PhysicalStorageBuffer %uint64 |
| %pptr_f = OpTypePointer Function %ptr |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %main = OpFunction %void None %voidfn |
| %entry = OpLabel |
| %val1 = OpVariable %pptr_f Function |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(body.c_str()); |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("expected AliasedPointer or RestrictPointer for " |
| "PhysicalStorageBuffer pointer")); |
| } |
| |
| TEST_F(ValidateDecorations, PSBAliasedRestrictPointerBoth) { |
| const std::string body = R"( |
| OpCapability PhysicalStorageBufferAddresses |
| OpCapability Int64 |
| OpCapability Shader |
| OpExtension "SPV_EXT_physical_storage_buffer" |
| OpMemoryModel PhysicalStorageBuffer64 GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %val1 RestrictPointer |
| OpDecorate %val1 AliasedPointer |
| %uint64 = OpTypeInt 64 0 |
| %ptr = OpTypePointer PhysicalStorageBuffer %uint64 |
| %pptr_f = OpTypePointer Function %ptr |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %main = OpFunction %void None %voidfn |
| %entry = OpLabel |
| %val1 = OpVariable %pptr_f Function |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(body.c_str()); |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("can't specify both AliasedPointer and RestrictPointer " |
| "for PhysicalStorageBuffer pointer")); |
| } |
| |
| TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamSuccess) { |
| const std::string body = R"( |
| OpCapability PhysicalStorageBufferAddresses |
| OpCapability Int64 |
| OpCapability Shader |
| OpExtension "SPV_EXT_physical_storage_buffer" |
| OpMemoryModel PhysicalStorageBuffer64 GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %fparam Restrict |
| %uint64 = OpTypeInt 64 0 |
| %ptr = OpTypePointer PhysicalStorageBuffer %uint64 |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %fnptr = OpTypeFunction %void %ptr |
| %main = OpFunction %void None %voidfn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %fn = OpFunction %void None %fnptr |
| %fparam = OpFunctionParameter %ptr |
| %lab = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(body.c_str()); |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamMissing) { |
| const std::string body = R"( |
| OpCapability PhysicalStorageBufferAddresses |
| OpCapability Int64 |
| OpCapability Shader |
| OpExtension "SPV_EXT_physical_storage_buffer" |
| OpMemoryModel PhysicalStorageBuffer64 GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| %uint64 = OpTypeInt 64 0 |
| %ptr = OpTypePointer PhysicalStorageBuffer %uint64 |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %fnptr = OpTypeFunction %void %ptr |
| %main = OpFunction %void None %voidfn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %fn = OpFunction %void None %fnptr |
| %fparam = OpFunctionParameter %ptr |
| %lab = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(body.c_str()); |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("expected Aliased or Restrict for " |
| "PhysicalStorageBuffer pointer")); |
| } |
| |
| TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamBoth) { |
| const std::string body = R"( |
| OpCapability PhysicalStorageBufferAddresses |
| OpCapability Int64 |
| OpCapability Shader |
| OpExtension "SPV_EXT_physical_storage_buffer" |
| OpMemoryModel PhysicalStorageBuffer64 GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %fparam Restrict |
| OpDecorate %fparam Aliased |
| %uint64 = OpTypeInt 64 0 |
| %ptr = OpTypePointer PhysicalStorageBuffer %uint64 |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %fnptr = OpTypeFunction %void %ptr |
| %main = OpFunction %void None %voidfn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %fn = OpFunction %void None %fnptr |
| %fparam = OpFunctionParameter %ptr |
| %lab = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(body.c_str()); |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("can't specify both Aliased and Restrict for " |
| "PhysicalStorageBuffer pointer")); |
| } |
| |
| TEST_F(ValidateDecorations, PSBFPRoundingModeSuccess) { |
| std::string spirv = R"( |
| OpCapability PhysicalStorageBufferAddresses |
| OpCapability Shader |
| OpCapability Linkage |
| OpCapability StorageBuffer16BitAccess |
| OpExtension "SPV_EXT_physical_storage_buffer" |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpExtension "SPV_KHR_variable_pointers" |
| OpExtension "SPV_KHR_16bit_storage" |
| OpMemoryModel PhysicalStorageBuffer64 GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpDecorate %_ FPRoundingMode RTE |
| OpDecorate %half_ptr_var AliasedPointer |
| %half = OpTypeFloat 16 |
| %float = OpTypeFloat 32 |
| %float_1_25 = OpConstant %float 1.25 |
| %half_ptr = OpTypePointer PhysicalStorageBuffer %half |
| %half_pptr_f = OpTypePointer Function %half_ptr |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %main = OpFunction %void None %func |
| %main_entry = OpLabel |
| %half_ptr_var = OpVariable %half_pptr_f Function |
| %val1 = OpLoad %half_ptr %half_ptr_var |
| %_ = OpFConvert %half %float_1_25 |
| OpStore %val1 %_ Aligned 2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, InvalidStraddle) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpMemberDecorate %inner_struct 0 Offset 0 |
| OpMemberDecorate %inner_struct 1 Offset 4 |
| OpDecorate %outer_struct Block |
| OpMemberDecorate %outer_struct 0 Offset 0 |
| OpMemberDecorate %outer_struct 1 Offset 8 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %float = OpTypeFloat 32 |
| %float2 = OpTypeVector %float 2 |
| %inner_struct = OpTypeStruct %float %float2 |
| %outer_struct = OpTypeStruct %float2 %inner_struct |
| %ptr_ssbo_outer = OpTypePointer StorageBuffer %outer_struct |
| %var = OpVariable %ptr_ssbo_outer StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Structure id 2 decorated as Block for variable in " |
| "StorageBuffer storage class must follow relaxed " |
| "storage buffer layout rules: member 1 is an " |
| "improperly straddling vector at offset 12")); |
| } |
| |
| TEST_F(ValidateDecorations, DescriptorArray) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| OpMemberDecorate %struct 1 Offset 1 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %float = OpTypeFloat 32 |
| %int = OpTypeInt 32 0 |
| %int_2 = OpConstant %int 2 |
| %float2 = OpTypeVector %float 2 |
| %struct = OpTypeStruct %float %float2 |
| %struct_array = OpTypeArray %struct %int_2 |
| %ptr_ssbo_array = OpTypePointer StorageBuffer %struct_array |
| %var = OpVariable %ptr_ssbo_array StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Structure id 2 decorated as Block for variable in " |
| "StorageBuffer storage class must follow standard " |
| "storage buffer layout rules: member 1 at offset 1 is " |
| "not aligned to 8")); |
| } |
| |
| TEST_F(ValidateDecorations, DescriptorRuntimeArray) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability RuntimeDescriptorArrayEXT |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpExtension "SPV_EXT_descriptor_indexing" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| OpMemberDecorate %struct 1 Offset 1 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %float = OpTypeFloat 32 |
| %int = OpTypeInt 32 0 |
| %float2 = OpTypeVector %float 2 |
| %struct = OpTypeStruct %float %float2 |
| %struct_array = OpTypeRuntimeArray %struct |
| %ptr_ssbo_array = OpTypePointer StorageBuffer %struct_array |
| %var = OpVariable %ptr_ssbo_array StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Structure id 2 decorated as Block for variable in " |
| "StorageBuffer storage class must follow standard " |
| "storage buffer layout rules: member 1 at offset 1 is " |
| "not aligned to 8")); |
| } |
| |
| TEST_F(ValidateDecorations, MultiDimensionalArray) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| OpDecorate %array_4 ArrayStride 4 |
| OpDecorate %array_3 ArrayStride 48 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 0 |
| %int_3 = OpConstant %int 3 |
| %int_4 = OpConstant %int 4 |
| %array_4 = OpTypeArray %int %int_4 |
| %array_3 = OpTypeArray %array_4 %int_3 |
| %struct = OpTypeStruct %array_3 |
| %ptr_struct = OpTypePointer Uniform %struct |
| %var = OpVariable %ptr_struct Uniform |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Structure id 2 decorated as Block for variable in " |
| "Uniform storage class must follow standard uniform " |
| "buffer layout rules: member 0 contains an array with " |
| "stride 4 not satisfying alignment to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, ImproperStraddleInArray) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| OpDecorate %array ArrayStride 24 |
| OpMemberDecorate %inner 0 Offset 0 |
| OpMemberDecorate %inner 1 Offset 4 |
| OpMemberDecorate %inner 2 Offset 12 |
| OpMemberDecorate %inner 3 Offset 16 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 0 |
| %int_2 = OpConstant %int 2 |
| %int2 = OpTypeVector %int 2 |
| %inner = OpTypeStruct %int %int2 %int %int |
| %array = OpTypeArray %inner %int_2 |
| %struct = OpTypeStruct %array |
| %ptr_struct = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr_struct StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Structure id 4 decorated as Block for variable in " |
| "StorageBuffer storage class must follow relaxed " |
| "storage buffer layout rules: member 1 is an " |
| "improperly straddling vector at offset 28")); |
| } |
| |
| TEST_F(ValidateDecorations, LargeArray) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| OpDecorate %array ArrayStride 24 |
| OpMemberDecorate %inner 0 Offset 0 |
| OpMemberDecorate %inner 1 Offset 8 |
| OpMemberDecorate %inner 2 Offset 16 |
| OpMemberDecorate %inner 3 Offset 20 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 0 |
| %int_2000000 = OpConstant %int 2000000 |
| %int2 = OpTypeVector %int 2 |
| %inner = OpTypeStruct %int %int2 %int %int |
| %array = OpTypeArray %inner %int_2000000 |
| %struct = OpTypeStruct %array |
| %ptr_struct = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr_struct StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| } |
| |
| // NonWritable |
| |
| // Returns a SPIR-V shader module with variables in various storage classes, |
| // parameterizable by which ID should be decorated as NonWritable. |
| std::string ShaderWithNonWritableTarget(const std::string& target, |
| bool member_decorate = false) { |
| const std::string decoration_inst = |
| std::string(member_decorate ? "OpMemberDecorate " : "OpDecorate ") + |
| target + (member_decorate ? " 0" : ""); |
| |
| return std::string(R"( |
| OpCapability Shader |
| OpCapability RuntimeDescriptorArrayEXT |
| OpExtension "SPV_EXT_descriptor_indexing" |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpName %label "label" |
| OpName %param_f "param_f" |
| OpName %param_p "param_p" |
| OpName %_ptr_imstor "_ptr_imstor" |
| OpName %_ptr_imsam "_ptr_imsam" |
| OpName %var_wg "var_wg" |
| OpName %var_imsam "var_imsam" |
| OpName %var_priv "var_priv" |
| OpName %var_func "var_func" |
| OpName %simple_struct "simple_struct" |
| |
| OpDecorate %struct_b Block |
| OpDecorate %struct_b_rtarr Block |
| OpMemberDecorate %struct_b 0 Offset 0 |
| OpMemberDecorate %struct_b_rtarr 0 Offset 0 |
| OpDecorate %rtarr ArrayStride 4 |
| )") + decoration_inst + |
| |
| R"( NonWritable |
| |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %float_0 = OpConstant %float 0 |
| %int = OpTypeInt 32 0 |
| %int_2 = OpConstant %int 2 |
| %struct_b = OpTypeStruct %float |
| %rtarr = OpTypeRuntimeArray %float |
| %struct_b_rtarr = OpTypeStruct %rtarr |
| %simple_struct = OpTypeStruct %float |
| ; storage image |
| %imstor = OpTypeImage %float 2D 0 0 0 2 R32f |
| ; sampled image |
| %imsam = OpTypeImage %float 2D 0 0 0 1 R32f |
| %array_imstor = OpTypeArray %imstor %int_2 |
| %rta_imstor = OpTypeRuntimeArray %imstor |
| |
| %_ptr_Uniform_stb = OpTypePointer Uniform %struct_b |
| %_ptr_StorageBuffer_stb = OpTypePointer StorageBuffer %struct_b |
| %_ptr_StorageBuffer_stb_rtarr = OpTypePointer StorageBuffer %struct_b_rtarr |
| %_ptr_Workgroup = OpTypePointer Workgroup %float |
| %_ptr_Private = OpTypePointer Private %float |
| %_ptr_Function = OpTypePointer Function %float |
| %_ptr_imstor = OpTypePointer UniformConstant %imstor |
| %_ptr_imsam = OpTypePointer UniformConstant %imsam |
| %_ptr_array_imstor = OpTypePointer UniformConstant %array_imstor |
| %_ptr_rta_imstor = OpTypePointer UniformConstant %rta_imstor |
| |
| %extra_fn = OpTypeFunction %void %float %_ptr_Private %_ptr_imstor |
| |
| %var_ubo = OpVariable %_ptr_Uniform_stb Uniform |
| %var_ssbo_sb = OpVariable %_ptr_StorageBuffer_stb StorageBuffer |
| %var_ssbo_sb_rtarr = OpVariable %_ptr_StorageBuffer_stb_rtarr StorageBuffer |
| %var_wg = OpVariable %_ptr_Workgroup Workgroup |
| %var_priv = OpVariable %_ptr_Private Private |
| %var_imstor = OpVariable %_ptr_imstor UniformConstant |
| %var_imsam = OpVariable %_ptr_imsam UniformConstant |
| %var_array_imstor = OpVariable %_ptr_array_imstor UniformConstant |
| %var_rta_imstor = OpVariable %_ptr_rta_imstor UniformConstant |
| |
| %helper = OpFunction %void None %extra_fn |
| %param_f = OpFunctionParameter %float |
| %param_p = OpFunctionParameter %_ptr_Private |
| %param_pimstor = OpFunctionParameter %_ptr_imstor |
| %helper_label = OpLabel |
| %helper_func_var = OpVariable %_ptr_Function Function |
| OpReturn |
| OpFunctionEnd |
| |
| %main = OpFunction %void None %void_fn |
| %label = OpLabel |
| %var_func = OpVariable %_ptr_Function Function |
| OpReturn |
| OpFunctionEnd |
| )"; |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableLabelTargetBad) { |
| std::string spirv = ShaderWithNonWritableTarget("%label"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("must be a memory object declaration")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableTypeTargetBad) { |
| std::string spirv = ShaderWithNonWritableTarget("%void"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("must be a memory object declaration")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableValueTargetBad) { |
| std::string spirv = ShaderWithNonWritableTarget("%float_0"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("must be a memory object declaration")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableValueParamBad) { |
| std::string spirv = ShaderWithNonWritableTarget("%param_f"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritablePointerParamButWrongTypeBad) { |
| std::string spirv = ShaderWithNonWritableTarget("%param_p"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Target of NonWritable decoration is invalid: must " |
| "point to a storage image, uniform block, or storage " |
| "buffer\n %param_p = OpFunctionParameter %_ptr_Private_float")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritablePointerParamStorageImageGood) { |
| std::string spirv = ShaderWithNonWritableTarget("%param_pimstor"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarStorageImageGood) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_imstor"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarSampledImageBad) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_imsam"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Target of NonWritable decoration is invalid: must " |
| "point to a storage image, uniform block, or storage " |
| "buffer\n %var_imsam")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarUboGood) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_ubo"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarSsboInUniformGood) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpDecorate %struct_bb BufferBlock |
| OpMemberDecorate %struct_bb 0 Offset 0 |
| OpDecorate %var_ssbo_u NonWritable |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct_bb = OpTypeStruct %float |
| %_ptr_Uniform_stbb = OpTypePointer Uniform %struct_bb |
| %var_ssbo_u = OpVariable %_ptr_Uniform_stbb Uniform |
| %main = OpFunction %void None %void_fn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarSsboInStorageBufferGood) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_ssbo_sb"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableMemberOfSsboInStorageBufferGood) { |
| std::string spirv = ShaderWithNonWritableTarget("%struct_b_rtarr", true); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableMemberOfStructGood) { |
| std::string spirv = ShaderWithNonWritableTarget("%simple_struct", true); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarWorkgroupBad) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_wg"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Target of NonWritable decoration is invalid: must " |
| "point to a storage image, uniform block, or storage " |
| "buffer\n %var_wg")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarWorkgroupV14Bad) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_wg"); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Target of NonWritable decoration is invalid: must " |
| "point to a storage image, uniform block, storage " |
| "buffer, or variable in Private or Function storage " |
| "class\n %var_wg")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarPrivateBad) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_priv"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Target of NonWritable decoration is invalid: must " |
| "point to a storage image, uniform block, or storage " |
| "buffer\n %var_priv")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarPrivateV13Bad) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_priv"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Target of NonWritable decoration is invalid: must " |
| "point to a storage image, uniform block, or storage " |
| "buffer\n %var_priv")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarPrivateV14Good) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_priv"); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarPrivateV13TargetV14Bad) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_priv"); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Target of NonWritable decoration is invalid: must " |
| "point to a storage image, uniform block, or storage " |
| "buffer\n %var_priv")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarFunctionBad) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_func"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Target of NonWritable decoration is invalid: must " |
| "point to a storage image, uniform block, or storage " |
| "buffer\n %var_func")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableArrayGood) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_array_imstor"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableRuntimeArrayGood) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_rta_imstor"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_P(ValidateVulkanCombineDecorationResult, Decorate) { |
| const char* const decoration = std::get<0>(GetParam()); |
| const char* const vuid = std::get<1>(GetParam()); |
| const TestResult& test_result = std::get<2>(GetParam()); |
| |
| CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); |
| generator.before_types_ = "OpDecorate %u32 "; |
| generator.before_types_ += decoration; |
| generator.before_types_ += "\n"; |
| |
| EntryPoint entry_point; |
| entry_point.name = "main"; |
| entry_point.execution_model = "Vertex"; |
| generator.entry_points_.push_back(std::move(entry_point)); |
| |
| CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); |
| ASSERT_EQ(test_result.validation_result, |
| ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| if (!test_result.error_str.empty()) { |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); |
| } |
| if (vuid) { |
| EXPECT_THAT(getDiagnosticString(), AnyVUID(vuid)); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| DecorationAllowListFailure, ValidateVulkanCombineDecorationResult, |
| Combine(Values("GLSLShared", "GLSLPacked"), |
| Values("VUID-StandaloneSpirv-GLSLShared-04669"), |
| Values(TestResult( |
| SPV_ERROR_INVALID_ID, |
| "is not valid for the Vulkan execution environment.")))); |
| |
| TEST_F(ValidateDecorations, NonWritableVarFunctionV13Bad) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_func"); |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Target of NonWritable decoration is invalid: must " |
| "point to a storage image, uniform block, or storage " |
| "buffer\n %var_func")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarFunctionV14Good) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_func"); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, NonWritableVarFunctionV13TargetV14Bad) { |
| std::string spirv = ShaderWithNonWritableTarget("%var_func"); |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Target of NonWritable decoration is invalid: must " |
| "point to a storage image, uniform block, or storage " |
| "buffer\n %var_func")); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockV13ValV14Good) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %1 BufferBlock |
| %1 = OpTypeStruct |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| } |
| |
| TEST_F(ValidateDecorations, BufferBlockV14Bad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %1 BufferBlock |
| %1 = OpTypeStruct |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_ERROR_WRONG_VERSION, |
| ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("2nd operand of Decorate: operand BufferBlock(3) " |
| "requires SPIR-V version 1.3 or earlier")); |
| } |
| |
| // Component |
| |
| TEST_F(ValidateDecorations, ComponentDecorationBadTarget) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpDecorate %t Component 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %t = OpTypeVector %float 2 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("must be a memory object declaration")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationBadStorageClass) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpDecorate %v Component 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %t = OpTypeVector %float 2 |
| %ptr_private = OpTypePointer Private %t |
| %v = OpVariable %ptr_private Private |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Target of Component decoration is invalid: must " |
| "point to a Storage Class of Input(1) or Output(3)")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationBadTypeVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Matrix |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpDecorate %v Component 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %vtype = OpTypeVector %float 4 |
| %t = OpTypeMatrix %vtype 4 |
| %ptr_input = OpTypePointer Input %t |
| %v = OpVariable %ptr_input Input |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Component-04924")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Component decoration specified for type")); |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a scalar or vector")); |
| } |
| |
| std::string ShaderWithComponentDecoration(const std::string& type, |
| const std::string& decoration) { |
| return R"( |
| OpCapability Shader |
| OpCapability Int64 |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %entryPointOutput |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %entryPointOutput Location 0 |
| OpDecorate %entryPointOutput )" + |
| decoration + R"( |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %v4float = OpTypeVector %float 4 |
| %uint = OpTypeInt 32 0 |
| %uint64 = OpTypeInt 64 0 |
| %v2uint64 = OpTypeVector %uint64 2 |
| %v3uint64 = OpTypeVector %uint64 3 |
| %uint_2 = OpConstant %uint 2 |
| %arr_v3float_uint_2 = OpTypeArray %v3float %uint_2 |
| %float_0 = OpConstant %float 0 |
| %_ptr_Output_type = OpTypePointer Output %)" + type + R"( |
| %entryPointOutput = OpVariable %_ptr_Output_type Output |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationIntGood0Vulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("uint", "Component 0"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationIntGood1Vulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("uint", "Component 1"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationIntGood2Vulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("uint", "Component 2"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationIntGood3Vulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("uint", "Component 3"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationIntBad4Vulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("uint", "Component 4"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Component-04920")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Component decoration value must not be greater than 3")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationVector3GoodVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("v3float", "Component 1"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationVector4GoodVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("v4float", "Component 0"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationVector4Bad1Vulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("v4float", "Component 1"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Component-04921")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Sequence of components starting with 1 " |
| "and ending with 4 gets larger than 3")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationVector4Bad3Vulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("v4float", "Component 3"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Component-04921")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Sequence of components starting with 3 " |
| "and ending with 6 gets larger than 3")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationArrayGoodVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = |
| ShaderWithComponentDecoration("arr_v3float_uint_2", "Component 1"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationArrayBadVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = |
| ShaderWithComponentDecoration("arr_v3float_uint_2", "Component 2"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Component-04921")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Sequence of components starting with 2 " |
| "and ending with 4 gets larger than 3")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecoration64ScalarGoodVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("uint64", "Component 0"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecoration64Scalar1BadVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("uint64", "Component 1"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Component-04923")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Component decoration value must not be 1 or 3 for " |
| "64-bit data types")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecoration64Scalar2GoodVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("uint64", "Component 2"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecoration64Scalar3BadVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("uint64", "Component 3"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Component-04923")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Component decoration value must not be 1 or 3 for " |
| "64-bit data types")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecoration64Vec0GoodVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("v2uint64", "Component 0"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecoration64Vec1BadVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("v2uint64", "Component 1"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Component-04923")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Component decoration value must not be 1 or 3 for " |
| "64-bit data types")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecoration64Vec2BadVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("v2uint64", "Component 2"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Component-04922")); |
| HasSubstr( |
| "Sequence of components starting with 2 " |
| "and ending with 6 gets larger than 3"); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecoration64VecWideBadVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = ShaderWithComponentDecoration("v3uint64", "Component 0"); |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Component-07703")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Component decoration only allowed on 64-bit scalar " |
| "and 2-component vector")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationBlockGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" %9 %12 |
| OpExecutionMode %4 OriginUpperLeft |
| OpDecorate %9 Location 0 |
| OpMemberDecorate %block 0 Location 2 |
| OpMemberDecorate %block 0 Component 1 |
| OpDecorate %block Block |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %float = OpTypeFloat 32 |
| %vec3 = OpTypeVector %float 3 |
| %8 = OpTypePointer Output %vec3 |
| %9 = OpVariable %8 Output |
| %block = OpTypeStruct %vec3 |
| %11 = OpTypePointer Input %block |
| %12 = OpVariable %11 Input |
| %int = OpTypeInt 32 1 |
| %14 = OpConstant %int 0 |
| %15 = OpTypePointer Input %vec3 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %16 = OpAccessChain %15 %12 %14 |
| %17 = OpLoad %vec3 %16 |
| OpStore %9 %17 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), Eq("")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationBlockBadVulkan) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" %9 %12 |
| OpExecutionMode %4 OriginUpperLeft |
| OpDecorate %9 Location 0 |
| OpMemberDecorate %block 0 Location 2 |
| OpMemberDecorate %block 0 Component 2 |
| OpDecorate %block Block |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %float = OpTypeFloat 32 |
| %vec3 = OpTypeVector %float 3 |
| %8 = OpTypePointer Output %vec3 |
| %9 = OpVariable %8 Output |
| %block = OpTypeStruct %vec3 |
| %11 = OpTypePointer Input %block |
| %12 = OpVariable %11 Input |
| %int = OpTypeInt 32 1 |
| %14 = OpConstant %int 0 |
| %15 = OpTypePointer Input %vec3 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %16 = OpAccessChain %15 %12 %14 |
| %17 = OpLoad %vec3 %16 |
| OpStore %9 %17 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Component-04921")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Sequence of components starting with 2 " |
| "and ending with 4 gets larger than 3")); |
| } |
| |
| TEST_F(ValidateDecorations, ComponentDecorationFunctionParameter) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| |
| OpDecorate %param_f Component 0 |
| |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %float_0 = OpConstant %float 0 |
| %int = OpTypeInt 32 0 |
| %int_2 = OpConstant %int 2 |
| %struct_b = OpTypeStruct %float |
| |
| %extra_fn = OpTypeFunction %void %float |
| |
| %helper = OpFunction %void None %extra_fn |
| %param_f = OpFunctionParameter %float |
| %helper_label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| %main = OpFunction %void None %void_fn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanStorageBufferBlock) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| %void = OpTypeVoid |
| %uint = OpTypeInt 32 0 |
| %struct = OpTypeStruct %uint |
| %ptr_ssbo = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr_ssbo StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanStorageBufferMissingBlock) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| %void = OpTypeVoid |
| %uint = OpTypeInt 32 0 |
| %struct = OpTypeStruct %uint |
| %ptr_ssbo = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr_ssbo StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("From Vulkan spec:\nSuch variables " |
| "must be identified with a Block decoration")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanStorageBufferArrayMissingBlock) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| %void = OpTypeVoid |
| %uint = OpTypeInt 32 0 |
| %uint_4 = OpConstant %uint 4 |
| %struct = OpTypeStruct %uint |
| %array = OpTypeArray %struct %uint_4 |
| %ptr_ssbo = OpTypePointer StorageBuffer %array |
| %var = OpVariable %ptr_ssbo StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("From Vulkan spec:\nSuch variables " |
| "must be identified with a Block decoration")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanStorageBufferRuntimeArrayMissingBlock) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability RuntimeDescriptorArrayEXT |
| OpExtension "SPV_EXT_descriptor_indexing" |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| %void = OpTypeVoid |
| %uint = OpTypeInt 32 0 |
| %struct = OpTypeStruct %uint |
| %array = OpTypeRuntimeArray %struct |
| %ptr_ssbo = OpTypePointer StorageBuffer %array |
| %var = OpVariable %ptr_ssbo StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("From Vulkan spec:\nSuch variables " |
| "must be identified with a Block decoration")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanUniformBlock) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| %void = OpTypeVoid |
| %uint = OpTypeInt 32 0 |
| %struct = OpTypeStruct %uint |
| %ptr_ubo = OpTypePointer Uniform %struct |
| %var = OpVariable %ptr_ubo Uniform |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanUniformBufferBlock) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %struct BufferBlock |
| OpMemberDecorate %struct 0 Offset 0 |
| %void = OpTypeVoid |
| %uint = OpTypeInt 32 0 |
| %struct = OpTypeStruct %uint |
| %ptr_ubo = OpTypePointer Uniform %struct |
| %var = OpVariable %ptr_ubo Uniform |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanUniformMissingBlock) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| %void = OpTypeVoid |
| %uint = OpTypeInt 32 0 |
| %struct = OpTypeStruct %uint |
| %ptr_ubo = OpTypePointer Uniform %struct |
| %var = OpVariable %ptr_ubo Uniform |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Uniform-06676")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("From Vulkan spec:\nSuch variables must be " |
| "identified with a Block or BufferBlock decoration")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanUniformArrayMissingBlock) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| %void = OpTypeVoid |
| %uint = OpTypeInt 32 0 |
| %uint_4 = OpConstant %uint 4 |
| %struct = OpTypeStruct %uint |
| %array = OpTypeArray %struct %uint_4 |
| %ptr_ubo = OpTypePointer Uniform %array |
| %var = OpVariable %ptr_ubo Uniform |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Uniform-06676")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("From Vulkan spec:\nSuch variables must be " |
| "identified with a Block or BufferBlock decoration")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanUniformRuntimeArrayMissingBlock) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability RuntimeDescriptorArrayEXT |
| OpExtension "SPV_EXT_descriptor_indexing" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| %void = OpTypeVoid |
| %uint = OpTypeInt 32 0 |
| %struct = OpTypeStruct %uint |
| %array = OpTypeRuntimeArray %struct |
| %ptr_ubo = OpTypePointer Uniform %array |
| %var = OpVariable %ptr_ubo Uniform |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Uniform-06676")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("From Vulkan spec:\nSuch variables must be " |
| "identified with a Block or BufferBlock decoration")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanArrayStrideZero) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| OpDecorate %array ArrayStride 0 |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 0 |
| %int_4 = OpConstant %int 4 |
| %array = OpTypeArray %int %int_4 |
| %struct = OpTypeStruct %array |
| %ptr_ssbo_struct = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr_ssbo_struct StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("contains an array with stride 0")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanArrayStrideTooSmall) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| OpDecorate %struct Block |
| OpMemberDecorate %struct 0 Offset 0 |
| OpDecorate %inner ArrayStride 4 |
| OpDecorate %outer ArrayStride 4 |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 0 |
| %int_4 = OpConstant %int 4 |
| %inner = OpTypeArray %int %int_4 |
| %outer = OpTypeArray %inner %int_4 |
| %struct = OpTypeStruct %outer |
| %ptr_ssbo_struct = OpTypePointer StorageBuffer %struct |
| %var = OpVariable %ptr_ssbo_struct StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "contains an array with stride 4, but with an element size of 16")); |
| } |
| |
| TEST_F(ValidateDecorations, FunctionsWithOpGroupDecorate) { |
| std::string spirv = R"( |
| OpCapability Addresses |
| OpCapability Linkage |
| OpCapability Kernel |
| OpCapability Int8 |
| %1 = OpExtInstImport "OpenCL.std" |
| OpMemoryModel Physical32 OpenCL |
| OpName %foo "foo" |
| OpName %entry "entry" |
| OpName %bar "bar" |
| OpName %entry_0 "entry" |
| OpName %k "k" |
| OpName %entry_1 "entry" |
| OpName %b "b" |
| OpDecorate %28 FuncParamAttr Zext |
| %28 = OpDecorationGroup |
| OpDecorate %k LinkageAttributes "k" Export |
| OpDecorate %foo LinkageAttributes "foo" Export |
| OpDecorate %bar LinkageAttributes "bar" Export |
| OpDecorate %b Alignment 1 |
| OpGroupDecorate %28 %foo %bar |
| %uchar = OpTypeInt 8 0 |
| %bool = OpTypeBool |
| %3 = OpTypeFunction %bool |
| %void = OpTypeVoid |
| %10 = OpTypeFunction %void |
| %_ptr_Function_uchar = OpTypePointer Function %uchar |
| %true = OpConstantTrue %bool |
| %foo = OpFunction %bool DontInline %3 |
| %entry = OpLabel |
| OpReturnValue %true |
| OpFunctionEnd |
| %bar = OpFunction %bool DontInline %3 |
| %entry_0 = OpLabel |
| OpReturnValue %true |
| OpFunctionEnd |
| %k = OpFunction %void DontInline %10 |
| %entry_1 = OpLabel |
| %b = OpVariable %_ptr_Function_uchar Function |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); |
| } |
| |
| TEST_F(ValidateDecorations, LocationVariableGood) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %in_var Location 0 |
| %float = OpTypeFloat 32 |
| %ptr_input_float = OpTypePointer Input %float |
| %in_var = OpVariable %ptr_input_float Input |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateDecorations, LocationStructMemberGood) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpMemberDecorate %struct 0 Location 0 |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| TEST_F(ValidateDecorations, LocationStructBad) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %struct Location 0 |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); |
| } |
| |
| TEST_F(ValidateDecorations, LocationFloatBad) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %float Location 0 |
| %float = OpTypeFloat 32 |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupSingleBlockVariable) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability WorkgroupMemoryExplicitLayoutKHR |
| OpExtension "SPV_KHR_workgroup_memory_explicit_layout" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %_ |
| OpExecutionMode %main LocalSize 8 1 1 |
| OpMemberDecorate %first 0 Offset 0 |
| OpDecorate %first Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %first = OpTypeStruct %int |
| %_ptr_Workgroup_first = OpTypePointer Workgroup %first |
| %_ = OpVariable %_ptr_Workgroup_first Workgroup |
| %int_0 = OpConstant %int 0 |
| %int_2 = OpConstant %int 2 |
| %_ptr_Workgroup_int = OpTypePointer Workgroup %int |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 |
| OpStore %13 %int_2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupBlockVariableRequiresV14) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability WorkgroupMemoryExplicitLayoutKHR |
| OpExtension "SPV_KHR_workgroup_memory_explicit_layout" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %_ |
| OpExecutionMode %main LocalSize 8 1 1 |
| OpMemberDecorate %first 0 Offset 0 |
| OpDecorate %first Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %first = OpTypeStruct %int |
| %_ptr_Workgroup_first = OpTypePointer Workgroup %first |
| %_ = OpVariable %_ptr_Workgroup_first Workgroup |
| %int_0 = OpConstant %int 0 |
| %int_2 = OpConstant %int 2 |
| %_ptr_Workgroup_int = OpTypePointer Workgroup %int |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 |
| OpStore %13 %int_2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); |
| EXPECT_EQ(SPV_ERROR_WRONG_VERSION, |
| ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("requires SPIR-V version 1.4 or later")); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupSingleNonBlockVariable) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %a |
| OpExecutionMode %main LocalSize 8 1 1 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Workgroup_int = OpTypePointer Workgroup %int |
| %a = OpVariable %_ptr_Workgroup_int Workgroup |
| %int_2 = OpConstant %int 2 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpStore %a %int_2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupMultiBlockVariable) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability WorkgroupMemoryExplicitLayoutKHR |
| OpExtension "SPV_KHR_workgroup_memory_explicit_layout" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %_ %__0 |
| OpExecutionMode %main LocalSize 8 1 1 |
| OpMemberDecorate %first 0 Offset 0 |
| OpDecorate %first Block |
| OpMemberDecorate %second 0 Offset 0 |
| OpDecorate %second Block |
| OpDecorate %_ Aliased |
| OpDecorate %__0 Aliased |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %first = OpTypeStruct %int |
| %_ptr_Workgroup_first = OpTypePointer Workgroup %first |
| %_ = OpVariable %_ptr_Workgroup_first Workgroup |
| %int_0 = OpConstant %int 0 |
| %int_2 = OpConstant %int 2 |
| %_ptr_Workgroup_int = OpTypePointer Workgroup %int |
| %second = OpTypeStruct %int |
| %_ptr_Workgroup_second = OpTypePointer Workgroup %second |
| %__0 = OpVariable %_ptr_Workgroup_second Workgroup |
| %int_3 = OpConstant %int 3 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 |
| OpStore %13 %int_2 |
| %18 = OpAccessChain %_ptr_Workgroup_int %__0 %int_0 |
| OpStore %18 %int_3 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupBlockVariableWith8BitType) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Int8 |
| OpCapability WorkgroupMemoryExplicitLayout8BitAccessKHR |
| OpExtension "SPV_KHR_workgroup_memory_explicit_layout" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %_ |
| OpExecutionMode %main LocalSize 2 1 1 |
| OpMemberDecorate %first 0 Offset 0 |
| OpDecorate %first Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %char = OpTypeInt 8 1 |
| %first = OpTypeStruct %char |
| %_ptr_Workgroup_first = OpTypePointer Workgroup %first |
| %_ = OpVariable %_ptr_Workgroup_first Workgroup |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %char_2 = OpConstant %char 2 |
| %_ptr_Workgroup_char = OpTypePointer Workgroup %char |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %14 = OpAccessChain %_ptr_Workgroup_char %_ %int_0 |
| OpStore %14 %char_2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupMultiNonBlockVariable) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %a %b |
| OpExecutionMode %main LocalSize 8 1 1 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Workgroup_int = OpTypePointer Workgroup %int |
| %a = OpVariable %_ptr_Workgroup_int Workgroup |
| %int_2 = OpConstant %int 2 |
| %b = OpVariable %_ptr_Workgroup_int Workgroup |
| %int_3 = OpConstant %int 3 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpStore %a %int_2 |
| OpStore %b %int_3 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupBlockVariableWith16BitType) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Float16 |
| OpCapability Int16 |
| OpCapability WorkgroupMemoryExplicitLayout16BitAccessKHR |
| OpExtension "SPV_KHR_workgroup_memory_explicit_layout" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %_ |
| OpExecutionMode %main LocalSize 2 1 1 |
| OpMemberDecorate %first 0 Offset 0 |
| OpMemberDecorate %first 1 Offset 2 |
| OpDecorate %first Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %short = OpTypeInt 16 1 |
| %half = OpTypeFloat 16 |
| %first = OpTypeStruct %short %half |
| %_ptr_Workgroup_first = OpTypePointer Workgroup %first |
| %_ = OpVariable %_ptr_Workgroup_first Workgroup |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %short_3 = OpConstant %short 3 |
| %_ptr_Workgroup_short = OpTypePointer Workgroup %short |
| %int_1 = OpConstant %int 1 |
| %half_0x1_898p_3 = OpConstant %half 0x1.898p+3 |
| %_ptr_Workgroup_half = OpTypePointer Workgroup %half |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %15 = OpAccessChain %_ptr_Workgroup_short %_ %int_0 |
| OpStore %15 %short_3 |
| %19 = OpAccessChain %_ptr_Workgroup_half %_ %int_1 |
| OpStore %19 %half_0x1_898p_3 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupBlockVariableScalarLayout) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability WorkgroupMemoryExplicitLayoutKHR |
| OpExtension "SPV_KHR_workgroup_memory_explicit_layout" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" %B |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpMemberDecorate %S 2 Offset 16 |
| OpMemberDecorate %S 3 Offset 28 |
| OpDecorate %S Block |
| OpDecorate %B Aliased |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %float %v3float %v3float %v3float |
| %_ptr_Workgroup_S = OpTypePointer Workgroup %S |
| %B = OpVariable %_ptr_Workgroup_S Workgroup |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| spvValidatorOptionsSetWorkgroupScalarBlockLayout(getValidatorOptions(), true); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)) |
| << getDiagnosticString(); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupMixBlockAndNonBlockBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability WorkgroupMemoryExplicitLayoutKHR |
| OpExtension "SPV_KHR_workgroup_memory_explicit_layout" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %_ %b |
| OpExecutionMode %main LocalSize 8 1 1 |
| OpMemberDecorate %first 0 Offset 0 |
| OpDecorate %first Block |
| OpDecorate %_ Aliased |
| OpDecorate %b Aliased |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %first = OpTypeStruct %int |
| %_ptr_Workgroup_first = OpTypePointer Workgroup %first |
| %_ = OpVariable %_ptr_Workgroup_first Workgroup |
| %int_0 = OpConstant %int 0 |
| %int_2 = OpConstant %int 2 |
| %_ptr_Workgroup_int = OpTypePointer Workgroup %int |
| %b = OpVariable %_ptr_Workgroup_int Workgroup |
| %int_3 = OpConstant %int 3 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 |
| OpStore %13 %int_2 |
| OpStore %b %int_3 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, |
| ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("either all or none of the Workgroup Storage Class variables " |
| "in the entry point interface must point to struct types " |
| "decorated with Block")); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupMultiBlockVariableMissingAliased) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability WorkgroupMemoryExplicitLayoutKHR |
| OpExtension "SPV_KHR_workgroup_memory_explicit_layout" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %_ %__0 |
| OpExecutionMode %main LocalSize 8 1 1 |
| OpMemberDecorate %first 0 Offset 0 |
| OpDecorate %first Block |
| OpMemberDecorate %second 0 Offset 0 |
| OpDecorate %second Block |
| OpDecorate %_ Aliased |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %first = OpTypeStruct %int |
| %_ptr_Workgroup_first = OpTypePointer Workgroup %first |
| %_ = OpVariable %_ptr_Workgroup_first Workgroup |
| %int_0 = OpConstant %int 0 |
| %int_2 = OpConstant %int 2 |
| %_ptr_Workgroup_int = OpTypePointer Workgroup %int |
| %second = OpTypeStruct %int |
| %_ptr_Workgroup_second = OpTypePointer Workgroup %second |
| %__0 = OpVariable %_ptr_Workgroup_second Workgroup |
| %int_3 = OpConstant %int 3 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 |
| OpStore %13 %int_2 |
| %18 = OpAccessChain %_ptr_Workgroup_int %__0 %int_0 |
| OpStore %18 %int_3 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, |
| ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("more than one Workgroup Storage Class variable in the " |
| "entry point interface point to a type decorated with Block, " |
| "all of them must be decorated with Aliased")); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableNotAStruct) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability WorkgroupMemoryExplicitLayoutKHR |
| OpExtension "SPV_KHR_workgroup_memory_explicit_layout" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %_ |
| OpExecutionMode %main LocalSize 8 1 1 |
| OpDecorate %first Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %int_3 = OpConstant %int 3 |
| %first = OpTypeArray %int %int_3 |
| %_ptr_Workgroup_first = OpTypePointer Workgroup %first |
| %_ = OpVariable %_ptr_Workgroup_first Workgroup |
| %int_0 = OpConstant %int 0 |
| %int_2 = OpConstant %int 2 |
| %_ptr_Workgroup_int = OpTypePointer Workgroup %int |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 |
| OpStore %13 %int_2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableMissingLayout) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability WorkgroupMemoryExplicitLayoutKHR |
| OpExtension "SPV_KHR_workgroup_memory_explicit_layout" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %_ |
| OpExecutionMode %main LocalSize 8 1 1 |
| OpDecorate %first Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %first = OpTypeStruct %int |
| %_ptr_Workgroup_first = OpTypePointer Workgroup %first |
| %_ = OpVariable %_ptr_Workgroup_first Workgroup |
| %int_0 = OpConstant %int 0 |
| %int_2 = OpConstant %int 2 |
| %_ptr_Workgroup_int = OpTypePointer Workgroup %int |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 |
| OpStore %13 %int_2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Block must be explicitly laid out with Offset decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableBadLayout) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability WorkgroupMemoryExplicitLayoutKHR |
| OpExtension "SPV_KHR_workgroup_memory_explicit_layout" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" %_ |
| OpExecutionMode %main LocalSize 8 1 1 |
| OpMemberDecorate %first 0 Offset 1 |
| OpDecorate %first Block |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %first = OpTypeStruct %int |
| %_ptr_Workgroup_first = OpTypePointer Workgroup %first |
| %_ = OpVariable %_ptr_Workgroup_first Workgroup |
| %int_0 = OpConstant %int 0 |
| %int_2 = OpConstant %int 2 |
| %_ptr_Workgroup_int = OpTypePointer Workgroup %int |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 |
| OpStore %13 %int_2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("Block for variable in Workgroup storage class must follow " |
| "relaxed storage buffer layout rules: " |
| "member 0 at offset 1 is not aligned to 4")); |
| } |
| |
| TEST_F(ValidateDecorations, BadMatrixStrideUniform) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Offset 0 |
| OpMemberDecorate %block 0 MatrixStride 3 |
| OpMemberDecorate %block 0 ColMajor |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %float = OpTypeFloat 32 |
| %float4 = OpTypeVector %float 4 |
| %matrix4x4 = OpTypeMatrix %float4 4 |
| %block = OpTypeStruct %matrix4x4 |
| %block_ptr = OpTypePointer Uniform %block |
| %var = OpVariable %block_ptr Uniform |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 2 decorated as Block for variable in Uniform storage " |
| "class must follow standard uniform buffer layout rules: member 0 is " |
| "a matrix with stride 3 not satisfying alignment to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, BadMatrixStrideStorageBuffer) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Offset 0 |
| OpMemberDecorate %block 0 MatrixStride 3 |
| OpMemberDecorate %block 0 ColMajor |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %float = OpTypeFloat 32 |
| %float4 = OpTypeVector %float 4 |
| %matrix4x4 = OpTypeMatrix %float4 4 |
| %block = OpTypeStruct %matrix4x4 |
| %block_ptr = OpTypePointer StorageBuffer %block |
| %var = OpVariable %block_ptr StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 2 decorated as Block for variable in StorageBuffer " |
| "storage class must follow standard storage buffer layout rules: " |
| "member 0 is a matrix with stride 3 not satisfying alignment to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, BadMatrixStridePushConstant) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Offset 0 |
| OpMemberDecorate %block 0 MatrixStride 3 |
| OpMemberDecorate %block 0 ColMajor |
| %void = OpTypeVoid |
| %float = OpTypeFloat 32 |
| %float4 = OpTypeVector %float 4 |
| %matrix4x4 = OpTypeMatrix %float4 4 |
| %block = OpTypeStruct %matrix4x4 |
| %block_ptr = OpTypePointer PushConstant %block |
| %var = OpVariable %block_ptr PushConstant |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 2 decorated as Block for variable in PushConstant " |
| "storage class must follow standard storage buffer layout rules: " |
| "member 0 is a matrix with stride 3 not satisfying alignment to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, BadMatrixStrideStorageBufferScalarLayout) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Offset 0 |
| OpMemberDecorate %block 0 MatrixStride 3 |
| OpMemberDecorate %block 0 RowMajor |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %float = OpTypeFloat 32 |
| %float4 = OpTypeVector %float 4 |
| %matrix4x4 = OpTypeMatrix %float4 4 |
| %block = OpTypeStruct %matrix4x4 |
| %block_ptr = OpTypePointer StorageBuffer %block |
| %var = OpVariable %block_ptr StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| options_->scalar_block_layout = true; |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Structure id 2 decorated as Block for variable in StorageBuffer " |
| "storage class must follow scalar storage buffer layout rules: " |
| "member 0 is a matrix with stride 3 not satisfying alignment to 4")); |
| } |
| |
| TEST_F(ValidateDecorations, MissingOffsetStructNestedInArray) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %array ArrayStride 4 |
| OpDecorate %outer Block |
| OpMemberDecorate %outer 0 Offset 0 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 0 |
| %int_4 = OpConstant %int 4 |
| %inner = OpTypeStruct %int |
| %array = OpTypeArray %inner %int_4 |
| %outer = OpTypeStruct %array |
| %ptr_ssbo_outer = OpTypePointer StorageBuffer %outer |
| %var = OpVariable %ptr_ssbo_outer StorageBuffer |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("Structure id 3 decorated as Block must be explicitly " |
| "laid out with Offset decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, AllOnesOffset) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| OpDecorate %outer Block |
| OpMemberDecorate %outer 0 Offset 0 |
| OpMemberDecorate %struct 0 Offset 4294967295 |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 0 |
| %struct = OpTypeStruct %int |
| %outer = OpTypeStruct %struct |
| %ptr = OpTypePointer Uniform %outer |
| %var = OpVariable %ptr Uniform |
| %void_fn = OpTypeFunction %void |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("decorated as Block must be explicitly laid out with " |
| "Offset decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, PerVertexVulkanGood) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability FragmentBarycentricKHR |
| OpExtension "SPV_KHR_fragment_shader_barycentric" |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %vertexIDs |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %vertexIDs Location 0 |
| OpDecorate %vertexIDs PerVertexKHR |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %uint = OpTypeInt 32 0 |
| %ptrFloat = OpTypePointer Input %float |
| %uint_3 = OpConstant %uint 3 |
| %floatArray = OpTypeArray %float %uint_3 |
| %ptrFloatArray = OpTypePointer Input %floatArray |
| %vertexIDs = OpVariable %ptrFloatArray Input |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %main = OpFunction %void None %func |
| %label = OpLabel |
| %access = OpAccessChain %ptrFloat %vertexIDs %int_0 |
| %load = OpLoad %float %access |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, PerVertexVulkanOutput) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability FragmentBarycentricKHR |
| OpExtension "SPV_KHR_fragment_shader_barycentric" |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %vertexIDs |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %vertexIDs Location 0 |
| OpDecorate %vertexIDs PerVertexKHR |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %uint = OpTypeInt 32 0 |
| %ptrFloat = OpTypePointer Output %float |
| %uint_3 = OpConstant %uint 3 |
| %floatArray = OpTypeArray %float %uint_3 |
| %ptrFloatArray = OpTypePointer Output %floatArray |
| %vertexIDs = OpVariable %ptrFloatArray Output |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %main = OpFunction %void None %func |
| %label = OpLabel |
| %access = OpAccessChain %ptrFloat %vertexIDs %int_0 |
| %load = OpLoad %float %access |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-PerVertexKHR-06777")); |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("storage class must be Input")); |
| } |
| |
| TEST_F(ValidateDecorations, PerVertexVulkanNonFragment) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability FragmentBarycentricKHR |
| OpExtension "SPV_KHR_fragment_shader_barycentric" |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" %vertexIDs |
| OpDecorate %vertexIDs Location 0 |
| OpDecorate %vertexIDs PerVertexKHR |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %uint = OpTypeInt 32 0 |
| %ptrFloat = OpTypePointer Input %float |
| %uint_3 = OpConstant %uint 3 |
| %floatArray = OpTypeArray %float %uint_3 |
| %ptrFloatArray = OpTypePointer Input %floatArray |
| %vertexIDs = OpVariable %ptrFloatArray Input |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %main = OpFunction %void None %func |
| %label = OpLabel |
| %access = OpAccessChain %ptrFloat %vertexIDs %int_0 |
| %load = OpLoad %float %access |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-PerVertexKHR-06777")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "PerVertexKHR can only be applied to Fragment Execution Models")); |
| } |
| |
| TEST_F(ValidateDecorations, PerVertexVulkanNonArray) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability FragmentBarycentricKHR |
| OpExtension "SPV_KHR_fragment_shader_barycentric" |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %vertexIDs |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %vertexIDs Location 0 |
| OpDecorate %vertexIDs PerVertexKHR |
| %void = OpTypeVoid |
| %func = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %ptrFloat = OpTypePointer Input %float |
| %vertexIDs = OpVariable %ptrFloat Input |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %main = OpFunction %void None %func |
| %label = OpLabel |
| %load = OpLoad %float %vertexIDs |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Input-06778")); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("PerVertexKHR must be declared as arrays")); |
| } |
| |
| TEST_F(ValidateDecorations, RelaxedPrecisionDecorationOnNumericTypeBad) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %float RelaxedPrecision |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("RelaxPrecision decoration cannot be applied to a type")); |
| } |
| |
| TEST_F(ValidateDecorations, RelaxedPrecisionDecorationOnStructMember) { |
| const spv_target_env env = SPV_ENV_VULKAN_1_0; |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpMemberDecorate %struct 0 RelaxedPrecision |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %struct = OpTypeStruct %float |
| %main = OpFunction %void None %voidfn |
| %label = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, env); |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanFlatMultipleInterfaceGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Geometry |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %layer %gl_Layer |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %layer Location 0 |
| OpDecorate %gl_Layer Flat |
| OpDecorate %gl_Layer BuiltIn Layer |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Output_int = OpTypePointer Output %int |
| %layer = OpVariable %_ptr_Output_int Output |
| %_ptr_Input_int = OpTypePointer Input %int |
| %gl_Layer = OpVariable %_ptr_Input_int Input |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %11 = OpLoad %int %gl_Layer |
| OpStore %layer %11 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanFlatMultipleInterfaceBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Geometry |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %layer %gl_Layer |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %layer Location 0 |
| OpDecorate %gl_Layer BuiltIn Layer |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Output_int = OpTypePointer Output %int |
| %layer = OpVariable %_ptr_Output_int Output |
| %_ptr_Input_int = OpTypePointer Input %int |
| %gl_Layer = OpVariable %_ptr_Input_int Input |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %11 = OpLoad %int %gl_Layer |
| OpStore %layer %11 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Flat-04744")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Fragment OpEntryPoint operand 4 with Input interfaces with integer " |
| "or float type must have a Flat decoration for Entry Point id 2.")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanNoFlatFloat32) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %in |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %in Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %_ptr_Function_float = OpTypePointer Function %float |
| %_ptr_Input_float = OpTypePointer Input %float |
| %in = OpVariable %_ptr_Input_float Input |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %b = OpVariable %_ptr_Function_float Function |
| %11 = OpLoad %float %in |
| OpStore %b %11 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanNoFlatFloat64) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Float64 |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %in |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %in Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %double = OpTypeFloat 64 |
| %_ptr_Function_double = OpTypePointer Function %double |
| %_ptr_Input_double = OpTypePointer Input %double |
| %in = OpVariable %_ptr_Input_double Input |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %b = OpVariable %_ptr_Function_double Function |
| %11 = OpLoad %double %in |
| OpStore %b %11 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Flat-04744")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Fragment OpEntryPoint operand 3 with Input interfaces with integer " |
| "or float type must have a Flat decoration for Entry Point id 2.")); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanNoFlatVectorFloat64) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpCapability Float64 |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %in |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %in Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %double = OpTypeFloat 64 |
| %v2double = OpTypeVector %double 2 |
| %_ptr_Function_v2double = OpTypePointer Function %v2double |
| %_ptr_Input_v2double = OpTypePointer Input %v2double |
| %in = OpVariable %_ptr_Input_v2double Input |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %b = OpVariable %_ptr_Function_v2double Function |
| %11 = OpLoad %v2double %in |
| OpStore %b %11 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_SUCCESS, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| TEST_F(ValidateDecorations, VulkanNoFlatIntVector) { |
| std::string spirv = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %in |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %in Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %v2int = OpTypeVector %int 2 |
| %_ptr_Function_v2int = OpTypePointer Function %v2int |
| %_ptr_Input_v2int = OpTypePointer Input %v2int |
| %in = OpVariable %_ptr_Input_v2int Input |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %b = OpVariable %_ptr_Function_v2int Function |
| %12 = OpLoad %v2int %in |
| OpStore %b %12 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Flat-04744")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "Fragment OpEntryPoint operand 3 with Input interfaces with integer " |
| "or float type must have a Flat decoration for Entry Point id 2.")); |
| } |
| |
| TEST_P(ValidateDecorationString, VulkanOutputInvalidInterface) { |
| const std::string decoration = GetParam(); |
| std::stringstream ss; |
| ss << R"( |
| OpCapability Shader |
| OpCapability SampleRateShading |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %out |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpDecorate %out )" |
| << decoration << R"( |
| OpDecorate %out Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Output_int = OpTypePointer Output %int |
| %out = OpVariable %_ptr_Output_int Output |
| %int_1 = OpConstant %int 1 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpStore %out %int_1 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(ss.str(), SPV_ENV_VULKAN_1_0); |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Flat-06201")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("decorated variable must not be used in fragment execution " |
| "model as an Output storage class for Entry Point id 2.")); |
| } |
| |
| TEST_P(ValidateDecorationString, VulkanVertexInputInvalidInterface) { |
| const std::string decoration = GetParam(); |
| std::stringstream ss; |
| ss << R"( |
| OpCapability Shader |
| OpCapability SampleRateShading |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" %out %in |
| OpSource GLSL 450 |
| OpDecorate %in )" |
| << decoration << R"( |
| OpDecorate %out Location 0 |
| OpDecorate %in Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Output_int = OpTypePointer Output %int |
| %out = OpVariable %_ptr_Output_int Output |
| %_ptr_Input_int = OpTypePointer Input %int |
| %in = OpVariable %_ptr_Input_int Input |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %11 = OpLoad %int %in |
| OpStore %out %11 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(ss.str(), SPV_ENV_VULKAN_1_0); |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| AnyVUID("VUID-StandaloneSpirv-Flat-06202")); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("decorated variable must not be used in vertex execution model " |
| "as an Input storage class for Entry Point id 2.")); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(FragmentInputInterface, ValidateDecorationString, |
| ::testing::Values("Flat", "NoPerspective", "Sample", |
| "Centroid")); |
| |
| TEST_F(ValidateDecorations, NVBindlessSamplerArrayInBlock) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpCapability BindlessTextureNV |
| OpExtension "SPV_NV_bindless_texture" |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpSamplerImageAddressingModeNV 64 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpName %main "main" |
| OpName %UBO "UBO" |
| OpMemberName %UBO 0 "uboSampler" |
| OpName %_ "" |
| OpDecorate %array ArrayStride 16 |
| OpMemberDecorate %UBO 0 Offset 0 |
| OpDecorate %UBO Block |
| OpDecorate %_ DescriptorSet 0 |
| OpDecorate %_ Binding 2 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %7 = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %8 = OpTypeSampledImage %7 |
| %uint = OpTypeInt 32 0 |
| %uint_3 = OpConstant %uint 3 |
| %array = OpTypeArray %8 %uint_3 |
| %UBO = OpTypeStruct %array |
| %pointer = OpTypePointer Uniform %UBO |
| %_ = OpVariable %pointer Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); |
| } |
| |
| TEST_F(ValidateDecorations, Std140ColMajorMat2x2) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Offset 0 |
| OpMemberDecorate %block 0 ColMajor |
| OpMemberDecorate %block 0 MatrixStride 8 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %float2 = OpTypeVector %float 2 |
| %matrix = OpTypeMatrix %float2 2 |
| %block = OpTypeStruct %matrix |
| %ptr_block = OpTypePointer Uniform %block |
| %var = OpVariable %ptr_block Uniform |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "member 0 is a matrix with stride 8 not satisfying alignment to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, Std140RowMajorMat2x2) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Offset 0 |
| OpMemberDecorate %block 0 RowMajor |
| OpMemberDecorate %block 0 MatrixStride 8 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %float2 = OpTypeVector %float 2 |
| %matrix = OpTypeMatrix %float2 2 |
| %block = OpTypeStruct %matrix |
| %ptr_block = OpTypePointer Uniform %block |
| %var = OpVariable %ptr_block Uniform |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "member 0 is a matrix with stride 8 not satisfying alignment to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, Std140ColMajorMat4x2) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Offset 0 |
| OpMemberDecorate %block 0 ColMajor |
| OpMemberDecorate %block 0 MatrixStride 8 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %float2 = OpTypeVector %float 2 |
| %matrix = OpTypeMatrix %float2 4 |
| %block = OpTypeStruct %matrix |
| %ptr_block = OpTypePointer Uniform %block |
| %var = OpVariable %ptr_block Uniform |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "member 0 is a matrix with stride 8 not satisfying alignment to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, Std140ColMajorMat2x3) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Offset 0 |
| OpMemberDecorate %block 0 ColMajor |
| OpMemberDecorate %block 0 MatrixStride 12 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %float3 = OpTypeVector %float 3 |
| %matrix = OpTypeMatrix %float3 2 |
| %block = OpTypeStruct %matrix |
| %ptr_block = OpTypePointer Uniform %block |
| %var = OpVariable %ptr_block Uniform |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT(getDiagnosticString(), |
| HasSubstr("member 0 is a matrix with stride 12 not satisfying " |
| "alignment to 16")); |
| } |
| |
| TEST_F(ValidateDecorations, MatrixMissingMajornessUniform) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Offset 0 |
| OpMemberDecorate %block 0 MatrixStride 16 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %float2 = OpTypeVector %float 2 |
| %matrix = OpTypeMatrix %float2 2 |
| %block = OpTypeStruct %matrix |
| %ptr_block = OpTypePointer Uniform %block |
| %var = OpVariable %ptr_block Uniform |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "must be explicitly laid out with RowMajor or ColMajor decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, MatrixMissingMajornessStorageBuffer) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Offset 0 |
| OpMemberDecorate %block 0 MatrixStride 16 |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %float2 = OpTypeVector %float 2 |
| %matrix = OpTypeMatrix %float2 2 |
| %block = OpTypeStruct %matrix |
| %ptr_block = OpTypePointer StorageBuffer %block |
| %var = OpVariable %ptr_block StorageBuffer |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "must be explicitly laid out with RowMajor or ColMajor decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, MatrixMissingMajornessPushConstant) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Offset 0 |
| OpMemberDecorate %block 0 MatrixStride 16 |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %float2 = OpTypeVector %float 2 |
| %matrix = OpTypeMatrix %float2 2 |
| %block = OpTypeStruct %matrix |
| %ptr_block = OpTypePointer PushConstant %block |
| %var = OpVariable %ptr_block PushConstant |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "must be explicitly laid out with RowMajor or ColMajor decorations")); |
| } |
| |
| TEST_F(ValidateDecorations, StructWithRowAndColMajor) { |
| const std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpDecorate %block Block |
| OpMemberDecorate %block 0 Offset 0 |
| OpMemberDecorate %block 0 MatrixStride 16 |
| OpMemberDecorate %block 0 ColMajor |
| OpMemberDecorate %block 1 Offset 32 |
| OpMemberDecorate %block 1 MatrixStride 16 |
| OpMemberDecorate %block 1 RowMajor |
| %void = OpTypeVoid |
| %void_fn = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %float2 = OpTypeVector %float 2 |
| %matrix = OpTypeMatrix %float2 2 |
| %block = OpTypeStruct %matrix %matrix |
| %ptr_block = OpTypePointer PushConstant %block |
| %var = OpVariable %ptr_block PushConstant |
| %main = OpFunction %void None %void_fn |
| %entry = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); |
| } |
| |
| } // namespace |
| } // namespace val |
| } // namespace spvtools |