blob: a38c061edf1e3b18988a495e148ac6dfa2ddf6aa [file]
// Copyright (c) 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Validation tests for explicit layout
#include <string>
#include <tuple>
#include <vector>
#include "gmock/gmock.h"
#include "source/val/decoration.h"
#include "spirv-tools/libspirv.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;
using ValidateExplicitLayout = spvtest::ValidateBase<bool>;
TEST_F(ValidateExplicitLayout, 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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("must be explicitly laid out with Offset or "
"OffsetIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, 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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("must be explicitly laid out with Offset or "
"OffsetIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, 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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("must be explicitly laid out with Offset or "
"OffsetIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, 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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("must be explicitly laid out with Offset or "
"OffsetIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, 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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("must be explicitly laid out with ArrayStride or "
"ArrayStrideIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, 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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("must be explicitly laid out with ArrayStride or "
"ArrayStrideIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, 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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("must be explicitly laid out with ArrayStride or "
"ArrayStrideIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, 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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("must be explicitly laid out with ArrayStride or "
"ArrayStrideIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, 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
OpMemberDecorate %Output 0 ColMajor
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("must be explicitly laid out with MatrixStride decorations"));
}
TEST_F(ValidateExplicitLayout, 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
OpMemberDecorate %Output 0 ColMajor
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("must be explicitly laid out with MatrixStride decorations"));
}
TEST_F(ValidateExplicitLayout, 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
OpMemberDecorate %Output 0 RowMajor
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("must be explicitly laid out with MatrixStride decorations"));
}
TEST_F(ValidateExplicitLayout, 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
OpMemberDecorate %Output 0 RowMajor
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("must be explicitly laid out with MatrixStride decorations"));
}
TEST_F(ValidateExplicitLayout, 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 %S 0 ColMajor
OpMemberDecorate %Output 0 Offset 0
OpMemberDecorate %Output 1 Offset 16
OpMemberDecorate %Output 2 Offset 32
OpDecorate %Output Block
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("must be explicitly laid out with MatrixStride decorations"));
}
TEST_F(ValidateExplicitLayout, 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 %S 0 ColMajor
OpMemberDecorate %Output 0 Offset 0
OpMemberDecorate %Output 1 Offset 16
OpMemberDecorate %Output 2 Offset 32
OpDecorate %Output BufferBlock
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("must be explicitly laid out with MatrixStride decorations"));
}
TEST_F(ValidateExplicitLayout, 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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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 member 1 at offset 4 is not aligned to 16"));
}
TEST_F(ValidateExplicitLayout,
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(ValidateExplicitLayout,
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 member 1 at offset 5 is not aligned to 4"));
}
TEST_F(ValidateExplicitLayout,
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(ValidateExplicitLayout,
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(ValidateExplicitLayout,
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(ValidateExplicitLayout,
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(ValidateExplicitLayout,
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(
ValidateExplicitLayout,
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(
ValidateExplicitLayout,
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(
ValidateExplicitLayout,
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(ValidateExplicitLayout, 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
OpDecorate %varSSBO32 DescriptorSet 0
OpDecorate %varSSBO32 Binding 0
OpDecorate %varSSBO16 DescriptorSet 0
OpDecorate %varSSBO16 Binding 1
%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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateExplicitLayout, 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, ValidateInstructions(SPV_ENV_VULKAN_1_0))
<< getDiagnosticString();
}
TEST_F(ValidateExplicitLayout, 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
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 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 member 1 at offset 8 is not aligned to 16"));
}
TEST_F(ValidateExplicitLayout,
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);
spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure member 1 at offset 8 is not aligned to 16"));
}
TEST_F(ValidateExplicitLayout, 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 member 1 at offset 8 is not aligned to 16"));
}
TEST_F(ValidateExplicitLayout,
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(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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 member 1 at offset 7 is not aligned to 4"));
}
TEST_F(ValidateExplicitLayout,
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(ValidateExplicitLayout,
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 member 1 at offset 4 is not aligned to 16"));
}
TEST_F(ValidateExplicitLayout,
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(ValidateExplicitLayout, 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 member 1 at offset 7 is not aligned to 4"));
}
TEST_F(ValidateExplicitLayout, 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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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(ValidateExplicitLayout,
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(ValidateExplicitLayout,
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 member 1 at offset 4 is not aligned to 16"));
}
TEST_F(ValidateExplicitLayout,
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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Structure member 2 at offset 24 is not aligned to 16"));
}
TEST_F(ValidateExplicitLayout,
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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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 member 5 at offset 71 is not aligned to 16"));
}
TEST_F(ValidateExplicitLayout,
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 177
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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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("Array stride 177 must satisfy alignment 16"));
}
TEST_F(ValidateExplicitLayout,
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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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 member 1 at offset 8 is not aligned to 16"));
}
TEST_F(ValidateExplicitLayout,
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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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 member 1 at offset 20 overlaps previous "
"member ending at offset 31"));
}
TEST_F(ValidateExplicitLayout,
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
OpDecorate %8 DescriptorSet 0
OpDecorate %8 Binding 0
%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 member 1 at offset 4 overlaps previous "
"member ending at offset 15"));
}
TEST_F(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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 member 1 at offset 16 overlaps previous "
"member ending at offset 31"));
}
TEST_F(ValidateExplicitLayout, 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
OpDecorate %dataOutput DescriptorSet 0
OpDecorate %dataOutput Binding 0
%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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateExplicitLayout, 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, ValidateInstructions(SPV_ENV_VULKAN_1_0))
<< getDiagnosticString();
}
TEST_F(ValidateExplicitLayout, 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, ValidateInstructions(SPV_ENV_VULKAN_1_0))
<< getDiagnosticString();
}
TEST_F(ValidateExplicitLayout, 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 member 2 at offset 64 overlaps previous "
"member ending at offset 79"));
}
TEST_F(ValidateExplicitLayout, 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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateExplicitLayout,
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, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateExplicitLayout, 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 member 1 at offset 60 overlaps "
"previous member ending at offset 63"));
}
TEST_F(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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 member 1 at offset 60 overlaps previous "
"member ending at offset 63"));
}
TEST_F(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 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 member 1 at offset 1 is not aligned to 4"));
}
TEST_F(ValidateExplicitLayout, 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("Vector has improper straddle due to offset 12"));
}
TEST_F(ValidateExplicitLayout, 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 member 1 at offset 1 is not aligned to 8"));
}
TEST_F(ValidateExplicitLayout, 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 member 1 at offset 1 is not aligned to 8"));
}
TEST_F(ValidateExplicitLayout, 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("Array stride 4 must satisfy alignment 16"));
}
TEST_F(ValidateExplicitLayout, 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("Vector has improper straddle due to offset 28"));
}
TEST_F(ValidateExplicitLayout, 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));
}
TEST_F(ValidateExplicitLayout, 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("Array must not have a stride of 0"));
}
TEST_F(ValidateExplicitLayout, 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("Array stride 4 is smaller than element type size 16"));
}
TEST_F(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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(ValidateExplicitLayout, WorkgroupBlockVariableWith16BitType) {
std::string spirv = R"(
OpCapability Shader
OpCapability Float16
OpCapability Int16
OpCapability WorkgroupMemoryExplicitLayoutKHR
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(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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("Structure member 0 must be explicitly laid out with "
"Offset or OffsetIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, 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("Structure member 0 at offset 1 is not aligned to 4"));
}
TEST_F(ValidateExplicitLayout, 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("Matrix with a stride 3 not satisfying alignment to 16"));
}
TEST_F(ValidateExplicitLayout, 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("Matrix with a stride 3 not satisfying alignment to 16"));
}
TEST_F(ValidateExplicitLayout, 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("Matrix with a stride 3 not satisfying alignment to 16"));
}
TEST_F(ValidateExplicitLayout, 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("Matrix with a stride 3 not satisfying alignment to 4"));
}
TEST_F(ValidateExplicitLayout, 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(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure member 0 must be explicitly laid out with "
"Offset or OffsetIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, 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("Matrix with a stride 8 not satisfying alignment to 16"));
}
TEST_F(ValidateExplicitLayout, 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("Matrix with a stride 8 not satisfying alignment to 16"));
}
TEST_F(ValidateExplicitLayout, 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("Matrix with a stride 8 not satisfying alignment to 16"));
}
TEST_F(ValidateExplicitLayout, 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("Matrix with a stride 12 not satisfying "
"alignment to 16"));
}
TEST_F(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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(ValidateExplicitLayout, 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));
}
TEST_F(ValidateExplicitLayout, PhysicalStorageBufferWithOffset) {
const std::string spirv = R"(
OpCapability Shader
OpCapability Int64
OpCapability PhysicalStorageBufferAddresses
OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint GLCompute %main "main" %pc
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %pc_block Block
OpMemberDecorate %pc_block 0 Offset 0
OpMemberDecorate %pssbo_struct 0 Offset 0
%void = OpTypeVoid
%long = OpTypeInt 64 0
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%pc_block = OpTypeStruct %long
%pc_block_ptr = OpTypePointer PushConstant %pc_block
%pc_long_ptr = OpTypePointer PushConstant %long
%pc = OpVariable %pc_block_ptr PushConstant
%pssbo_struct = OpTypeStruct %float
%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_struct
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0
%addr = OpLoad %long %pc_gep
%ptr = OpConvertUToPtr %pssbo_ptr %addr
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
}
TEST_F(ValidateExplicitLayout, PhysicalStorageBufferMissingOffset) {
const std::string spirv = R"(
OpCapability Shader
OpCapability Int64
OpCapability PhysicalStorageBufferAddresses
OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint GLCompute %main "main" %pc
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %pc_block Block
OpMemberDecorate %pc_block 0 Offset 0
%void = OpTypeVoid
%long = OpTypeInt 64 0
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%pc_block = OpTypeStruct %long
%pc_block_ptr = OpTypePointer PushConstant %pc_block
%pc_long_ptr = OpTypePointer PushConstant %long
%pc = OpVariable %pc_block_ptr PushConstant
%pssbo_struct = OpTypeStruct %float
%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_struct
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0
%addr = OpLoad %long %pc_gep
%ptr = OpConvertUToPtr %pssbo_ptr %addr
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure member 0 must be explicitly laid out with "
"Offset or OffsetIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, PhysicalStorageBufferMissingArrayStride) {
const std::string spirv = R"(
OpCapability Shader
OpCapability Int64
OpCapability PhysicalStorageBufferAddresses
OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint GLCompute %main "main" %pc
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %pc_block Block
OpMemberDecorate %pc_block 0 Offset 0
%void = OpTypeVoid
%long = OpTypeInt 64 0
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%pc_block = OpTypeStruct %long
%pc_block_ptr = OpTypePointer PushConstant %pc_block
%pc_long_ptr = OpTypePointer PushConstant %long
%pc = OpVariable %pc_block_ptr PushConstant
%pssbo_array = OpTypeArray %float %int_4
%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_array
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0
%addr = OpLoad %long %pc_gep
%ptr = OpConvertUToPtr %pssbo_ptr %addr
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Array must be explicitly laid out with ArrayStride or "
"ArrayStrideIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, MatrixArrayMissingMajorness) {
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 %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 MatrixStride 16
OpDecorate %array ArrayStride 32
%void = OpTypeVoid
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_2 = OpConstant %int 2
%vec = OpTypeVector %float 2
%mat = OpTypeMatrix %vec 2
%array = OpTypeArray %mat %int_2
%block = OpTypeStruct %array
%ptr = OpTypePointer Uniform %block
%var = OpVariable %ptr Uniform
%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(
"must be explicitly laid out with RowMajor or ColMajor decorations"));
}
TEST_F(ValidateExplicitLayout, MatrixArrayMissingStride) {
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 %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 ColMajor
OpDecorate %array ArrayStride 32
%void = OpTypeVoid
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_2 = OpConstant %int 2
%vec = OpTypeVector %float 2
%mat = OpTypeMatrix %vec 2
%array = OpTypeArray %mat %int_2
%block = OpTypeStruct %array
%ptr = OpTypePointer Uniform %block
%var = OpVariable %ptr Uniform
%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("must be explicitly laid out with MatrixStride decorations"));
}
TEST_F(ValidateExplicitLayout, MatrixArrayBadStride) {
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 %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 ColMajor
OpMemberDecorate %block 0 MatrixStride 8
OpDecorate %array ArrayStride 32
%void = OpTypeVoid
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_2 = OpConstant %int 2
%vec = OpTypeVector %float 2
%mat = OpTypeMatrix %vec 2
%array = OpTypeArray %mat %int_2
%block = OpTypeStruct %array
%ptr = OpTypePointer Uniform %block
%var = OpVariable %ptr Uniform
%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("Matrix with a stride 8 not satisfying alignment to 16"));
}
TEST_F(ValidateExplicitLayout, MatrixArrayArrayMissingMajorness) {
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 %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 MatrixStride 16
OpDecorate %array ArrayStride 32
OpDecorate %rta ArrayStride 64
%void = OpTypeVoid
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_2 = OpConstant %int 2
%vec = OpTypeVector %float 2
%mat = OpTypeMatrix %vec 2
%array = OpTypeArray %mat %int_2
%rta = OpTypeRuntimeArray %array
%block = OpTypeStruct %rta
%ptr = OpTypePointer StorageBuffer %block
%var = OpVariable %ptr 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(
"must be explicitly laid out with RowMajor or ColMajor decorations"));
}
TEST_F(ValidateExplicitLayout, MatrixArrayArrayMissingStride) {
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 %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 ColMajor
OpDecorate %array ArrayStride 32
OpDecorate %rta ArrayStride 64
%void = OpTypeVoid
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_2 = OpConstant %int 2
%vec = OpTypeVector %float 2
%mat = OpTypeMatrix %vec 2
%array = OpTypeArray %mat %int_2
%rta = OpTypeRuntimeArray %array
%block = OpTypeStruct %rta
%ptr = OpTypePointer StorageBuffer %block
%var = OpVariable %ptr 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("must be explicitly laid out with MatrixStride decorations"));
}
TEST_F(ValidateExplicitLayout, MatrixArrayArrayBadStride) {
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 %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 ColMajor
OpMemberDecorate %block 0 MatrixStride 8
OpDecorate %array ArrayStride 32
OpDecorate %a ArrayStride 64
%void = OpTypeVoid
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_2 = OpConstant %int 2
%vec = OpTypeVector %float 2
%mat = OpTypeMatrix %vec 2
%array = OpTypeArray %mat %int_2
%a = OpTypeArray %array %int_2
%block = OpTypeStruct %a
%ptr = OpTypePointer Uniform %block
%var = OpVariable %ptr Uniform
%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("Matrix with a stride 8 not satisfying alignment to 16"));
}
TEST_F(ValidateExplicitLayout, UntypedVariableWorkgroupRequiresStruct) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability WorkgroupMemoryExplicitLayoutKHR
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %var
%void = OpTypeVoid
%int = OpTypeInt 32 0
%ptr = OpTypeUntypedPointerKHR Workgroup
%var = OpUntypedVariableKHR %ptr Workgroup %int
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Untyped workgroup variables in shaders must be block "
"decorated structs"));
}
TEST_F(ValidateExplicitLayout, UntypedVariableWorkgroupRequiresBlockStruct) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability WorkgroupMemoryExplicitLayoutKHR
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %var
%void = OpTypeVoid
%int = OpTypeInt 32 0
%struct = OpTypeStruct %int
%ptr = OpTypeUntypedPointerKHR Workgroup
%var = OpUntypedVariableKHR %ptr Workgroup %struct
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Untyped workgroup variables in shaders must be block "
"decorated"));
}
TEST_F(ValidateExplicitLayout, UntypedArrayLengthMissingOffset) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpExtension "SPV_KHR_untyped_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %struct Block
OpDecorate %block Block
OpMemberDecorate %block 0 Offset 0
OpDecorate %array ArrayStride 4
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%array = OpTypeRuntimeArray %int
%struct = OpTypeStruct %array
%block = OpTypeStruct %array
%ptr = OpTypeUntypedPointerKHR StorageBuffer
%var = OpUntypedVariableKHR %ptr StorageBuffer %block
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%len = OpUntypedArrayLengthKHR %int %struct %var 0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure member 0 must be explicitly laid out with "
"Offset or OffsetIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, BlockArrayWithoutStride) {
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
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%int = OpTypeInt 32 0
%int_4 = OpConstant %int 4
%struct = OpTypeStruct %int
%array = OpTypeArray %struct %int_4
%ptr = OpTypePointer StorageBuffer %array
%var = OpVariable %ptr StorageBuffer
%void = OpTypeVoid
%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(ValidateExplicitLayout, BlockArrayWithoutStrideUntypedAccessChain) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpExtension "SPV_KHR_untyped_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%int = OpTypeInt 32 0
%int_4 = OpConstant %int 4
%struct = OpTypeStruct %int
%array = OpTypeArray %struct %int_4
%void = OpTypeVoid
%ptr = OpTypeUntypedPointerKHR StorageBuffer
%var = OpUntypedVariableKHR %ptr StorageBuffer %array
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %ptr %array %var
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateExplicitLayout, InvalidLayoutBlockFunctionPre1p4) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %block Block
%void = OpTypeVoid
%int = OpTypeInt 32 0
%block = OpTypeStruct %int
%ptr_function_block = OpTypePointer Function %block
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%var = OpVariable %ptr_function_block Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
}
TEST_F(ValidateExplicitLayout, InvalidLayoutBlockFunctionPost1p4) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %block Block
%void = OpTypeVoid
%int = OpTypeInt 32 0
%block = OpTypeStruct %int
%ptr_function_block = OpTypePointer Function %block
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%var = OpVariable %ptr_function_block Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
EXPECT_THAT(getDiagnosticString(),
AnyVUID("VUID-StandaloneSpirv-None-10684"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
}
TEST_F(ValidateExplicitLayout, InvalidLayoutOffsetPrivatePre1p4) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpMemberDecorate %block 0 Offset 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%block = OpTypeStruct %int
%ptr_private_block = OpTypePointer Private %block
%void_fn = OpTypeFunction %void
%var = OpVariable %ptr_private_block Private
%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(ValidateExplicitLayout, InvalidLayoutOffsetPrivatePost1p4) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpMemberDecorate %block 0 Offset 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%block = OpTypeStruct %int
%ptr_private_block = OpTypePointer Private %block
%void_fn = OpTypeFunction %void
%var = OpVariable %ptr_private_block Private
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
EXPECT_THAT(getDiagnosticString(),
AnyVUID("VUID-StandaloneSpirv-None-10684"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
}
TEST_F(ValidateExplicitLayout,
InvalidLayoutArrayStrideWorkgroupExplicitLayout_MissingBlock) {
const 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 1 1 1
OpDecorate %array ArrayStride 4
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_4 = OpConstant %int 4
%array = OpTypeArray %int %int_4
%ptr_wg_block = OpTypePointer Workgroup %array
%void_fn = OpTypeFunction %void
%var = OpVariable %ptr_wg_block Workgroup
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
}
TEST_F(ValidateExplicitLayout,
InvalidLayoutArrayStrideWorkgroupExplicitLayout) {
const 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 1 1 1
OpDecorate %array ArrayStride 4
OpDecorate %block Block
OpMemberDecorate %block 0 Offset 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_4 = OpConstant %int 4
%array = OpTypeArray %int %int_4
%block = OpTypeStruct %array
%ptr_wg_block = OpTypePointer Workgroup %block
%void_fn = OpTypeFunction %void
%var = OpVariable %ptr_wg_block Workgroup
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
}
TEST_F(ValidateExplicitLayout, InvalidLayoutArrayStrideWorkgroup) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %array ArrayStride 4
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_4 = OpConstant %int 4
%array = OpTypeArray %int %int_4
%ptr_wg_block = OpTypePointer Workgroup %array
%void_fn = OpTypeFunction %void
%var = OpVariable %ptr_wg_block Workgroup
%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-None-10684"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
}
TEST_F(ValidateExplicitLayout, InvalidLayoutArrayStrideUniformConstant) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %array ArrayStride 4
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_4 = OpConstant %int 4
%sampler = OpTypeSampler
%array = OpTypeArray %sampler %int_4
%ptr_uc_block = OpTypePointer UniformConstant %array
%void_fn = OpTypeFunction %void
%var = OpVariable %ptr_uc_block UniformConstant
%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-None-10684"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
}
TEST_F(ValidateExplicitLayout, InvalidLayoutMatrixStrideFunctionPost1p4) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpMemberDecorate %block 0 MatrixStride 16
%void = OpTypeVoid
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%mat4x4 = OpTypeMatrix %v4float 4
%block = OpTypeStruct %mat4x4
%ptr_function_block = OpTypePointer Function %block
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%var = OpVariable %ptr_function_block Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
EXPECT_THAT(getDiagnosticString(),
AnyVUID("VUID-StandaloneSpirv-None-10684"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
}
TEST_F(ValidateExplicitLayout, InvalidLayoutNestedMatrixStrideFunctionPost1p4) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpMemberDecorate %block 0 MatrixStride 16
%void = OpTypeVoid
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%mat4x4 = OpTypeMatrix %v4float 4
%block = OpTypeStruct %mat4x4
%block2 = OpTypeStruct %block
%int = OpTypeInt 32 0
%int_2 = OpConstant %int 2
%array = OpTypeArray %block2 %int_2
%ptr_function_array = OpTypePointer Function %array
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%var = OpVariable %ptr_function_array Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
EXPECT_THAT(getDiagnosticString(),
AnyVUID("VUID-StandaloneSpirv-None-10684"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
}
TEST_F(ValidateExplicitLayout, InvalidLayoutBufferBlockWorkgroup) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %block BufferBlock
%void = OpTypeVoid
%int = OpTypeInt 32 0
%block = OpTypeStruct %int
%ptr_wg_block = OpTypePointer Workgroup %block
%void_fn = OpTypeFunction %void
%var = OpVariable %ptr_wg_block Workgroup
%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-None-10684"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
}
TEST_F(ValidateExplicitLayout, InvalidLayoutUntypedStore) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpExtension "SPV_KHR_untyped_pointers"
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
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%block = OpTypeStruct %int
%block_null = OpConstantNull %block
%ptr = OpTypeUntypedPointerKHR StorageBuffer
%var = OpUntypedVariableKHR %ptr StorageBuffer %block
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpStore %var %block_null
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateExplicitLayout, ExplicitLayoutOnPtrPhysicalStorageBuffer) {
const std::string spirv = R"(
OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_KHR_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %_ptr_PhysicalStorageBuffer_int ArrayStride 4
%void = OpTypeVoid
%void_fn = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int ; ArrayStride 4
%Foo = OpTypeStruct %_ptr_PhysicalStorageBuffer_int
%_ptr_Function_Foo = OpTypePointer Function %Foo
%int_0 = OpConstant %int 0
%_ptr_Function__ptr_PhysicalStorageBuffer_int = OpTypePointer Function %_ptr_PhysicalStorageBuffer_int
%ulong = OpTypeInt 64 0
%ulong_0 = OpConstant %ulong 0
%main = OpFunction %void None %void_fn
%entry = OpLabel
%obj = OpVariable %_ptr_Function_Foo Function
%obj_member = OpAccessChain %_ptr_Function__ptr_PhysicalStorageBuffer_int %obj %int_0
%nullptr = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_int %ulong_0
OpStore %obj_member %nullptr
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
}
TEST_F(ValidateExplicitLayout, RuntimeArrayNotLargestOffsetInBlock) {
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 %var DescriptorSet 0
OpDecorate %var Binding 0
OpDecorate %block Block
OpMemberDecorate %block 0 Offset 16
OpMemberDecorate %block 1 Offset 0
%void = OpTypeVoid
%void_fn = OpTypeFunction %void
%int = OpTypeInt 32 0
%array = OpTypeRuntimeArray %int
%block = OpTypeStruct %int %array
%ptr = OpTypePointer StorageBuffer %block
%var = OpVariable %ptr 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("has a runtime array at offset 0, but other members at "
"larger offsets"));
EXPECT_THAT(getDiagnosticString(),
AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
}
TEST_F(ValidateExplicitLayout, RuntimeArrayNotLargestOffsetInBufferBlock) {
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 %block BufferBlock
OpMemberDecorate %block 0 Offset 16
OpMemberDecorate %block 1 Offset 0
OpDecorate %array ArrayStride 4
%void = OpTypeVoid
%void_fn = OpTypeFunction %void
%int = OpTypeInt 32 0
%array = OpTypeRuntimeArray %int
%block = OpTypeStruct %int %array
%ptr = OpTypePointer Uniform %block
%var = OpVariable %ptr 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("has a runtime array at offset 0, but other members at "
"larger offsets"));
EXPECT_THAT(getDiagnosticString(),
AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
}
TEST_F(ValidateExplicitLayout, LongVectorUniformPass_ImproperStraddle) {
const std::string spirv = R"(
OpCapability Shader
OpCapability Int8
OpCapability UniformAndStorageBuffer8BitAccess
OpCapability LongVectorEXT
OpExtension "SPV_KHR_8bit_storage"
OpExtension "SPV_EXT_long_vector"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %BP_main "main"
OpDecorate %input0 DescriptorSet 0
OpDecorate %input0 Binding 0
OpDecorate %a10testtype ArrayStride 16
OpDecorate %buf BufferBlock
OpMemberDecorate %buf 0 Offset 0
%void = OpTypeVoid
%bool = OpTypeBool
%u32 = OpTypeInt 32 0
%voidf = OpTypeFunction %void
%c_u32_10 = OpConstant %u32 10
%vectorSizeConst = OpConstant %u32 12
%scalartype = OpTypeInt 8 1
%testtype = OpTypeVectorIdEXT %scalartype %vectorSizeConst
%a10testtype = OpTypeArray %testtype %c_u32_10
%buf = OpTypeStruct %a10testtype
%bufptr = OpTypePointer Uniform %buf
%input0 = OpVariable %bufptr Uniform
%BP_main = OpFunction %void None %voidf
%BP_label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateExplicitLayout, LongVectorUniformPass) {
const std::string spirv = R"(
OpCapability Shader
OpCapability Int8
OpCapability UniformAndStorageBuffer8BitAccess
OpCapability LongVectorEXT
OpExtension "SPV_KHR_8bit_storage"
OpExtension "SPV_EXT_long_vector"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %BP_main "main"
OpDecorate %input0 DescriptorSet 0
OpDecorate %input0 Binding 0
OpDecorate %a10testtype ArrayStride 12
OpDecorate %buf BufferBlock
OpMemberDecorate %buf 0 Offset 0
%void = OpTypeVoid
%bool = OpTypeBool
%u32 = OpTypeInt 32 0
%voidf = OpTypeFunction %void
%c_u32_10 = OpConstant %u32 10
%vectorSizeConst = OpConstant %u32 12
%scalartype = OpTypeInt 8 1
%testtype = OpTypeVectorIdEXT %scalartype %vectorSizeConst
%a10testtype = OpTypeArray %testtype %c_u32_10
%buf = OpTypeStruct %a10testtype
%bufptr = OpTypePointer Uniform %buf
%input0 = OpVariable %bufptr Uniform
%BP_main = OpFunction %void None %voidf
%BP_label = OpLabel
OpReturn
OpFunctionEnd
)";
options_->scalar_block_layout = true;
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateExplicitLayout, BufferPointerEXTMissingOffsetBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpDecorate %Struct Block
OpDecorate %storage_buffer_array_type ArrayStride 16
%void = OpTypeVoid
%int = OpTypeInt 32 0
%float = OpTypeFloat 32
%int_0 = OpConstant %int 0
%storage_buffer_type = OpTypeBufferEXT StorageBuffer
%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
%Struct = OpTypeStruct %float
%_ptr_StorageBuffer_Struct = OpTypePointer StorageBuffer %Struct
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%ptr_storagebuffer = OpTypeUntypedPointerKHR StorageBuffer
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
%buffer_data_ptr = OpBufferPointerEXT %_ptr_StorageBuffer_Struct %buffer_ptr
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure member 0 must be explicitly laid out with "
"Offset or OffsetIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, BufferPointerEXTUnalignedOffsetBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpDecorate %Struct Block
OpMemberDecorate %Struct 0 Offset 2
OpDecorate %storage_buffer_array_type ArrayStride 16
%void = OpTypeVoid
%int = OpTypeInt 32 0
%float = OpTypeFloat 32
%int_0 = OpConstant %int 0
%storage_buffer_type = OpTypeBufferEXT StorageBuffer
%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
%Struct = OpTypeStruct %float
%_ptr_StorageBuffer_Struct = OpTypePointer StorageBuffer %Struct
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%ptr_storagebuffer = OpTypeUntypedPointerKHR StorageBuffer
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
%buffer_data_ptr = OpBufferPointerEXT %_ptr_StorageBuffer_Struct %buffer_ptr
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure member 0 at offset 2 is not aligned to 4"));
}
TEST_F(ValidateExplicitLayout, BufferPointerEXTMissingArrayStrideBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpDecorate %Struct Block
OpMemberDecorate %Struct 0 Offset 0
OpDecorate %storage_buffer_array_type ArrayStride 16
%void = OpTypeVoid
%int = OpTypeInt 32 0
%float = OpTypeFloat 32
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%array = OpTypeArray %float %int_4
%storage_buffer_type = OpTypeBufferEXT StorageBuffer
%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
%Struct = OpTypeStruct %array
%_ptr_StorageBuffer_Struct = OpTypePointer StorageBuffer %Struct
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%ptr_storagebuffer = OpTypeUntypedPointerKHR StorageBuffer
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
%buffer_data_ptr = OpBufferPointerEXT %_ptr_StorageBuffer_Struct %buffer_ptr
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Array must be explicitly laid out with ArrayStride or "
"ArrayStrideIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, BufferPointerEXTMissingMatrixStrideBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpDecorate %Struct Block
OpMemberDecorate %Struct 0 Offset 0
OpDecorate %storage_buffer_array_type ArrayStride 16
%void = OpTypeVoid
%int = OpTypeInt 32 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%int_0 = OpConstant %int 0
%mat = OpTypeMatrix %v4float 4
%storage_buffer_type = OpTypeBufferEXT StorageBuffer
%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
%Struct = OpTypeStruct %mat
%_ptr_Uniform_Struct = OpTypePointer Uniform %Struct
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%ptr_uniform = OpTypeUntypedPointerKHR Uniform
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
%buffer_data_ptr = OpBufferPointerEXT %_ptr_Uniform_Struct %buffer_ptr
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure member 0 containing a matrix must be "
"explicitly laid out with RowMajor or ColMajor "
"decorations"));
}
TEST_F(ValidateExplicitLayout, BufferPointerEXTStorageBufferScalarGood) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpDecorate %Struct Block
OpMemberDecorate %Struct 0 Offset 0
OpMemberDecorate %Struct 1 Offset 12
OpDecorate %storage_buffer_array_type ArrayStride 16
%void = OpTypeVoid
%int = OpTypeInt 32 0
%float = OpTypeFloat 32
%v3float = OpTypeVector %float 3
%int_0 = OpConstant %int 0
%storage_buffer_type = OpTypeBufferEXT StorageBuffer
%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
%Struct = OpTypeStruct %v3float %float
%_ptr_StorageBuffer_Struct = OpTypePointer StorageBuffer %Struct
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%ptr_storagebuffer = OpTypeUntypedPointerKHR StorageBuffer
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
%buffer_data_ptr = OpBufferPointerEXT %_ptr_StorageBuffer_Struct %buffer_ptr
OpReturn
OpFunctionEnd
)";
options_->scalar_block_layout = true;
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
}
TEST_F(ValidateExplicitLayout, BufferPointerEXTUniformExtendedAlignmentBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpDecorate %Struct Block
OpMemberDecorate %Struct 0 Offset 0
OpMemberDecorate %Struct 1 Offset 4
OpDecorate %storage_buffer_array_type ArrayStride 16
OpDecorate %array ArrayStride 16
%void = OpTypeVoid
%int = OpTypeInt 32 0
%float = OpTypeFloat 32
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%array = OpTypeArray %float %int_4
%storage_buffer_type = OpTypeBufferEXT StorageBuffer
%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
%Struct = OpTypeStruct %float %array
%_ptr_Uniform_Struct = OpTypePointer Uniform %Struct
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
%buffer_data_ptr = OpBufferPointerEXT %_ptr_Uniform_Struct %buffer_ptr
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure member 1 at offset 4 is not aligned to 16"));
}
TEST_F(ValidateExplicitLayout, BufferPointerEXTArrayResultBadStrideBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpDecorate %storage_buffer_array_type ArrayStride 16
OpDecorate %array ArrayStride 2
%void = OpTypeVoid
%int = OpTypeInt 32 0
%float = OpTypeFloat 32
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%array = OpTypeArray %float %int_4
%storage_buffer_type = OpTypeBufferEXT StorageBuffer
%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
%_ptr_StorageBuffer_array = OpTypePointer StorageBuffer %array
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
%buffer_data_ptr = OpBufferPointerEXT %_ptr_StorageBuffer_array %buffer_ptr
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Array stride 2 must satisfy alignment 4"));
}
TEST_F(ValidateExplicitLayout, StructOffsetIdEXTSamplerHeapGood) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn SamplerHeapEXT
OpMemberDecorateIdEXT %Struct 0 OffsetIdEXT %int_0
OpMemberDecorateIdEXT %Struct 1 OffsetIdEXT %int_4
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%sampler = OpTypeSampler
%Struct = OpTypeStruct %sampler %int
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %Struct %resource_heap %int_0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
}
TEST_F(ValidateExplicitLayout, StructOffsetIdEXTSamplerHeapUnalignedBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn SamplerHeapEXT
OpMemberDecorateIdEXT %Struct 0 OffsetIdEXT %int_0
OpMemberDecorateIdEXT %Struct 1 OffsetIdEXT %int_2
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_2 = OpConstant %int 2
%sampler = OpTypeSampler
%Struct = OpTypeStruct %sampler %int
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %Struct %resource_heap %int_0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure member 1 at offset 2 is not aligned to 4"));
}
TEST_F(ValidateExplicitLayout, StructOffsetIdEXTResourceHeapOverlapBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpCapability SampledBuffer
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpMemberDecorateIdEXT %Struct 0 OffsetIdEXT %int_0
OpMemberDecorateIdEXT %Struct 1 OffsetIdEXT %int_4
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%image = OpTypeImage %int Buffer 0 0 0 2 R32ui
%Struct = OpTypeStruct %image %int
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %Struct %resource_heap %int_0
OpReturn
OpFunctionEnd
)";
options_->image_descriptor_layout.size = 8;
options_->image_descriptor_layout.alignment = 8;
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure member 1 at offset 4 overlaps previous "
"member ending at offset 7"));
}
TEST_F(ValidateExplicitLayout, StructOffsetIdEXTResourceHeapOverlapSpecId) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpCapability SampledBuffer
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpMemberDecorateIdEXT %Struct 0 OffsetIdEXT %int_0
OpMemberDecorateIdEXT %Struct 1 OffsetIdEXT %int_4
OpDecorate %int_4 SpecId 1
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_4 = OpSpecConstant %int 4
%image = OpTypeImage %int Buffer 0 0 0 2 R32ui
%Struct = OpTypeStruct %image %int
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %Struct %resource_heap %int_0
OpReturn
OpFunctionEnd
)";
options_->image_descriptor_layout.size = 8;
options_->image_descriptor_layout.alignment = 8;
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
}
TEST_F(ValidateExplicitLayout, ArrayStrideIdEXTSamplerHeapGood) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn SamplerHeapEXT
OpDecorateId %array ArrayStrideIdEXT %int_4
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%int_10 = OpConstant %int 10
%sampler = OpTypeSampler
%array = OpTypeArray %sampler %int_10
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %array %resource_heap %int_0
OpReturn
OpFunctionEnd
)";
options_->sampler_descriptor_layout.size = 4;
options_->sampler_descriptor_layout.alignment = 4;
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
}
TEST_F(ValidateExplicitLayout, ArrayStrideIdEXTSamplerHeapTooSmallBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn SamplerHeapEXT
OpDecorateId %array ArrayStrideIdEXT %int_2
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_2 = OpConstant %int 2
%int_10 = OpConstant %int 10
%sampler = OpTypeSampler
%array = OpTypeArray %sampler %int_10
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %array %resource_heap %int_0
OpReturn
OpFunctionEnd
)";
options_->sampler_descriptor_layout.size = 4;
options_->sampler_descriptor_layout.alignment = 2;
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Array stride 2 is smaller than element type size 4"));
}
TEST_F(ValidateExplicitLayout,
ArrayStrideIdEXTSamplerHeapTooSmallSpecConstant) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn SamplerHeapEXT
OpDecorateId %array ArrayStrideIdEXT %int_2
OpDecorate %int_2 SpecId 1
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_2 = OpSpecConstant %int 2
%int_10 = OpConstant %int 10
%sampler = OpTypeSampler
%array = OpTypeArray %sampler %int_10
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %array %resource_heap %int_0
OpReturn
OpFunctionEnd
)";
options_->sampler_descriptor_layout.size = 4;
options_->sampler_descriptor_layout.alignment = 2;
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
}
TEST_F(ValidateExplicitLayout, ImageArraySpecConstant) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpCapability ImageBuffer
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main" %2
OpExecutionMode %1 LocalSize 1 1 1
OpDecorate %2 BuiltIn ResourceHeapEXT
OpDecorate %3 SpecId 0
OpDecorateId %4 ArrayStrideIdEXT %3
%5 = OpTypeVoid
%6 = OpTypeFunction %5
%7 = OpTypeInt 32 0
%8 = OpConstant %7 0
%9 = OpConstant %7 2
%10 = OpConstant %7 51966
%11 = OpConstant %7 0
%3 = OpSpecConstant %7 0
%12 = OpTypeUntypedPointerKHR UniformConstant
%2 = OpUntypedVariableKHR %12 UniformConstant
%13 = OpTypeImage %7 Buffer 0 0 0 2 R32ui
%4 = OpTypeRuntimeArray %13
%1 = OpFunction %5 None %6
%14 = OpLabel
%15 = OpUntypedAccessChainKHR %12 %4 %2 %9
%16 = OpLoad %13 %15
OpImageWrite %16 %8 %10
OpReturn
OpFunctionEnd
)";
options_->image_descriptor_layout.size = 64;
options_->image_descriptor_layout.alignment = 64;
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
}
TEST_F(ValidateExplicitLayout, CheckNoLayoutWithOffsetIdEXTBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpMemberDecorateIdEXT %Struct 0 OffsetIdEXT %int_0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%sampler = OpTypeSampler
%Struct = OpTypeStruct %sampler %int
%_ptr_Function_Struct = OpTypePointer Function %Struct
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%var = OpVariable %_ptr_Function_Struct Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("has an explicit layout from the OffsetIdEXT decoration"));
}
TEST_F(ValidateExplicitLayout, CheckNoLayoutWithArrayStrideIdEXTBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorateId %array ArrayStrideIdEXT %int_4
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_4 = OpConstant %int 4
%int_10 = OpConstant %int 10
%sampler = OpTypeSampler
%array = OpTypeArray %sampler %int_10
%_ptr_Workgroup_array = OpTypePointer Workgroup %array
%var = OpVariable %_ptr_Workgroup_array Workgroup
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("has an explicit layout from the ArrayStrideIdEXT decoration"));
}
TEST_F(ValidateExplicitLayout, DescriptorArrayWithArrayStrideBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability DescriptorHeapEXT
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
OpDecorate %array ArrayStride 16
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_4 = OpConstant %int 4
%sampler = OpTypeSampler
%array = OpTypeArray %sampler %int_4
%_ptr_UniformConstant_array = OpTypePointer UniformConstant %array
%var = OpVariable %_ptr_UniformConstant_array UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("the UniformConstant storage class has an explicit "
"layout from the ArrayStride decoration"));
}
TEST_F(ValidateExplicitLayout, DescriptorArrayWithArrayStrideIdEXTBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpCapability SampledBuffer
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %var
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
OpDecorateId %array ArrayStrideIdEXT %int_8
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_4 = OpConstant %int 4
%int_8 = OpConstant %int 8
%image = OpTypeImage %int Buffer 0 0 0 2 R32ui
%array = OpTypeArray %image %int_4
%_ptr_UniformConstant_array = OpTypePointer UniformConstant %array
%var = OpVariable %_ptr_UniformConstant_array UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("the UniformConstant storage class has an explicit "
"layout from the ArrayStrideIdEXT decoration"));
}
TEST_F(ValidateExplicitLayout, ResourceHeapMissingArrayStride) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_EXT_descriptor_heap"
OpExtension "SPV_KHR_untyped_pointers"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap %_
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpDecorate %Heap Block
OpMemberDecorate %Heap 0 Offset 0
OpDecorate %UBO Block
OpMemberDecorate %UBO 0 Offset 0
OpDecorate %_ Binding 0
OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
%int = OpTypeInt 32 1
%int_3 = OpConstant %int 3
%uint = OpTypeInt 32 0
%Heap = OpTypeStruct %uint
%int_0 = OpConstant %int 0
%uint_0 = OpConstant %uint 0
%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer
%16 = OpTypeBufferEXT StorageBuffer
%17 = OpConstantSizeOfEXT %int %16
%_runtimearr_16 = OpTypeRuntimeArray %16
%UBO = OpTypeStruct %uint
%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO
%_ = OpVariable %_ptr_Uniform_UBO Uniform
%main = OpFunction %void None %3
%5 = OpLabel
%15 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_16 %resource_heap %int_3
%19 = OpBufferPointerEXT %_ptr_StorageBuffer %15
%20 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %Heap %19 %int_0
OpStore %20 %uint_0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Array must be explicitly laid out with ArrayStride or "
"ArrayStrideIdEXT decorations"));
}
TEST_F(ValidateExplicitLayout, SamplerDescriptorLayoutSamplerHeapGood) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn SamplerHeapEXT
OpDecorate %Struct Block
OpMemberDecorate %Struct 0 Offset 0
OpMemberDecorate %Struct 1 Offset 8
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%sampler = OpTypeSampler
%Struct = OpTypeStruct %sampler %int
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %Struct %resource_heap %int_0
OpReturn
OpFunctionEnd
)";
options_->sampler_descriptor_layout.size = 8;
options_->sampler_descriptor_layout.alignment = 8;
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
}
TEST_F(ValidateExplicitLayout, ImageDescriptorLayoutResourceHeapGood) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpCapability SampledBuffer
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpDecorate %array ArrayStride 16
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%image = OpTypeImage %int Buffer 0 0 0 2 R32ui
%array = OpTypeArray %image %int_4
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %array %resource_heap %int_0
OpReturn
OpFunctionEnd
)";
options_->image_descriptor_layout.size = 16;
options_->image_descriptor_layout.alignment = 16;
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
}
TEST_F(ValidateExplicitLayout, BufferDescriptorLayoutResourceHeapTooSmallBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpDecorate %array ArrayStride 8
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%buffer = OpTypeBufferEXT StorageBuffer
%array = OpTypeArray %buffer %int_4
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %array %resource_heap %int_0
OpReturn
OpFunctionEnd
)";
options_->buffer_descriptor_layout.size = 16;
options_->buffer_descriptor_layout.alignment = 8;
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Array stride 8 is smaller than element type size 16"));
}
TEST_F(ValidateExplicitLayout, BindlessTextureNVSamplerHeapUnalignedBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability UntypedPointersKHR
OpCapability DescriptorHeapEXT
OpCapability BindlessTextureNV
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_EXT_descriptor_heap"
OpExtension "SPV_NV_bindless_texture"
OpMemoryModel Logical GLSL450
OpSamplerImageAddressingModeNV 64
OpEntryPoint GLCompute %main "main" %resource_heap
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %resource_heap BuiltIn SamplerHeapEXT
OpDecorate %Struct Block
OpMemberDecorate %Struct 0 Offset 4
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%sampler = OpTypeSampler
%Struct = OpTypeStruct %sampler
%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %Struct %resource_heap %int_0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure member 0 at offset 4 is not aligned to 8"));
}
TEST_F(ValidateExplicitLayout,
StorageBufferPtrInFunctionVariableWithArrayStrideGood) {
const std::string spirv = R"(
OpCapability Shader
OpCapability VariablePointersStorageBuffer
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %ptr_sb ArrayStride 8
%void = OpTypeVoid
%int = OpTypeInt 32 0
%float = OpTypeFloat 32
%ptr_sb = OpTypePointer StorageBuffer %float
%_ptr_Function_ptr_sb = OpTypePointer Function %ptr_sb
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%var = OpVariable %_ptr_Function_ptr_sb Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
}
TEST_F(ValidateExplicitLayout, NonPointerInFunctionVariableWithArrayStrideBad) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %array ArrayStride 4
%void = OpTypeVoid
%int = OpTypeInt 32 0
%float = OpTypeFloat 32
%int_4 = OpConstant %int 4
%array = OpTypeArray %float %int_4
%_ptr_Function_array = OpTypePointer Function %array
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%var = OpVariable %_ptr_Function_array Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Invalid explicit layout decorations on type"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("the Function storage class has an explicit layout "
"from the ArrayStride decoration"));
}
using UntypedPointerLayout =
spvtest::ValidateBase<std::tuple<std::string, std::string>>;
TEST_P(UntypedPointerLayout, BadOffset) {
const auto sc = std::get<0>(GetParam());
const auto op = std::get<1>(GetParam());
const std::string set = (sc == "StorageBuffer" || sc == "Uniform"
? R"(OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
)"
: R"()");
const std::string spirv = R"(
OpCapability Shader
OpCapability VariablePointers
OpCapability UntypedPointersKHR
OpCapability WorkgroupMemoryExplicitLayoutKHR
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_KHR_variable_pointers"
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %var
OpExecutionMode %main LocalSize 1 1 1
OpName %var "var"
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpMemberDecorate %struct 1 Offset 4
)" + set + R"(OpMemberDecorate %test_type 0 Offset 0
OpMemberDecorate %test_type 1 Offset 1
OpDecorate %ptr ArrayStride 16
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%struct = OpTypeStruct %int %int
%test_type = OpTypeStruct %int %int
%test_val = OpConstantNull %test_type
%ptr = OpTypeUntypedPointerKHR )" +
sc + R"(
%var = OpUntypedVariableKHR %ptr )" +
sc + R"( %struct
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
)" + op + R"(
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
const bool read_only = sc == "Uniform" || sc == "PushConstant";
if (!read_only || op.find("OpStore") == std::string::npos) {
EXPECT_THAT(getDiagnosticString(),
HasSubstr("member 1 at offset 1 is not aligned to"));
}
}
TEST_P(UntypedPointerLayout, BadStride_TooSmall) {
const auto sc = std::get<0>(GetParam());
const auto op = std::get<1>(GetParam());
const std::string set = (sc == "StorageBuffer" || sc == "Uniform"
? R"(OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
)"
: R"()");
const std::string spirv = R"(
OpCapability Shader
OpCapability VariablePointers
OpCapability UntypedPointersKHR
OpCapability WorkgroupMemoryExplicitLayoutKHR
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_KHR_variable_pointers"
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %var
OpExecutionMode %main LocalSize 1 1 1
OpName %var "var"
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpMemberDecorate %struct 1 Offset 4
)" + set + R"(OpDecorate %test_type ArrayStride 4
OpDecorate %ptr ArrayStride 16
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%int4 = OpTypeVector %int 4
%test_type = OpTypeArray %int4 %int_4
%test_val = OpConstantNull %test_type
%struct = OpTypeStruct %int %int
%ptr = OpTypeUntypedPointerKHR )" +
sc + R"(
%var = OpUntypedVariableKHR %ptr )" +
sc + R"( %struct
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
)" + op + R"(
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
const bool read_only = sc == "Uniform" || sc == "PushConstant";
if (sc == "Uniform") {
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Array stride 4 must satisfy alignment 16"));
} else if (!read_only || op.find("OpStore") == std::string::npos) {
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Array stride 4 is smaller than element type size 16"));
}
}
TEST_P(UntypedPointerLayout, BadStride_Unaligned) {
const auto sc = std::get<0>(GetParam());
const auto op = std::get<1>(GetParam());
const std::string set = (sc == "StorageBuffer" || sc == "Uniform"
? R"(OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
)"
: R"()");
const std::string spirv = R"(
OpCapability Shader
OpCapability VariablePointers
OpCapability UntypedPointersKHR
OpCapability WorkgroupMemoryExplicitLayoutKHR
OpExtension "SPV_KHR_untyped_pointers"
OpExtension "SPV_KHR_variable_pointers"
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %var
OpExecutionMode %main LocalSize 1 1 1
OpName %var "var"
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpMemberDecorate %struct 1 Offset 4
)" + set + R"(OpDecorate %test_type ArrayStride 15
OpDecorate %ptr ArrayStride 16
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%int4 = OpTypeVector %int 4
%test_type = OpTypeArray %int4 %int_4
%test_val = OpConstantNull %test_type
%struct = OpTypeStruct %int %int
%ptr = OpTypeUntypedPointerKHR )" +
sc + R"(
%var = OpUntypedVariableKHR %ptr )" +
sc + R"( %struct
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
)" + op + R"(
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
const bool read_only = sc == "Uniform" || sc == "PushConstant";
if (sc == "Uniform") {
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Array stride 15 must satisfy alignment 16"));
} else if (!read_only || op.find("OpStore") == std::string::npos) {
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Array stride 15 must satisfy alignment 4"));
}
}
INSTANTIATE_TEST_SUITE_P(
ValidateUntypedPointerLayout, UntypedPointerLayout,
Combine(Values("StorageBuffer", "Uniform", "PushConstant", "Workgroup"),
Values("%gep = OpUntypedAccessChainKHR %ptr %test_type %var %int_0",
"%gep = OpUntypedInBoundsAccessChainKHR %ptr %test_type "
"%var %int_0",
"%gep = OpUntypedPtrAccessChainKHR %ptr %test_type %var "
"%int_0 %int_0",
"%gep = OpUntypedInBoundsPtrAccessChainKHR %ptr %test_type "
"%var %int_0 %int_0",
"%ld = OpLoad %test_type %var", "OpStore %var %test_val")));
} // namespace
} // namespace val
} // namespace spvtools