| // Copyright (c) 2018 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 memory/storage |
| |
| #include <string> |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "test/unit_spirv.h" |
| #include "test/val/val_fixtures.h" |
| |
| namespace spvtools { |
| namespace val { |
| namespace { |
| |
| using ::testing::Eq; |
| using ::testing::HasSubstr; |
| |
| using ValidateMemory = spvtest::ValidateBase<bool>; |
| |
| TEST_F(ValidateMemory, VulkanUniformConstantOnNonOpaqueResourceBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" |
| OpExecutionMode %func OriginUpperLeft |
| %float = OpTypeFloat 32 |
| %float_ptr = OpTypePointer UniformConstant %float |
| %2 = OpVariable %float_ptr UniformConstant |
| %void = OpTypeVoid |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("From Vulkan spec, section 14.5.2:\n" |
| "Variables identified with the UniformConstant storage class " |
| "are used only as handles to refer to opaque resources. Such " |
| "variables must be typed as OpTypeImage, OpTypeSampler, " |
| "OpTypeSampledImage, OpTypeAccelerationStructureNV, or an " |
| "array of one of these types.")); |
| } |
| |
| TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" |
| OpExecutionMode %func OriginUpperLeft |
| %sampler = OpTypeSampler |
| %sampler_ptr = OpTypePointer UniformConstant %sampler |
| %2 = OpVariable %sampler_ptr UniformConstant |
| %void = OpTypeVoid |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| } |
| |
| TEST_F(ValidateMemory, VulkanUniformConstantOnNonOpaqueResourceArrayBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" |
| OpExecutionMode %func OriginUpperLeft |
| %float = OpTypeFloat 32 |
| %uint = OpTypeInt 32 0 |
| %array_size = OpConstant %uint 5 |
| %array = OpTypeArray %float %array_size |
| %array_ptr = OpTypePointer UniformConstant %array |
| %2 = OpVariable %array_ptr UniformConstant |
| %void = OpTypeVoid |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("From Vulkan spec, section 14.5.2:\n" |
| "Variables identified with the UniformConstant storage class " |
| "are used only as handles to refer to opaque resources. Such " |
| "variables must be typed as OpTypeImage, OpTypeSampler, " |
| "OpTypeSampledImage, OpTypeAccelerationStructureNV, or an " |
| "array of one of these types.")); |
| } |
| |
| TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceArrayGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" |
| OpExecutionMode %func OriginUpperLeft |
| %sampler = OpTypeSampler |
| %uint = OpTypeInt 32 0 |
| %array_size = OpConstant %uint 5 |
| %array = OpTypeArray %sampler %array_size |
| %array_ptr = OpTypePointer UniformConstant %array |
| %2 = OpVariable %array_ptr UniformConstant |
| %void = OpTypeVoid |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| } |
| |
| TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceRuntimeArrayGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" |
| OpExecutionMode %func OriginUpperLeft |
| %sampler = OpTypeSampler |
| %uint = OpTypeInt 32 0 |
| %array = OpTypeRuntimeArray %sampler |
| %array_ptr = OpTypePointer UniformConstant %array |
| %2 = OpVariable %array_ptr UniformConstant |
| %void = OpTypeVoid |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| } |
| |
| TEST_F(ValidateMemory, VulkanUniformOnIntBad) { |
| char src[] = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %kernel "main" |
| OpExecutionMode %kernel LocalSize 1 1 1 |
| |
| OpDecorate %var DescriptorSet 0 |
| OpDecorate %var Binding 0 |
| |
| %voidty = OpTypeVoid |
| %kernelty = OpTypeFunction %voidty |
| %intty = OpTypeInt 32 0 |
| %varty = OpTypePointer Uniform %intty |
| %value = OpConstant %intty 42 |
| |
| %var = OpVariable %varty Uniform |
| |
| %kernel = OpFunction %voidty None %kernelty |
| %label = OpLabel |
| OpStore %var %value |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(src, SPV_ENV_VULKAN_1_1); |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("From Vulkan spec, section 14.5.2:\n" |
| "Variables identified with the Uniform storage class are used " |
| "to access transparent buffer backed resources. Such variables " |
| "must be typed as OpTypeStruct, or an array of this type")); |
| } |
| |
| // #version 440 |
| // #extension GL_EXT_nonuniform_qualifier : enable |
| // layout(binding = 1) uniform sampler2D s2d[][2]; |
| // layout(location = 0) in nonuniformEXT int i; |
| // void main() |
| // { |
| // vec4 v = texture(s2d[i][i], vec2(0.3)); |
| // } |
| TEST_F(ValidateMemory, VulkanUniformOnRuntimeArrayOfArrayBad) { |
| char src[] = 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 %21 NonUniformEXT |
| OpDecorate %22 NonUniformEXT |
| OpDecorate %25 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 |
| %uint = OpTypeInt 32 0 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_11_uint_2 = OpTypeArray %11 %uint_2 |
| %_runtimearr__arr_11_uint_2 = OpTypeRuntimeArray %_arr_11_uint_2 |
| %_ptr_Uniform__runtimearr__arr_11_uint_2 = OpTypePointer Uniform %_runtimearr__arr_11_uint_2 |
| %s2d = OpVariable %_ptr_Uniform__runtimearr__arr_11_uint_2 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 |
| %28 = 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 |
| %21 = OpLoad %int %i |
| %22 = OpLoad %int %i |
| %24 = OpAccessChain %_ptr_Uniform_11 %s2d %21 %22 |
| %25 = OpLoad %11 %24 |
| %30 = OpImageSampleExplicitLod %v4float %25 %28 Lod %float_0 |
| OpStore %v %30 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(src, SPV_ENV_VULKAN_1_1); |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("From Vulkan spec, section 14.5.2:\n" |
| "Variables identified with the Uniform storage class are used " |
| "to access transparent buffer backed resources. Such variables " |
| "must be typed as OpTypeStruct, or an array of this type")); |
| } |
| |
| // #version 440 |
| // layout (set=1, binding=1) uniform sampler2D variableName[2][2]; |
| // void main() { |
| // } |
| TEST_F(ValidateMemory, VulkanUniformOnArrayOfArrayBad) { |
| char src[] = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 440 |
| OpName %main "main" |
| OpName %variableName "variableName" |
| OpDecorate %variableName DescriptorSet 1 |
| OpDecorate %variableName Binding 1 |
| %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_2 = OpConstant %uint 2 |
| %_arr_8_uint_2 = OpTypeArray %8 %uint_2 |
| %_arr__arr_8_uint_2_uint_2 = OpTypeArray %_arr_8_uint_2 %uint_2 |
| %_ptr_Uniform__arr__arr_8_uint_2_uint_2 = OpTypePointer Uniform %_arr__arr_8_uint_2_uint_2 |
| %variableName = OpVariable %_ptr_Uniform__arr__arr_8_uint_2_uint_2 Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(src, SPV_ENV_VULKAN_1_1); |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr("From Vulkan spec, section 14.5.2:\n" |
| "Variables identified with the Uniform storage class are used " |
| "to access transparent buffer backed resources. Such variables " |
| "must be typed as OpTypeStruct, or an array of this type")); |
| } |
| |
| TEST_F(ValidateMemory, MismatchingStorageClassesBad) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" |
| OpExecutionMode %func OriginUpperLeft |
| %float = OpTypeFloat 32 |
| %float_ptr = OpTypePointer Uniform %float |
| %void = OpTypeVoid |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| %2 = OpVariable %float_ptr Function |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv.c_str()); |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); |
| EXPECT_THAT( |
| getDiagnosticString(), |
| HasSubstr( |
| "From SPIR-V spec, section 3.32.8 on OpVariable:\n" |
| "Its Storage Class operand must be the same as the Storage Class " |
| "operand of the result type.")); |
| } |
| |
| TEST_F(ValidateMemory, MatchingStorageClassesGood) { |
| std::string spirv = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" |
| OpExecutionMode %func OriginUpperLeft |
| %float = OpTypeFloat 32 |
| %float_ptr = OpTypePointer Function %float |
| %void = OpTypeVoid |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| %2 = OpVariable %float_ptr Function |
| OpReturn |
| OpFunctionEnd |
| )"; |
| CompileSuccessfully(spirv.c_str()); |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); |
| } |
| |
| } // namespace |
| } // namespace val |
| } // namespace spvtools |