[val] Explicit layout refactor (#6792)
Fixes #6780
* Combine layout checks into a single pass to share logic
* checks both required explicit layouts and invalid layouts
* remove checks from validate_decorations.cpp
* Introduce enums for layout modes and requirements
* Centralize which instructions are examined for layouts
* Add options to specify descriptor sizes to the validator (buffer,
sampler, image, and tensor)
diff --git a/Android.mk b/Android.mk
index 4d091d6..31c31ae 100644
--- a/Android.mk
+++ b/Android.mk
@@ -54,6 +54,7 @@
source/val/validate_decorations.cpp \
source/val/validate_derivatives.cpp \
source/val/validate_dot_product.cpp \
+ source/val/validate_explicit_layout.cpp \
source/val/validate_extensions.cpp \
source/val/validate_execution_limitations.cpp \
source/val/validate_function.cpp \
diff --git a/BUILD.gn b/BUILD.gn
index 7443aa5..a6672fd 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -366,6 +366,7 @@
"source/val/validate_derivatives.cpp",
"source/val/validate_dot_product.cpp",
"source/val/validate_execution_limitations.cpp",
+ "source/val/validate_explicit_layout.cpp",
"source/val/validate_extensions.cpp",
"source/val/validate_function.cpp",
"source/val/validate_graph.cpp",
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index 0c5b129..bfbdb31 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -788,6 +788,24 @@
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetFriendlyNames(
spv_validator_options options, bool val);
+// Sets custom size and alignment for buffer and acceleration structure
+// descriptor heap resources.
+SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetBufferDescriptorLayout(
+ spv_validator_options options, uint32_t size, uint32_t alignment);
+
+// Sets custom size and alignment for image and sampled image descriptor heap
+// resources.
+SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetImageDescriptorLayout(
+ spv_validator_options options, uint32_t size, uint32_t alignment);
+
+// Sets custom size and alignment for sampler descriptor heap resources.
+SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSamplerDescriptorLayout(
+ spv_validator_options options, uint32_t size, uint32_t alignment);
+
+// Sets custom size and alignment for tensor descriptor heap resources.
+SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetTensorDescriptorLayout(
+ spv_validator_options options, uint32_t size, uint32_t alignment);
+
// Creates an optimizer options object with default options. Returns a valid
// options object. The object remains valid until it is passed into
// |spvOptimizerOptionsDestroy|.
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index 1b3ed86..b82fa50 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/include/spirv-tools/libspirv.hpp
@@ -138,6 +138,28 @@
spvValidatorOptionsSetAllowVulkan32BitBitwise(options_, val);
}
+ // Sets custom size and alignment for buffer and acceleration structure
+ // descriptor heap resources.
+ void SetBufferDescriptorLayout(uint32_t size, uint32_t alignment) {
+ spvValidatorOptionsSetBufferDescriptorLayout(options_, size, alignment);
+ }
+
+ // Sets custom size and alignment for image and sampled image descriptor heap
+ // resources.
+ void SetImageDescriptorLayout(uint32_t size, uint32_t alignment) {
+ spvValidatorOptionsSetImageDescriptorLayout(options_, size, alignment);
+ }
+
+ // Sets custom size and alignment for sampler descriptor heap resources.
+ void SetSamplerDescriptorLayout(uint32_t size, uint32_t alignment) {
+ spvValidatorOptionsSetSamplerDescriptorLayout(options_, size, alignment);
+ }
+
+ // Sets custom size and alignment for tensor descriptor heap resources.
+ void SetTensorDescriptorLayout(uint32_t size, uint32_t alignment) {
+ spvValidatorOptionsSetTensorDescriptorLayout(options_, size, alignment);
+ }
+
// Records whether or not the validator should relax the rules on pointer
// usage in logical addressing mode.
//
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index dbe6090..538ae6a 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -263,6 +263,7 @@
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_dot_product.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_extensions.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_execution_limitations.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_explicit_layout.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_function.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_graph.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_group.cpp
diff --git a/source/spirv_validator_options.cpp b/source/spirv_validator_options.cpp
index a9591f6..bbe6703 100644
--- a/source/spirv_validator_options.cpp
+++ b/source/spirv_validator_options.cpp
@@ -140,3 +140,30 @@
bool val) {
options->use_friendly_names = val;
}
+
+void spvValidatorOptionsSetBufferDescriptorLayout(spv_validator_options options,
+ uint32_t size,
+ uint32_t alignment) {
+ options->buffer_descriptor_layout.size = size;
+ options->buffer_descriptor_layout.alignment = alignment;
+}
+
+void spvValidatorOptionsSetImageDescriptorLayout(spv_validator_options options,
+ uint32_t size,
+ uint32_t alignment) {
+ options->image_descriptor_layout.size = size;
+ options->image_descriptor_layout.alignment = alignment;
+}
+
+void spvValidatorOptionsSetSamplerDescriptorLayout(
+ spv_validator_options options, uint32_t size, uint32_t alignment) {
+ options->sampler_descriptor_layout.size = size;
+ options->sampler_descriptor_layout.alignment = alignment;
+}
+
+void spvValidatorOptionsSetTensorDescriptorLayout(spv_validator_options options,
+ uint32_t size,
+ uint32_t alignment) {
+ options->tensor_descriptor_layout.size = size;
+ options->tensor_descriptor_layout.alignment = alignment;
+}
diff --git a/source/spirv_validator_options.h b/source/spirv_validator_options.h
index 9f0c125..131d80d 100644
--- a/source/spirv_validator_options.h
+++ b/source/spirv_validator_options.h
@@ -35,6 +35,11 @@
uint32_t max_id_bound{0x3FFFFF};
};
+struct OpaqueResourceLayout {
+ uint32_t size{0};
+ uint32_t alignment{1};
+};
+
// Manages command line options passed to the SPIR-V Validator. New struct
// members may be added for any new option.
struct spv_validator_options_t {
@@ -66,6 +71,11 @@
bool allow_vulkan_32_bit_bitwise;
bool before_hlsl_legalization;
bool use_friendly_names;
+
+ OpaqueResourceLayout buffer_descriptor_layout;
+ OpaqueResourceLayout image_descriptor_layout;
+ OpaqueResourceLayout sampler_descriptor_layout;
+ OpaqueResourceLayout tensor_descriptor_layout;
};
#endif // SOURCE_SPIRV_VALIDATOR_OPTIONS_H_
diff --git a/source/val/validate.cpp b/source/val/validate.cpp
index b3eab0c..c2b67c5 100644
--- a/source/val/validate.cpp
+++ b/source/val/validate.cpp
@@ -427,6 +427,7 @@
if (auto error = PerformCfgChecks(*vstate)) return error;
if (auto error = CheckIdDefinitionDominateUse(*vstate)) return error;
if (auto error = ValidateDecorations(*vstate)) return error;
+ if (auto error = ValidateExplicitLayout(*vstate)) return error;
if (auto error = ValidateInterfaces(*vstate)) return error;
// TODO(dsinclair): Restructure ValidateBuiltins so we can move into the
// for() above as it loops over all ordered_instructions internally.
diff --git a/source/val/validate.h b/source/val/validate.h
index 717fb34..35a6524 100644
--- a/source/val/validate.h
+++ b/source/val/validate.h
@@ -144,6 +144,8 @@
/// has been propagated down to the group members.
spv_result_t ValidateDecorations(ValidationState_t& _);
+spv_result_t ValidateExplicitLayout(ValidationState_t& _);
+
/// Performs validation of built-in variables.
spv_result_t ValidateBuiltIns(ValidationState_t& _);
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index 623a470..9b1d66d 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -35,43 +35,6 @@
namespace val {
namespace {
-// Distinguish between row and column major matrix layouts.
-enum MatrixLayout { kRowMajor, kColumnMajor };
-
-// A functor for hashing a pair of integers.
-struct PairHash {
- std::size_t operator()(const std::pair<uint32_t, uint32_t> pair) const {
- const uint32_t a = pair.first;
- const uint32_t b = pair.second;
- const uint32_t rotated_b = (b >> 2) | ((b & 3) << 30);
- return a ^ rotated_b;
- }
-};
-
-// Struct member layout attributes that are inherited through arrays.
-struct LayoutConstraints {
- explicit LayoutConstraints(
- MatrixLayout the_majorness = MatrixLayout::kColumnMajor,
- uint32_t stride = 0)
- : majorness(the_majorness), matrix_stride(stride) {}
- MatrixLayout majorness;
- uint32_t matrix_stride;
-};
-
-// A type for mapping (struct id, member id) to layout constraints.
-using MemberConstraints = std::unordered_map<std::pair<uint32_t, uint32_t>,
- LayoutConstraints, PairHash>;
-
-// Returns the array stride of the given array type.
-uint32_t GetArrayStride(uint32_t array_id, ValidationState_t& vstate) {
- for (auto& decoration : vstate.id_decorations(array_id)) {
- if (spv::Decoration::ArrayStride == decoration.dec_type()) {
- return decoration.params()[0];
- }
- }
- return 0;
-}
-
// Returns true if the given structure type has a Block decoration.
bool isBlock(uint32_t struct_id, ValidationState_t& vstate) {
const auto& decorations = vstate.id_decorations(struct_id);
@@ -111,612 +74,6 @@
return members;
}
-// Returns whether the given structure is missing Offset decoration for any
-// member. Handles also nested structures.
-bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) {
- const auto* inst = vstate.FindDef(struct_id);
- std::vector<bool> hasOffset;
- std::vector<uint32_t> struct_members;
- if (inst->opcode() == spv::Op::OpTypeStruct) {
- // Check offsets of member decorations.
- struct_members = getStructMembers(struct_id, vstate);
- hasOffset.resize(struct_members.size(), false);
-
- for (auto& decoration : vstate.id_decorations(struct_id)) {
- if ((spv::Decoration::Offset == decoration.dec_type() ||
- spv::Decoration::OffsetIdEXT == decoration.dec_type()) &&
- Decoration::kInvalidMember != decoration.struct_member_index()) {
- // Offset 0xffffffff is not valid so ignore it for simplicity's sake.
- if (decoration.params()[0] == 0xffffffff) return true;
- hasOffset[decoration.struct_member_index()] = true;
- }
- }
- } else if (inst->opcode() == spv::Op::OpTypeArray ||
- inst->opcode() == spv::Op::OpTypeRuntimeArray) {
- hasOffset.resize(1, true);
- struct_members.push_back(inst->GetOperandAs<uint32_t>(1u));
- }
- // Look through nested structs (which may be in an array).
- bool nestedStructsMissingOffset = false;
- for (auto id : struct_members) {
- if (isMissingOffsetInStruct(id, vstate)) {
- nestedStructsMissingOffset = true;
- break;
- }
- }
- return nestedStructsMissingOffset ||
- !std::all_of(hasOffset.begin(), hasOffset.end(),
- [](const bool b) { return b; });
-}
-
-// Rounds x up to the next alignment. Assumes alignment is a power of two.
-uint32_t align(uint32_t x, uint32_t alignment) {
- return (x + alignment - 1) & ~(alignment - 1);
-}
-
-// Returns base alignment of struct member. If |roundUp| is true, also
-// ensure that structs, arrays, and matrices are aligned at least to a
-// multiple of 16 bytes. (That is, when roundUp is true, this function
-// returns the *extended* alignment as it's called by the Vulkan spec.)
-uint32_t getBaseAlignment(uint32_t member_id, bool roundUp,
- const LayoutConstraints& inherited,
- MemberConstraints& constraints,
- ValidationState_t& vstate) {
- const auto inst = vstate.FindDef(member_id);
- const auto& words = inst->words();
- // Minimal alignment is byte-aligned.
- uint32_t baseAlignment = 1;
- switch (inst->opcode()) {
- case spv::Op::OpTypeSampledImage:
- case spv::Op::OpTypeSampler:
- case spv::Op::OpTypeImage:
- if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
- return vstate.samplerimage_variable_address_mode() / 8;
- // SPV_EXT_descriptor_heap provides a way to access opaque images, we
- // assume alignment is validated at runtime as it is determined by the
- // client API
- if (vstate.HasCapability(spv::Capability::DescriptorHeapEXT)) return 1;
- assert(0);
- return 0;
- case spv::Op::OpTypeInt:
- case spv::Op::OpTypeFloat:
- baseAlignment = words[2] / 8;
- break;
- case spv::Op::OpTypeVector: {
- const auto componentId = words[2];
- const auto numComponents = words[3];
- const auto componentAlignment = getBaseAlignment(
- componentId, roundUp, inherited, constraints, vstate);
- baseAlignment =
- componentAlignment *
- ((numComponents == 3 || numComponents > 4) ? 4 : numComponents);
- break;
- }
- case spv::Op::OpTypeVectorIdEXT: {
- const auto componentId = words[2];
- const auto numComponents = vstate.GetDimension(inst->id());
- assert(numComponents != 0);
- const auto componentAlignment = getBaseAlignment(
- componentId, roundUp, inherited, constraints, vstate);
- baseAlignment =
- componentAlignment *
- ((numComponents == 3 || numComponents > 4) ? 4 : numComponents);
- break;
- }
- case spv::Op::OpTypeMatrix: {
- const auto column_type = words[2];
- if (inherited.majorness == kColumnMajor) {
- baseAlignment = getBaseAlignment(column_type, roundUp, inherited,
- constraints, vstate);
- } else {
- // A row-major matrix of C columns has a base alignment equal to the
- // base alignment of a vector of C matrix components.
- const auto num_columns = words[3];
- const auto component_inst = vstate.FindDef(column_type);
- const auto component_id = component_inst->words()[2];
- const auto componentAlignment = getBaseAlignment(
- component_id, roundUp, inherited, constraints, vstate);
- baseAlignment =
- componentAlignment * (num_columns == 3 ? 4 : num_columns);
- }
- if (roundUp) baseAlignment = align(baseAlignment, 16u);
- } break;
- case spv::Op::OpTypeArray:
- case spv::Op::OpTypeRuntimeArray:
- baseAlignment =
- getBaseAlignment(words[2], roundUp, inherited, constraints, vstate);
- if (roundUp) baseAlignment = align(baseAlignment, 16u);
- break;
- case spv::Op::OpTypeStruct: {
- const auto members = getStructMembers(member_id, vstate);
- for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
- memberIdx < numMembers; ++memberIdx) {
- const auto id = members[memberIdx];
- const auto& constraint =
- constraints[std::make_pair(member_id, memberIdx)];
- baseAlignment = std::max(
- baseAlignment,
- getBaseAlignment(id, roundUp, constraint, constraints, vstate));
- }
- if (roundUp) baseAlignment = align(baseAlignment, 16u);
- break;
- }
- case spv::Op::OpTypePointer:
- case spv::Op::OpTypeUntypedPointerKHR:
- baseAlignment = vstate.pointer_size_and_alignment();
- break;
- default:
- assert(0);
- break;
- }
-
- return baseAlignment;
-}
-
-// Returns scalar alignment of a type.
-uint32_t getScalarAlignment(uint32_t type_id, ValidationState_t& vstate) {
- const auto inst = vstate.FindDef(type_id);
- const auto& words = inst->words();
- switch (inst->opcode()) {
- case spv::Op::OpTypeSampledImage:
- case spv::Op::OpTypeSampler:
- case spv::Op::OpTypeImage:
- if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
- return vstate.samplerimage_variable_address_mode() / 8;
- // SPV_EXT_descriptor_heap provides a way to access opaque images, we
- // assume alignment is validated at runtime as it is determined by the
- // client API
- if (vstate.HasCapability(spv::Capability::DescriptorHeapEXT)) return 1;
- assert(0);
- return 0;
- case spv::Op::OpTypeInt:
- case spv::Op::OpTypeFloat:
- return words[2] / 8;
- case spv::Op::OpTypeVector:
- case spv::Op::OpTypeVectorIdEXT:
- case spv::Op::OpTypeMatrix:
- case spv::Op::OpTypeArray:
- case spv::Op::OpTypeRuntimeArray: {
- const auto compositeMemberTypeId = words[2];
- return getScalarAlignment(compositeMemberTypeId, vstate);
- }
- case spv::Op::OpTypeStruct: {
- const auto members = getStructMembers(type_id, vstate);
- uint32_t max_member_alignment = 1;
- for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
- memberIdx < numMembers; ++memberIdx) {
- const auto id = members[memberIdx];
- uint32_t member_alignment = getScalarAlignment(id, vstate);
- if (member_alignment > max_member_alignment) {
- max_member_alignment = member_alignment;
- }
- }
- return max_member_alignment;
- } break;
- case spv::Op::OpTypePointer:
- case spv::Op::OpTypeUntypedPointerKHR:
- return vstate.pointer_size_and_alignment();
- default:
- assert(0);
- break;
- }
-
- return 1;
-}
-
-// Returns size of a struct member. Doesn't include padding at the end of struct
-// or array. Assumes that in the struct case, all members have offsets.
-uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited,
- MemberConstraints& constraints, ValidationState_t& vstate) {
- const auto inst = vstate.FindDef(member_id);
- const auto& words = inst->words();
- switch (inst->opcode()) {
- case spv::Op::OpTypeSampledImage:
- case spv::Op::OpTypeSampler:
- case spv::Op::OpTypeImage:
- if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
- return vstate.samplerimage_variable_address_mode() / 8;
- // SPV_EXT_descriptor_heap provides a way to access opaque images, we
- // assume alignment is validated at runtime as it is determined by the
- // client API
- if (vstate.HasCapability(spv::Capability::DescriptorHeapEXT)) return 1;
- assert(0);
- return 0;
- case spv::Op::OpTypeInt:
- case spv::Op::OpTypeFloat:
- return words[2] / 8;
- case spv::Op::OpTypeVector: {
- const auto componentId = words[2];
- const auto numComponents = words[3];
- const auto componentSize =
- getSize(componentId, inherited, constraints, vstate);
- const auto size = componentSize * numComponents;
- return size;
- }
- case spv::Op::OpTypeVectorIdEXT: {
- const auto componentId = words[2];
- const auto numComponents = vstate.GetDimension(inst->id());
- assert(numComponents != 0);
- const auto componentSize =
- getSize(componentId, inherited, constraints, vstate);
- const auto size = componentSize * numComponents;
- return size;
- }
- case spv::Op::OpTypeArray: {
- const auto sizeInst = vstate.FindDef(words[3]);
- if (spvOpcodeIsSpecConstant(sizeInst->opcode())) return 0;
- assert(spv::Op::OpConstant == sizeInst->opcode());
- const uint32_t num_elem = sizeInst->words()[3];
- const uint32_t elem_type = words[2];
- const uint32_t elem_size =
- getSize(elem_type, inherited, constraints, vstate);
- // Account for gaps due to alignments in the first N-1 elements,
- // then add the size of the last element.
- const auto size =
- (num_elem - 1) * GetArrayStride(member_id, vstate) + elem_size;
- return size;
- }
- case spv::Op::OpTypeRuntimeArray:
- return 0;
- case spv::Op::OpTypeMatrix: {
- const auto num_columns = words[3];
- if (inherited.majorness == kColumnMajor) {
- return num_columns * inherited.matrix_stride;
- } else {
- // Row major case.
- const auto column_type = words[2];
- const auto component_inst = vstate.FindDef(column_type);
- const auto num_rows = component_inst->words()[3];
- const auto scalar_elem_type = component_inst->words()[2];
- const uint32_t scalar_elem_size =
- getSize(scalar_elem_type, inherited, constraints, vstate);
- return (num_rows - 1) * inherited.matrix_stride +
- num_columns * scalar_elem_size;
- }
- }
- case spv::Op::OpTypeStruct: {
- const auto& members = getStructMembers(member_id, vstate);
- if (members.empty()) return 0;
- const auto lastIdx = uint32_t(members.size() - 1);
- const auto& lastMember = members.back();
- uint32_t offset = 0xffffffff;
- // Find the offset of the last element and add the size.
- auto member_decorations =
- vstate.id_member_decorations(member_id, lastIdx);
- for (auto decoration = member_decorations.begin;
- decoration != member_decorations.end; ++decoration) {
- assert(decoration->struct_member_index() == (int)lastIdx);
- if (spv::Decoration::Offset == decoration->dec_type()) {
- offset = decoration->params()[0];
- }
- }
- // This check depends on the fact that all members have offsets. This
- // has been checked earlier in the flow.
- assert(offset != 0xffffffff);
- const auto& constraint = constraints[std::make_pair(lastMember, lastIdx)];
- return offset + getSize(lastMember, constraint, constraints, vstate);
- }
- case spv::Op::OpTypePointer:
- case spv::Op::OpTypeUntypedPointerKHR:
- return vstate.pointer_size_and_alignment();
- default:
- assert(0);
- return 0;
- }
-}
-
-// A member is defined to improperly straddle if either of the following are
-// true:
-// - It is a vector with total size less than or equal to 16 bytes, and has
-// Offset decorations placing its first byte at F and its last byte at L, where
-// floor(F / 16) != floor(L / 16).
-// - It is a vector with total size greater than 16 bytes and has its Offset
-// decorations placing its first byte at a non-integer multiple of 16.
-bool hasImproperStraddle(uint32_t id, uint32_t offset,
- const LayoutConstraints& inherited,
- MemberConstraints& constraints,
- ValidationState_t& vstate) {
- const auto size = getSize(id, inherited, constraints, vstate);
- const auto F = offset;
- const auto L = offset + size - 1;
- if (size <= 16) {
- if ((F >> 4) != (L >> 4)) return true;
- } else {
- if (F % 16 != 0) return true;
- }
- return false;
-}
-
-// Returns true if |offset| satsifies an alignment to |alignment|. In the case
-// of |alignment| of zero, the |offset| must also be zero.
-bool IsAlignedTo(uint32_t offset, uint32_t alignment) {
- if (alignment == 0) return offset == 0;
- return 0 == (offset % alignment);
-}
-
-// Returns SPV_SUCCESS if the given struct satisfies standard layout rules for
-// Block or BufferBlocks in Vulkan. Otherwise emits a diagnostic and returns
-// something other than SPV_SUCCESS. Matrices inherit the specified column
-// or row major-ness.
-spv_result_t checkLayout(uint32_t struct_id, spv::StorageClass storage_class,
- const char* decoration_str, bool blockRules,
- bool scalar_block_layout, uint32_t incoming_offset,
- MemberConstraints& constraints,
- ValidationState_t& vstate) {
- if (vstate.options()->skip_block_layout) return SPV_SUCCESS;
-
- // blockRules are the same as bufferBlock rules if the uniform buffer
- // standard layout extension is being used.
- if (vstate.options()->uniform_buffer_standard_layout) blockRules = false;
-
- // Relaxed layout and scalar layout can both be in effect at the same time.
- // For example, relaxed layout is implied by Vulkan 1.1. But scalar layout
- // is more permissive than relaxed layout.
- const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout();
-
- auto fail = [&vstate, struct_id, storage_class, decoration_str, blockRules,
- relaxed_block_layout,
- scalar_block_layout](uint32_t member_idx) -> DiagnosticStream {
- DiagnosticStream ds = std::move(
- vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id))
- << "Structure id " << struct_id << " decorated as " << decoration_str
- << " for variable in " << StorageClassToString(storage_class)
- << " storage class must follow "
- << (scalar_block_layout
- ? "scalar "
- : (relaxed_block_layout ? "relaxed " : "standard "))
- << (blockRules ? "uniform buffer" : "storage buffer")
- << " layout rules: member " << member_idx << " ");
- return ds;
- };
-
- // People often use spirv-val from Vulkan Validation Layers, it ends up
- // mapping the various block layout rules from the enabled feature. This
- // offers a hint to help the user understand possbily why things are not
- // working when the shader itself "seems" valid, but just was a lack of adding
- // a supported feature
- auto extra = [&vstate, scalar_block_layout, storage_class,
- relaxed_block_layout, blockRules]() {
- if (!scalar_block_layout) {
- if (storage_class == spv::StorageClass::Workgroup) {
- return vstate.MissingFeature(
- "workgroupMemoryExplicitLayoutScalarBlockLayout feature",
- "--workgroup-scalar-block-layout", true);
- } else if (!relaxed_block_layout) {
- return vstate.MissingFeature("VK_KHR_relaxed_block_layout extension",
- "--relax-block-layout", true);
- } else if (blockRules) {
- return vstate.MissingFeature("uniformBufferStandardLayout feature",
- "--uniform-buffer-standard-layout", true);
- } else {
- return vstate.MissingFeature("scalarBlockLayout feature",
- "--scalar-block-layout", true);
- }
- }
- return std::string("");
- };
-
- // If we are checking the layout of untyped pointers or physical storage
- // buffer pointers, we may not actually have a struct here. Instead, pretend
- // we have a struct with a single member at offset 0.
- const auto& struct_type = vstate.FindDef(struct_id);
- std::vector<uint32_t> members;
- if (struct_type->opcode() == spv::Op::OpTypeStruct) {
- members = getStructMembers(struct_id, vstate);
- } else {
- members.push_back(struct_id);
- }
-
- // To check for member overlaps, we want to traverse the members in
- // offset order.
- struct MemberOffsetPair {
- uint32_t member;
- uint32_t offset;
- };
- std::vector<MemberOffsetPair> member_offsets;
-
- // With untyped pointers or physical storage buffers, we might be checking
- // layouts that do not originate from a structure.
- if (struct_type->opcode() == spv::Op::OpTypeStruct) {
- member_offsets.reserve(members.size());
- for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
- memberIdx < numMembers; memberIdx++) {
- uint32_t offset = 0xffffffff;
- auto member_decorations =
- vstate.id_member_decorations(struct_id, memberIdx);
- for (auto decoration = member_decorations.begin;
- decoration != member_decorations.end; ++decoration) {
- assert(decoration->struct_member_index() == (int)memberIdx);
- switch (decoration->dec_type()) {
- case spv::Decoration::Offset:
- offset = decoration->params()[0];
- break;
- default:
- break;
- }
- }
- member_offsets.push_back(
- MemberOffsetPair{memberIdx, incoming_offset + offset});
- }
- std::stable_sort(
- member_offsets.begin(), member_offsets.end(),
- [](const MemberOffsetPair& lhs, const MemberOffsetPair& rhs) {
- return lhs.offset < rhs.offset;
- });
- } else {
- member_offsets.push_back({0, 0});
- }
-
- // Now scan from lowest offset to highest offset.
- uint32_t nextValidOffset = 0;
- for (size_t ordered_member_idx = 0;
- ordered_member_idx < member_offsets.size(); ordered_member_idx++) {
- const auto& member_offset = member_offsets[ordered_member_idx];
- const auto memberIdx = member_offset.member;
- const auto offset = member_offset.offset;
- auto id = members[member_offset.member];
- const LayoutConstraints& constraint =
- constraints[std::make_pair(struct_id, uint32_t(memberIdx))];
- // Scalar layout takes precedence because it's more permissive, and implying
- // an alignment that divides evenly into the alignment that would otherwise
- // be used.
- const auto alignment =
- scalar_block_layout
- ? getScalarAlignment(id, vstate)
- : getBaseAlignment(id, blockRules, constraint, constraints, vstate);
- const auto inst = vstate.FindDef(id);
- const auto opcode = inst->opcode();
- const auto size = getSize(id, constraint, constraints, vstate);
- // Check offset.
- if (offset == 0xffffffff)
- return fail(memberIdx) << "is missing an Offset decoration" << extra();
-
- if (opcode == spv::Op::OpTypeRuntimeArray &&
- ordered_member_idx != member_offsets.size() - 1) {
- return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id))
- << vstate.VkErrorID(4680) << "Structure id " << struct_id
- << " has a runtime array at offset " << offset
- << ", but other members at larger offsets";
- }
-
- if (!scalar_block_layout && relaxed_block_layout &&
- (opcode == spv::Op::OpTypeVector ||
- opcode == spv::Op::OpTypeVectorIdEXT)) {
- // In relaxed block layout, the vector offset must be aligned to the
- // vector's scalar element type.
- const auto componentId = inst->words()[2];
- const auto scalar_alignment = getScalarAlignment(componentId, vstate);
- if (!IsAlignedTo(offset, scalar_alignment)) {
- return fail(memberIdx) << "at offset " << offset
- << " is not aligned to scalar element size "
- << scalar_alignment << extra();
- }
- } else {
- // Without relaxed block layout, the offset must be divisible by the
- // alignment requirement.
- if (!IsAlignedTo(offset, alignment)) {
- return fail(memberIdx) << "at offset " << offset
- << " is not aligned to " << alignment << extra();
- }
- }
- if (offset < nextValidOffset)
- return fail(memberIdx) << "at offset " << offset
- << " overlaps previous member ending at offset "
- << nextValidOffset - 1 << extra();
- if (!scalar_block_layout && relaxed_block_layout) {
- // Check improper straddle of vectors.
- if ((spv::Op::OpTypeVector == opcode ||
- spv::Op::OpTypeVectorIdEXT == opcode) &&
- hasImproperStraddle(id, offset, constraint, constraints, vstate))
- return fail(memberIdx)
- << "is an improperly straddling vector at offset " << offset
- << extra();
- }
- // Check struct members recursively.
- spv_result_t recursive_status = SPV_SUCCESS;
- if (spv::Op::OpTypeStruct == opcode &&
- SPV_SUCCESS != (recursive_status = checkLayout(
- id, storage_class, decoration_str, blockRules,
- scalar_block_layout, offset, constraints, vstate)))
- return recursive_status;
- // Check matrix stride.
- if (spv::Op::OpTypeMatrix == opcode) {
- const auto stride = constraint.matrix_stride;
- if (!IsAlignedTo(stride, alignment)) {
- return fail(memberIdx)
- << "is a matrix with stride " << stride
- << " not satisfying alignment to " << alignment << extra();
- }
- }
-
- // Check arrays and runtime arrays recursively.
- auto array_inst = inst;
- auto array_alignment = alignment;
- while (array_inst->opcode() == spv::Op::OpTypeArray ||
- array_inst->opcode() == spv::Op::OpTypeRuntimeArray) {
- const auto typeId = array_inst->word(2);
- const auto element_inst = vstate.FindDef(typeId);
- // Check array stride.
- uint32_t array_stride = 0;
- for (auto& decoration : vstate.id_decorations(array_inst->id())) {
- if (spv::Decoration::ArrayStride == decoration.dec_type()) {
- array_stride = decoration.params()[0];
- if (array_stride == 0) {
- return fail(memberIdx)
- << "contains an array with stride 0" << extra();
- }
- if (!IsAlignedTo(array_stride, array_alignment))
- return fail(memberIdx)
- << "contains an array with stride " << decoration.params()[0]
- << " not satisfying alignment to " << alignment << extra();
- }
- }
-
- bool is_int32 = false;
- bool is_const = false;
- uint32_t num_elements = 0;
- if (array_inst->opcode() == spv::Op::OpTypeArray) {
- std::tie(is_int32, is_const, num_elements) =
- vstate.EvalInt32IfConst(array_inst->word(3));
- }
- num_elements = std::max(1u, num_elements);
- // Check each element recursively if it is a struct. There is a
- // limitation to this check if the array size is a spec constant or is a
- // runtime array then we will only check a single element. This means
- // some improper straddles might be missed.
- if (spv::Op::OpTypeStruct == element_inst->opcode()) {
- std::vector<bool> seen(16, false);
- for (uint32_t i = 0; i < num_elements; ++i) {
- uint32_t next_offset = i * array_stride + offset;
- // Stop checking if offsets repeat in terms of 16-byte multiples.
- if (seen[next_offset % 16]) {
- break;
- }
-
- if (SPV_SUCCESS !=
- (recursive_status = checkLayout(
- typeId, storage_class, decoration_str, blockRules,
- scalar_block_layout, next_offset, constraints, vstate)))
- return recursive_status;
-
- seen[next_offset % 16] = true;
- }
- } else if (spv::Op::OpTypeMatrix == element_inst->opcode()) {
- // Matrix stride would be on the array element in the struct.
- const auto stride = constraint.matrix_stride;
- if (!IsAlignedTo(stride, alignment)) {
- return fail(memberIdx)
- << "is a matrix with stride " << stride
- << " not satisfying alignment to " << alignment << extra();
- }
- }
-
- // Proceed to the element in case it is an array.
- array_inst = element_inst;
- array_alignment = scalar_block_layout
- ? getScalarAlignment(array_inst->id(), vstate)
- : getBaseAlignment(array_inst->id(), blockRules,
- constraint, constraints, vstate);
-
- const auto element_size =
- getSize(element_inst->id(), constraint, constraints, vstate);
- if (element_size > array_stride) {
- return fail(memberIdx)
- << "contains an array with stride " << array_stride
- << ", but with an element size of " << element_size << extra();
- }
- }
- nextValidOffset = offset + size;
- if (!scalar_block_layout &&
- (spv::Op::OpTypeArray == opcode || spv::Op::OpTypeStruct == opcode)) {
- // Non-scalar block layout rules don't permit anything in the padding of
- // a struct or array.
- nextValidOffset = align(nextValidOffset, alignment);
- }
- }
- return SPV_SUCCESS;
-}
-
// Returns true if variable or structure id has given decoration. Handles also
// nested structures.
bool hasDecoration(uint32_t id, spv::Decoration decoration,
@@ -735,45 +92,6 @@
return false;
}
-// Returns true if all ids of given type have a specified decoration.
-bool checkForRequiredDecoration(uint32_t struct_id,
- std::function<bool(spv::Decoration)> checker,
- spv::Op type, ValidationState_t& vstate) {
- const auto& members = getStructMembers(struct_id, vstate);
- for (size_t memberIdx = 0; memberIdx < members.size(); memberIdx++) {
- auto id = members[memberIdx];
- if (type == spv::Op::OpTypeMatrix) {
- // Matrix decorations also apply to arrays of matrices.
- auto memberInst = vstate.FindDef(id);
- while (memberInst->opcode() == spv::Op::OpTypeArray ||
- memberInst->opcode() == spv::Op::OpTypeRuntimeArray) {
- memberInst = vstate.FindDef(memberInst->GetOperandAs<uint32_t>(1u));
- }
- id = memberInst->id();
- }
- if (type != vstate.FindDef(id)->opcode()) continue;
- bool found = false;
- for (auto& dec : vstate.id_decorations(id)) {
- if (checker(dec.dec_type())) found = true;
- }
- for (auto& dec : vstate.id_decorations(struct_id)) {
- if (checker(dec.dec_type()) &&
- (int)memberIdx == dec.struct_member_index()) {
- found = true;
- }
- }
- if (!found) {
- return false;
- }
- }
- for (auto id : getStructMembers(struct_id, spv::Op::OpTypeStruct, vstate)) {
- if (!checkForRequiredDecoration(id, checker, type, vstate)) {
- return false;
- }
- }
- return true;
-}
-
spv_result_t CheckLinkageAttrOfFunctions(ValidationState_t& vstate) {
for (const auto& function : vstate.functions()) {
if (function.block_count() == 0u) {
@@ -1120,89 +438,6 @@
return SPV_SUCCESS;
}
-// Load |constraints| with all the member constraints for structs contained
-// within the given array type.
-void ComputeMemberConstraintsForArray(MemberConstraints* constraints,
- uint32_t array_id,
- const LayoutConstraints& inherited,
- ValidationState_t& vstate);
-
-// Load |constraints| with all the member constraints for the given struct,
-// and all its contained structs.
-void ComputeMemberConstraintsForStruct(MemberConstraints* constraints,
- uint32_t struct_id,
- const LayoutConstraints& inherited,
- ValidationState_t& vstate) {
- assert(constraints);
- const auto& members = getStructMembers(struct_id, vstate);
- for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
- memberIdx < numMembers; memberIdx++) {
- LayoutConstraints& constraint =
- (*constraints)[std::make_pair(struct_id, memberIdx)];
- constraint = inherited;
- auto member_decorations =
- vstate.id_member_decorations(struct_id, memberIdx);
- for (auto decoration = member_decorations.begin;
- decoration != member_decorations.end; ++decoration) {
- assert(decoration->struct_member_index() == (int)memberIdx);
- switch (decoration->dec_type()) {
- case spv::Decoration::RowMajor:
- constraint.majorness = kRowMajor;
- break;
- case spv::Decoration::ColMajor:
- constraint.majorness = kColumnMajor;
- break;
- case spv::Decoration::MatrixStride:
- constraint.matrix_stride = decoration->params()[0];
- break;
- default:
- break;
- }
- }
-
- // Now recurse
- auto member_type_id = members[memberIdx];
- const auto member_type_inst = vstate.FindDef(member_type_id);
- const auto opcode = member_type_inst->opcode();
- switch (opcode) {
- case spv::Op::OpTypeArray:
- case spv::Op::OpTypeRuntimeArray:
- ComputeMemberConstraintsForArray(constraints, member_type_id, inherited,
- vstate);
- break;
- case spv::Op::OpTypeStruct:
- ComputeMemberConstraintsForStruct(constraints, member_type_id,
- inherited, vstate);
- break;
- default:
- break;
- }
- }
-}
-
-void ComputeMemberConstraintsForArray(MemberConstraints* constraints,
- uint32_t array_id,
- const LayoutConstraints& inherited,
- ValidationState_t& vstate) {
- assert(constraints);
- auto elem_type_id = vstate.FindDef(array_id)->words()[2];
- const auto elem_type_inst = vstate.FindDef(elem_type_id);
- const auto opcode = elem_type_inst->opcode();
- switch (opcode) {
- case spv::Op::OpTypeArray:
- case spv::Op::OpTypeRuntimeArray:
- ComputeMemberConstraintsForArray(constraints, elem_type_id, inherited,
- vstate);
- break;
- case spv::Op::OpTypeStruct:
- ComputeMemberConstraintsForStruct(constraints, elem_type_id, inherited,
- vstate);
- break;
- default:
- break;
- }
-}
-
spv_result_t CheckDecorationsOfVariables(ValidationState_t& vstate) {
if (!spvIsVulkanEnv(vstate.context()->target_env)) {
return SPV_SUCCESS;
@@ -1269,10 +504,6 @@
std::unordered_set<uint32_t> uses_push_constant;
for (const auto& inst : vstate.ordered_instructions()) {
const auto& words = inst.words();
- auto type_id = inst.type_id();
- const Instruction* type_inst = vstate.FindDef(type_id);
- bool scalar_block_layout = false;
- MemberConstraints constraints;
if (spv::Op::OpVariable == inst.opcode() ||
spv::Op::OpUntypedVariableKHR == inst.opcode()) {
const bool untyped_pointer =
@@ -1367,10 +598,6 @@
id = id_inst->GetOperandAs<uint32_t>(1u);
id_inst = vstate.FindDef(id);
}
- // Struct requirement is checked on variables so just move on here.
- if (spv::Op::OpTypeStruct != id_inst->opcode()) continue;
- ComputeMemberConstraintsForStruct(&constraints, id,
- LayoutConstraints(), vstate);
}
if (spvIsVulkanEnv(vstate.context()->target_env)) {
@@ -1416,11 +643,6 @@
const bool blockDeco = spv::Decoration::Block == dec.dec_type();
const bool bufferDeco =
spv::Decoration::BufferBlock == dec.dec_type();
- const bool blockRules = uniform && blockDeco;
- const bool bufferRules = (uniform && bufferDeco) ||
- ((push_constant || storage_buffer ||
- phys_storage_buffer || workgroup) &&
- blockDeco);
if (uniform && blockDeco) {
vstate.RegisterPointerToUniformBlock(ptrInst->id());
vstate.RegisterStructForUniformBlock(id);
@@ -1430,187 +652,9 @@
vstate.RegisterPointerToStorageBuffer(ptrInst->id());
vstate.RegisterStructForStorageBuffer(id);
}
-
- if (blockRules || bufferRules) {
- const char* deco_str = blockDeco ? "Block" : "BufferBlock";
- spv_result_t recursive_status = SPV_SUCCESS;
- scalar_block_layout =
- workgroup ? vstate.options()->workgroup_scalar_block_layout
- : vstate.options()->scalar_block_layout;
-
- if (isMissingOffsetInStruct(id, vstate)) {
- return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
- << "Structure id " << id << " decorated as " << deco_str
- << " must be explicitly laid out with Offset "
- "decorations.";
- }
-
- if (!checkForRequiredDecoration(
- id,
- [](spv::Decoration d) {
- return d == spv::Decoration::ArrayStride ||
- d == spv::Decoration::ArrayStrideIdEXT;
- },
- spv::Op::OpTypeArray, vstate)) {
- return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
- << "Structure id " << id << " decorated as " << deco_str
- << " must be explicitly laid out with ArrayStride or "
- "ArrayStrideIdEXT "
- "decorations.";
- }
-
- if (!checkForRequiredDecoration(
- id,
- [](spv::Decoration d) {
- return d == spv::Decoration::MatrixStride;
- },
- spv::Op::OpTypeMatrix, vstate)) {
- return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
- << "Structure id " << id << " decorated as " << deco_str
- << " must be explicitly laid out with MatrixStride "
- "decorations.";
- }
-
- if (!checkForRequiredDecoration(
- id,
- [](spv::Decoration d) {
- return d == spv::Decoration::RowMajor ||
- d == spv::Decoration::ColMajor;
- },
- spv::Op::OpTypeMatrix, vstate)) {
- return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
- << "Structure id " << id << " decorated as " << deco_str
- << " must be explicitly laid out with RowMajor or "
- "ColMajor decorations.";
- }
-
- if (spvIsVulkanEnv(vstate.context()->target_env)) {
- if (blockRules &&
- (SPV_SUCCESS !=
- (recursive_status = checkLayout(
- id, storageClass, deco_str, true, scalar_block_layout,
- 0, constraints, vstate)))) {
- return recursive_status;
- } else if (bufferRules &&
- (SPV_SUCCESS != (recursive_status = checkLayout(
- id, storageClass, deco_str,
- false, scalar_block_layout, 0,
- constraints, vstate)))) {
- return recursive_status;
- }
- }
- }
}
}
}
- } else if (type_inst && type_inst->opcode() == spv::Op::OpTypePointer &&
- type_inst->GetOperandAs<spv::StorageClass>(1u) ==
- spv::StorageClass::PhysicalStorageBuffer) {
- const bool buffer = true;
- const auto pointee_type_id = type_inst->GetOperandAs<uint32_t>(2u);
- const auto* data_type_inst = vstate.FindDef(pointee_type_id);
- scalar_block_layout = vstate.options()->scalar_block_layout;
- if (data_type_inst->opcode() == spv::Op::OpTypeStruct) {
- ComputeMemberConstraintsForStruct(&constraints, pointee_type_id,
- LayoutConstraints(), vstate);
- }
- if (auto res = checkLayout(
- pointee_type_id, spv::StorageClass::PhysicalStorageBuffer,
- "Block", !buffer, scalar_block_layout, 0, constraints, vstate)) {
- return res;
- }
- } else if (vstate.HasCapability(spv::Capability::UntypedPointersKHR) &&
- spvIsVulkanEnv(vstate.context()->target_env)) {
- // Untyped variables are checked above. Here we check that instructions
- // using an untyped pointer have a valid layout.
- uint32_t ptr_ty_id = 0;
- uint32_t data_type_id = 0;
- switch (inst.opcode()) {
- case spv::Op::OpUntypedAccessChainKHR:
- case spv::Op::OpUntypedInBoundsAccessChainKHR:
- case spv::Op::OpUntypedPtrAccessChainKHR:
- case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
- ptr_ty_id = inst.type_id();
- data_type_id = inst.GetOperandAs<uint32_t>(2);
- break;
- case spv::Op::OpLoad:
- if (vstate.GetIdOpcode(vstate.GetOperandTypeId(&inst, 2)) ==
- spv::Op::OpTypeUntypedPointerKHR) {
- const auto ptr_id = inst.GetOperandAs<uint32_t>(2);
- ptr_ty_id = vstate.FindDef(ptr_id)->type_id();
- data_type_id = inst.type_id();
- }
- break;
- case spv::Op::OpStore:
- if (vstate.GetIdOpcode(vstate.GetOperandTypeId(&inst, 0)) ==
- spv::Op::OpTypeUntypedPointerKHR) {
- const auto ptr_id = inst.GetOperandAs<uint32_t>(0);
- ptr_ty_id = vstate.FindDef(ptr_id)->type_id();
- data_type_id = vstate.GetOperandTypeId(&inst, 1);
- }
- break;
- case spv::Op::OpUntypedArrayLengthKHR:
- ptr_ty_id = vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->type_id();
- data_type_id = inst.GetOperandAs<uint32_t>(2);
- break;
- default:
- break;
- }
-
- if (ptr_ty_id == 0 || data_type_id == 0) {
- // Not an untyped pointer.
- continue;
- }
-
- const auto sc =
- vstate.FindDef(ptr_ty_id)->GetOperandAs<spv::StorageClass>(1);
-
- auto data_type = vstate.FindDef(data_type_id);
- scalar_block_layout =
- sc == spv::StorageClass::Workgroup
- ? vstate.options()->workgroup_scalar_block_layout
- : vstate.options()->scalar_block_layout;
-
- // If the data type is an array that contains a Block- or
- // BufferBlock-decorated struct, then use the struct for layout checks
- // instead of the array. In this case, the array represents a descriptor
- // array which should not have an explicit layout.
- if (data_type->opcode() == spv::Op::OpTypeArray ||
- data_type->opcode() == spv::Op::OpTypeRuntimeArray) {
- const auto ele_type =
- vstate.FindDef(data_type->GetOperandAs<uint32_t>(1u));
- if (ele_type->opcode() == spv::Op::OpTypeStruct &&
- (vstate.HasDecoration(ele_type->id(), spv::Decoration::Block) ||
- vstate.HasDecoration(ele_type->id(),
- spv::Decoration::BufferBlock))) {
- data_type = ele_type;
- data_type_id = ele_type->id();
- }
- }
-
- // Assume uniform storage class uses block rules unless we see a
- // BufferBlock decorated struct in the data type.
- bool bufferRules = sc == spv::StorageClass::Uniform ? false : true;
- if (data_type->opcode() == spv::Op::OpTypeStruct) {
- if (sc == spv::StorageClass::Uniform) {
- bufferRules =
- vstate.HasDecoration(data_type_id, spv::Decoration::BufferBlock);
- }
- ComputeMemberConstraintsForStruct(&constraints, data_type_id,
- LayoutConstraints(), vstate);
- }
- const char* deco_str =
- bufferRules
- ? (sc == spv::StorageClass::Uniform ? "BufferBlock" : "Block")
- : "Block";
-
- if (!vstate.IsDescriptorHeapBaseVariable(&inst)) {
- if (auto result =
- checkLayout(data_type_id, sc, deco_str, !bufferRules,
- scalar_block_layout, 0, constraints, vstate)) {
- return result;
- }
- }
}
}
return SPV_SUCCESS;
@@ -1887,12 +931,11 @@
"memory object "
"declaration (a variable or a function parameter)";
}
- const auto var_storage_class =
- opcode == spv::Op::OpVariable
- ? inst.GetOperandAs<spv::StorageClass>(2)
- : opcode == spv::Op::OpUntypedVariableKHR
- ? inst.GetOperandAs<spv::StorageClass>(3)
- : spv::StorageClass::Max;
+ const auto var_storage_class = opcode == spv::Op::OpVariable
+ ? inst.GetOperandAs<spv::StorageClass>(2)
+ : opcode == spv::Op::OpUntypedVariableKHR
+ ? inst.GetOperandAs<spv::StorageClass>(3)
+ : spv::StorageClass::Max;
if (opcode == spv::Op::OpBufferPointerEXT) {
auto result_type = vstate.FindDef(inst.type_id());
@@ -2250,253 +1293,6 @@
return SPV_SUCCESS;
}
-bool AllowsLayout(ValidationState_t& vstate, const spv::StorageClass sc) {
- switch (sc) {
- case spv::StorageClass::StorageBuffer:
- case spv::StorageClass::Uniform:
- case spv::StorageClass::PhysicalStorageBuffer:
- case spv::StorageClass::PushConstant:
- // Always explicitly laid out.
- return true;
- case spv::StorageClass::UniformConstant:
- return false;
- case spv::StorageClass::Workgroup:
- return vstate.HasCapability(
- spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
- case spv::StorageClass::Function:
- case spv::StorageClass::Private:
- return vstate.version() <= SPV_SPIRV_VERSION_WORD(1, 4);
- case spv::StorageClass::Input:
- case spv::StorageClass::Output:
- // Block is used generally and mesh shaders use Offset.
- return true;
- default:
- // TODO: Some storage classes in ray tracing use explicit layout
- // decorations, but it is not well documented which. For now treat other
- // storage classes as allowed to be laid out. See Vulkan internal issue
- // 4192.
- return true;
- }
-}
-
-// Returns a decoration used to make it explicit
-spv::Decoration UsesExplicitLayout(
- ValidationState_t& vstate, uint32_t type_id,
- std::unordered_map<uint32_t, spv::Decoration>& cache) {
- if (type_id == 0) {
- return spv::Decoration::Max;
- }
-
- if (cache.count(type_id)) {
- return cache[type_id];
- }
-
- spv::Decoration res = spv::Decoration::Max;
- const auto type_inst = vstate.FindDef(type_id);
- if (type_inst->opcode() == spv::Op::OpTypeStruct ||
- type_inst->opcode() == spv::Op::OpTypeArray ||
- type_inst->opcode() == spv::Op::OpTypeRuntimeArray ||
- type_inst->opcode() == spv::Op::OpTypePointer ||
- type_inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
- const auto& id_decs = vstate.id_decorations();
- const auto iter = id_decs.find(type_id);
- if (iter != id_decs.end()) {
- bool allowLayoutDecorations = false;
- if (type_inst->opcode() == spv::Op::OpTypePointer ||
- type_inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
- const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1);
- allowLayoutDecorations = AllowsLayout(vstate, sc);
- }
- if (!allowLayoutDecorations) {
- for (const auto& d : iter->second) {
- const spv::Decoration dec = d.dec_type();
- if (dec == spv::Decoration::Block ||
- dec == spv::Decoration::BufferBlock ||
- dec == spv::Decoration::Offset ||
- dec == spv::Decoration::ArrayStride ||
- dec == spv::Decoration::MatrixStride) {
- res = dec;
- break;
- }
- }
- }
- }
-
- if (res == spv::Decoration::Max) {
- switch (type_inst->opcode()) {
- case spv::Op::OpTypeStruct:
- for (uint32_t i = 1;
- res == spv::Decoration::Max && i < type_inst->operands().size();
- i++) {
- res = UsesExplicitLayout(
- vstate, type_inst->GetOperandAs<uint32_t>(i), cache);
- }
- break;
- case spv::Op::OpTypeArray:
- case spv::Op::OpTypeRuntimeArray:
- res = UsesExplicitLayout(vstate, type_inst->GetOperandAs<uint32_t>(1),
- cache);
- break;
- case spv::Op::OpTypePointer: {
- const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1);
- if (!AllowsLayout(vstate, sc)) {
- res = UsesExplicitLayout(
- vstate, type_inst->GetOperandAs<uint32_t>(2), cache);
- }
- }
- default:
- break;
- }
- }
- }
-
- cache[type_id] = res;
- return res;
-}
-
-spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
- if (!spvIsVulkanEnv(vstate.context()->target_env)) {
- return SPV_SUCCESS;
- }
-
- std::unordered_map<uint32_t, spv::Decoration> cache;
- for (const auto& inst : vstate.ordered_instructions()) {
- const auto type_id = inst.type_id();
- const auto type_inst = vstate.FindDef(type_id);
-
- spv::StorageClass sc = spv::StorageClass::Max;
- spv::Decoration layout_dec = spv::Decoration::Max;
- uint32_t fail_id = 0;
- uint32_t base_id = 0;
- // Variables are the main place to check for improper decorations, but some
- // untyped pointer instructions must also be checked since those types may
- // never be instantiated by a variable. Unlike verifying a valid layout,
- // physical storage buffer does not need checked here since it is always
- // explicitly laid out.
- switch (inst.opcode()) {
- case spv::Op::OpVariable:
- case spv::Op::OpUntypedVariableKHR: {
- sc = inst.GetOperandAs<spv::StorageClass>(2);
- auto check_id = type_id;
- base_id = inst.id();
- if (inst.opcode() == spv::Op::OpUntypedVariableKHR) {
- if (inst.operands().size() > 3) {
- check_id = inst.GetOperandAs<uint32_t>(3);
- }
- }
- if (!AllowsLayout(vstate, sc)) {
- layout_dec = UsesExplicitLayout(vstate, check_id, cache);
- if (layout_dec != spv::Decoration::Max) {
- fail_id = check_id;
- }
- }
- break;
- }
- case spv::Op::OpUntypedAccessChainKHR:
- case spv::Op::OpUntypedInBoundsAccessChainKHR:
- case spv::Op::OpUntypedPtrAccessChainKHR:
- case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: {
- // Check both the base type and return type. The return type may have an
- // invalid array stride.
- sc = type_inst->GetOperandAs<spv::StorageClass>(1);
- base_id = vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->id();
- if (!AllowsLayout(vstate, sc)) {
- const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
- layout_dec = UsesExplicitLayout(vstate, base_type_id, cache);
- if (layout_dec != spv::Decoration::Max) {
- fail_id = base_type_id;
- } else {
- layout_dec = UsesExplicitLayout(vstate, type_id, cache);
- if (layout_dec != spv::Decoration::Max) {
- fail_id = type_id;
- }
- }
- }
- break;
- }
- case spv::Op::OpUntypedArrayLengthKHR: {
- // Check the data type.
- const auto ptr_ty_id =
- vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->type_id();
- const auto ptr_ty = vstate.FindDef(ptr_ty_id);
- sc = ptr_ty->GetOperandAs<spv::StorageClass>(1);
- base_id = vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->id();
- if (!AllowsLayout(vstate, sc)) {
- const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
- layout_dec = UsesExplicitLayout(vstate, base_type_id, cache);
- if (layout_dec != spv::Decoration::Max) {
- fail_id = base_type_id;
- }
- }
- break;
- }
- case spv::Op::OpLoad: {
- const auto ptr_id = inst.GetOperandAs<uint32_t>(2);
- const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id());
- base_id = ptr_id;
- if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
- // For untyped pointers check the return type for an invalid layout.
- sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
- if (!AllowsLayout(vstate, sc)) {
- layout_dec = UsesExplicitLayout(vstate, type_id, cache);
- if (layout_dec != spv::Decoration::Max) {
- fail_id = type_id;
- }
- }
- }
- break;
- }
- case spv::Op::OpStore: {
- const auto ptr_id = inst.GetOperandAs<uint32_t>(1);
- const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id());
- base_id = inst.GetOperandAs<uint32_t>(0);
- if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
- // For untyped pointers, check the type of the data operand for an
- // invalid layout.
- sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
- if (!AllowsLayout(vstate, sc)) {
- const auto data_type_id = vstate.GetOperandTypeId(&inst, 1);
- layout_dec = UsesExplicitLayout(vstate, data_type_id, cache);
- if (layout_dec != spv::Decoration::Max) {
- fail_id = inst.GetOperandAs<uint32_t>(2);
- }
- }
- }
- break;
- }
- case spv::Op::OpBufferPointerEXT: {
- const auto ptr_id = inst.GetOperandAs<uint32_t>(1);
- const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id());
- sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
- // OpBufferPointerEXT needs to be in explicit layout, which it is,
- // because it must be Uniform/StorageBuffer
- if (sc != spv::StorageClass::StorageBuffer &&
- sc != spv::StorageClass::Uniform) {
- return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
- << "OpBufferPointerEXT Result Type must be a pointer type "
- << "with a Storage Class of Uniform or StorageBuffer.";
- }
- break;
- }
- default:
- break;
- }
-
- if (fail_id != 0 &&
- !vstate.IsDescriptorHeapBaseVariable(vstate.FindDef(base_id))) {
- return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
- << vstate.VkErrorID(10684)
- << "Invalid explicit layout decorations on type for operand "
- << vstate.getIdName(fail_id) << ", the "
- << spvtools::StorageClassToString(sc)
- << " storage class has a explicit layout from the "
- << vstate.SpvDecorationString(layout_dec) << " decoration.";
- }
- }
-
- return SPV_SUCCESS;
-}
-
} // namespace
spv_result_t ValidateDecorations(ValidationState_t& vstate) {
@@ -2509,7 +1305,6 @@
if (auto error = CheckVulkanMemoryModelDeprecatedDecorations(vstate))
return error;
if (auto error = CheckDecorationsFromDecoration(vstate)) return error;
- if (auto error = CheckInvalidVulkanExplicitLayout(vstate)) return error;
return SPV_SUCCESS;
}
diff --git a/source/val/validate_explicit_layout.cpp b/source/val/validate_explicit_layout.cpp
new file mode 100644
index 0000000..e25c79e
--- /dev/null
+++ b/source/val/validate_explicit_layout.cpp
@@ -0,0 +1,1130 @@
+// Copyright (c) 2026 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <algorithm>
+#include <cassert>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "source/spirv_constant.h"
+#include "source/spirv_target_env.h"
+#include "source/util/hash_combine.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+enum class LayoutMode : uint8_t {
+ // Vulkan scalar block rules
+ kScalar,
+ // Vulkan standard alignment rules (i.e. std430)
+ kStandard,
+ // Vulkan extended alignment rules (i.e. std140)
+ kExtended,
+};
+
+std::ostream& operator<<(std::ostream& str, const LayoutMode& mode) {
+ switch (mode) {
+ case LayoutMode::kScalar:
+ str << "scalar";
+ break;
+ case LayoutMode::kStandard:
+ str << "standard";
+ break;
+ case LayoutMode::kExtended:
+ str << "extended";
+ break;
+ }
+ return str;
+}
+
+enum class LayoutRequirement : uint8_t {
+ // Must be laid out
+ kRequired,
+ // Must not be laid out
+ kProhibited,
+ // Either laid or not
+ kAllowed,
+};
+
+struct Impl {
+ ValidationState_t& vstate;
+
+ // Relevant information to describe a memory instruction for the purposes of
+ // layout validation.
+ struct MemoryReference {
+ // The data type of the memory instruction (after stripping any descriptor
+ // array).
+ uint32_t type_id = 0;
+ // The descriptor array type id (if there is one).
+ uint32_t descriptor_array_id = 0;
+ // The storage class for the memory instruction.
+ spv::StorageClass storage_class;
+ // The layout mode (only relevant if a layout is required).
+ LayoutMode layout;
+ // The layout requirement for the instruction.
+ LayoutRequirement requirement;
+ // Whether it is an untyped pointer base.
+ bool untyped = false;
+ };
+
+ // Matrix constraints from a struct member to carry to the actual matrix type.
+ struct MatrixConstraints {
+ uint32_t stride = 0;
+ bool col_major = true;
+ };
+
+ // Cache valid checks for types that should have no layout.
+ std::unordered_set<uint32_t> no_layout_cache_;
+
+ // Struct member info
+ struct MemberInfo {
+ // Structure member index (note: index + 1 is instruction index).
+ uint32_t index;
+ // Whether or not an Offset/OffsetIdEXT decoration is present.
+ bool has_offset = false;
+ // Offset value. Max uint32_t is used for no evaluation (e.g. spec
+ // constant).
+ uint32_t offset = std::numeric_limits<uint32_t>::max();
+ // Whether or not RowMajor or ColMajor decoration is present.
+ bool has_matrix = false;
+ // Matrix constraints (stride and majorness).
+ MatrixConstraints matrix_constraints;
+ };
+
+ // Cache of structure member information.
+ std::unordered_map<uint32_t, std::vector<MemberInfo>> struct_members_;
+
+ struct LayoutKey {
+ uint32_t type_id = 0;
+ LayoutMode layout;
+ uint32_t incoming_offset = 0;
+ MatrixConstraints matrix_constraints{};
+
+ bool operator==(const LayoutKey& other) const {
+ return type_id == other.type_id && layout == other.layout &&
+ incoming_offset == other.incoming_offset &&
+ matrix_constraints.stride == other.matrix_constraints.stride &&
+ matrix_constraints.col_major == other.matrix_constraints.col_major;
+ }
+ };
+ struct LayoutKeyHash {
+ size_t operator()(const LayoutKey& key) const noexcept {
+ return spvtools::utils::hash_combine(
+ 0, key.type_id, static_cast<uint32_t>(key.layout),
+ key.incoming_offset, key.matrix_constraints.stride,
+ key.matrix_constraints.col_major);
+ }
+ };
+
+ // Caches valid results of CheckLayout calls.
+ std::unordered_set<LayoutKey, LayoutKeyHash> layout_cache_;
+
+ // Returns the layout requirements for `sc`.
+ // Workgroup is expected to be explicitly laid out if `is_block` is true.
+ // UniformConstant is expected to be explicitly laid out if `descriptor_heap`
+ // is true.
+ LayoutRequirement GetStorageClassRequirement(spv::StorageClass sc,
+ bool is_block,
+ bool descriptor_heap) {
+ switch (sc) {
+ case spv::StorageClass::Workgroup:
+ return is_block ? LayoutRequirement::kRequired
+ : LayoutRequirement::kProhibited;
+ case spv::StorageClass::StorageBuffer:
+ case spv::StorageClass::Uniform:
+ case spv::StorageClass::PushConstant:
+ case spv::StorageClass::PhysicalStorageBuffer:
+ return LayoutRequirement::kRequired;
+ case spv::StorageClass::UniformConstant:
+ return descriptor_heap ? LayoutRequirement::kRequired
+ : LayoutRequirement::kProhibited;
+ case spv::StorageClass::Function:
+ case spv::StorageClass::Private:
+ return vstate.version() <= SPV_SPIRV_VERSION_WORD(1, 4)
+ ? LayoutRequirement::kAllowed
+ : LayoutRequirement::kProhibited;
+ case spv::StorageClass::Input:
+ case spv::StorageClass::Output:
+ // Block is used generally and mesh shaders use Offset.
+ // TODO: This is a little over permissive.
+ return LayoutRequirement::kAllowed;
+ default:
+ // TODO: Some storage classes in ray tracing use explicit layout
+ // decorations, but it is not well documented which. For now treat
+ // other storage classes as allowed to be laid out. See Vulkan
+ // internal issue 4192.
+ return LayoutRequirement::kAllowed;
+ }
+ }
+
+ // Returns the layout rules for `sc`.
+ LayoutMode GetStorageClassLayout(spv::StorageClass sc, bool is_buffer_block) {
+ switch (sc) {
+ case spv::StorageClass::Workgroup:
+ return vstate.options()->workgroup_scalar_block_layout
+ ? LayoutMode::kScalar
+ : LayoutMode::kStandard;
+ break;
+ case spv::StorageClass::StorageBuffer:
+ case spv::StorageClass::PushConstant:
+ case spv::StorageClass::UniformConstant:
+ case spv::StorageClass::PhysicalStorageBuffer:
+ return vstate.options()->scalar_block_layout ? LayoutMode::kScalar
+ : LayoutMode::kStandard;
+ break;
+ case spv::StorageClass::Uniform:
+ return vstate.options()->scalar_block_layout
+ ? LayoutMode::kScalar
+ : ((is_buffer_block ||
+ vstate.options()->uniform_buffer_standard_layout)
+ ? LayoutMode::kStandard
+ : LayoutMode::kExtended);
+ break;
+ default:
+ break;
+ }
+ return LayoutMode::kStandard;
+ }
+
+ // Returns true if `inst` is a memory reference instruction
+ // Populates `reference` with the necessary information.
+ //
+ // The following are interesting memory references:
+ // For typed pointers:
+ // * OpVariable
+ // * Memory instructions on PhysicalStorageBuffer
+ // * OpBufferPointerEXT
+ // For untyped pointers:
+ // * All memory instructions
+ bool GetMemoryReference(const Instruction* inst, MemoryReference* reference) {
+ auto* type_inst = vstate.FindDef(inst->type_id());
+ if (inst->opcode() == spv::Op::OpVariable) {
+ auto sc = type_inst->GetOperandAs<spv::StorageClass>(1u);
+ const bool is_descriptor_heap = vstate.IsDescriptorHeapBaseVariable(inst);
+ reference->storage_class = sc;
+ reference->type_id = type_inst->GetOperandAs<uint32_t>(2u);
+ const bool is_block_decorated =
+ vstate.GetIdOpcode(reference->type_id) == spv::Op::OpTypeStruct &&
+ vstate.HasDecoration(reference->type_id, spv::Decoration::Block);
+ reference->requirement = GetStorageClassRequirement(
+ sc, is_block_decorated, is_descriptor_heap);
+
+ // Unwrap the descriptor array.
+ if (sc == spv::StorageClass::StorageBuffer ||
+ sc == spv::StorageClass::Uniform ||
+ sc == spv::StorageClass::UniformConstant) {
+ const auto* data_type = vstate.FindDef(reference->type_id);
+ if (data_type->opcode() == spv::Op::OpTypeArray ||
+ data_type->opcode() == spv::Op::OpTypeRuntimeArray) {
+ reference->descriptor_array_id = reference->type_id;
+ reference->type_id = data_type->GetOperandAs<uint32_t>(1u);
+ }
+ }
+
+ const bool buffer_block =
+ vstate.GetIdOpcode(reference->type_id) == spv::Op::OpTypeStruct &&
+ vstate.HasDecoration(reference->type_id,
+ spv::Decoration::BufferBlock);
+ reference->layout = GetStorageClassLayout(sc, buffer_block);
+
+ return true;
+ } else if (type_inst && type_inst->opcode() == spv::Op::OpTypePointer &&
+ type_inst->GetOperandAs<spv::StorageClass>(1u) ==
+ spv::StorageClass::PhysicalStorageBuffer) {
+ reference->storage_class = spv::StorageClass::PhysicalStorageBuffer;
+ reference->type_id = type_inst->GetOperandAs<uint32_t>(2u);
+ reference->layout = GetStorageClassLayout(
+ spv::StorageClass::PhysicalStorageBuffer, false);
+ reference->requirement = LayoutRequirement::kRequired;
+
+ return true;
+ } else if (inst->opcode() == spv::Op::OpBufferPointerEXT &&
+ type_inst->opcode() == spv::Op::OpTypePointer) {
+ auto sc = type_inst->GetOperandAs<spv::StorageClass>(1u);
+ reference->storage_class = sc;
+ uint32_t pointee_ty_id = type_inst->GetOperandAs<uint32_t>(2u);
+ const bool buffer_block =
+ vstate.GetIdOpcode(pointee_ty_id) == spv::Op::OpTypeStruct &&
+ vstate.HasDecoration(pointee_ty_id, spv::Decoration::BufferBlock);
+ reference->layout = GetStorageClassLayout(sc, buffer_block);
+ reference->requirement = LayoutRequirement::kRequired;
+ reference->type_id = pointee_ty_id;
+
+ return true;
+ } else if (vstate.HasCapability(spv::Capability::UntypedPointersKHR) &&
+ spvIsVulkanEnv(vstate.context()->target_env)) {
+ uint32_t ptr_ty_id = 0;
+ uint32_t data_ty_id = 0;
+ switch (inst->opcode()) {
+ case spv::Op::OpUntypedVariableKHR:
+ if (inst->operands().size() > 3) {
+ ptr_ty_id = inst->type_id();
+ data_ty_id = inst->GetOperandAs<uint32_t>(3u);
+ } else {
+ return false;
+ }
+ break;
+ case spv::Op::OpUntypedAccessChainKHR:
+ case spv::Op::OpUntypedInBoundsAccessChainKHR:
+ case spv::Op::OpUntypedPtrAccessChainKHR:
+ case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
+ ptr_ty_id = inst->type_id();
+ data_ty_id = inst->GetOperandAs<uint32_t>(2);
+ break;
+ case spv::Op::OpLoad:
+ if (vstate.GetIdOpcode(vstate.GetOperandTypeId(inst, 2)) ==
+ spv::Op::OpTypeUntypedPointerKHR) {
+ const auto ptr_id = inst->GetOperandAs<uint32_t>(2);
+ ptr_ty_id = vstate.FindDef(ptr_id)->type_id();
+ data_ty_id = inst->type_id();
+ } else {
+ return false;
+ }
+ break;
+ case spv::Op::OpStore:
+ if (vstate.GetIdOpcode(vstate.GetOperandTypeId(inst, 0)) ==
+ spv::Op::OpTypeUntypedPointerKHR) {
+ const auto ptr_id = inst->GetOperandAs<uint32_t>(0);
+ ptr_ty_id = vstate.FindDef(ptr_id)->type_id();
+ data_ty_id = vstate.GetOperandTypeId(inst, 1);
+ } else {
+ return false;
+ }
+ break;
+ case spv::Op::OpUntypedArrayLengthKHR:
+ ptr_ty_id =
+ vstate.FindDef(inst->GetOperandAs<uint32_t>(3))->type_id();
+ data_ty_id = inst->GetOperandAs<uint32_t>(2);
+ break;
+ default:
+ return false;
+ }
+
+ // If the data type is an array that contains a Block- or
+ // BufferBlock-decorated struct, then use the struct for layout checks
+ // instead of the array. In this case, the array represents a descriptor
+ // array which should not have an explicit layout.
+ const auto* data_type = vstate.FindDef(data_ty_id);
+ if (data_type->opcode() == spv::Op::OpTypeArray ||
+ data_type->opcode() == spv::Op::OpTypeRuntimeArray) {
+ uint32_t ele_ty_id = data_type->GetOperandAs<uint32_t>(1u);
+ if (vstate.HasDecoration(ele_ty_id, spv::Decoration::Block) ||
+ vstate.HasDecoration(ele_ty_id, spv::Decoration::BufferBlock)) {
+ reference->descriptor_array_id = data_ty_id;
+ data_ty_id = ele_ty_id;
+ }
+ }
+
+ auto sc = vstate.FindDef(ptr_ty_id)->GetOperandAs<spv::StorageClass>(1u);
+ reference->storage_class = sc;
+ reference->type_id = data_ty_id;
+ reference->requirement = LayoutRequirement::kRequired;
+ reference->untyped = true;
+ const bool buffer_block =
+ vstate.GetIdOpcode(data_ty_id) == spv::Op::OpTypeStruct &&
+ vstate.HasDecoration(data_ty_id, spv::Decoration::BufferBlock);
+ reference->layout = GetStorageClassLayout(sc, buffer_block);
+
+ return true;
+ }
+
+ // Not a memory reference.
+ return false;
+ }
+
+ // Checks that no instruction in the type tree of `type_id` has any explicit
+ // layout decorations.
+ spv_result_t CheckNoLayout(const Instruction* inst, uint32_t type_id,
+ spv::StorageClass sc) {
+ if (no_layout_cache_.count(type_id)) {
+ return SPV_SUCCESS;
+ }
+
+ const auto* type_inst = vstate.FindDef(type_id);
+ if (type_inst->opcode() == spv::Op::OpTypePointer) {
+ // PhysicalStorageBuffer and variable pointers can have ArrayStride
+ // decorations even if they are stored in a non-laid storage class (e.g.
+ // Function).
+ auto ptr_sc = type_inst->GetOperandAs<spv::StorageClass>(1u);
+ if (GetStorageClassRequirement(ptr_sc, true, false) !=
+ LayoutRequirement::kProhibited) {
+ return SPV_SUCCESS;
+ }
+ }
+
+ const auto& id_decs = vstate.id_decorations();
+ const auto iter = id_decs.find(type_id);
+ if (iter != id_decs.end()) {
+ for (const auto& d : iter->second) {
+ const spv::Decoration dec = d.dec_type();
+ if (dec == spv::Decoration::Block ||
+ dec == spv::Decoration::BufferBlock ||
+ dec == spv::Decoration::Offset ||
+ dec == spv::Decoration::OffsetIdEXT ||
+ dec == spv::Decoration::ArrayStride ||
+ dec == spv::Decoration::ArrayStrideIdEXT ||
+ dec == spv::Decoration::MatrixStride ||
+ dec == spv::Decoration::RowMajor ||
+ dec == spv::Decoration::ColMajor) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, inst)
+ << vstate.VkErrorID(10684)
+ << "Invalid explicit layout decorations on type "
+ << vstate.getIdName(type_id) << ", the "
+ << spvtools::StorageClassToString(sc)
+ << " storage class has an explicit layout from the "
+ << vstate.SpvDecorationString(dec) << " decoration";
+ }
+ }
+ }
+
+ switch (type_inst->opcode()) {
+ case spv::Op::OpTypeStruct:
+ for (uint32_t i = 1; i < type_inst->operands().size(); i++) {
+ if (auto error = CheckNoLayout(
+ inst, type_inst->GetOperandAs<uint32_t>(i), sc)) {
+ return error;
+ }
+ }
+ break;
+ case spv::Op::OpTypeRuntimeArray:
+ case spv::Op::OpTypeArray:
+ if (auto error = CheckNoLayout(
+ inst, type_inst->GetOperandAs<uint32_t>(1u), sc)) {
+ return error;
+ }
+ break;
+ case spv::Op::OpTypePointer: {
+ auto ptr_sc = type_inst->GetOperandAs<spv::StorageClass>(1u);
+ if (auto error = CheckNoLayout(
+ inst, type_inst->GetOperandAs<uint32_t>(2u), ptr_sc)) {
+ return error;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ no_layout_cache_.insert(type_id);
+
+ return SPV_SUCCESS;
+ }
+
+ // Returns true if type_id contains a matrix. Only looks through arrays.
+ bool ContainsMatrix(uint32_t type_id) {
+ const auto* type_inst = vstate.FindDef(type_id);
+ switch (type_inst->opcode()) {
+ case spv::Op::OpTypeMatrix:
+ return true;
+ case spv::Op::OpTypeArray:
+ case spv::Op::OpTypeRuntimeArray:
+ return ContainsMatrix(type_inst->GetOperandAs<uint32_t>(1u));
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ // Gets the value for an id-based layout decoration (e.g. ArrayStrideIdEXT and
+ // OffsetIdEXT). Returns max uint32_t if the value cannot be evaluated (e.g.
+ // spec constant).
+ uint32_t GetIdDecorationValue(uint32_t id) {
+ const auto* inst = vstate.FindDef(id);
+ if (!spvOpcodeIsConstant(inst->opcode())) {
+ return std::numeric_limits<uint32_t>::max();
+ }
+ uint64_t value = 0;
+ if (vstate.EvalConstantValUint64(id, &value)) {
+ return static_cast<uint32_t>(value);
+ }
+ return std::numeric_limits<uint32_t>::max();
+ }
+
+ // Returns whether an array stride decoration is present on the array and its
+ // value.
+ std::pair<bool, uint32_t> GetArrayStride(uint32_t array_id) {
+ uint32_t stride = std::numeric_limits<uint32_t>::max();
+ bool has_stride = false;
+ for (auto& d : vstate.id_decorations(array_id)) {
+ if (d.dec_type() == spv::Decoration::ArrayStride) {
+ stride = d.params()[0];
+ has_stride = true;
+ break;
+ } else if (d.dec_type() == spv::Decoration::ArrayStrideIdEXT) {
+ stride = GetIdDecorationValue(d.params()[0]);
+ has_stride = true;
+ break;
+ }
+ }
+ return std::make_pair(has_stride, stride);
+ }
+
+ // Gets the offset value from an offset decoration.
+ uint32_t GetOffset(spv::Decoration dec, uint32_t param) {
+ // param is a literal value
+ if (dec == spv::Decoration::Offset) {
+ return param;
+ }
+ // param is an id
+ return GetIdDecorationValue(param);
+ }
+
+ // Returns true if value is aligned to align.
+ bool IsAlignedTo(uint32_t value, uint32_t align) {
+ if (align == 0) return value == 0;
+ return (value % align) == 0;
+ }
+
+ // Rounds up value to next multiple of align.
+ uint32_t AlignTo(uint32_t value, uint32_t align) {
+ return (value + align - 1) & ~(align - 1);
+ }
+
+ // A member is defined to improperly straddle if either of the following are
+ // true:
+ // - It is a vector with total size less than or equal to 16 bytes, and has
+ // Offset decorations placing its first byte at F and its last byte at L,
+ // where floor(F / 16) != floor(L / 16).
+ // - It is a vector with total size greater than 16 bytes and has its Offset
+ // decorations placing its first byte at a non-integer multiple of 16.
+ bool HasImproperStraddle(uint32_t offset, uint32_t size) {
+ const auto F = offset;
+ const auto L = offset + size - 1;
+ if (size <= 16) {
+ if ((F >> 4) != (L >> 4)) return true;
+ } else {
+ if (F % 16 != 0) return true;
+ }
+ return false;
+ }
+
+ // Returns the alignment for type_id for the given layout rules.
+ uint32_t GetAlign(uint32_t type_id, LayoutMode mode,
+ const MatrixConstraints& matrix_constraints) {
+ const auto* type_inst = vstate.FindDef(type_id);
+ uint32_t align = 1;
+ switch (type_inst->opcode()) {
+ case spv::Op::OpTypeSampledImage:
+ case spv::Op::OpTypeSampler:
+ case spv::Op::OpTypeImage:
+ if (vstate.HasCapability(spv::Capability::BindlessTextureNV)) {
+ return vstate.samplerimage_variable_address_mode() / 8;
+ }
+ if (type_inst->opcode() == spv::Op::OpTypeSampler) {
+ return vstate.options()->sampler_descriptor_layout.alignment;
+ }
+ if (type_inst->opcode() == spv::Op::OpTypeImage) {
+ return vstate.options()->image_descriptor_layout.alignment;
+ }
+ break;
+ case spv::Op::OpTypeBufferEXT:
+ case spv::Op::OpTypeAccelerationStructureKHR:
+ return vstate.options()->buffer_descriptor_layout.alignment;
+ case spv::Op::OpTypeInt:
+ case spv::Op::OpTypeFloat:
+ return type_inst->GetOperandAs<uint32_t>(1u) / 8;
+ case spv::Op::OpTypeVector:
+ case spv::Op::OpTypeVectorIdEXT: {
+ const auto ele_id = type_inst->GetOperandAs<uint32_t>(1u);
+ const auto num_eles = vstate.GetDimension(type_id);
+ align = GetAlign(ele_id, mode, {});
+ if (mode == LayoutMode::kScalar || vstate.IsRelaxedBlockLayout()) {
+ return align;
+ }
+ return align * ((num_eles == 3 || num_eles > 4) ? 4 : num_eles);
+ }
+ case spv::Op::OpTypeMatrix:
+ if (mode == LayoutMode::kScalar) {
+ const auto* vec_inst =
+ vstate.FindDef(type_inst->GetOperandAs<uint32_t>(1u));
+ const auto ele_id = vec_inst->GetOperandAs<uint32_t>(1u);
+ return GetAlign(ele_id, mode, {});
+ }
+
+ if (matrix_constraints.col_major) {
+ align = GetAlign(type_inst->GetOperandAs<uint32_t>(1u), mode, {});
+ } else {
+ // A row-major matrix of C columns has a base alignment equal to the
+ // base alignment of a vector of C matrix components.
+ const auto num_cols = type_inst->GetOperandAs<uint32_t>(2u);
+ const auto* col_inst =
+ vstate.FindDef(type_inst->GetOperandAs<uint32_t>(1u));
+ const auto ele_id = col_inst->GetOperandAs<uint32_t>(1u);
+ align = GetAlign(ele_id, mode, {});
+ // The equivalent vector may not exist so we replicate the vector rule
+ // here.
+ if (mode != LayoutMode::kScalar && !vstate.IsRelaxedBlockLayout()) {
+ align = align * (num_cols == 3 ? 4 : num_cols);
+ }
+ }
+ if (mode == LayoutMode::kExtended) {
+ align = AlignTo(align, 16u);
+ }
+ return align;
+ case spv::Op::OpTypeArray:
+ case spv::Op::OpTypeRuntimeArray:
+ align = GetAlign(type_inst->GetOperandAs<uint32_t>(1u), mode,
+ matrix_constraints);
+ if (mode == LayoutMode::kExtended) {
+ align = AlignTo(align, 16u);
+ }
+ return align;
+ case spv::Op::OpTypeStruct:
+ for (uint32_t i = 1; i < type_inst->operands().size(); i++) {
+ const auto member_id = type_inst->GetOperandAs<uint32_t>(i);
+ MatrixConstraints mat_constraints;
+ GetMatrixConstraints(type_id, i - 1, &mat_constraints);
+ align = std::max(align, GetAlign(member_id, mode, mat_constraints));
+ if (mode == LayoutMode::kExtended) {
+ align = AlignTo(align, 16u);
+ }
+ }
+ return align;
+ case spv::Op::OpTypePointer:
+ case spv::Op::OpTypeUntypedPointerKHR:
+ return vstate.pointer_size_and_alignment();
+ default:
+ break;
+ }
+ assert(0 && "unhandled type");
+ return 1;
+ }
+
+ // Returns the size of the given type.
+ uint32_t GetSize(uint32_t type_id,
+ const MatrixConstraints& matrix_constraints) {
+ const auto* type_inst = vstate.FindDef(type_id);
+ switch (type_inst->opcode()) {
+ case spv::Op::OpTypeSampledImage:
+ case spv::Op::OpTypeSampler:
+ case spv::Op::OpTypeImage:
+ if (vstate.HasCapability(spv::Capability::BindlessTextureNV)) {
+ return vstate.samplerimage_variable_address_mode() / 8;
+ }
+ if (type_inst->opcode() == spv::Op::OpTypeSampler) {
+ return vstate.options()->sampler_descriptor_layout.size;
+ }
+ if (type_inst->opcode() == spv::Op::OpTypeImage) {
+ return vstate.options()->image_descriptor_layout.size;
+ }
+ break;
+ case spv::Op::OpTypeBufferEXT:
+ case spv::Op::OpTypeAccelerationStructureKHR:
+ return vstate.options()->buffer_descriptor_layout.size;
+ case spv::Op::OpTypeInt:
+ case spv::Op::OpTypeFloat:
+ return type_inst->GetOperandAs<uint32_t>(1u) / 8;
+ case spv::Op::OpTypeVector:
+ case spv::Op::OpTypeVectorIdEXT: {
+ const auto ele_id = type_inst->GetOperandAs<uint32_t>(1u);
+ const auto num_eles = vstate.GetDimension(type_id);
+ return GetSize(ele_id, {}) * num_eles;
+ }
+ case spv::Op::OpTypeArray: {
+ const auto count_id = type_inst->GetOperandAs<uint32_t>(2u);
+ uint64_t count = 0;
+ if (!vstate.EvalConstantValUint64(count_id, &count)) {
+ return 0;
+ }
+ const auto [has_stride, stride] = GetArrayStride(type_id);
+ const auto ele_size =
+ GetSize(type_inst->GetOperandAs<uint32_t>(1u), matrix_constraints);
+ // uint32 max is a marker for unevaluatable.
+ if (stride == std::numeric_limits<uint32_t>::max()) {
+ return ele_size;
+ }
+ return (static_cast<uint32_t>(count) - 1) * stride + ele_size;
+ }
+ case spv::Op::OpTypeRuntimeArray:
+ return 0;
+ case spv::Op::OpTypeMatrix: {
+ const auto num_cols = type_inst->GetOperandAs<uint32_t>(2u);
+ if (matrix_constraints.col_major) {
+ return num_cols * matrix_constraints.stride;
+ } else {
+ const auto* col_inst =
+ vstate.FindDef(type_inst->GetOperandAs<uint32_t>(1u));
+ const auto ele_id = col_inst->GetOperandAs<uint32_t>(1u);
+ const auto num_rows = col_inst->GetOperandAs<uint32_t>(2u);
+ return (num_rows - 1) * matrix_constraints.stride +
+ num_cols * GetSize(ele_id, {});
+ }
+ }
+ case spv::Op::OpTypeStruct: {
+ const auto& members = GetStructMembers(type_id);
+ if (members.empty()) return 0;
+ const auto& last = members.back();
+ if (last.offset == std::numeric_limits<uint32_t>::max()) {
+ return 0;
+ }
+ return last.offset +
+ GetSize(type_inst->GetOperandAs<uint32_t>(last.index + 1),
+ last.matrix_constraints);
+ }
+ case spv::Op::OpTypePointer:
+ case spv::Op::OpTypeUntypedPointerKHR:
+ return vstate.pointer_size_and_alignment();
+ default:
+ break;
+ }
+ assert(0 && "unhandled type");
+ return 0;
+ }
+
+ // Returns true if type_id has a matrix and populates matrix_constraints.
+ bool GetMatrixConstraints(uint32_t type_id, uint32_t index,
+ MatrixConstraints* matrix_constraints) {
+ bool has_matrix = false;
+ auto member_decorations = vstate.id_member_decorations(type_id, index);
+ for (auto decoration = member_decorations.begin;
+ decoration != member_decorations.end; ++decoration) {
+ if (decoration->dec_type() == spv::Decoration::ColMajor ||
+ decoration->dec_type() == spv::Decoration::RowMajor) {
+ has_matrix = true;
+ matrix_constraints->col_major =
+ decoration->dec_type() == spv::Decoration::ColMajor;
+ }
+ if (decoration->dec_type() == spv::Decoration::MatrixStride) {
+ matrix_constraints->stride = decoration->params()[0];
+ }
+ }
+ return has_matrix;
+ }
+
+ // Gathers (and caches) structure members and their decorations.
+ const std::vector<MemberInfo>& GetStructMembers(uint32_t type_id) {
+ if (struct_members_.count(type_id)) {
+ return struct_members_[type_id];
+ }
+
+ const auto* type_inst = vstate.FindDef(type_id);
+ std::vector<MemberInfo> member_info;
+ member_info.reserve(type_inst->operands().size() - 1);
+ for (uint32_t i = 1; i < type_inst->operands().size(); i++) {
+ auto member_idx = i - 1;
+ auto member_decorations =
+ vstate.id_member_decorations(type_id, member_idx);
+ member_info.push_back(MemberInfo{member_idx});
+ auto& member = member_info.back();
+ member.has_matrix =
+ GetMatrixConstraints(type_id, member_idx, &member.matrix_constraints);
+ for (auto decoration = member_decorations.begin;
+ decoration != member_decorations.end; ++decoration) {
+ switch (decoration->dec_type()) {
+ case spv::Decoration::Offset:
+ case spv::Decoration::OffsetIdEXT:
+ member.has_offset = true;
+ member.offset =
+ GetOffset(decoration->dec_type(), decoration->params()[0]);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ // Sort by offset value.
+ std::stable_sort(member_info.begin(), member_info.end(),
+ [](const MemberInfo& lhs, const MemberInfo& rhs) {
+ return lhs.offset < rhs.offset;
+ });
+ struct_members_[type_id] = std::move(member_info);
+ return struct_members_[type_id];
+ }
+
+ // Returns some common messaging to improve diagnostics.
+ std::string CommonError(const Instruction* inst,
+ spv::StorageClass storage_class, LayoutMode mode) {
+ std::string s;
+ std::ostringstream str(s);
+ str << " Instantiated via " << vstate.getIdName(inst->id()) << " in the "
+ << spvtools::StorageClassToString(storage_class)
+ << " storage class using " << mode << " layout rules.";
+ if (mode != LayoutMode::kScalar) {
+ if (storage_class == spv::StorageClass::Workgroup) {
+ str << vstate.MissingFeature(
+ "workgroupMemoryExplicitLayoutScalarBlockLayout feature",
+ "--workgroup-scalar-block-layout", true);
+ } else if (!vstate.IsRelaxedBlockLayout()) {
+ str << vstate.MissingFeature("VK_KHR_relaxed_block_layout extension",
+ "--relax-block-layout", true);
+ } else if (storage_class == spv::StorageClass::Uniform &&
+ mode != LayoutMode::kStandard) {
+ str << vstate.MissingFeature("uniformBufferStandardLayout feature",
+ "--uniform-buffer-standard-layout", true);
+ } else {
+ str << vstate.MissingFeature("scalarBlockLayout feature",
+ "--scalar-block-layout", true);
+ }
+ }
+ return str.str();
+ }
+
+ // Checks struct layouts
+ // * Each member must
+ // * Have an offset decoration
+ // * If the member is a matrix or array(s) of matrices
+ // * Must have majorness and matrix stride decorations
+ // * If member is a runtime array, it must be last by offset
+ // * Offset must be aligned
+ // * Total offset must be aligned
+ // * Offset + size < next elements offset
+ // * If member is a vector, it must have a valid straddle
+ // * The member has a valid layout
+ spv_result_t CheckStructLayout(const Instruction* inst, uint32_t type_id,
+ spv::StorageClass storage_class,
+ LayoutMode mode, uint32_t incoming_offset,
+ const MatrixConstraints& matrix_constraints) {
+ const auto* type_inst = vstate.FindDef(type_id);
+ const auto& member_info = GetStructMembers(type_id);
+ for (auto& member : member_info) {
+ const auto member_id =
+ type_inst->GetOperandAs<uint32_t>(member.index + 1);
+ if (!member.has_offset) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Structure member " << member.index
+ << " must be explicitly laid out with Offset or OffsetIdEXT "
+ "decorations."
+ << CommonError(inst, storage_class, mode);
+ }
+ if (ContainsMatrix(member_id)) {
+ if (!member.has_matrix) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Structure member " << member.index
+ << " containing a matrix must be explicitly laid out "
+ "with RowMajor or ColMajor decorations."
+ << CommonError(inst, storage_class, mode);
+ } else if (member.matrix_constraints.stride == 0) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Structure member " << member.index
+ << " containing a matrix must be explicitly laid out "
+ "with MatrixStride decorations."
+ << CommonError(inst, storage_class, mode);
+ }
+ }
+ }
+
+ uint32_t next_offset = 0;
+ uint32_t ordered_index = 0;
+ for (const auto& member : member_info) {
+ auto member_idx = member.index;
+ auto offset = member.offset;
+ // We have no information
+ if (offset == std::numeric_limits<uint32_t>::max()) {
+ continue;
+ }
+ auto member_id = type_inst->GetOperandAs<uint32_t>(member_idx + 1);
+ auto member_inst = vstate.FindDef(member_id);
+ if (member_inst->opcode() == spv::Op::OpTypeRuntimeArray &&
+ ordered_index != member_info.size() - 1) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << vstate.VkErrorID(4680)
+ << "Structure has a runtime array at offset " << offset
+ << ", but other members at larger offsets."
+ << CommonError(inst, storage_class, mode);
+ }
+ ordered_index++;
+
+ MatrixConstraints mat_constraints = matrix_constraints;
+ if (member.has_matrix) {
+ mat_constraints = member.matrix_constraints;
+ }
+ uint32_t align = GetAlign(member_id, mode, mat_constraints);
+ if (!IsAlignedTo(offset, align)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Structure member " << member_idx << " at offset " << offset
+ << " is not aligned to " << align << "."
+ << CommonError(inst, storage_class, mode);
+ }
+ if (!IsAlignedTo(offset + incoming_offset, align)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Structure member " << member_idx << " at offset " << offset
+ << " plus incoming offset " << incoming_offset
+ << " is not aligned to " << align << "."
+ << CommonError(inst, storage_class, mode);
+ }
+ if (offset < next_offset) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Structure member " << member_idx << " at offset " << offset
+ << " overlaps previous member ending at offset "
+ << next_offset - 1 << "."
+ << CommonError(inst, storage_class, mode);
+ }
+ if (mode != LayoutMode::kScalar && vstate.IsRelaxedBlockLayout()) {
+ if (member_inst->opcode() == spv::Op::OpTypeVector ||
+ member_inst->opcode() == spv::Op::OpTypeVectorIdEXT) {
+ uint32_t size = GetSize(member_id, {});
+ if (HasImproperStraddle(incoming_offset + offset, size)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Structure member " << member_idx
+ << ": Vector has improper straddle due to offset "
+ << incoming_offset + offset << "."
+ << CommonError(inst, storage_class, mode);
+ }
+ }
+ }
+ if (auto error = CheckLayout(inst, member_id, storage_class, mode,
+ incoming_offset + offset, mat_constraints)) {
+ return error;
+ }
+
+ uint32_t size = GetSize(member_id, mat_constraints);
+ next_offset = size + offset;
+ if (mode != LayoutMode::kScalar &&
+ (member_inst->opcode() == spv::Op::OpTypeArray ||
+ member_inst->opcode() == spv::Op::OpTypeStruct)) {
+ next_offset = AlignTo(next_offset, align);
+ }
+ }
+
+ return SPV_SUCCESS;
+ }
+
+ // Checks array layouts
+ // * Arrays must have stride decoration
+ // * Stride must be non-zero
+ // * Stride must be aligned
+ // * Stride must be greater or equal to element size
+ // * Elements have valid layout
+ spv_result_t CheckArrayLayout(const Instruction* inst, uint32_t type_id,
+ spv::StorageClass storage_class,
+ LayoutMode mode, uint32_t incoming_offset,
+ const MatrixConstraints& matrix_constraints) {
+ const auto* type_inst = vstate.FindDef(type_id);
+ auto [has_stride, stride] = GetArrayStride(type_id);
+ if (!has_stride) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Array must be explicitly laid out with ArrayStride or "
+ "ArrayStrideIdEXT decorations."
+ << CommonError(inst, storage_class, mode);
+ }
+ uint32_t ele_id = type_inst->GetOperandAs<uint32_t>(1u);
+ uint32_t ele_size = GetSize(ele_id, matrix_constraints);
+ uint32_t align = GetAlign(type_id, mode, matrix_constraints);
+ if (stride == 0) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Array must not have a stride of 0."
+ << CommonError(inst, storage_class, mode);
+ }
+
+ // uint32 max stride is unevaluatable (e.g. spec constant).
+ if (stride == std::numeric_limits<uint32_t>::max()) {
+ return SPV_SUCCESS;
+ }
+
+ if (!IsAlignedTo(stride, align)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Array stride " << stride << " must satisfy alignment " << align
+ << "." << CommonError(inst, storage_class, mode);
+ }
+ if (stride != std::numeric_limits<uint32_t>::max() && stride < ele_size) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Array stride " << stride
+ << " is smaller than element type size " << ele_size << "."
+ << CommonError(inst, storage_class, mode);
+ }
+
+ uint32_t num_elements = 0;
+ if (type_inst->opcode() == spv::Op::OpTypeArray) {
+ uint64_t count = 0;
+ if (vstate.EvalConstantValUint64(type_inst->GetOperandAs<uint32_t>(2u),
+ &count)) {
+ num_elements = static_cast<uint32_t>(count);
+ }
+ }
+ num_elements = std::max(1u, num_elements);
+ std::vector<bool> seen(16, false);
+ for (uint32_t i = 0; i < num_elements; ++i) {
+ uint32_t next_offset = i * stride + incoming_offset;
+ // Stop checking if offsets repeat in terms of 16-byte multiples.
+ if (seen[next_offset % 16]) {
+ break;
+ }
+
+ if (auto error = CheckLayout(inst, ele_id, storage_class, mode,
+ next_offset, matrix_constraints)) {
+ return error;
+ }
+
+ seen[next_offset % 16] = true;
+ }
+ return SPV_SUCCESS;
+ }
+
+ // Checks the layout matrices
+ // * Stride must be a multiple of align
+ // * Stride must be greater or equal to minor size
+ spv_result_t CheckMatrixLayout(const Instruction* inst, uint32_t type_id,
+ spv::StorageClass storage_class,
+ LayoutMode mode,
+ const MatrixConstraints& matrix_constraints) {
+ // We already checked that any struct containing a matrix has a non-zero
+ // stride so if we have 0 stride here then it will come from the result
+ // of an access chain or other instruction. Other rules are meant to
+ // catch any misuse we can skip it here.
+ if (matrix_constraints.stride == 0) {
+ return SPV_SUCCESS;
+ }
+ const auto* type_inst = vstate.FindDef(type_id);
+ const auto align = GetAlign(type_id, mode, matrix_constraints);
+ if (!IsAlignedTo(matrix_constraints.stride, align)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Matrix with a stride " << matrix_constraints.stride
+ << " not satisfying alignment to " << align << "."
+ << CommonError(inst, storage_class, mode) << "\n";
+ }
+ const auto ele_id = type_inst->GetOperandAs<uint32_t>(1u);
+ uint32_t size = 0;
+ if (matrix_constraints.col_major) {
+ size = GetSize(ele_id, {});
+ } else {
+ // Element size is # cols * ele size.
+ const auto* ele_inst = vstate.FindDef(ele_id);
+ const auto scalar_id = ele_inst->GetOperandAs<uint32_t>(1u);
+ size = GetSize(scalar_id, {}) * type_inst->GetOperandAs<uint32_t>(2u);
+ }
+ if (matrix_constraints.stride < size) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+ << "Matrix stride " << matrix_constraints.stride
+ << " is smaller than column size " << size << "."
+ << CommonError(inst, storage_class, mode);
+ }
+ return SPV_SUCCESS;
+ }
+
+ // Returns true if type_id satisfies the given layout rules.
+ spv_result_t CheckLayout(const Instruction* inst, uint32_t type_id,
+ spv::StorageClass storage_class, LayoutMode mode,
+ uint32_t incoming_offset,
+ const MatrixConstraints& matrix_constraints) {
+ if (vstate.options()->skip_block_layout) {
+ return SPV_SUCCESS;
+ }
+
+ LayoutKey key{type_id, mode, incoming_offset, matrix_constraints};
+ if (layout_cache_.count(key)) {
+ return SPV_SUCCESS;
+ }
+
+ const auto* type_inst = vstate.FindDef(type_id);
+ switch (type_inst->opcode()) {
+ case spv::Op::OpTypeStruct:
+ if (auto error =
+ CheckStructLayout(inst, type_id, storage_class, mode,
+ incoming_offset, matrix_constraints)) {
+ return error;
+ }
+ break;
+ case spv::Op::OpTypeArray:
+ case spv::Op::OpTypeRuntimeArray:
+ if (auto error =
+ CheckArrayLayout(inst, type_id, storage_class, mode,
+ incoming_offset, matrix_constraints)) {
+ return error;
+ }
+ break;
+ case spv::Op::OpTypeMatrix:
+ if (auto error = CheckMatrixLayout(inst, type_id, storage_class, mode,
+ matrix_constraints)) {
+ return error;
+ }
+ break;
+ default:
+ break;
+ }
+
+ layout_cache_.insert(key);
+
+ return SPV_SUCCESS;
+ }
+
+ // Performs a single pass over the IR and for each memory reference determines
+ // what validation is necessary.
+ spv_result_t Run() {
+ if (!spvIsVulkanEnv(vstate.context()->target_env)) {
+ return SPV_SUCCESS;
+ }
+
+ for (const auto& inst : vstate.ordered_instructions()) {
+ MemoryReference reference;
+ if (!GetMemoryReference(&inst, &reference)) {
+ continue;
+ }
+
+ // Descriptor arrays shouldn't have a stride. Check for that here since
+ // most descriptors require a layout.
+ if (reference.descriptor_array_id != 0) {
+ const bool array_stride = vstate.HasDecoration(
+ reference.descriptor_array_id, spv::Decoration::ArrayStride);
+ const bool array_stride_id = vstate.HasDecoration(
+ reference.descriptor_array_id, spv::Decoration::ArrayStrideIdEXT);
+ if (array_stride || array_stride_id) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+ << vstate.VkErrorID(10684)
+ << "Invalid explicit layout decorations on type "
+ << vstate.getIdName(reference.descriptor_array_id) << ", the "
+ << spvtools::StorageClassToString(reference.storage_class)
+ << " storage class has an explicit layout from the "
+ << vstate.SpvDecorationString(
+ array_stride ? spv::Decoration::ArrayStride
+ : spv::Decoration::ArrayStrideIdEXT)
+ << " decoration";
+ }
+ }
+
+ // Untyped pointers require a layout. Workgroup variables must be blocks
+ // to have a layout.
+ if (reference.untyped &&
+ reference.storage_class == spv::StorageClass::Workgroup &&
+ inst.opcode() == spv::Op::OpUntypedVariableKHR &&
+ (vstate.GetIdOpcode(reference.type_id) != spv::Op::OpTypeStruct ||
+ !vstate.HasDecoration(reference.type_id, spv::Decoration::Block))) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+ << vstate.VkErrorID(10684)
+ << "Untyped variables in Workgroup storage class must be "
+ "block-decorated structs";
+ }
+
+ if (reference.requirement == LayoutRequirement::kRequired) {
+ if (auto error =
+ CheckLayout(&inst, reference.type_id, reference.storage_class,
+ reference.layout, 0, {})) {
+ return error;
+ }
+ } else if (reference.requirement == LayoutRequirement::kProhibited) {
+ if (auto error = CheckNoLayout(&inst, reference.type_id,
+ reference.storage_class)) {
+ return error;
+ }
+ }
+ }
+ return SPV_SUCCESS;
+ }
+};
+
+} // namespace
+
+spv_result_t ValidateExplicitLayout(ValidationState_t& vstate) {
+ return Impl{vstate}.Run();
+}
+
+} // namespace val
+} // namespace spvtools
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index 8a825c1..761063b 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -2824,21 +2824,29 @@
spv_result_t ValidateBufferPointerEXT(ValidationState_t& _,
const Instruction* inst) {
- const auto storage_class_ptr = _.FindDef(inst->GetOperandAs<uint32_t>(0));
+ const auto storage_class_ptr = _.FindDef(inst->type_id());
if (storage_class_ptr->opcode() != spv::Op::OpTypeUntypedPointerKHR &&
storage_class_ptr->opcode() != spv::Op::OpTypePointer) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpBufferPointerEXT's Result Type should be "
<< "a pointer type.";
- } else {
- // Buffer operand
- auto buffer =
- _.FindUntypedBaseVariable(_.FindDef(inst->GetOperandAs<uint32_t>(2)));
- if (!buffer || !_.IsBuiltin(buffer->id(), spv::BuiltIn::ResourceHeapEXT)) {
- return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "OpBufferPointerEXT's buffer must be an untyped pointer"
- << " into a variable declared with the ResourceHeapEXT built-in";
- }
+ }
+
+ auto sc = storage_class_ptr->GetOperandAs<spv::StorageClass>(1u);
+ if (sc != spv::StorageClass::StorageBuffer &&
+ sc != spv::StorageClass::Uniform) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "OpBufferPointerEXT Result Type must be a pointer type "
+ << "with a Storage Class of Uniform or StorageBuffer.";
+ }
+
+ // Buffer operand
+ auto buffer =
+ _.FindUntypedBaseVariable(_.FindDef(inst->GetOperandAs<uint32_t>(2)));
+ if (!buffer || !_.IsBuiltin(buffer->id(), spv::BuiltIn::ResourceHeapEXT)) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "OpBufferPointerEXT's buffer must be an untyped pointer"
+ << " into a variable declared with the ResourceHeapEXT built-in";
}
return SPV_SUCCESS;
}
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 27bfc18..fe57eea 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -1849,6 +1849,27 @@
if (inst->opcode() == spv::Op::OpConstantNull) {
*val = 0;
+ } else if (inst->opcode() == spv::Op::OpConstantSizeOfEXT) {
+ auto type_op = GetIdOpcode(inst->GetOperandAs<uint32_t>(2u));
+ *val = 0;
+ switch (type_op) {
+ case spv::Op::OpTypeBufferEXT:
+ case spv::Op::OpTypeAccelerationStructureKHR:
+ *val = options()->buffer_descriptor_layout.size;
+ break;
+ case spv::Op::OpTypeSampler:
+ *val = options()->sampler_descriptor_layout.size;
+ break;
+ case spv::Op::OpTypeImage:
+ *val = options()->image_descriptor_layout.size;
+ break;
+ case spv::Op::OpTypeTensorARM:
+ *val = options()->tensor_descriptor_layout.size;
+ break;
+ default:
+ break;
+ }
+ return *val > 0;
} else if (inst->opcode() != spv::Op::OpConstant) {
// Spec constant values cannot be evaluated so don't consider constant for
// static validation
@@ -1874,6 +1895,27 @@
if (inst->opcode() == spv::Op::OpConstantNull) {
*val = 0;
+ } else if (inst->opcode() == spv::Op::OpConstantSizeOfEXT) {
+ auto type_op = GetIdOpcode(inst->GetOperandAs<uint32_t>(2u));
+ *val = 0;
+ switch (type_op) {
+ case spv::Op::OpTypeBufferEXT:
+ case spv::Op::OpTypeAccelerationStructureKHR:
+ *val = static_cast<int64_t>(options()->buffer_descriptor_layout.size);
+ break;
+ case spv::Op::OpTypeSampler:
+ *val = static_cast<int64_t>(options()->sampler_descriptor_layout.size);
+ break;
+ case spv::Op::OpTypeImage:
+ *val = static_cast<int64_t>(options()->image_descriptor_layout.size);
+ break;
+ case spv::Op::OpTypeTensorARM:
+ *val = static_cast<int64_t>(options()->tensor_descriptor_layout.size);
+ break;
+ default:
+ break;
+ }
+ return *val > 0;
} else if (inst->opcode() != spv::Op::OpConstant) {
// Spec constant values cannot be evaluated so don't consider constant for
// static validation
diff --git a/test/opt/remove_unused_interface_variables_test.cpp b/test/opt/remove_unused_interface_variables_test.cpp
index bb273e5..2d5fb3a 100644
--- a/test/opt/remove_unused_interface_variables_test.cpp
+++ b/test/opt/remove_unused_interface_variables_test.cpp
@@ -195,6 +195,7 @@
; CHECK: OpEntryPoint Fragment %1 "main" %2
OpExecutionMode %1 OriginUpperLeft
OpDecorate %2 BuiltIn ResourceHeapEXT
+ OpDecorateId %_runtimearr_type_buffer_ext ArrayStrideIdEXT %buffer_size
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
@@ -206,6 +207,7 @@
%type_buffer_image = OpTypeImage %float Buffer 2 0 0 1 Rgba32f
%type_buffer_image_0 = OpTypeImage %float Buffer 2 0 0 2 Rgba32f
%type_buffer_ext = OpTypeBufferEXT StorageBuffer
+ %buffer_size = OpConstantSizeOfEXT %uint %type_buffer_ext
%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
%2 = OpUntypedVariableKHR %type_untyped_pointer UniformConstant
%1 = OpFunction %void None %10
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index 91a3dd5..73ed334 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -38,6 +38,7 @@
val_derivatives_test.cpp
val_dot_product_test.cpp
val_entry_point_test.cpp
+ val_explicit_layout_test.cpp
val_explicit_reserved_test.cpp
val_invalid_type_test.cpp
val_extensions_test.cpp
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index 6447be9..53cef8b 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -1073,130 +1073,6 @@
EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type"));
}
-TEST_F(ValidateDecorations, BlockMissingOffsetBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpDecorate %Output Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %Output = OpTypeStruct %float
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("must be explicitly laid out with Offset decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockMissingOffsetBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpDecorate %Output BufferBlock
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %Output = OpTypeStruct %float
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("must be explicitly laid out with Offset decorations"));
-}
-
-TEST_F(ValidateDecorations, BlockNestedStructMissingOffsetBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 16
- OpMemberDecorate %Output 2 Offset 32
- OpDecorate %Output Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v4float = OpTypeVector %float 4
- %v3float = OpTypeVector %float 3
- %int = OpTypeInt 32 1
- %S = OpTypeStruct %v3float %int
- %Output = OpTypeStruct %float %v4float %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("must be explicitly laid out with Offset decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockNestedStructMissingOffsetBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 16
- OpMemberDecorate %Output 2 Offset 32
- OpDecorate %Output BufferBlock
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v4float = OpTypeVector %float 4
- %v3float = OpTypeVector %float 3
- %int = OpTypeInt 32 1
- %S = OpTypeStruct %v3float %int
- %Output = OpTypeStruct %float %v4float %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("must be explicitly laid out with Offset decorations"));
-}
-
TEST_F(ValidateDecorations, BlockGLSLSharedBad) {
std::string spirv = R"(
OpCapability Shader
@@ -1221,8 +1097,7 @@
)";
CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
@@ -1493,440 +1368,6 @@
HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]"));
}
-TEST_F(ValidateDecorations, BlockMissingArrayStrideBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpDecorate %Output Block
- OpMemberDecorate %Output 0 Offset 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %int = OpTypeInt 32 1
- %int_3 = OpConstant %int 3
- %array = OpTypeArray %float %int_3
- %Output = OpTypeStruct %array
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("must be explicitly laid out with ArrayStride or "
- "ArrayStrideIdEXT decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockMissingArrayStrideBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpDecorate %Output BufferBlock
- OpMemberDecorate %Output 0 Offset 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %int = OpTypeInt 32 1
- %int_3 = OpConstant %int 3
- %array = OpTypeArray %float %int_3
- %Output = OpTypeStruct %array
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("must be explicitly laid out with ArrayStride or "
- "ArrayStrideIdEXT decorations"));
-}
-
-TEST_F(ValidateDecorations, BlockNestedStructMissingArrayStrideBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 16
- OpMemberDecorate %Output 2 Offset 32
- OpDecorate %Output Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v4float = OpTypeVector %float 4
- %int = OpTypeInt 32 1
- %int_3 = OpConstant %int 3
- %array = OpTypeArray %float %int_3
- %S = OpTypeStruct %array
- %Output = OpTypeStruct %float %v4float %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("must be explicitly laid out with ArrayStride or "
- "ArrayStrideIdEXT decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockNestedStructMissingArrayStrideBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 16
- OpMemberDecorate %Output 2 Offset 32
- OpDecorate %Output BufferBlock
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v4float = OpTypeVector %float 4
- %int = OpTypeInt 32 1
- %int_3 = OpConstant %int 3
- %array = OpTypeArray %float %int_3
- %S = OpTypeStruct %array
- %Output = OpTypeStruct %float %v4float %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("must be explicitly laid out with ArrayStride or "
- "ArrayStrideIdEXT decorations"));
-}
-
-TEST_F(ValidateDecorations, BlockMissingMatrixStrideBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpDecorate %Output Block
- OpMemberDecorate %Output 0 Offset 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %matrix = OpTypeMatrix %v3float 4
- %Output = OpTypeStruct %matrix
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockMissingMatrixStrideBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpDecorate %Output BufferBlock
- OpMemberDecorate %Output 0 Offset 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %matrix = OpTypeMatrix %v3float 4
- %Output = OpTypeStruct %matrix
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, BlockMissingMatrixStrideArrayBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpDecorate %Output Block
- OpMemberDecorate %Output 0 Offset 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %matrix = OpTypeMatrix %v3float 4
- %int = OpTypeInt 32 1
- %int_3 = OpConstant %int 3
- %array = OpTypeArray %matrix %int_3
- %Output = OpTypeStruct %matrix
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockMissingMatrixStrideArrayBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpDecorate %Output BufferBlock
- OpMemberDecorate %Output 0 Offset 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %matrix = OpTypeMatrix %v3float 4
- %int = OpTypeInt 32 1
- %int_3 = OpConstant %int 3
- %array = OpTypeArray %matrix %int_3
- %Output = OpTypeStruct %matrix
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, BlockNestedStructMissingMatrixStrideBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 16
- OpMemberDecorate %Output 2 Offset 32
- OpDecorate %Output Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %v4float = OpTypeVector %float 4
- %matrix = OpTypeMatrix %v3float 4
- %S = OpTypeStruct %matrix
- %Output = OpTypeStruct %float %v4float %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockNestedStructMissingMatrixStrideBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 16
- OpMemberDecorate %Output 2 Offset 32
- OpDecorate %Output BufferBlock
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %v4float = OpTypeVector %float 4
- %matrix = OpTypeMatrix %v3float 4
- %S = OpTypeStruct %matrix
- %Output = OpTypeStruct %float %v4float %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, BlockStandardUniformBufferLayout) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %F 0 Offset 0
- OpMemberDecorate %F 1 Offset 8
- OpDecorate %_arr_float_uint_2 ArrayStride 16
- OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48
- OpMemberDecorate %O 0 Offset 0
- OpMemberDecorate %O 1 Offset 16
- OpMemberDecorate %O 2 Offset 32
- OpMemberDecorate %O 3 Offset 64
- OpMemberDecorate %O 4 ColMajor
- OpMemberDecorate %O 4 Offset 80
- OpMemberDecorate %O 4 MatrixStride 16
- OpDecorate %_arr_O_uint_2 ArrayStride 176
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 8
- OpMemberDecorate %Output 2 Offset 16
- OpMemberDecorate %Output 3 Offset 32
- OpMemberDecorate %Output 4 Offset 48
- OpMemberDecorate %Output 5 Offset 64
- OpMemberDecorate %Output 6 ColMajor
- OpMemberDecorate %Output 6 Offset 96
- OpMemberDecorate %Output 6 MatrixStride 16
- OpMemberDecorate %Output 7 Offset 128
- OpDecorate %Output Block
- 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(ValidateDecorations, BlockLayoutPermitsTightVec3ScalarPackingGood) {
- // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 12
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %S = OpTypeStruct %v3float %float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
- << getDiagnosticString();
-}
-
TEST_F(ValidateDecorations, BlockCantAppearWithinABlockBad) {
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/1587
std::string spirv = R"(
@@ -2141,642 +1582,6 @@
"another Block or BufferBlock."));
}
-TEST_F(ValidateDecorations, BlockLayoutForbidsTightScalarVec3PackingBad) {
- // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 4
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Structure id 2 decorated as Block for variable in Uniform "
- "storage class must follow standard uniform buffer layout "
- "rules: member 1 at offset 4 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations,
- BlockLayoutPermitsTightScalarVec3PackingWithRelaxedLayoutGood) {
- // Same as previous test, but with explicit option to relax block layout.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 4
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations,
- BlockLayoutPermitsTightScalarVec3PackingBadOffsetWithRelaxedLayoutBad) {
- // Same as previous test, but with the vector not aligned to its scalar
- // element. Use offset 5 instead of a multiple of 4.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 5
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 2 decorated as Block for variable in Uniform storage "
- "class must follow relaxed uniform buffer layout rules: member 1 at "
- "offset 5 is not aligned to scalar element size 4"));
-}
-
-TEST_F(ValidateDecorations,
- BlockLayoutPermitsTightScalarVec3PackingWithVulkan1_1Good) {
- // Same as previous test, but with Vulkan 1.1. Vulkan 1.1 included
- // VK_KHR_relaxed_block_layout in core.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 4
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
- EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations,
- BlockLayoutPermitsTightScalarVec3PackingWithScalarLayoutGood) {
- // Same as previous test, but with scalar block layout.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 4
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations,
- BlockLayoutPermitsScalarAlignedArrayWithScalarLayoutGood) {
- // The array at offset 4 is ok with scalar block layout.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 4
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- OpDecorate %arr_float ArrayStride 4
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %uint = OpTypeInt 32 0
- %uint_3 = OpConstant %uint 3
- %float = OpTypeFloat 32
- %arr_float = OpTypeArray %float %uint_3
- %S = OpTypeStruct %float %arr_float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations,
- BlockLayoutPermitsScalarAlignedArrayOfVec3WithScalarLayoutGood) {
- // The array at offset 4 is ok with scalar block layout, even though
- // its elements are vec3.
- // This is the same as the previous case, but the array elements are vec3
- // instead of float.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 4
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- OpDecorate %arr_vec3 ArrayStride 12
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %uint = OpTypeInt 32 0
- %uint_3 = OpConstant %uint 3
- %float = OpTypeFloat 32
- %vec3 = OpTypeVector %float 3
- %arr_vec3 = OpTypeArray %vec3 %uint_3
- %S = OpTypeStruct %float %arr_vec3
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations,
- BlockLayoutPermitsScalarAlignedStructWithScalarLayoutGood) {
- // Scalar block layout permits the struct at offset 4, even though
- // it contains a vector with base alignment 8 and scalar alignment 4.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 4
- OpMemberDecorate %st 0 Offset 0
- OpMemberDecorate %st 1 Offset 8
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %vec2 = OpTypeVector %float 2
- %st = OpTypeStruct %vec2 %float
- %S = OpTypeStruct %float %st
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(
- ValidateDecorations,
- BlockLayoutPermitsFieldsInBaseAlignmentPaddingAtEndOfStructWithScalarLayoutGood) {
- // Scalar block layout permits fields in what would normally be the padding at
- // the end of a struct.
- std::string spirv = R"(
- OpCapability Shader
- OpCapability Float64
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %st 0 Offset 0
- OpMemberDecorate %st 1 Offset 8
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 12
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %double = OpTypeFloat 64
- %st = OpTypeStruct %double %float
- %S = OpTypeStruct %st %float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(
- ValidateDecorations,
- BlockLayoutPermitsStraddlingVectorWithScalarLayoutOverrideRelaxBlockLayoutGood) {
- // Same as previous, but set relaxed block layout first. Scalar layout always
- // wins.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 4
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %vec4 = OpTypeVector %float 4
- %S = OpTypeStruct %float %vec4
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
- spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(
- ValidateDecorations,
- BlockLayoutPermitsStraddlingVectorWithRelaxedLayoutOverridenByScalarBlockLayoutGood) {
- // Same as previous, but set scalar block layout first. Scalar layout always
- // wins.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 4
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %vec4 = OpTypeVector %float 4
- %S = OpTypeStruct %float %vec4
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
- spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations, BufferBlock16bitStandardStorageBufferLayout) {
- std::string spirv = R"(
- OpCapability Shader
- OpCapability StorageUniform16
- OpExtension "SPV_KHR_16bit_storage"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpDecorate %f32arr ArrayStride 4
- OpDecorate %f16arr ArrayStride 2
- OpMemberDecorate %SSBO32 0 Offset 0
- OpMemberDecorate %SSBO16 0 Offset 0
- OpDecorate %SSBO32 BufferBlock
- OpDecorate %SSBO16 BufferBlock
- 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,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, BlockArrayExtendedAlignmentGood) {
- // For uniform buffer, Array base alignment is 16, and ArrayStride
- // must be a multiple of 16.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpDecorate %_arr_float_uint_2 ArrayStride 16
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 16
- OpDecorate %S Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v2float = OpTypeVector %float 2
- %uint = OpTypeInt 32 0
- %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
- %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_PushConstant_S = OpTypePointer PushConstant %S
- %u = OpVariable %_ptr_PushConstant_S PushConstant
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
- << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations, BlockArrayBaseAlignmentBad) {
- // For uniform buffer, Array base alignment is 16.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpDecorate %_arr_float_uint_2 ArrayStride 16
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 8
- OpDecorate %S Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v2float = OpTypeVector %float 2
- %uint = OpTypeInt 32 0
- %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
- %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %u = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 3 decorated as Block for variable in Uniform "
- "storage class must follow standard uniform buffer layout rules: "
- "member 1 at offset 8 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations, BlockArrayBaseAlignmentWithRelaxedLayoutStillBad) {
- // For uniform buffer, Array base alignment is 16, and ArrayStride
- // must be a multiple of 16. This case uses relaxed block layout. Relaxed
- // layout only relaxes rules for vector alignment, not array alignment.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpDecorate %_arr_float_uint_2 ArrayStride 16
- OpDecorate %u DescriptorSet 0
- OpDecorate %u Binding 0
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 8
- OpDecorate %S Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v2float = OpTypeVector %float 2
- %uint = OpTypeInt 32 0
- %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
- %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %u = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 4 decorated as Block for variable in Uniform "
- "storage class must follow standard uniform buffer layout rules: "
- "member 1 at offset 8 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations, BlockArrayBaseAlignmentWithVulkan1_1StillBad) {
- // Same as previous test, but with Vulkan 1.1, which includes
- // VK_KHR_relaxed_block_layout in core.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpDecorate %_arr_float_uint_2 ArrayStride 16
- OpDecorate %u DescriptorSet 0
- OpDecorate %u Binding 0
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 8
- OpDecorate %S Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v2float = OpTypeVector %float 2
- %uint = OpTypeInt 32 0
- %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
- %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %u = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 4 decorated as Block for variable in Uniform "
- "storage class must follow relaxed uniform buffer layout rules: "
- "member 1 at offset 8 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations,
- BlockArrayBaseAlignmentWithBlockStandardLayoutGood) {
- // Same as previous test, but with VK_KHR_uniform_buffer_standard_layout
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpDecorate %_arr_float_uint_2 ArrayStride 16
- OpDecorate %u DescriptorSet 0
- OpDecorate %u Binding 0
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 8
- OpDecorate %S Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v2float = OpTypeVector %float 2
- %uint = OpTypeInt 32 0
- %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
- %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %u = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- spvValidatorOptionsSetUniformBufferStandardLayout(getValidatorOptions(),
- true);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
TEST_F(ValidateDecorations, VulkanBufferBlockOnStorageBufferBad) {
std::string spirv = R"(
OpCapability Shader
@@ -2810,145 +1615,6 @@
"the StorageBuffer storage class"));
}
-TEST_F(ValidateDecorations, PushConstantArrayBaseAlignmentGood) {
- // Tests https://github.com/KhronosGroup/SPIRV-Tools/issues/1664
- // From GLSL vertex shader:
- // #version 450
- // layout(push_constant) uniform S { vec2 v; float arr[2]; } u;
- // void main() { }
-
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpDecorate %_arr_float_uint_2 ArrayStride 4
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 8
- OpDecorate %S Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v2float = OpTypeVector %float 2
- %uint = OpTypeInt 32 0
- %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
- %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_PushConstant_S = OpTypePointer PushConstant %S
- %u = OpVariable %_ptr_PushConstant_S PushConstant
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
- << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations, PushConstantArrayBadAlignmentBad) {
- // Like the previous test, but with offset 7 instead of 8.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpDecorate %_arr_float_uint_2 ArrayStride 4
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 7
- OpDecorate %S Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v2float = OpTypeVector %float 2
- %uint = OpTypeInt 32 0
- %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
- %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_PushConstant_S = OpTypePointer PushConstant %S
- %u = OpVariable %_ptr_PushConstant_S PushConstant
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 3 decorated as Block for variable in PushConstant "
- "storage class must follow standard storage buffer layout rules: "
- "member 1 at offset 7 is not aligned to 4"));
-}
-
-TEST_F(ValidateDecorations,
- PushConstantLayoutPermitsTightVec3ScalarPackingGood) {
- // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 12
- OpDecorate %S Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %S = OpTypeStruct %v3float %float
-%_ptr_PushConstant_S = OpTypePointer PushConstant %S
- %B = OpVariable %_ptr_PushConstant_S PushConstant
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
- << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations,
- PushConstantLayoutForbidsTightScalarVec3PackingBad) {
- // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 4
- OpDecorate %S Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer PushConstant %S
- %B = OpVariable %_ptr_Uniform_S PushConstant
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 2 decorated as Block for variable in PushConstant "
- "storage class must follow standard storage buffer layout "
- "rules: member 1 at offset 4 is not aligned to 16"));
-}
-
TEST_F(ValidateDecorations, PushConstantMissingBlockGood) {
std::string spirv = R"(
OpCapability Shader
@@ -3776,1074 +2442,6 @@
<< getDiagnosticString();
}
-TEST_F(ValidateDecorations, StorageBufferStorageClassArrayBaseAlignmentGood) {
- // Spot check buffer rules when using StorageBuffer storage class with Block
- // decoration.
- std::string spirv = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_storage_buffer_storage_class"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpDecorate %_arr_float_uint_2 ArrayStride 4
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 8
- OpDecorate %S Block
- OpDecorate %u DescriptorSet 0
- OpDecorate %u Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v2float = OpTypeVector %float 2
- %uint = OpTypeInt 32 0
- %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
- %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_Uniform_S = OpTypePointer StorageBuffer %S
- %u = OpVariable %_ptr_Uniform_S StorageBuffer
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
- << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations, StorageBufferStorageClassArrayBadAlignmentBad) {
- // Like the previous test, but with offset 7.
- std::string spirv = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_storage_buffer_storage_class"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpDecorate %_arr_float_uint_2 ArrayStride 4
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 7
- OpDecorate %S Block
- OpDecorate %u DescriptorSet 0
- OpDecorate %u Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v2float = OpTypeVector %float 2
- %uint = OpTypeInt 32 0
- %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
- %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_Uniform_S = OpTypePointer StorageBuffer %S
- %u = OpVariable %_ptr_Uniform_S StorageBuffer
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 3 decorated as Block for variable in StorageBuffer "
- "storage class must follow standard storage buffer layout rules: "
- "member 1 at offset 7 is not aligned to 4"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockStandardStorageBufferLayout) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %F 0 Offset 0
- OpMemberDecorate %F 1 Offset 8
- OpDecorate %_arr_float_uint_2 ArrayStride 4
- OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48
- OpMemberDecorate %O 0 Offset 0
- OpMemberDecorate %O 1 Offset 16
- OpMemberDecorate %O 2 Offset 24
- OpMemberDecorate %O 3 Offset 32
- OpMemberDecorate %O 4 ColMajor
- OpMemberDecorate %O 4 Offset 48
- OpMemberDecorate %O 4 MatrixStride 16
- OpDecorate %_arr_O_uint_2 ArrayStride 144
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 8
- OpMemberDecorate %Output 2 Offset 16
- OpMemberDecorate %Output 3 Offset 32
- OpMemberDecorate %Output 4 Offset 48
- OpMemberDecorate %Output 5 Offset 52
- OpMemberDecorate %Output 6 ColMajor
- OpMemberDecorate %Output 6 Offset 64
- OpMemberDecorate %Output 6 MatrixStride 16
- OpMemberDecorate %Output 7 Offset 96
- OpDecorate %Output BufferBlock
- 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(ValidateDecorations,
- StorageBufferLayoutPermitsTightVec3ScalarPackingGood) {
- // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
- std::string spirv = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_storage_buffer_storage_class"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 12
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %S = OpTypeStruct %v3float %float
-%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S
- %B = OpVariable %_ptr_StorageBuffer_S StorageBuffer
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
- << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations,
- StorageBufferLayoutForbidsTightScalarVec3PackingBad) {
- // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
- std::string spirv = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_storage_buffer_storage_class"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 4
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %S = OpTypeStruct %float %v3float
-%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S
- %B = OpVariable %_ptr_StorageBuffer_S StorageBuffer
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 2 decorated as Block for variable in StorageBuffer "
- "storage class must follow standard storage buffer layout "
- "rules: member 1 at offset 4 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations,
- BlockStandardUniformBufferLayoutIncorrectOffset0Bad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %F 0 Offset 0
- OpMemberDecorate %F 1 Offset 8
- OpDecorate %_arr_float_uint_2 ArrayStride 16
- OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48
- OpMemberDecorate %O 0 Offset 0
- OpMemberDecorate %O 1 Offset 16
- OpMemberDecorate %O 2 Offset 24
- OpMemberDecorate %O 3 Offset 33
- OpMemberDecorate %O 4 ColMajor
- OpMemberDecorate %O 4 Offset 80
- OpMemberDecorate %O 4 MatrixStride 16
- OpDecorate %_arr_O_uint_2 ArrayStride 176
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 8
- OpMemberDecorate %Output 2 Offset 16
- OpMemberDecorate %Output 3 Offset 32
- OpMemberDecorate %Output 4 Offset 48
- OpMemberDecorate %Output 5 Offset 64
- OpMemberDecorate %Output 6 ColMajor
- OpMemberDecorate %Output 6 Offset 96
- OpMemberDecorate %Output 6 MatrixStride 16
- OpMemberDecorate %Output 7 Offset 128
- OpDecorate %Output Block
- 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 id 6 decorated as Block for variable in Uniform "
- "storage class must follow standard uniform buffer layout "
- "rules: member 2 at offset 152 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations,
- BlockStandardUniformBufferLayoutIncorrectOffset1Bad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %F 0 Offset 0
- OpMemberDecorate %F 1 Offset 8
- OpDecorate %_arr_float_uint_2 ArrayStride 16
- OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48
- OpMemberDecorate %O 0 Offset 0
- OpMemberDecorate %O 1 Offset 16
- OpMemberDecorate %O 2 Offset 32
- OpMemberDecorate %O 3 Offset 64
- OpMemberDecorate %O 4 ColMajor
- OpMemberDecorate %O 4 Offset 80
- OpMemberDecorate %O 4 MatrixStride 16
- OpDecorate %_arr_O_uint_2 ArrayStride 176
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 8
- OpMemberDecorate %Output 2 Offset 16
- OpMemberDecorate %Output 3 Offset 32
- OpMemberDecorate %Output 4 Offset 48
- OpMemberDecorate %Output 5 Offset 71
- OpMemberDecorate %Output 6 ColMajor
- OpMemberDecorate %Output 6 Offset 96
- OpMemberDecorate %Output 6 MatrixStride 16
- OpMemberDecorate %Output 7 Offset 128
- OpDecorate %Output Block
- 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 id 8 decorated as Block for variable in Uniform "
- "storage class must follow standard uniform buffer layout "
- "rules: member 5 at offset 71 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations, BlockUniformBufferLayoutIncorrectArrayStrideBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %F 0 Offset 0
- OpMemberDecorate %F 1 Offset 8
- OpDecorate %_arr_float_uint_2 ArrayStride 16
- OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 49
- OpMemberDecorate %O 0 Offset 0
- OpMemberDecorate %O 1 Offset 16
- OpMemberDecorate %O 2 Offset 32
- OpMemberDecorate %O 3 Offset 64
- OpMemberDecorate %O 4 ColMajor
- OpMemberDecorate %O 4 Offset 80
- OpMemberDecorate %O 4 MatrixStride 16
- OpDecorate %_arr_O_uint_2 ArrayStride 176
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 8
- OpMemberDecorate %Output 2 Offset 16
- OpMemberDecorate %Output 3 Offset 32
- OpMemberDecorate %Output 4 Offset 48
- OpMemberDecorate %Output 5 Offset 64
- OpMemberDecorate %Output 6 ColMajor
- OpMemberDecorate %Output 6 Offset 96
- OpMemberDecorate %Output 6 MatrixStride 16
- OpMemberDecorate %Output 7 Offset 128
- OpDecorate %Output Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v2float = OpTypeVector %float 2
- %v3float = OpTypeVector %float 3
- %int = OpTypeInt 32 1
- %uint = OpTypeInt 32 0
- %v2uint = OpTypeVector %uint 2
- %F = OpTypeStruct %int %v2uint
- %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-%mat2v3float = OpTypeMatrix %v3float 2
- %v3uint = OpTypeVector %uint 3
-%mat3v3float = OpTypeMatrix %v3float 3
-%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2
- %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2
-%_arr_O_uint_2 = OpTypeArray %O %uint_2
- %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 6 decorated as Block for variable in Uniform storage "
- "class must follow standard uniform buffer layout rules: member 4 "
- "contains "
- "an array with stride 49 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations,
- BufferBlockStandardStorageBufferLayoutImproperStraddleBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 8
- OpDecorate %Output BufferBlock
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %Output = OpTypeStruct %float %v3float
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Structure id 3 decorated as BufferBlock for variable in "
- "Uniform storage class must follow standard storage buffer "
- "layout rules: member 1 at offset 8 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations,
- BlockUniformBufferLayoutOffsetInsideArrayPaddingBad) {
- // In this case the 2nd member fits entirely within the padding.
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpDecorate %_arr_float_uint_2 ArrayStride 16
- OpMemberDecorate %Output 0 Offset 0
- OpMemberDecorate %Output 1 Offset 20
- OpDecorate %Output Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %uint = OpTypeInt 32 0
- %v2uint = OpTypeVector %uint 2
- %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
- %Output = OpTypeStruct %_arr_float_uint_2 %float
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 4 decorated as Block for variable in Uniform storage "
- "class must follow standard uniform buffer layout rules: member 1 at "
- "offset 20 overlaps previous member ending at offset 31"));
-}
-
-TEST_F(ValidateDecorations,
- BlockUniformBufferLayoutOffsetInsideStructPaddingBad) {
- // In this case the 2nd member fits entirely within the padding.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %1 "main"
- OpExecutionMode %1 LocalSize 1 1 1
- OpMemberDecorate %_struct_6 0 Offset 0
- OpMemberDecorate %_struct_2 0 Offset 0
- OpMemberDecorate %_struct_2 1 Offset 4
- OpDecorate %_struct_2 Block
- %void = OpTypeVoid
- %4 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %_struct_6 = OpTypeStruct %float
- %_struct_2 = OpTypeStruct %_struct_6 %float
-%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
- %8 = OpVariable %_ptr_Uniform__struct_2 Uniform
- %1 = OpFunction %void None %4
- %9 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 3 decorated as Block for variable in Uniform storage "
- "class must follow standard uniform buffer layout rules: member 1 at "
- "offset 4 overlaps previous member ending at offset 15"));
-}
-
-TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderGoodUniversal1_0) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpMemberDecorate %Outer 0 Offset 4
- OpMemberDecorate %Outer 1 Offset 0
- OpDecorate %Outer Block
- OpDecorate %O DescriptorSet 0
- OpDecorate %O Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %uint = OpTypeInt 32 0
- %Outer = OpTypeStruct %uint %uint
-%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer
- %O = OpVariable %_ptr_Uniform_Outer Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_0));
-}
-
-TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderGoodOpenGL4_5) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpMemberDecorate %Outer 0 Offset 4
- OpMemberDecorate %Outer 1 Offset 0
- OpDecorate %Outer Block
- OpDecorate %O DescriptorSet 0
- OpDecorate %O Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %uint = OpTypeInt 32 0
- %Outer = OpTypeStruct %uint %uint
-%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer
- %O = OpVariable %_ptr_Uniform_Outer Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_OPENGL_4_5));
-}
-
-TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderGoodVulkan1_1) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpMemberDecorate %Outer 0 Offset 4
- OpMemberDecorate %Outer 1 Offset 0
- OpDecorate %Outer Block
- OpDecorate %O DescriptorSet 0
- OpDecorate %O Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %uint = OpTypeInt 32 0
- %Outer = OpTypeStruct %uint %uint
-%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer
- %O = OpVariable %_ptr_Uniform_Outer Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1))
- << getDiagnosticString();
- EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations, BlockLayoutOffsetOverlapBad) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpMemberDecorate %Outer 0 Offset 0
- OpMemberDecorate %Outer 1 Offset 16
- OpMemberDecorate %Inner 0 Offset 0
- OpMemberDecorate %Inner 1 Offset 16
- OpDecorate %Outer Block
- OpDecorate %O DescriptorSet 0
- OpDecorate %O Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %uint = OpTypeInt 32 0
- %Inner = OpTypeStruct %uint %uint
- %Outer = OpTypeStruct %Inner %uint
-%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer
- %O = OpVariable %_ptr_Uniform_Outer Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 3 decorated as Block for variable in Uniform storage "
- "class must follow standard uniform buffer layout rules: member 1 at "
- "offset 16 overlaps previous member ending at offset 31"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockEmptyStruct) {
- std::string spirv = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpMemberDecorate %Output 0 Offset 0
- OpDecorate %Output BufferBlock
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %S = OpTypeStruct
- %Output = OpTypeStruct %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
-}
-
-TEST_F(ValidateDecorations, RowMajorMatrixTightPackingGood) {
- // Row major matrix rule:
- // A row-major matrix of C columns has a base alignment equal to
- // the base alignment of a vector of C matrix components.
- // Note: The "matrix component" is the scalar element type.
-
- // The matrix has 3 columns and 2 rows (C=3, R=2).
- // So the base alignment of b is the same as a vector of 3 floats, which is 16
- // bytes. The matrix consists of two of these, and therefore occupies 2 x 16
- // bytes, or 32 bytes.
- //
- // So the offsets can be:
- // a -> 0
- // b -> 16
- // c -> 48
- // d -> 60 ; d fits at bytes 12-15 after offset of c. Tight (vec3;float)
- // packing
-
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %1 "main"
- OpSource GLSL 450
- OpMemberDecorate %_struct_2 0 Offset 0
- OpMemberDecorate %_struct_2 1 RowMajor
- OpMemberDecorate %_struct_2 1 Offset 16
- OpMemberDecorate %_struct_2 1 MatrixStride 16
- OpMemberDecorate %_struct_2 2 Offset 48
- OpMemberDecorate %_struct_2 3 Offset 60
- OpDecorate %_struct_2 Block
- OpDecorate %3 DescriptorSet 0
- OpDecorate %3 Binding 0
- %void = OpTypeVoid
- %5 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v4float = OpTypeVector %float 4
- %v2float = OpTypeVector %float 2
-%mat3v2float = OpTypeMatrix %v2float 3
- %v3float = OpTypeVector %float 3
- %_struct_2 = OpTypeStruct %v4float %mat3v2float %v3float %float
-%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
- %3 = OpVariable %_ptr_Uniform__struct_2 Uniform
- %1 = OpFunction %void None %5
- %12 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
- << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations, ArrayArrayRowMajorMatrixTightPackingGood) {
- // Like the previous case, but we have an array of arrays of matrices.
- // The RowMajor decoration goes on the struct member (surprisingly).
-
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %1 "main"
- OpSource GLSL 450
- OpMemberDecorate %_struct_2 0 Offset 0
- OpMemberDecorate %_struct_2 1 RowMajor
- OpMemberDecorate %_struct_2 1 Offset 16
- OpMemberDecorate %_struct_2 1 MatrixStride 16
- OpMemberDecorate %_struct_2 2 Offset 80
- OpMemberDecorate %_struct_2 3 Offset 92
- OpDecorate %arr_mat ArrayStride 32
- OpDecorate %arr_arr_mat ArrayStride 32
- OpDecorate %_struct_2 Block
- OpDecorate %3 DescriptorSet 0
- OpDecorate %3 Binding 0
- %void = OpTypeVoid
- %5 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v4float = OpTypeVector %float 4
- %v2float = OpTypeVector %float 2
-%mat3v2float = OpTypeMatrix %v2float 3
-%uint = OpTypeInt 32 0
-%uint_1 = OpConstant %uint 1
-%uint_2 = OpConstant %uint 2
- %arr_mat = OpTypeArray %mat3v2float %uint_1
-%arr_arr_mat = OpTypeArray %arr_mat %uint_2
- %v3float = OpTypeVector %float 3
- %_struct_2 = OpTypeStruct %v4float %arr_arr_mat %v3float %float
-%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
- %3 = OpVariable %_ptr_Uniform__struct_2 Uniform
- %1 = OpFunction %void None %5
- %12 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
- << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations, ArrayArrayRowMajorMatrixNextMemberOverlapsBad) {
- // Like the previous case, but the offset of member 2 overlaps the matrix.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %1 "main"
- OpSource GLSL 450
- OpMemberDecorate %_struct_2 0 Offset 0
- OpMemberDecorate %_struct_2 1 RowMajor
- OpMemberDecorate %_struct_2 1 Offset 16
- OpMemberDecorate %_struct_2 1 MatrixStride 16
- OpMemberDecorate %_struct_2 2 Offset 64
- OpMemberDecorate %_struct_2 3 Offset 92
- OpDecorate %arr_mat ArrayStride 32
- OpDecorate %arr_arr_mat ArrayStride 32
- OpDecorate %_struct_2 Block
- OpDecorate %3 DescriptorSet 0
- OpDecorate %3 Binding 0
- %void = OpTypeVoid
- %5 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v4float = OpTypeVector %float 4
- %v2float = OpTypeVector %float 2
-%mat3v2float = OpTypeMatrix %v2float 3
-%uint = OpTypeInt 32 0
-%uint_1 = OpConstant %uint 1
-%uint_2 = OpConstant %uint 2
- %arr_mat = OpTypeArray %mat3v2float %uint_1
-%arr_arr_mat = OpTypeArray %arr_mat %uint_2
- %v3float = OpTypeVector %float 3
- %_struct_2 = OpTypeStruct %v4float %arr_arr_mat %v3float %float
-%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
- %3 = OpVariable %_ptr_Uniform__struct_2 Uniform
- %1 = OpFunction %void None %5
- %12 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 2 decorated as Block for variable in Uniform storage "
- "class must follow standard uniform buffer layout rules: member 2 at "
- "offset 64 overlaps previous member ending at offset 79"));
-}
-
-TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackGood) {
- // Original GLSL
-
- // #version 450
- // layout (set=0,binding=0) buffer S {
- // uvec3 arr[2][2]; // first 3 elements are 16 bytes, last is 12
- // uint i; // Can't have offset 60 = 3x16 + 12
- // } B;
- // void main() {}
-
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %1 "main"
- OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
- OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
- OpMemberDecorate %_struct_4 0 Offset 0
- OpMemberDecorate %_struct_4 1 Offset 64
- OpDecorate %_struct_4 BufferBlock
- OpDecorate %5 DescriptorSet 0
- OpDecorate %5 Binding 0
- %void = OpTypeVoid
- %7 = OpTypeFunction %void
- %uint = OpTypeInt 32 0
- %v3uint = OpTypeVector %uint 3
- %uint_2 = OpConstant %uint 2
-%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
-%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
- %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
-%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
- %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
- %1 = OpFunction %void None %7
- %12 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackGoodScalar) {
- // Original GLSL
-
- // #version 450
- // layout (set=0,binding=0) buffer S {
- // uvec3 arr[2][2]; // first 3 elements are 16 bytes, last is 12
- // uint i; // Can have offset 60 = 3x16 + 12
- // } B;
- // void main() {}
-
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %1 "main"
- OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
- OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
- OpMemberDecorate %_struct_4 0 Offset 0
- OpMemberDecorate %_struct_4 1 Offset 60
- OpDecorate %_struct_4 BufferBlock
- OpDecorate %5 DescriptorSet 0
- OpDecorate %5 Binding 0
- %void = OpTypeVoid
- %7 = OpTypeFunction %void
- %uint = OpTypeInt 32 0
- %v3uint = OpTypeVector %uint 3
- %uint_2 = OpConstant %uint 2
-%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
-%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
- %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
-%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
- %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
- %1 = OpFunction %void None %7
- %12 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- options_->scalar_block_layout = true;
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackBad) {
- // Like previous but, the offset of the second member is too small.
-
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %1 "main"
- OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
- OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
- OpMemberDecorate %_struct_4 0 Offset 0
- OpMemberDecorate %_struct_4 1 Offset 60
- OpDecorate %_struct_4 BufferBlock
- OpDecorate %5 DescriptorSet 0
- OpDecorate %5 Binding 0
- %void = OpTypeVoid
- %7 = OpTypeFunction %void
- %uint = OpTypeInt 32 0
- %v3uint = OpTypeVector %uint 3
- %uint_2 = OpConstant %uint 2
-%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
-%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
- %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
-%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
- %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
- %1 = OpFunction %void None %7
- %12 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Structure id 4 decorated as BufferBlock for variable "
- "in Uniform storage class must follow standard storage "
- "buffer layout rules: member 1 at offset 60 overlaps "
- "previous member ending at offset 63"));
-}
-
-TEST_F(ValidateDecorations, UniformBufferArraySizeCalculationPackGood) {
- // Like the corresponding buffer block case, but the array padding must
- // count for the last element as well, and so the offset of the second
- // member must be at least 64.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %1 "main"
- OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
- OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
- OpMemberDecorate %_struct_4 0 Offset 0
- OpMemberDecorate %_struct_4 1 Offset 64
- OpDecorate %_struct_4 Block
- OpDecorate %5 DescriptorSet 0
- OpDecorate %5 Binding 0
- %void = OpTypeVoid
- %7 = OpTypeFunction %void
- %uint = OpTypeInt 32 0
- %v3uint = OpTypeVector %uint 3
- %uint_2 = OpConstant %uint 2
-%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
-%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
- %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
-%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
- %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
- %1 = OpFunction %void None %7
- %12 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, UniformBufferArraySizeCalculationPackBad) {
- // Like previous but, the offset of the second member is too small.
-
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %1 "main"
- OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
- OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
- OpMemberDecorate %_struct_4 0 Offset 0
- OpMemberDecorate %_struct_4 1 Offset 60
- OpDecorate %_struct_4 Block
- OpDecorate %5 DescriptorSet 0
- OpDecorate %5 Binding 0
- %void = OpTypeVoid
- %7 = OpTypeFunction %void
- %uint = OpTypeInt 32 0
- %v3uint = OpTypeVector %uint 3
- %uint_2 = OpConstant %uint 2
-%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
-%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
- %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
-%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
- %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
- %1 = OpFunction %void None %7
- %12 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 4 decorated as Block for variable in Uniform storage "
- "class must follow standard uniform buffer layout rules: member 1 at "
- "offset 60 overlaps previous member ending at offset 63"));
-}
-
-TEST_F(ValidateDecorations, LayoutNotCheckedWhenSkipBlockLayout) {
- // Checks that block layout is not verified in skipping block layout mode.
- // Even for obviously wrong layout.
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 3 ; wrong alignment
- OpMemberDecorate %S 1 Offset 3 ; same offset as before!
- OpDecorate %S Block
- OpDecorate %B DescriptorSet 0
- OpDecorate %B Binding 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
- %B = OpVariable %_ptr_Uniform_S Uniform
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv);
- spvValidatorOptionsSetSkipBlockLayout(getValidatorOptions(), true);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
TEST_F(ValidateDecorations, EntryPointVariableWrongStorageClass) {
const std::string spirv = R"(
OpCapability Shader
@@ -5508,42 +3106,6 @@
"'1[%1]'"));
}
-TEST_F(ValidateDecorations, RecurseThroughRuntimeArray) {
- const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %outer Block
-OpMemberDecorate %inner 0 Offset 0
-OpMemberDecorate %inner 1 Offset 1
-OpDecorate %runtime ArrayStride 16
-OpMemberDecorate %outer 0 Offset 0
-%int = OpTypeInt 32 0
-%inner = OpTypeStruct %int %int
-%runtime = OpTypeRuntimeArray %inner
-%outer = OpTypeStruct %runtime
-%outer_ptr = OpTypePointer StorageBuffer %outer
-%var = OpVariable %outer_ptr StorageBuffer
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 3 decorated as Block for variable in StorageBuffer "
- "storage class must follow standard storage buffer layout "
- "rules: member 1 at offset 1 is not aligned to 4"));
-}
-
TEST_F(ValidateDecorations, VulkanStructWithoutDecorationWithRuntimeArray) {
std::string str = R"(
OpCapability Shader
@@ -6623,229 +4185,6 @@
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
}
-TEST_F(ValidateDecorations, InvalidStraddle) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpMemberDecorate %inner_struct 0 Offset 0
-OpMemberDecorate %inner_struct 1 Offset 4
-OpDecorate %outer_struct Block
-OpMemberDecorate %outer_struct 0 Offset 0
-OpMemberDecorate %outer_struct 1 Offset 8
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%inner_struct = OpTypeStruct %float %float2
-%outer_struct = OpTypeStruct %float2 %inner_struct
-%ptr_ssbo_outer = OpTypePointer StorageBuffer %outer_struct
-%var = OpVariable %ptr_ssbo_outer StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Structure id 2 decorated as Block for variable in "
- "StorageBuffer storage class must follow relaxed "
- "storage buffer layout rules: member 1 is an "
- "improperly straddling vector at offset 12"));
-}
-
-TEST_F(ValidateDecorations, DescriptorArray) {
- const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpMemberDecorate %struct 1 Offset 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%int_2 = OpConstant %int 2
-%float2 = OpTypeVector %float 2
-%struct = OpTypeStruct %float %float2
-%struct_array = OpTypeArray %struct %int_2
-%ptr_ssbo_array = OpTypePointer StorageBuffer %struct_array
-%var = OpVariable %ptr_ssbo_array StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Structure id 2 decorated as Block for variable in "
- "StorageBuffer storage class must follow standard "
- "storage buffer layout rules: member 1 at offset 1 is "
- "not aligned to 8"));
-}
-
-TEST_F(ValidateDecorations, DescriptorRuntimeArray) {
- const std::string spirv = R"(
-OpCapability Shader
-OpCapability RuntimeDescriptorArrayEXT
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpExtension "SPV_EXT_descriptor_indexing"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpMemberDecorate %struct 1 Offset 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%float2 = OpTypeVector %float 2
-%struct = OpTypeStruct %float %float2
-%struct_array = OpTypeRuntimeArray %struct
-%ptr_ssbo_array = OpTypePointer StorageBuffer %struct_array
-%var = OpVariable %ptr_ssbo_array StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Structure id 2 decorated as Block for variable in "
- "StorageBuffer storage class must follow standard "
- "storage buffer layout rules: member 1 at offset 1 is "
- "not aligned to 8"));
-}
-
-TEST_F(ValidateDecorations, MultiDimensionalArray) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpDecorate %array_4 ArrayStride 4
-OpDecorate %array_3 ArrayStride 48
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_3 = OpConstant %int 3
-%int_4 = OpConstant %int 4
-%array_4 = OpTypeArray %int %int_4
-%array_3 = OpTypeArray %array_4 %int_3
-%struct = OpTypeStruct %array_3
-%ptr_struct = OpTypePointer Uniform %struct
-%var = OpVariable %ptr_struct Uniform
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Structure id 2 decorated as Block for variable in "
- "Uniform storage class must follow standard uniform "
- "buffer layout rules: member 0 contains an array with "
- "stride 4 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, ImproperStraddleInArray) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpDecorate %array ArrayStride 24
-OpMemberDecorate %inner 0 Offset 0
-OpMemberDecorate %inner 1 Offset 4
-OpMemberDecorate %inner 2 Offset 12
-OpMemberDecorate %inner 3 Offset 16
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_2 = OpConstant %int 2
-%int2 = OpTypeVector %int 2
-%inner = OpTypeStruct %int %int2 %int %int
-%array = OpTypeArray %inner %int_2
-%struct = OpTypeStruct %array
-%ptr_struct = OpTypePointer StorageBuffer %struct
-%var = OpVariable %ptr_struct StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Structure id 4 decorated as Block for variable in "
- "StorageBuffer storage class must follow relaxed "
- "storage buffer layout rules: member 1 is an "
- "improperly straddling vector at offset 28"));
-}
-
-TEST_F(ValidateDecorations, LargeArray) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpDecorate %array ArrayStride 24
-OpMemberDecorate %inner 0 Offset 0
-OpMemberDecorate %inner 1 Offset 8
-OpMemberDecorate %inner 2 Offset 16
-OpMemberDecorate %inner 3 Offset 20
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_2000000 = OpConstant %int 2000000
-%int2 = OpTypeVector %int 2
-%inner = OpTypeStruct %int %int2 %int %int
-%array = OpTypeArray %inner %int_2000000
-%struct = OpTypeStruct %array
-%ptr_struct = OpTypePointer StorageBuffer %struct
-%var = OpVariable %ptr_struct StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-}
-
// NonReadable/NonWritable
// Returns a SPIR-V shader module with variables in various storage classes,
@@ -7417,7 +4756,8 @@
%uint_2 = OpConstant %uint 2
%arr_v3float_uint_2 = OpTypeArray %v3float %uint_2
%float_0 = OpConstant %float 0
-%_ptr_Output_type = OpTypePointer Output %)" + type + R"(
+%_ptr_Output_type = OpTypePointer Output %)" +
+ type + R"(
%entryPointOutput = OpVariable %_ptr_Output_type Output
%main = OpFunction %void None %3
%5 = OpLabel
@@ -7998,72 +5338,6 @@
"identified with a Block or BufferBlock decoration"));
}
-TEST_F(ValidateDecorations, VulkanArrayStrideZero) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpDecorate %array ArrayStride 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_4 = OpConstant %int 4
-%array = OpTypeArray %int %int_4
-%struct = OpTypeStruct %array
-%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
-%var = OpVariable %ptr_ssbo_struct StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("contains an array with stride 0"));
-}
-
-TEST_F(ValidateDecorations, VulkanArrayStrideTooSmall) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpDecorate %inner ArrayStride 4
-OpDecorate %outer ArrayStride 4
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_4 = OpConstant %int 4
-%inner = OpTypeArray %int %int_4
-%outer = OpTypeArray %inner %int_4
-%struct = OpTypeStruct %outer
-%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
-%var = OpVariable %ptr_ssbo_struct StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "contains an array with stride 4, but with an element size of 16"));
-}
-
TEST_F(ValidateDecorations, FunctionsWithOpGroupDecorate) {
std::string spirv = R"(
OpCapability Addresses
@@ -8169,38 +5443,6 @@
EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable"));
}
-TEST_F(ValidateDecorations, WorkgroupSingleBlockVariable) {
- std::string spirv = R"(
- OpCapability Shader
- OpCapability WorkgroupMemoryExplicitLayoutKHR
- OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main" %_
- OpExecutionMode %main LocalSize 8 1 1
- OpMemberDecorate %first 0 Offset 0
- OpDecorate %first Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %int = OpTypeInt 32 1
- %first = OpTypeStruct %int
-%_ptr_Workgroup_first = OpTypePointer Workgroup %first
- %_ = OpVariable %_ptr_Workgroup_first Workgroup
- %int_0 = OpConstant %int 0
- %int_2 = OpConstant %int 2
-%_ptr_Workgroup_int = OpTypePointer Workgroup %int
- %main = OpFunction %void None %3
- %5 = OpLabel
- %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
- OpStore %13 %int_2
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
-}
-
TEST_F(ValidateDecorations, WorkgroupBlockVariableRequiresV14) {
std::string spirv = R"(
OpCapability Shader
@@ -8235,209 +5477,6 @@
HasSubstr("requires SPIR-V version 1.4 or later"));
}
-TEST_F(ValidateDecorations, WorkgroupSingleNonBlockVariable) {
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main" %a
- OpExecutionMode %main LocalSize 8 1 1
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %int = OpTypeInt 32 1
-%_ptr_Workgroup_int = OpTypePointer Workgroup %int
- %a = OpVariable %_ptr_Workgroup_int Workgroup
- %int_2 = OpConstant %int 2
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpStore %a %int_2
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
-}
-
-TEST_F(ValidateDecorations, WorkgroupMultiBlockVariable) {
- std::string spirv = R"(
- OpCapability Shader
- OpCapability WorkgroupMemoryExplicitLayoutKHR
- OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main" %_ %__0
- OpExecutionMode %main LocalSize 8 1 1
- OpMemberDecorate %first 0 Offset 0
- OpDecorate %first Block
- OpMemberDecorate %second 0 Offset 0
- OpDecorate %second Block
- OpDecorate %_ Aliased
- OpDecorate %__0 Aliased
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %int = OpTypeInt 32 1
- %first = OpTypeStruct %int
-%_ptr_Workgroup_first = OpTypePointer Workgroup %first
- %_ = OpVariable %_ptr_Workgroup_first Workgroup
- %int_0 = OpConstant %int 0
- %int_2 = OpConstant %int 2
-%_ptr_Workgroup_int = OpTypePointer Workgroup %int
- %second = OpTypeStruct %int
-%_ptr_Workgroup_second = OpTypePointer Workgroup %second
- %__0 = OpVariable %_ptr_Workgroup_second Workgroup
- %int_3 = OpConstant %int 3
- %main = OpFunction %void None %3
- %5 = OpLabel
- %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
- OpStore %13 %int_2
- %18 = OpAccessChain %_ptr_Workgroup_int %__0 %int_0
- OpStore %18 %int_3
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
-}
-
-TEST_F(ValidateDecorations, WorkgroupBlockVariableWith8BitType) {
- std::string spirv = R"(
- OpCapability Shader
- OpCapability Int8
- OpCapability WorkgroupMemoryExplicitLayout8BitAccessKHR
- OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main" %_
- OpExecutionMode %main LocalSize 2 1 1
- OpMemberDecorate %first 0 Offset 0
- OpDecorate %first Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %char = OpTypeInt 8 1
- %first = OpTypeStruct %char
-%_ptr_Workgroup_first = OpTypePointer Workgroup %first
- %_ = OpVariable %_ptr_Workgroup_first Workgroup
- %int = OpTypeInt 32 1
- %int_0 = OpConstant %int 0
- %char_2 = OpConstant %char 2
-%_ptr_Workgroup_char = OpTypePointer Workgroup %char
- %main = OpFunction %void None %3
- %5 = OpLabel
- %14 = OpAccessChain %_ptr_Workgroup_char %_ %int_0
- OpStore %14 %char_2
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
-}
-
-TEST_F(ValidateDecorations, WorkgroupMultiNonBlockVariable) {
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main" %a %b
- OpExecutionMode %main LocalSize 8 1 1
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %int = OpTypeInt 32 1
-%_ptr_Workgroup_int = OpTypePointer Workgroup %int
- %a = OpVariable %_ptr_Workgroup_int Workgroup
- %int_2 = OpConstant %int 2
- %b = OpVariable %_ptr_Workgroup_int Workgroup
- %int_3 = OpConstant %int 3
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpStore %a %int_2
- OpStore %b %int_3
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
- EXPECT_EQ(SPV_SUCCESS,
- ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
-}
-
-TEST_F(ValidateDecorations, WorkgroupBlockVariableWith16BitType) {
- std::string spirv = R"(
- OpCapability Shader
- OpCapability Float16
- OpCapability Int16
- OpCapability 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(ValidateDecorations, WorkgroupBlockVariableScalarLayout) {
- std::string spirv = R"(
- OpCapability Shader
- OpCapability WorkgroupMemoryExplicitLayoutKHR
- OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main" %B
- OpSource GLSL 450
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 4
- OpMemberDecorate %S 2 Offset 16
- OpMemberDecorate %S 3 Offset 28
- OpDecorate %S Block
- OpDecorate %B Aliased
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v3float = OpTypeVector %float 3
- %S = OpTypeStruct %float %v3float %v3float %v3float
-%_ptr_Workgroup_S = OpTypePointer Workgroup %S
- %B = OpVariable %_ptr_Workgroup_S Workgroup
- %main = OpFunction %void None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
- spvValidatorOptionsSetWorkgroupScalarBlockLayout(getValidatorOptions(), true);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4))
- << getDiagnosticString();
-}
-
TEST_F(ValidateDecorations, WorkgroupMixBlockAndNonBlockBad) {
std::string spirv = R"(
OpCapability Shader
@@ -8559,77 +5598,6 @@
EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type"));
}
-TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableMissingLayout) {
- std::string spirv = R"(
- OpCapability Shader
- OpCapability WorkgroupMemoryExplicitLayoutKHR
- OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main" %_
- OpExecutionMode %main LocalSize 8 1 1
- OpDecorate %first Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %int = OpTypeInt 32 1
- %first = OpTypeStruct %int
-%_ptr_Workgroup_first = OpTypePointer Workgroup %first
- %_ = OpVariable %_ptr_Workgroup_first Workgroup
- %int_0 = OpConstant %int 0
- %int_2 = OpConstant %int 2
-%_ptr_Workgroup_int = OpTypePointer Workgroup %int
- %main = OpFunction %void None %3
- %5 = OpLabel
- %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
- OpStore %13 %int_2
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Block must be explicitly laid out with Offset decorations"));
-}
-
-TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableBadLayout) {
- std::string spirv = R"(
- OpCapability Shader
- OpCapability WorkgroupMemoryExplicitLayoutKHR
- OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main" %_
- OpExecutionMode %main LocalSize 8 1 1
- OpMemberDecorate %first 0 Offset 1
- OpDecorate %first Block
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %int = OpTypeInt 32 1
- %first = OpTypeStruct %int
-%_ptr_Workgroup_first = OpTypePointer Workgroup %first
- %_ = OpVariable %_ptr_Workgroup_first Workgroup
- %int_0 = OpConstant %int 0
- %int_2 = OpConstant %int 2
-%_ptr_Workgroup_int = OpTypePointer Workgroup %int
- %main = OpFunction %void None %3
- %5 = OpLabel
- %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
- OpStore %13 %int_2
- OpReturn
- OpFunctionEnd
- )";
-
- CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
- EXPECT_EQ(SPV_ERROR_INVALID_ID,
- ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Block for variable in Workgroup storage class must follow "
- "relaxed storage buffer layout rules: "
- "member 0 at offset 1 is not aligned to 4"));
-}
-
TEST_F(ValidateDecorations, WorkgroupBlockNoCapability) {
std::string spirv = R"(
OpCapability Shader
@@ -8661,215 +5629,6 @@
"unless declaring the WorkgroupMemoryExplicitLayoutKHR capability"));
}
-TEST_F(ValidateDecorations, BadMatrixStrideUniform) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 3
-OpMemberDecorate %block 0 ColMajor
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%float4 = OpTypeVector %float 4
-%matrix4x4 = OpTypeMatrix %float4 4
-%block = OpTypeStruct %matrix4x4
-%block_ptr = OpTypePointer Uniform %block
-%var = OpVariable %block_ptr Uniform
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 2 decorated as Block for variable in Uniform storage "
- "class must follow standard uniform buffer layout rules: member 0 is "
- "a matrix with stride 3 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, BadMatrixStrideStorageBuffer) {
- const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 3
-OpMemberDecorate %block 0 ColMajor
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%float4 = OpTypeVector %float 4
-%matrix4x4 = OpTypeMatrix %float4 4
-%block = OpTypeStruct %matrix4x4
-%block_ptr = OpTypePointer StorageBuffer %block
-%var = OpVariable %block_ptr StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 2 decorated as Block for variable in StorageBuffer "
- "storage class must follow standard storage buffer layout rules: "
- "member 0 is a matrix with stride 3 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, BadMatrixStridePushConstant) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 3
-OpMemberDecorate %block 0 ColMajor
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%float4 = OpTypeVector %float 4
-%matrix4x4 = OpTypeMatrix %float4 4
-%block = OpTypeStruct %matrix4x4
-%block_ptr = OpTypePointer PushConstant %block
-%var = OpVariable %block_ptr PushConstant
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 2 decorated as Block for variable in PushConstant "
- "storage class must follow standard storage buffer layout rules: "
- "member 0 is a matrix with stride 3 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, BadMatrixStrideStorageBufferScalarLayout) {
- const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 3
-OpMemberDecorate %block 0 RowMajor
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%float4 = OpTypeVector %float 4
-%matrix4x4 = OpTypeMatrix %float4 4
-%block = OpTypeStruct %matrix4x4
-%block_ptr = OpTypePointer StorageBuffer %block
-%var = OpVariable %block_ptr StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- options_->scalar_block_layout = true;
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Structure id 2 decorated as Block for variable in StorageBuffer "
- "storage class must follow scalar storage buffer layout rules: "
- "member 0 is a matrix with stride 3 not satisfying alignment to 4"));
-}
-
-TEST_F(ValidateDecorations, MissingOffsetStructNestedInArray) {
- const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %array ArrayStride 4
-OpDecorate %outer Block
-OpMemberDecorate %outer 0 Offset 0
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_4 = OpConstant %int 4
-%inner = OpTypeStruct %int
-%array = OpTypeArray %inner %int_4
-%outer = OpTypeStruct %array
-%ptr_ssbo_outer = OpTypePointer StorageBuffer %outer
-%var = OpVariable %ptr_ssbo_outer StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Structure id 3 decorated as Block must be explicitly "
- "laid out with Offset decorations"));
-}
-
-TEST_F(ValidateDecorations, AllOnesOffset) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %outer Block
-OpMemberDecorate %outer 0 Offset 0
-OpMemberDecorate %struct 0 Offset 4294967295
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%struct = OpTypeStruct %int
-%outer = OpTypeStruct %struct
-%ptr = OpTypePointer Uniform %outer
-%var = OpVariable %ptr Uniform
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("decorated as Block must be explicitly laid out with "
- "Offset decorations"));
-}
-
TEST_F(ValidateDecorations, PerVertexVulkanGood) {
const std::string spirv = R"(
OpCapability Shader
@@ -9384,306 +6143,6 @@
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}
-TEST_F(ValidateDecorations, Std140ColMajorMat2x2) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 ColMajor
-OpMemberDecorate %block 0 MatrixStride 8
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 2
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer Uniform %block
-%var = OpVariable %ptr_block Uniform
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "member 0 is a matrix with stride 8 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, Std140RowMajorMat2x2) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 RowMajor
-OpMemberDecorate %block 0 MatrixStride 8
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 2
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer Uniform %block
-%var = OpVariable %ptr_block Uniform
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "member 0 is a matrix with stride 8 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, Std140ColMajorMat4x2) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 ColMajor
-OpMemberDecorate %block 0 MatrixStride 8
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 4
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer Uniform %block
-%var = OpVariable %ptr_block Uniform
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "member 0 is a matrix with stride 8 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, Std140ColMajorMat2x3) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 ColMajor
-OpMemberDecorate %block 0 MatrixStride 12
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float3 = OpTypeVector %float 3
-%matrix = OpTypeMatrix %float3 2
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer Uniform %block
-%var = OpVariable %ptr_block Uniform
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("member 0 is a matrix with stride 12 not satisfying "
- "alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, MatrixMissingMajornessUniform) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 16
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 2
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer Uniform %block
-%var = OpVariable %ptr_block Uniform
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "must be explicitly laid out with RowMajor or ColMajor decorations"));
-}
-
-TEST_F(ValidateDecorations, MatrixMissingMajornessStorageBuffer) {
- const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 16
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 2
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer StorageBuffer %block
-%var = OpVariable %ptr_block StorageBuffer
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "must be explicitly laid out with RowMajor or ColMajor decorations"));
-}
-
-TEST_F(ValidateDecorations, MatrixMissingMajornessPushConstant) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 16
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 2
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer PushConstant %block
-%var = OpVariable %ptr_block PushConstant
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "must be explicitly laid out with RowMajor or ColMajor decorations"));
-}
-
-TEST_F(ValidateDecorations, StructWithRowAndColMajor) {
- const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 16
-OpMemberDecorate %block 0 ColMajor
-OpMemberDecorate %block 1 Offset 32
-OpMemberDecorate %block 1 MatrixStride 16
-OpMemberDecorate %block 1 RowMajor
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 2
-%block = OpTypeStruct %matrix %matrix
-%ptr_block = OpTypePointer PushConstant %block
-%var = OpVariable %ptr_block PushConstant
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, 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(ValidateDecorations, UntypedVariableDuplicateInterface) {
const std::string spirv = R"(
OpCapability Shader
@@ -9715,314 +6174,6 @@
"interface '2[%var]' is disallowed"));
}
-TEST_F(ValidateDecorations, 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("decorated as Block for variable in PhysicalStorageBuffer "
- "storage class must follow relaxed storage buffer layout "
- "rules: member 0 is missing an Offset decoration"));
-}
-
-TEST_F(ValidateDecorations, 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(
- "decorated as Block for variable in PhysicalStorageBuffer storage "
- "class must follow relaxed storage buffer layout rules: member 0 "
- "contains an array with stride 0, but with an element size of 4"));
-}
-
-TEST_F(ValidateDecorations, 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(ValidateDecorations, 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(ValidateDecorations, 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("is a matrix with stride 8 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, 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(ValidateDecorations, 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(ValidateDecorations, 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("is a matrix with stride 8 not satisfying alignment to 16"));
-}
-
TEST_F(ValidateDecorations, MultipleBuiltinsInputVertex) {
const std::string body = R"(
OpCapability Shader
@@ -10320,61 +6471,6 @@
AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-09659"));
}
-TEST_F(ValidateDecorations, 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(ValidateDecorations, 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(ValidateDecorations, UntypedVariableStorageBufferMissingBlock) {
const std::string spirv = R"(
OpCapability Shader
@@ -10479,7 +6575,8 @@
%struct = OpTypeStruct %int
%ptr = OpTypeUntypedPointerKHR )" +
sc + R"(
-%var = OpUntypedVariableKHR %ptr )" + sc + R"( %struct
+%var = OpUntypedVariableKHR %ptr )" +
+ sc + R"( %struct
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
@@ -10513,7 +6610,8 @@
%struct = OpTypeStruct %int
%ptr = OpTypeUntypedPointerKHR )" +
sc + R"(
-%var = OpUntypedVariableKHR %ptr )" + sc + R"( %struct
+%var = OpUntypedVariableKHR %ptr )" +
+ sc + R"( %struct
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
@@ -10532,164 +6630,6 @@
UntypedVariableSetAndBinding,
Values("StorageBuffer", "Uniform"));
-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) {
- 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 (!read_only || op.find("OpStore") == std::string::npos) {
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("array with stride 4 not satisfying alignment to 16"));
- }
-}
-
-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",
- "%ld = OpLoad %test_type %var", "OpStore %var %test_val")));
-
-TEST_F(ValidateDecorations, 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("member 0 is missing an Offset decoration"));
-}
-
TEST_F(ValidateDecorations, ComponentMultipleArrays) {
const std::string spirv = R"(
OpCapability Tessellation
@@ -10958,527 +6898,6 @@
"decorated with ArrayStride"));
}
-TEST_F(ValidateDecorations, 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(ValidateDecorations, 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(ValidateDecorations, 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(ValidateDecorations, 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 for operand"));
-}
-
-TEST_F(ValidateDecorations, 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(ValidateDecorations, 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 for operand"));
-}
-
-TEST_F(ValidateDecorations, 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
-%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_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
-}
-
-TEST_F(ValidateDecorations, 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 for operand"));
-}
-
-TEST_F(ValidateDecorations, 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 for operand"));
-}
-
-TEST_F(ValidateDecorations, 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 for operand"));
-}
-
-TEST_F(ValidateDecorations, 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 for operand"));
-}
-
-TEST_F(ValidateDecorations, 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 for operand"));
-}
-
-TEST_F(ValidateDecorations, 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(ValidateDecorations, 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(ValidateDecorations, 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(ValidateDecorations, 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
-%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(ValidateDecorations, 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
-
-)";
-
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-}
-
TEST_F(ValidateDecorations, LongVectorUniformSpecConstantFail) {
const std::string spirv = R"(
OpCapability Shader
diff --git a/test/val/val_explicit_layout_test.cpp b/test/val/val_explicit_layout_test.cpp
new file mode 100644
index 0000000..a38c061
--- /dev/null
+++ b/test/val/val_explicit_layout_test.cpp
@@ -0,0 +1,5659 @@
+// 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
diff --git a/test/val/val_extension_spv_ext_descriptor_heap.cpp b/test/val/val_extension_spv_ext_descriptor_heap.cpp
index e8b0b01..9c56fbc 100644
--- a/test/val/val_extension_spv_ext_descriptor_heap.cpp
+++ b/test/val/val_extension_spv_ext_descriptor_heap.cpp
@@ -1228,8 +1228,9 @@
%uint = OpTypeInt 32 0
%_ptr_Output_uint = OpTypePointer Output %uint
%o = OpVariable %_ptr_Output_uint Output
-%_ptr_Uniform = OpTypeUntypedPointerKHR UniformConstant
-%resource_heap = OpUntypedVariableKHR %_ptr_Uniform UniformConstant
+%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
%int = OpTypeInt 32 1
%int_9 = OpConstant %int 9
%U = OpTypeStruct %uint
@@ -1239,7 +1240,7 @@
%_runtimearr_17 = OpTypeRuntimeArray %17
%main = OpFunction %void None %3
%5 = OpLabel
- %16 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_17 %resource_heap %int_9
+ %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_17 %resource_heap %int_9
%20 = OpBufferPointerEXT %_ptr_Uniform %16
%21 = OpUntypedAccessChainKHR %_ptr_Uniform %U %20 %int_0
%22 = OpLoad %uint %21
@@ -1819,15 +1820,15 @@
OpExecutionMode %1 LocalSize 1 1 1
OpDecorate %2 BuiltIn ResourceHeapEXT
OpMemberDecorate %struct 0 Offset 0
- OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0
+ OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_4
OpMemberDecorate %image_struct 0 Offset 0
%void = OpTypeVoid
%7 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
+ %uint_4 = OpConstant %uint 4
%uint_51966 = OpConstant %uint 51966
- %uint_0_0 = OpConstant %uint 0
%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
%2 = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
%14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui
diff --git a/test/val/val_validation_state_test.cpp b/test/val/val_validation_state_test.cpp
index a5e88de..05a5de4 100644
--- a/test/val/val_validation_state_test.cpp
+++ b/test/val/val_validation_state_test.cpp
@@ -232,6 +232,31 @@
EXPECT_EQ(100u, options_->universal_limits_.max_access_chain_indexes);
}
+TEST_F(ValidationStateTest, CheckDescriptorHeapLayoutOptions) {
+ EXPECT_EQ(0u, options_->buffer_descriptor_layout.size);
+ EXPECT_EQ(1u, options_->buffer_descriptor_layout.alignment);
+ EXPECT_EQ(0u, options_->image_descriptor_layout.size);
+ EXPECT_EQ(1u, options_->image_descriptor_layout.alignment);
+ EXPECT_EQ(0u, options_->sampler_descriptor_layout.size);
+ EXPECT_EQ(1u, options_->sampler_descriptor_layout.alignment);
+ EXPECT_EQ(0u, options_->tensor_descriptor_layout.size);
+ EXPECT_EQ(1u, options_->tensor_descriptor_layout.alignment);
+
+ spvValidatorOptionsSetBufferDescriptorLayout(options_, 16u, 8u);
+ spvValidatorOptionsSetImageDescriptorLayout(options_, 64u, 16u);
+ spvValidatorOptionsSetSamplerDescriptorLayout(options_, 32u, 8u);
+ spvValidatorOptionsSetTensorDescriptorLayout(options_, 128u, 32u);
+
+ EXPECT_EQ(16u, options_->buffer_descriptor_layout.size);
+ EXPECT_EQ(8u, options_->buffer_descriptor_layout.alignment);
+ EXPECT_EQ(64u, options_->image_descriptor_layout.size);
+ EXPECT_EQ(16u, options_->image_descriptor_layout.alignment);
+ EXPECT_EQ(32u, options_->sampler_descriptor_layout.size);
+ EXPECT_EQ(8u, options_->sampler_descriptor_layout.alignment);
+ EXPECT_EQ(128u, options_->tensor_descriptor_layout.size);
+ EXPECT_EQ(32u, options_->tensor_descriptor_layout.alignment);
+}
+
TEST_F(ValidationStateTest, CheckNonRecursiveBodyGood) {
std::string spirv = std::string(kHeader) + kNonRecursiveBody;
CompileSuccessfully(spirv);
diff --git a/tools/val/val.cpp b/tools/val/val.cpp
index 377fd0b..6729930 100644
--- a/tools/val/val.cpp
+++ b/tools/val/val.cpp
@@ -76,6 +76,10 @@
not be allowed by the target environment.
--before-hlsl-legalization Allows code patterns that are intended to be
fixed by spirv-opt's legalization passes.
+ --buffer-descriptor-layout <size>:<align> Set size and alignment for buffer and acceleration structure descriptor heap resources.
+ --image-descriptor-layout <size>:<align> Set size and alignment for image and sampled image descriptor heap resources.
+ --sampler-descriptor-layout <size>:<align> Set size and alignment for sampler descriptor heap resources.
+ --tensor-descriptor-layout <size>:<align> Set size and alignment for tensor descriptor heap resources.
--version Display validator version information.
--target-env {%s}
Use validation rules from the specified environment.
@@ -222,6 +226,82 @@
options.SetAllowVulkan32BitBitwise(true);
} else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
options.SetRelaxStructStore(true);
+ } else if (0 == strcmp(cur_arg, "--buffer-descriptor-layout")) {
+ if (argi + 1 < argc) {
+ uint32_t size = 0, alignment = 0;
+ if (sscanf(argv[++argi], "%u:%u", &size, &alignment) == 2 &&
+ size > 0 && alignment > 0) {
+ options.SetBufferDescriptorLayout(size, alignment);
+ } else {
+ fprintf(stderr,
+ "error: Invalid argument to --buffer-descriptor-layout "
+ "(expected <size>:<align>)\n");
+ continue_processing = false;
+ return_code = 1;
+ }
+ } else {
+ fprintf(stderr,
+ "error: Missing argument to --buffer-descriptor-layout\n");
+ continue_processing = false;
+ return_code = 1;
+ }
+ } else if (0 == strcmp(cur_arg, "--image-descriptor-layout")) {
+ if (argi + 1 < argc) {
+ uint32_t size = 0, alignment = 0;
+ if (sscanf(argv[++argi], "%u:%u", &size, &alignment) == 2 &&
+ size > 0 && alignment > 0) {
+ options.SetImageDescriptorLayout(size, alignment);
+ } else {
+ fprintf(stderr,
+ "error: Invalid argument to --image-descriptor-layout "
+ "(expected <size>:<align>)\n");
+ continue_processing = false;
+ return_code = 1;
+ }
+ } else {
+ fprintf(stderr,
+ "error: Missing argument to --image-descriptor-layout\n");
+ continue_processing = false;
+ return_code = 1;
+ }
+ } else if (0 == strcmp(cur_arg, "--sampler-descriptor-layout")) {
+ if (argi + 1 < argc) {
+ uint32_t size = 0, alignment = 0;
+ if (sscanf(argv[++argi], "%u:%u", &size, &alignment) == 2 &&
+ size > 0 && alignment > 0) {
+ options.SetSamplerDescriptorLayout(size, alignment);
+ } else {
+ fprintf(stderr,
+ "error: Invalid argument to --sampler-descriptor-layout "
+ "(expected <size>:<align>)\n");
+ continue_processing = false;
+ return_code = 1;
+ }
+ } else {
+ fprintf(stderr,
+ "error: Missing argument to --sampler-descriptor-layout\n");
+ continue_processing = false;
+ return_code = 1;
+ }
+ } else if (0 == strcmp(cur_arg, "--tensor-descriptor-layout")) {
+ if (argi + 1 < argc) {
+ uint32_t size = 0, alignment = 0;
+ if (sscanf(argv[++argi], "%u:%u", &size, &alignment) == 2 &&
+ size > 0 && alignment > 0) {
+ options.SetTensorDescriptorLayout(size, alignment);
+ } else {
+ fprintf(stderr,
+ "error: Invalid argument to --tensor-descriptor-layout "
+ "(expected <size>:<align>)\n");
+ continue_processing = false;
+ return_code = 1;
+ }
+ } else {
+ fprintf(stderr,
+ "error: Missing argument to --tensor-descriptor-layout\n");
+ continue_processing = false;
+ return_code = 1;
+ }
} else if (0 == cur_arg[1]) {
// Setting a filename of "-" to indicate stdin.
if (!inFile) {