From 91dfde0e148b5e1a63e3790914cedad9149b9417 Mon Sep 17 00:00:00 2001 From: spencer-lunarg Date: Wed, 18 Dec 2024 00:35:08 +0900 Subject: [PATCH] gpu: Fix Chaining of Accesses for GeneralBuffer --- .../descriptor_class_general_buffer_pass.cpp | 38 +- .../descriptor_class_general_buffer_pass.h | 7 +- layers/gpu/spirv/pass.cpp | 17 +- layers/gpu/spirv/pass.h | 2 +- tests/framework/layer_validation_tests.h | 5 + ...gpu_av_descriptor_class_general_buffer.cpp | 492 ++++++++++++++++-- ...scriptor_class_general_buffer_positive.cpp | 470 ++++++++++++++++- 7 files changed, 974 insertions(+), 57 deletions(-) diff --git a/layers/gpu/spirv/descriptor_class_general_buffer_pass.cpp b/layers/gpu/spirv/descriptor_class_general_buffer_pass.cpp index 990b4566d95..337be87e916 100644 --- a/layers/gpu/spirv/descriptor_class_general_buffer_pass.cpp +++ b/layers/gpu/spirv/descriptor_class_general_buffer_pass.cpp @@ -14,6 +14,7 @@ */ #include "descriptor_class_general_buffer_pass.h" +#include "instruction.h" #include "module.h" #include #include @@ -42,18 +43,20 @@ uint32_t DescriptorClassGeneralBufferPass::GetLinkFunctionId() { uint32_t DescriptorClassGeneralBufferPass::CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InjectionData& injection_data) { - assert(access_chain_inst_ && var_inst_); + assert(!access_chain_insts_.empty() && var_inst_); const Constant& set_constant = module_.type_manager_.GetConstantUInt32(descriptor_set_); const Constant& binding_constant = module_.type_manager_.GetConstantUInt32(descriptor_binding_); const uint32_t descriptor_index_id = CastToUint32(descriptor_index_id_, block, inst_it); // might be int32 // For now, only do bounds check for non-aggregate types // TODO - Do bounds check for aggregate loads and stores - const Type* pointer_type = module_.type_manager_.FindTypeById(access_chain_inst_->TypeId()); + // + // Grab front() as it will be the "final" type we access + const Type* pointer_type = module_.type_manager_.FindTypeById(access_chain_insts_.front()->TypeId()); const Type* pointee_type = module_.type_manager_.FindTypeById(pointer_type->inst_.Word(3)); if (pointee_type && pointee_type->spv_type_ != SpvType::kArray && pointee_type->spv_type_ != SpvType::kRuntimeArray && pointee_type->spv_type_ != SpvType::kStruct) { - descriptor_offset_id_ = GetLastByte(*var_inst_, *access_chain_inst_, block, inst_it); // Get Last Byte Index + descriptor_offset_id_ = GetLastByte(*var_inst_, access_chain_insts_, block, inst_it); // Get Last Byte Index } else { descriptor_offset_id_ = module_.type_manager_.GetConstantZeroUint32().Id(); } @@ -75,7 +78,6 @@ uint32_t DescriptorClassGeneralBufferPass::CreateFunctionCall(BasicBlock& block, } void DescriptorClassGeneralBufferPass::Reset() { - access_chain_inst_ = nullptr; var_inst_ = nullptr; target_instruction_ = nullptr; descriptor_set_ = 0; @@ -91,15 +93,23 @@ bool DescriptorClassGeneralBufferPass::RequiresInstrumentation(const Function& f return false; } - // TODO - Should have loop to walk Load/Store to the Pointer, - // this case will not cover things such as OpCopyObject or double OpAccessChains - access_chain_inst_ = function.FindInstruction(inst.Operand(0)); - if (!access_chain_inst_ || access_chain_inst_->Opcode() != spv::OpAccessChain) { + const Instruction* next_access_chain = function.FindInstruction(inst.Operand(0)); + if (!next_access_chain || next_access_chain->Opcode() != spv::OpAccessChain) { return false; } - - const uint32_t variable_id = access_chain_inst_->Operand(0); - const Variable* variable = module_.type_manager_.FindVariableById(variable_id); + access_chain_insts_.clear(); // only clear right before we know we will need again + + const Variable* variable = nullptr; + // We need to walk down possibly multiple chained OpAccessChains or OpCopyObject to get the variable + while (next_access_chain && next_access_chain->Opcode() == spv::OpAccessChain) { + access_chain_insts_.push_back(next_access_chain); + const uint32_t access_chain_base_id = next_access_chain->Operand(0); + variable = module_.type_manager_.FindVariableById(access_chain_base_id); + if (variable) { + break; // found + } + next_access_chain = function.FindInstruction(access_chain_base_id); + } if (!variable) { return false; } @@ -134,15 +144,15 @@ bool DescriptorClassGeneralBufferPass::RequiresInstrumentation(const Function& f // A load through a descriptor array will have at least 3 operands. We // do not want to instrument loads of descriptors here which are part of // an image-based reference. - if (is_descriptor_array && access_chain_inst_->Length() >= 6) { - descriptor_index_id_ = access_chain_inst_->Operand(1); + if (is_descriptor_array && access_chain_insts_.back()->Length() >= 6) { + descriptor_index_id_ = access_chain_insts_.back()->Operand(1); } else { // There is no array of this descriptor, so we essentially have an array of 1 descriptor_index_id_ = module_.type_manager_.GetConstantZeroUint32().Id(); } for (const auto& annotation : module_.annotations_) { - if (annotation->Opcode() == spv::OpDecorate && annotation->Word(1) == variable_id) { + if (annotation->Opcode() == spv::OpDecorate && annotation->Word(1) == variable->Id()) { if (annotation->Word(2) == spv::DecorationDescriptorSet) { descriptor_set_ = annotation->Word(3); } else if (annotation->Word(2) == spv::DecorationBinding) { diff --git a/layers/gpu/spirv/descriptor_class_general_buffer_pass.h b/layers/gpu/spirv/descriptor_class_general_buffer_pass.h index abd1cfad12f..40093943b31 100644 --- a/layers/gpu/spirv/descriptor_class_general_buffer_pass.h +++ b/layers/gpu/spirv/descriptor_class_general_buffer_pass.h @@ -37,7 +37,12 @@ class DescriptorClassGeneralBufferPass : public Pass { uint32_t link_function_id = 0; uint32_t GetLinkFunctionId(); - const Instruction* access_chain_inst_ = nullptr; + // List of OpAccessChains fom the Store/Load down to the OpVariable + // The front() will be closet to the exact spot accesssed + // The back() will be closest to the OpVariable + // (note GLSL will try to always create a single large OpAccessChain) + std::vector access_chain_insts_; + // The OpVariable that is being accessed const Instruction* var_inst_ = nullptr; uint32_t descriptor_set_ = 0; diff --git a/layers/gpu/spirv/pass.cpp b/layers/gpu/spirv/pass.cpp index e575ff6b137..39b803823de 100644 --- a/layers/gpu/spirv/pass.cpp +++ b/layers/gpu/spirv/pass.cpp @@ -14,6 +14,7 @@ */ #include "pass.h" +#include "instruction.h" #include "module.h" #include "gpu/shaders/gpuav_error_codes.h" @@ -208,17 +209,17 @@ const Instruction* Pass::GetMemeberDecoration(uint32_t id, uint32_t member_index // Find outermost buffer type and its access chain index. // Because access chains indexes can be runtime values, we need to build arithmetic logic in the SPIR-V to get the runtime value of // the indexing -uint32_t Pass::GetLastByte(const Instruction& var_inst, const Instruction& access_chain_inst, BasicBlock& block, +uint32_t Pass::GetLastByte(const Instruction& var_inst, std::vector& access_chain_insts, BasicBlock& block, InstructionIt* inst_it) { const Type* pointer_type = module_.type_manager_.FindTypeById(var_inst.TypeId()); const Type* descriptor_type = module_.type_manager_.FindTypeById(pointer_type->inst_.Word(3)); uint32_t current_type_id = 0; - uint32_t ac_word_index = 4; + uint32_t ac_word_index = 4; // points to first "Index" operand of an OpAccessChain if (descriptor_type->spv_type_ == SpvType::kArray || descriptor_type->spv_type_ == SpvType::kRuntimeArray) { current_type_id = descriptor_type->inst_.Operand(0); - ac_word_index++; + ac_word_index++; // this jumps over the array of descriptors so we first start on the descriptor itself } else if (descriptor_type->spv_type_ == SpvType::kStruct) { current_type_id = descriptor_type->Id(); } else { @@ -236,8 +237,9 @@ uint32_t Pass::GetLastByte(const Instruction& var_inst, const Instruction& acces uint32_t matrix_stride_id = 0; bool in_matrix = false; - while (ac_word_index < access_chain_inst.Length()) { - const uint32_t ac_index_id = access_chain_inst.Word(ac_word_index); + auto access_chain_iter = access_chain_insts.rbegin(); + while (access_chain_iter != access_chain_insts.rend()) { + const uint32_t ac_index_id = (*access_chain_iter)->Word(ac_word_index); uint32_t current_offset_id = 0; const Type* current_type = module_.type_manager_.FindTypeById(current_type_id); @@ -335,7 +337,12 @@ uint32_t Pass::GetLastByte(const Instruction& var_inst, const Instruction& acces block.CreateInstruction(spv::OpIAdd, {uint32_type.Id(), new_sum_id, sum_id, current_offset_id}, inst_it); sum_id = new_sum_id; } + ac_word_index++; + if (ac_word_index >= (*access_chain_iter)->Length()) { + ++access_chain_iter; + ac_word_index = 4; // reset + } } // Add in offset of last byte of referenced object diff --git a/layers/gpu/spirv/pass.h b/layers/gpu/spirv/pass.h index e488dcff9c0..cbd8ee2f5c5 100644 --- a/layers/gpu/spirv/pass.h +++ b/layers/gpu/spirv/pass.h @@ -45,7 +45,7 @@ class Pass { const Instruction* GetDecoration(uint32_t id, spv::Decoration decoration); const Instruction* GetMemeberDecoration(uint32_t id, uint32_t member_index, spv::Decoration decoration); - uint32_t GetLastByte(const Instruction& var_inst, const Instruction& access_chain_inst, BasicBlock& block, + uint32_t GetLastByte(const Instruction& var_inst, std::vector& access_chain_insts, BasicBlock& block, InstructionIt* inst_it); // Generate SPIR-V needed to help convert things to be uniformly uint32_t // If no inst_it is passed in, any new instructions will be added to end of the Block diff --git a/tests/framework/layer_validation_tests.h b/tests/framework/layer_validation_tests.h index 199319f5bb8..01a9e5f3717 100644 --- a/tests/framework/layer_validation_tests.h +++ b/tests/framework/layer_validation_tests.h @@ -260,6 +260,11 @@ class GpuAVDescriptorIndexingTest : public GpuAVTest { void InitGpuVUDescriptorIndexing(); }; +class GpuAVDescriptorClassGeneralBuffer : public GpuAVTest { + public: + void ComputeStorageBufferTest(const char *shader, bool is_glsl, VkDeviceSize buffer_size, const char *expected_error = nullptr); +}; + class GpuAVRayQueryTest : public GpuAVTest { public: void InitGpuAVRayQuery(); diff --git a/tests/unit/gpu_av_descriptor_class_general_buffer.cpp b/tests/unit/gpu_av_descriptor_class_general_buffer.cpp index ffa40fe47e8..453c6d99abe 100644 --- a/tests/unit/gpu_av_descriptor_class_general_buffer.cpp +++ b/tests/unit/gpu_av_descriptor_class_general_buffer.cpp @@ -16,12 +16,11 @@ #include "../framework/descriptor_helper.h" #include "../layers/gpu/shaders/gpuav_shaders_constants.h" -class NegativeGpuAVDescriptorClassGeneralBuffer : public GpuAVTest { +class NegativeGpuAVDescriptorClassGeneralBuffer : public GpuAVDescriptorClassGeneralBuffer { public: void ShaderBufferSizeTest(VkDeviceSize buffer_size, VkDeviceSize binding_offset, VkDeviceSize binding_range, VkDescriptorType descriptor_type, const char *fragment_shader, std::vector expected_errors, bool shader_objects = false); - void ComputeStorageBufferTest(const char *expected_error, const char *shader, VkDeviceSize buffer_size); }; TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, RobustBuffer) { @@ -725,37 +724,6 @@ TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, StorageBuffer) { m_errorMonitor->VerifyFound(); } -void NegativeGpuAVDescriptorClassGeneralBuffer::ComputeStorageBufferTest(const char *expected_error, const char *shader, - VkDeviceSize buffer_size) { - SetTargetApiVersion(VK_API_VERSION_1_2); - - RETURN_IF_SKIP(InitGpuAvFramework()); - RETURN_IF_SKIP(InitState()); - - CreateComputePipelineHelper pipe(*this); - pipe.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}; - pipe.cs_ = std::make_unique(this, shader, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2); - pipe.CreateComputePipeline(); - - // too small - vkt::Buffer in_buffer(*m_device, buffer_size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps); - - pipe.descriptor_set_->WriteDescriptorBufferInfo(0, in_buffer.handle(), 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); - pipe.descriptor_set_->UpdateDescriptorSets(); - - m_command_buffer.Begin(); - vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle()); - vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_.handle(), 0, 1, - &pipe.descriptor_set_->set_, 0, nullptr); - vk::CmdDispatch(m_command_buffer.handle(), 1, 1, 1); - m_command_buffer.End(); - - m_errorMonitor->SetDesiredError(expected_error); - m_default_queue->Submit(m_command_buffer); - m_default_queue->Wait(); - m_errorMonitor->VerifyFound(); -} - TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, Vector) { TEST_DESCRIPTION("index into a vector OOB"); @@ -772,7 +740,7 @@ TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, Vector) { in_buffer.b.y = 0.0; } )glsl"; - ComputeStorageBufferTest("VUID-vkCmdDispatch-storageBuffers-06936", cs_source, 20); + ComputeStorageBufferTest(cs_source, true, 20, "VUID-vkCmdDispatch-storageBuffers-06936"); } TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, Matrix) { @@ -791,7 +759,7 @@ TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, Matrix) { in_buffer.b[2][1] = 0.0; } )glsl"; - ComputeStorageBufferTest("VUID-vkCmdDispatch-storageBuffers-06936", cs_source, 30); + ComputeStorageBufferTest(cs_source, true, 30, "VUID-vkCmdDispatch-storageBuffers-06936"); } TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, Geometry) { @@ -1352,3 +1320,457 @@ TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, PartialBoundDescriptorSSBO) { m_default_queue->Wait(); m_errorMonitor->VerifyFound(); } + +TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, VectorArray) { + char const *cs_source = R"glsl( + #version 450 + layout(set = 0, binding = 0) buffer foo { + uvec4 a[8]; // stride 16 + }; + void main() { + a[3].y = 44; // write at byte[52:56] + } + )glsl"; + ComputeStorageBufferTest(cs_source, true, 48, "VUID-vkCmdDispatch-storageBuffers-06936"); +} + +// TODO - Handle tracking copy size of Arrays +TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, DISABLED_ArrayCopyGLSL) { + char const *cs_source = R"glsl( + #version 450 + layout(set = 0, binding = 0, std430) buffer foo { + uvec4 a; + uint padding; + uint b[4]; // b[3] is OOB + uint c; // offset 36 + }; + + void main() { + uint d[4] = {4, 5, 6, 7}; + b = d; + } + )glsl"; + ComputeStorageBufferTest(cs_source, true, 32, "VUID-vkCmdDispatch-storageBuffers-06936"); +} + +// TODO - Handle tracking copy size of Arrays +TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, DISABLED_ArrayCopyTwoBindingsGLSL) { + SetTargetApiVersion(VK_API_VERSION_1_2); + RETURN_IF_SKIP(InitGpuAvFramework()); + RETURN_IF_SKIP(InitState()); + + char const *cs_source = R"glsl( + #version 450 + layout(set = 0, binding = 0, std430) buffer foo1 { + uvec4 a; + uint padding; + uint b[4]; + uint c; + }; + + layout(set = 0, binding = 1, std430) buffer foo2 { + uint d; + uint e[4]; + uvec2 f; + }; + + void main() { + b = e; + } + )glsl"; + + CreateComputePipelineHelper pipe(*this); + pipe.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, + {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}; + pipe.cs_ = std::make_unique(this, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2); + pipe.CreateComputePipeline(); + + vkt::Buffer in_buffer(*m_device, 256, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps); + // not enough bound + pipe.descriptor_set_->WriteDescriptorBufferInfo(0, in_buffer.handle(), 0, 32, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + pipe.descriptor_set_->WriteDescriptorBufferInfo(1, in_buffer.handle(), 64, 16, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + pipe.descriptor_set_->UpdateDescriptorSets(); + + m_command_buffer.Begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle()); + vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_.handle(), 0, 1, + &pipe.descriptor_set_->set_, 0, nullptr); + vk::CmdDispatch(m_command_buffer.handle(), 1, 1, 1); + m_command_buffer.End(); + + m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-storageBuffers-06936"); + m_default_queue->Submit(m_command_buffer); + m_default_queue->Wait(); + m_errorMonitor->VerifyFound(); +} + +// TODO - Handle tracking copy size of Arrays +TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, DISABLED_ArrayCopyTwoBindingsSlang) { + SetTargetApiVersion(VK_API_VERSION_1_2); + RETURN_IF_SKIP(InitGpuAvFramework()); + RETURN_IF_SKIP(InitState()); + + // struct Bar1 { + // uint4 a; + // uint b[4]; + // uint c; + // }; + // + // struct Bar2 { + // uint4 d; + // uint e[4]; + // uint f; + // }; + // + // [[vk::binding(0, 0)]] + // RWStructuredBuffer foo1; + // + // [[vk::binding(0, 1)]] + // RWStructuredBuffer foo2; + // + // [shader("compute")] + // void main() { + // foo1[0].b = foo2[0].e; + // } + char const *cs_source = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %foo1 %foo2 + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_uint_int_4 ArrayStride 4 + OpMemberDecorate %_Array_std430_uint4 0 Offset 0 + OpMemberDecorate %Bar1_std430 0 Offset 0 + OpMemberDecorate %Bar1_std430 1 Offset 16 + OpMemberDecorate %Bar1_std430 2 Offset 32 + OpDecorate %_runtimearr_Bar1_std430 ArrayStride 48 + OpDecorate %RWStructuredBuffer Block + OpMemberDecorate %RWStructuredBuffer 0 Offset 0 + OpDecorate %foo1 Binding 0 + OpDecorate %foo1 DescriptorSet 0 + OpMemberDecorate %Bar2_std430 0 Offset 0 + OpMemberDecorate %Bar2_std430 1 Offset 16 + OpMemberDecorate %Bar2_std430 2 Offset 32 + OpDecorate %_runtimearr_Bar2_std430 ArrayStride 48 + OpDecorate %RWStructuredBuffer_0 Block + OpMemberDecorate %RWStructuredBuffer_0 0 Offset 0 + OpDecorate %foo2 Binding 0 + OpDecorate %foo2 DescriptorSet 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %int_4 = OpConstant %int 4 + %int_0 = OpConstant %int 0 + %v4uint = OpTypeVector %uint 4 +%_arr_uint_int_4 = OpTypeArray %uint %int_4 +%_Array_std430_uint4 = OpTypeStruct %_arr_uint_int_4 +%Bar1_std430 = OpTypeStruct %v4uint %_Array_std430_uint4 %uint +%_ptr_StorageBuffer_Bar1_std430 = OpTypePointer StorageBuffer %Bar1_std430 +%_runtimearr_Bar1_std430 = OpTypeRuntimeArray %Bar1_std430 +%RWStructuredBuffer = OpTypeStruct %_runtimearr_Bar1_std430 +%_ptr_StorageBuffer_RWStructuredBuffer = OpTypePointer StorageBuffer %RWStructuredBuffer + %int_1 = OpConstant %int 1 +%_ptr_StorageBuffer__Array_std430_uint4 = OpTypePointer StorageBuffer %_Array_std430_uint4 +%Bar2_std430 = OpTypeStruct %v4uint %_Array_std430_uint4 %uint +%_ptr_StorageBuffer_Bar2_std430 = OpTypePointer StorageBuffer %Bar2_std430 +%_runtimearr_Bar2_std430 = OpTypeRuntimeArray %Bar2_std430 +%RWStructuredBuffer_0 = OpTypeStruct %_runtimearr_Bar2_std430 +%_ptr_StorageBuffer_RWStructuredBuffer_0 = OpTypePointer StorageBuffer %RWStructuredBuffer_0 + %foo1 = OpVariable %_ptr_StorageBuffer_RWStructuredBuffer StorageBuffer + %foo2 = OpVariable %_ptr_StorageBuffer_RWStructuredBuffer_0 StorageBuffer + %main = OpFunction %void None %3 + %4 = OpLabel + %17 = OpAccessChain %_ptr_StorageBuffer_Bar1_std430 %foo1 %int_0 %int_0 + %24 = OpAccessChain %_ptr_StorageBuffer__Array_std430_uint4 %17 %int_1 + %27 = OpAccessChain %_ptr_StorageBuffer_Bar2_std430 %foo2 %int_0 %int_0 + %32 = OpAccessChain %_ptr_StorageBuffer__Array_std430_uint4 %27 %int_1 + %33 = OpLoad %_Array_std430_uint4 %32 + %64 = OpCompositeExtract %_arr_uint_int_4 %33 0 + %65 = OpCompositeExtract %uint %64 0 + %66 = OpCompositeExtract %uint %64 1 + %67 = OpCompositeExtract %uint %64 2 + %68 = OpCompositeExtract %uint %64 3 + %110 = OpCompositeConstruct %_arr_uint_int_4 %65 %66 %67 %68 + %83 = OpCompositeConstruct %_Array_std430_uint4 %110 + OpStore %24 %83 + OpReturn + OpFunctionEnd + )"; + + CreateComputePipelineHelper pipe(*this); + pipe.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, + {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}; + pipe.cs_ = std::make_unique(this, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM); + pipe.CreateComputePipeline(); + + vkt::Buffer in_buffer(*m_device, 256, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps); + pipe.descriptor_set_->WriteDescriptorBufferInfo(0, in_buffer.handle(), 0, 64, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + pipe.descriptor_set_->WriteDescriptorBufferInfo(1, in_buffer.handle(), 64, 64, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + pipe.descriptor_set_->UpdateDescriptorSets(); + + m_command_buffer.Begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle()); + vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_.handle(), 0, 1, + &pipe.descriptor_set_->set_, 0, nullptr); + vk::CmdDispatch(m_command_buffer.handle(), 1, 1, 1); + m_command_buffer.End(); + + m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-storageBuffers-06936"); + m_default_queue->Submit(m_command_buffer); + m_default_queue->Wait(); + m_errorMonitor->VerifyFound(); +} + +// TODO - Handle tracking copy size of Arrays +TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, DISABLED_ArrayCopySlang) { + // struct Bar { + // uint4 a; + // uint pad; + // uint b[4]; // b[3] is OOB + // uint c; // offset 36 + // }; + // + // [[vk::binding(0, 0)]] + // RWStructuredBuffer foo; + // + // [shader("compute")] + // void main() { + // uint d[4] = {4, 5, 6, 7}; + // foo[0].b = d; + // } + char const *cs_source = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %foo + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_uint_int_4 ArrayStride 4 + OpMemberDecorate %_Array_std430_uint4 0 Offset 0 + OpMemberDecorate %Bar_std430 0 Offset 0 + OpMemberDecorate %Bar_std430 1 Offset 16 + OpMemberDecorate %Bar_std430 2 Offset 20 + OpMemberDecorate %Bar_std430 3 Offset 36 + OpDecorate %_runtimearr_Bar_std430 ArrayStride 48 + OpDecorate %RWStructuredBuffer Block + OpMemberDecorate %RWStructuredBuffer 0 Offset 0 + OpDecorate %foo Binding 0 + OpDecorate %foo DescriptorSet 0 + OpDecorate %_arr_uint_int_4_0 ArrayStride 4 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %v4uint = OpTypeVector %uint 4 + %int_4 = OpConstant %int 4 +%_arr_uint_int_4 = OpTypeArray %uint %int_4 +%_Array_std430_uint4 = OpTypeStruct %_arr_uint_int_4 + %Bar_std430 = OpTypeStruct %v4uint %uint %_Array_std430_uint4 %uint +%_ptr_StorageBuffer_Bar_std430 = OpTypePointer StorageBuffer %Bar_std430 +%_runtimearr_Bar_std430 = OpTypeRuntimeArray %Bar_std430 +%RWStructuredBuffer = OpTypeStruct %_runtimearr_Bar_std430 +%_ptr_StorageBuffer_RWStructuredBuffer = OpTypePointer StorageBuffer %RWStructuredBuffer + %int_2 = OpConstant %int 2 +%_ptr_StorageBuffer__Array_std430_uint4 = OpTypePointer StorageBuffer %_Array_std430_uint4 +%_arr_uint_int_4_0 = OpTypeArray %uint %int_4 + %25 = OpTypeFunction %_Array_std430_uint4 %_arr_uint_int_4_0 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_6 = OpConstant %uint 6 + %uint_7 = OpConstant %uint 7 + %35 = OpConstantComposite %_arr_uint_int_4_0 %uint_4 %uint_5 %uint_6 %uint_7 + %foo = OpVariable %_ptr_StorageBuffer_RWStructuredBuffer StorageBuffer + %main = OpFunction %void None %3 + %4 = OpLabel + %14 = OpAccessChain %_ptr_StorageBuffer_Bar_std430 %foo %int_0 %int_0 + %21 = OpAccessChain %_ptr_StorageBuffer__Array_std430_uint4 %14 %int_2 + %22 = OpFunctionCall %_Array_std430_uint4 %packStorage %35 + OpStore %21 %22 + OpReturn + OpFunctionEnd +%packStorage = OpFunction %_Array_std430_uint4 None %25 + %26 = OpFunctionParameter %_arr_uint_int_4_0 + %27 = OpLabel + %28 = OpCompositeExtract %uint %26 0 + %29 = OpCompositeExtract %uint %26 1 + %30 = OpCompositeExtract %uint %26 2 + %31 = OpCompositeExtract %uint %26 3 + %32 = OpCompositeConstruct %_arr_uint_int_4 %28 %29 %30 %31 + %33 = OpCompositeConstruct %_Array_std430_uint4 %32 + OpReturnValue %33 + OpFunctionEnd + )"; + ComputeStorageBufferTest(cs_source, false, 32, "VUID-vkCmdDispatch-storageBuffers-06936"); +} + +// TODO - Handle tracking copy size of Structs +TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, DISABLED_StructCopyGLSL) { + char const *cs_source = R"glsl( + #version 450 + + struct Bar { + uint x; + uint y; + uint z[2]; + }; + + layout(set = 0, binding = 0, std430) buffer foo { + uvec4 a; + uint padding; + Bar b; // size 16 at offset 20 + uint c; + }; + + void main() { + Bar new_bar; + b = new_bar; + } + )glsl"; + ComputeStorageBufferTest(cs_source, true, 32, "VUID-vkCmdDispatch-storageBuffers-06936"); +} + +// TODO - Handle tracking copy size of Structs +TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, DISABLED_MStructopySlang) { + // struct Bar { + // uint x; + // uint y; + // uint z[2]; + // }; + // + // struct FooBuffer { + // float4 a; + // Bar b; + // uint c; + // }; + // + // [[vk::binding(0, 0)]] + // RWStructuredBuffer foo; + // + // [shader("compute")] + // void main() { + // foo[1].b = foo[0].b; + // } + char const *cs_source = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %foo + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_uint_int_2 ArrayStride 4 + OpMemberDecorate %_Array_std430_uint2 0 Offset 0 + OpMemberDecorate %Bar_std430 0 Offset 0 + OpMemberDecorate %Bar_std430 1 Offset 4 + OpMemberDecorate %Bar_std430 2 Offset 8 + OpMemberDecorate %FooBuffer_std430 0 Offset 0 + OpMemberDecorate %FooBuffer_std430 1 Offset 16 + OpMemberDecorate %FooBuffer_std430 2 Offset 32 + OpDecorate %_runtimearr_FooBuffer_std430 ArrayStride 48 + OpDecorate %RWStructuredBuffer Block + OpMemberDecorate %RWStructuredBuffer 0 Offset 0 + OpDecorate %foo Binding 0 + OpDecorate %foo DescriptorSet 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %int_2 = OpConstant %int 2 +%_arr_uint_int_2 = OpTypeArray %uint %int_2 +%_Array_std430_uint2 = OpTypeStruct %_arr_uint_int_2 + %Bar_std430 = OpTypeStruct %uint %uint %_Array_std430_uint2 +%FooBuffer_std430 = OpTypeStruct %v4float %Bar_std430 %uint +%_ptr_StorageBuffer_FooBuffer_std430 = OpTypePointer StorageBuffer %FooBuffer_std430 +%_runtimearr_FooBuffer_std430 = OpTypeRuntimeArray %FooBuffer_std430 +%RWStructuredBuffer = OpTypeStruct %_runtimearr_FooBuffer_std430 +%_ptr_StorageBuffer_RWStructuredBuffer = OpTypePointer StorageBuffer %RWStructuredBuffer +%_ptr_StorageBuffer_Bar_std430 = OpTypePointer StorageBuffer %Bar_std430 + %foo = OpVariable %_ptr_StorageBuffer_RWStructuredBuffer StorageBuffer + %main = OpFunction %void None %3 + %4 = OpLabel + %17 = OpAccessChain %_ptr_StorageBuffer_FooBuffer_std430 %foo %int_0 %int_1 + %23 = OpAccessChain %_ptr_StorageBuffer_Bar_std430 %17 %int_1 + %24 = OpAccessChain %_ptr_StorageBuffer_FooBuffer_std430 %foo %int_0 %int_0 + %25 = OpAccessChain %_ptr_StorageBuffer_Bar_std430 %24 %int_1 + %26 = OpLoad %Bar_std430 %25 + %80 = OpCompositeExtract %uint %26 0 + %81 = OpCompositeExtract %uint %26 1 + %82 = OpCompositeExtract %_Array_std430_uint2 %26 2 + %87 = OpCompositeExtract %_arr_uint_int_2 %82 0 + %88 = OpCompositeExtract %uint %87 0 + %89 = OpCompositeExtract %uint %87 1 + %163 = OpCompositeConstruct %_arr_uint_int_2 %88 %89 + %149 = OpCompositeConstruct %_Array_std430_uint2 %163 + %121 = OpCompositeConstruct %Bar_std430 %80 %81 %149 + OpStore %23 %121 + OpReturn + OpFunctionEnd + )"; + ComputeStorageBufferTest(cs_source, false, 72, "VUID-vkCmdDispatch-storageBuffers-06936"); +} + +TEST_F(NegativeGpuAVDescriptorClassGeneralBuffer, ChainOfAccessChains) { + TEST_DESCRIPTION("Slang can sometimes generate a single OpAccessChain like GLSL/HLSL"); + + // struct Bar { + // uint a; + // uint d[4]; // this really ends up being a 1 element struct with the array in it + // }; + // + // [[vk::binding(0, 0)]] + // RWStructuredBuffer foo; // 20 byte stride + // + // [shader("compute")] + // void main() { + // foo[1].d[3] = 44; + // } + char const *cs_source = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %foo + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_uint_int_4 ArrayStride 4 + OpMemberDecorate %_Array_std430_uint4 0 Offset 0 + OpMemberDecorate %Bar_std430 0 Offset 0 + OpMemberDecorate %Bar_std430 1 Offset 4 + OpDecorate %_runtimearr_Bar_std430 ArrayStride 20 + OpDecorate %RWStructuredBuffer Block + OpMemberDecorate %RWStructuredBuffer 0 Offset 0 + OpDecorate %foo Binding 0 + OpDecorate %foo DescriptorSet 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %int_4 = OpConstant %int 4 +%_arr_uint_int_4 = OpTypeArray %uint %int_4 +%_Array_std430_uint4 = OpTypeStruct %_arr_uint_int_4 + %Bar_std430 = OpTypeStruct %uint %_Array_std430_uint4 +%_ptr_StorageBuffer_Bar_std430 = OpTypePointer StorageBuffer %Bar_std430 +%_runtimearr_Bar_std430 = OpTypeRuntimeArray %Bar_std430 +%RWStructuredBuffer = OpTypeStruct %_runtimearr_Bar_std430 +%_ptr_StorageBuffer_RWStructuredBuffer = OpTypePointer StorageBuffer %RWStructuredBuffer +%_ptr_StorageBuffer__Array_std430_uint4 = OpTypePointer StorageBuffer %_Array_std430_uint4 +%_ptr_StorageBuffer__arr_uint_int_4 = OpTypePointer StorageBuffer %_arr_uint_int_4 +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %int_3 = OpConstant %int 3 + %uint_44 = OpConstant %uint 44 + %foo = OpVariable %_ptr_StorageBuffer_RWStructuredBuffer StorageBuffer + %main = OpFunction %void None %3 + %4 = OpLabel + %14 = OpAccessChain %_ptr_StorageBuffer_Bar_std430 %foo %int_0 %int_1 + %20 = OpAccessChain %_ptr_StorageBuffer__Array_std430_uint4 %14 %int_1 + %22 = OpAccessChain %_ptr_StorageBuffer__arr_uint_int_4 %20 %int_0 + %24 = OpAccessChain %_ptr_StorageBuffer_uint %22 %int_3 + OpStore %24 %uint_44 + OpReturn + OpFunctionEnd + )"; + ComputeStorageBufferTest(cs_source, false, 32, "VUID-vkCmdDispatch-storageBuffers-06936"); +} diff --git a/tests/unit/gpu_av_descriptor_class_general_buffer_positive.cpp b/tests/unit/gpu_av_descriptor_class_general_buffer_positive.cpp index 6a01cca1e59..9406e4744f9 100644 --- a/tests/unit/gpu_av_descriptor_class_general_buffer_positive.cpp +++ b/tests/unit/gpu_av_descriptor_class_general_buffer_positive.cpp @@ -15,7 +15,36 @@ #include "../framework/pipeline_helper.h" #include "../framework/descriptor_helper.h" -class PositiveGpuAVDescriptorClassGeneralBuffer : public GpuAVTest {}; +class PositiveGpuAVDescriptorClassGeneralBuffer : public GpuAVDescriptorClassGeneralBuffer {}; + +void GpuAVDescriptorClassGeneralBuffer::ComputeStorageBufferTest(const char *shader, bool is_glsl, VkDeviceSize buffer_size, + const char *expected_error) { + SetTargetApiVersion(VK_API_VERSION_1_2); + RETURN_IF_SKIP(InitGpuAvFramework()); + RETURN_IF_SKIP(InitState()); + + CreateComputePipelineHelper pipe(*this); + pipe.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}; + pipe.cs_ = std::make_unique(this, shader, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, + is_glsl ? SPV_SOURCE_GLSL : SPV_SOURCE_ASM); + pipe.CreateComputePipeline(); + + vkt::Buffer in_buffer(*m_device, buffer_size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps); + pipe.descriptor_set_->WriteDescriptorBufferInfo(0, in_buffer.handle(), 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + pipe.descriptor_set_->UpdateDescriptorSets(); + + m_command_buffer.Begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle()); + vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_.handle(), 0, 1, + &pipe.descriptor_set_->set_, 0, nullptr); + vk::CmdDispatch(m_command_buffer.handle(), 1, 1, 1); + m_command_buffer.End(); + + if (expected_error) m_errorMonitor->SetDesiredError(expected_error); + m_default_queue->Submit(m_command_buffer); + m_default_queue->Wait(); + if (expected_error) m_errorMonitor->VerifyFound(); +} TEST_F(PositiveGpuAVDescriptorClassGeneralBuffer, Basic) { AddRequiredExtensions(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); @@ -773,3 +802,442 @@ TEST_F(PositiveGpuAVDescriptorClassGeneralBuffer, RobustBuffer) { m_default_queue->Submit(m_command_buffer); m_default_queue->Wait(); } + +TEST_F(PositiveGpuAVDescriptorClassGeneralBuffer, VectorArray) { + char const *cs_source = R"glsl( + #version 450 + layout(set = 0, binding = 0) buffer foo { + uvec4 a[8]; // stride 16 + }; + void main() { + a[3].y = 44; // write at byte[52:56] + } + )glsl"; + + ComputeStorageBufferTest(cs_source, true, 64); +} + +TEST_F(PositiveGpuAVDescriptorClassGeneralBuffer, ArrayCopyGLSL) { + char const *cs_source = R"glsl( + #version 450 + layout(set = 0, binding = 0) buffer foo { + uvec4 a; + uint b[4]; + uint c; + }; + + void main() { + uint d[4] = {4, 5, 6, 7}; + b = d; + } + )glsl"; + ComputeStorageBufferTest(cs_source, true, 32); +} + +TEST_F(PositiveGpuAVDescriptorClassGeneralBuffer, ArrayCopySlang) { + // struct Bar { + // uint4 a; + // uint b[4]; + // uint c; + // }; + // + // [[vk::binding(0, 0)]] + // RWStructuredBuffer foo; + // + // [shader("compute")] + // void main() { + // uint d[4] = {4, 5, 6, 7}; + // foo[0].b = d; + // } + char const *cs_source = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %foo + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_uint_int_4 ArrayStride 4 + OpMemberDecorate %_Array_std430_uint4 0 Offset 0 + OpMemberDecorate %Bar_std430 0 Offset 0 + OpMemberDecorate %Bar_std430 1 Offset 16 + OpMemberDecorate %Bar_std430 2 Offset 32 + OpDecorate %_runtimearr_Bar_std430 ArrayStride 48 + OpDecorate %RWStructuredBuffer Block + OpMemberDecorate %RWStructuredBuffer 0 Offset 0 + OpDecorate %foo Binding 0 + OpDecorate %foo DescriptorSet 0 + OpDecorate %_arr_uint_int_4_0 ArrayStride 4 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %v4uint = OpTypeVector %uint 4 + %int_4 = OpConstant %int 4 +%_arr_uint_int_4 = OpTypeArray %uint %int_4 +%_Array_std430_uint4 = OpTypeStruct %_arr_uint_int_4 + %Bar_std430 = OpTypeStruct %v4uint %_Array_std430_uint4 %uint +%_ptr_StorageBuffer_Bar_std430 = OpTypePointer StorageBuffer %Bar_std430 +%_runtimearr_Bar_std430 = OpTypeRuntimeArray %Bar_std430 +%RWStructuredBuffer = OpTypeStruct %_runtimearr_Bar_std430 +%_ptr_StorageBuffer_RWStructuredBuffer = OpTypePointer StorageBuffer %RWStructuredBuffer + %int_1 = OpConstant %int 1 +%_ptr_StorageBuffer__Array_std430_uint4 = OpTypePointer StorageBuffer %_Array_std430_uint4 +%_arr_uint_int_4_0 = OpTypeArray %uint %int_4 + %25 = OpTypeFunction %_Array_std430_uint4 %_arr_uint_int_4_0 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_6 = OpConstant %uint 6 + %uint_7 = OpConstant %uint 7 + %35 = OpConstantComposite %_arr_uint_int_4_0 %uint_4 %uint_5 %uint_6 %uint_7 + %foo = OpVariable %_ptr_StorageBuffer_RWStructuredBuffer StorageBuffer + %main = OpFunction %void None %3 + %4 = OpLabel + %14 = OpAccessChain %_ptr_StorageBuffer_Bar_std430 %foo %int_0 %int_0 + %21 = OpAccessChain %_ptr_StorageBuffer__Array_std430_uint4 %14 %int_1 + %22 = OpFunctionCall %_Array_std430_uint4 %packStorage %35 + OpStore %21 %22 + OpReturn + OpFunctionEnd +%packStorage = OpFunction %_Array_std430_uint4 None %25 + %26 = OpFunctionParameter %_arr_uint_int_4_0 + %27 = OpLabel + %28 = OpCompositeExtract %uint %26 0 + %29 = OpCompositeExtract %uint %26 1 + %30 = OpCompositeExtract %uint %26 2 + %31 = OpCompositeExtract %uint %26 3 + %32 = OpCompositeConstruct %_arr_uint_int_4 %28 %29 %30 %31 + %33 = OpCompositeConstruct %_Array_std430_uint4 %32 + OpReturnValue %33 + OpFunctionEnd + )"; + ComputeStorageBufferTest(cs_source, false, 32); +} + +TEST_F(PositiveGpuAVDescriptorClassGeneralBuffer, ArrayCopyTwoBindingsGLSL) { + SetTargetApiVersion(VK_API_VERSION_1_2); + RETURN_IF_SKIP(InitGpuAvFramework()); + RETURN_IF_SKIP(InitState()); + + char const *cs_source = R"glsl( + #version 450 + layout(set = 0, binding = 0, std430) buffer foo1 { + uvec4 a; + uint b[4]; + uint c; + }; + + layout(set = 0, binding = 1, std430) buffer foo2 { + uint d; + uint e[4]; + uvec2 f; + }; + + void main() { + b = e; + } + )glsl"; + + CreateComputePipelineHelper pipe(*this); + pipe.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, + {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}; + pipe.cs_ = std::make_unique(this, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2); + pipe.CreateComputePipeline(); + + vkt::Buffer in_buffer(*m_device, 256, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps); + pipe.descriptor_set_->WriteDescriptorBufferInfo(0, in_buffer.handle(), 0, 64, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + pipe.descriptor_set_->WriteDescriptorBufferInfo(1, in_buffer.handle(), 64, 64, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + pipe.descriptor_set_->UpdateDescriptorSets(); + + m_command_buffer.Begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle()); + vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_.handle(), 0, 1, + &pipe.descriptor_set_->set_, 0, nullptr); + vk::CmdDispatch(m_command_buffer.handle(), 1, 1, 1); + m_command_buffer.End(); + + m_default_queue->Submit(m_command_buffer); + m_default_queue->Wait(); +} + +TEST_F(PositiveGpuAVDescriptorClassGeneralBuffer, ArrayCopyTwoBindingsSlang) { + SetTargetApiVersion(VK_API_VERSION_1_2); + RETURN_IF_SKIP(InitGpuAvFramework()); + RETURN_IF_SKIP(InitState()); + + // struct Bar1 { + // uint4 a; + // uint b[4]; + // uint c; + // }; + // + // struct Bar2 { + // uint4 d; + // uint e[4]; + // uint f; + // }; + // + // [[vk::binding(0, 0)]] + // RWStructuredBuffer foo1; + // + // [[vk::binding(1, 0)]] + // RWStructuredBuffer foo2; + // + // [shader("compute")] + // void main() { + // foo1[0].b = foo2[0].e; + // } + char const *cs_source = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %foo1 %foo2 + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_uint_int_4 ArrayStride 4 + OpMemberDecorate %_Array_std430_uint4 0 Offset 0 + OpMemberDecorate %Bar1_std430 0 Offset 0 + OpMemberDecorate %Bar1_std430 1 Offset 16 + OpMemberDecorate %Bar1_std430 2 Offset 32 + OpDecorate %_runtimearr_Bar1_std430 ArrayStride 48 + OpDecorate %RWStructuredBuffer Block + OpMemberDecorate %RWStructuredBuffer 0 Offset 0 + OpDecorate %foo1 Binding 0 + OpDecorate %foo1 DescriptorSet 0 + OpMemberDecorate %Bar2_std430 0 Offset 0 + OpMemberDecorate %Bar2_std430 1 Offset 16 + OpMemberDecorate %Bar2_std430 2 Offset 32 + OpDecorate %_runtimearr_Bar2_std430 ArrayStride 48 + OpDecorate %RWStructuredBuffer_0 Block + OpMemberDecorate %RWStructuredBuffer_0 0 Offset 0 + OpDecorate %foo2 Binding 1 + OpDecorate %foo2 DescriptorSet 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %int_4 = OpConstant %int 4 + %int_0 = OpConstant %int 0 + %v4uint = OpTypeVector %uint 4 +%_arr_uint_int_4 = OpTypeArray %uint %int_4 +%_Array_std430_uint4 = OpTypeStruct %_arr_uint_int_4 +%Bar1_std430 = OpTypeStruct %v4uint %_Array_std430_uint4 %uint +%_ptr_StorageBuffer_Bar1_std430 = OpTypePointer StorageBuffer %Bar1_std430 +%_runtimearr_Bar1_std430 = OpTypeRuntimeArray %Bar1_std430 +%RWStructuredBuffer = OpTypeStruct %_runtimearr_Bar1_std430 +%_ptr_StorageBuffer_RWStructuredBuffer = OpTypePointer StorageBuffer %RWStructuredBuffer + %int_1 = OpConstant %int 1 +%_ptr_StorageBuffer__Array_std430_uint4 = OpTypePointer StorageBuffer %_Array_std430_uint4 +%Bar2_std430 = OpTypeStruct %v4uint %_Array_std430_uint4 %uint +%_ptr_StorageBuffer_Bar2_std430 = OpTypePointer StorageBuffer %Bar2_std430 +%_runtimearr_Bar2_std430 = OpTypeRuntimeArray %Bar2_std430 +%RWStructuredBuffer_0 = OpTypeStruct %_runtimearr_Bar2_std430 +%_ptr_StorageBuffer_RWStructuredBuffer_0 = OpTypePointer StorageBuffer %RWStructuredBuffer_0 + %foo1 = OpVariable %_ptr_StorageBuffer_RWStructuredBuffer StorageBuffer + %foo2 = OpVariable %_ptr_StorageBuffer_RWStructuredBuffer_0 StorageBuffer + %main = OpFunction %void None %3 + %4 = OpLabel + %17 = OpAccessChain %_ptr_StorageBuffer_Bar1_std430 %foo1 %int_0 %int_0 + %24 = OpAccessChain %_ptr_StorageBuffer__Array_std430_uint4 %17 %int_1 + %27 = OpAccessChain %_ptr_StorageBuffer_Bar2_std430 %foo2 %int_0 %int_0 + %32 = OpAccessChain %_ptr_StorageBuffer__Array_std430_uint4 %27 %int_1 + %33 = OpLoad %_Array_std430_uint4 %32 + %64 = OpCompositeExtract %_arr_uint_int_4 %33 0 + %65 = OpCompositeExtract %uint %64 0 + %66 = OpCompositeExtract %uint %64 1 + %67 = OpCompositeExtract %uint %64 2 + %68 = OpCompositeExtract %uint %64 3 + %110 = OpCompositeConstruct %_arr_uint_int_4 %65 %66 %67 %68 + %83 = OpCompositeConstruct %_Array_std430_uint4 %110 + OpStore %24 %83 + OpReturn + OpFunctionEnd + )"; + + CreateComputePipelineHelper pipe(*this); + pipe.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, + {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}; + pipe.cs_ = std::make_unique(this, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM); + pipe.CreateComputePipeline(); + + vkt::Buffer in_buffer(*m_device, 256, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps); + pipe.descriptor_set_->WriteDescriptorBufferInfo(0, in_buffer.handle(), 0, 64, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + pipe.descriptor_set_->WriteDescriptorBufferInfo(1, in_buffer.handle(), 64, 64, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + pipe.descriptor_set_->UpdateDescriptorSets(); + + m_command_buffer.Begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle()); + vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_.handle(), 0, 1, + &pipe.descriptor_set_->set_, 0, nullptr); + vk::CmdDispatch(m_command_buffer.handle(), 1, 1, 1); + m_command_buffer.End(); + + m_default_queue->Submit(m_command_buffer); + m_default_queue->Wait(); +} + +TEST_F(PositiveGpuAVDescriptorClassGeneralBuffer, StructCopyGLSL) { + char const *cs_source = R"glsl( + #version 450 + + struct Bar { + uint x; + uint y; + uint z[2]; + }; + + layout(set = 0, binding = 0, std430) buffer foo { + uvec4 a; + Bar b; // size 16 at offset 16 + uint c; + }; + + void main() { + Bar new_bar; + b = new_bar; + } + )glsl"; + ComputeStorageBufferTest(cs_source, true, 32); +} + +TEST_F(PositiveGpuAVDescriptorClassGeneralBuffer, StructCopySlang) { + // struct Bar { + // uint x; + // uint y; + // uint z[2]; + // }; + // + // struct FooBuffer { + // float4 a; + // Bar b; + // uint c; + // }; + // + // [[vk::binding(0, 0)]] + // RWStructuredBuffer foo; + // + // [shader("compute")] + // void main() { + // foo[1].b = foo[0].b; + // } + char const *cs_source = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %foo + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_uint_int_2 ArrayStride 4 + OpMemberDecorate %_Array_std430_uint2 0 Offset 0 + OpMemberDecorate %Bar_std430 0 Offset 0 + OpMemberDecorate %Bar_std430 1 Offset 4 + OpMemberDecorate %Bar_std430 2 Offset 8 + OpMemberDecorate %FooBuffer_std430 0 Offset 0 + OpMemberDecorate %FooBuffer_std430 1 Offset 16 + OpMemberDecorate %FooBuffer_std430 2 Offset 32 + OpDecorate %_runtimearr_FooBuffer_std430 ArrayStride 48 + OpDecorate %RWStructuredBuffer Block + OpMemberDecorate %RWStructuredBuffer 0 Offset 0 + OpDecorate %foo Binding 0 + OpDecorate %foo DescriptorSet 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %int_2 = OpConstant %int 2 +%_arr_uint_int_2 = OpTypeArray %uint %int_2 +%_Array_std430_uint2 = OpTypeStruct %_arr_uint_int_2 + %Bar_std430 = OpTypeStruct %uint %uint %_Array_std430_uint2 +%FooBuffer_std430 = OpTypeStruct %v4float %Bar_std430 %uint +%_ptr_StorageBuffer_FooBuffer_std430 = OpTypePointer StorageBuffer %FooBuffer_std430 +%_runtimearr_FooBuffer_std430 = OpTypeRuntimeArray %FooBuffer_std430 +%RWStructuredBuffer = OpTypeStruct %_runtimearr_FooBuffer_std430 +%_ptr_StorageBuffer_RWStructuredBuffer = OpTypePointer StorageBuffer %RWStructuredBuffer +%_ptr_StorageBuffer_Bar_std430 = OpTypePointer StorageBuffer %Bar_std430 + %foo = OpVariable %_ptr_StorageBuffer_RWStructuredBuffer StorageBuffer + %main = OpFunction %void None %3 + %4 = OpLabel + %17 = OpAccessChain %_ptr_StorageBuffer_FooBuffer_std430 %foo %int_0 %int_1 + %23 = OpAccessChain %_ptr_StorageBuffer_Bar_std430 %17 %int_1 + %24 = OpAccessChain %_ptr_StorageBuffer_FooBuffer_std430 %foo %int_0 %int_0 + %25 = OpAccessChain %_ptr_StorageBuffer_Bar_std430 %24 %int_1 + %26 = OpLoad %Bar_std430 %25 + %80 = OpCompositeExtract %uint %26 0 + %81 = OpCompositeExtract %uint %26 1 + %82 = OpCompositeExtract %_Array_std430_uint2 %26 2 + %87 = OpCompositeExtract %_arr_uint_int_2 %82 0 + %88 = OpCompositeExtract %uint %87 0 + %89 = OpCompositeExtract %uint %87 1 + %163 = OpCompositeConstruct %_arr_uint_int_2 %88 %89 + %149 = OpCompositeConstruct %_Array_std430_uint2 %163 + %121 = OpCompositeConstruct %Bar_std430 %80 %81 %149 + OpStore %23 %121 + OpReturn + OpFunctionEnd + )"; + ComputeStorageBufferTest(cs_source, false, 80); +} + +TEST_F(PositiveGpuAVDescriptorClassGeneralBuffer, ChainOfAccessChains) { + TEST_DESCRIPTION("Slang can sometimes generate a single OpAccessChain like GLSL/HLSL"); + + // struct Bar { + // uint a; + // uint d[4]; + // }; + // + // [[vk::binding(0, 0)]] + // RWStructuredBuffer foo; // 20 byte stride + // + // [shader("compute")] + // void main() { + // foo[1].d[3] = 44; + // } + char const *cs_source = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %foo + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_uint_int_4 ArrayStride 4 + OpMemberDecorate %_Array_std430_uint4 0 Offset 0 + OpMemberDecorate %Bar_std430 0 Offset 0 + OpMemberDecorate %Bar_std430 1 Offset 4 + OpDecorate %_runtimearr_Bar_std430 ArrayStride 20 + OpDecorate %RWStructuredBuffer Block + OpMemberDecorate %RWStructuredBuffer 0 Offset 0 + OpDecorate %foo Binding 0 + OpDecorate %foo DescriptorSet 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %int_4 = OpConstant %int 4 +%_arr_uint_int_4 = OpTypeArray %uint %int_4 +%_Array_std430_uint4 = OpTypeStruct %_arr_uint_int_4 + %Bar_std430 = OpTypeStruct %uint %_Array_std430_uint4 +%_ptr_StorageBuffer_Bar_std430 = OpTypePointer StorageBuffer %Bar_std430 +%_runtimearr_Bar_std430 = OpTypeRuntimeArray %Bar_std430 +%RWStructuredBuffer = OpTypeStruct %_runtimearr_Bar_std430 +%_ptr_StorageBuffer_RWStructuredBuffer = OpTypePointer StorageBuffer %RWStructuredBuffer +%_ptr_StorageBuffer__Array_std430_uint4 = OpTypePointer StorageBuffer %_Array_std430_uint4 +%_ptr_StorageBuffer__arr_uint_int_4 = OpTypePointer StorageBuffer %_arr_uint_int_4 +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %int_3 = OpConstant %int 3 + %uint_44 = OpConstant %uint 44 + %foo = OpVariable %_ptr_StorageBuffer_RWStructuredBuffer StorageBuffer + %main = OpFunction %void None %3 + %4 = OpLabel + %14 = OpAccessChain %_ptr_StorageBuffer_Bar_std430 %foo %int_0 %int_1 + %20 = OpAccessChain %_ptr_StorageBuffer__Array_std430_uint4 %14 %int_1 + %22 = OpAccessChain %_ptr_StorageBuffer__arr_uint_int_4 %20 %int_0 + %24 = OpAccessChain %_ptr_StorageBuffer_uint %22 %int_3 + OpStore %24 %uint_44 + OpReturn + OpFunctionEnd + )"; + ComputeStorageBufferTest(cs_source, false, 48); +}