From dac5088de7a2f6d6049b3e5f95aedd76639112b9 Mon Sep 17 00:00:00 2001 From: spencer-lunarg Date: Fri, 16 Aug 2024 08:08:51 -0400 Subject: [PATCH] gpu: Add NonBindlessOOBBuffer Pass --- BUILD.gn | 4 + layers/core_checks/cc_descriptor.cpp | 2 +- .../gpu_shader_instrumentor.cpp | 43 +++- .../instrumentation/gpu_shader_instrumentor.h | 4 +- .../instrumentation/gpuav_instrumentation.cpp | 56 +++++ .../instrumentation/gpuav_instrumentation.h | 3 + layers/gpu/shaders/gpu_error_codes.h | 7 + layers/gpu/shaders/gpu_error_header.h | 9 + .../non_bindless_oob_buffer.comp | 135 +++++++++++ layers/gpu/spirv/CMakeLists.txt | 4 + layers/gpu/spirv/bindless_descriptor_pass.cpp | 189 +--------------- layers/gpu/spirv/bindless_descriptor_pass.h | 3 - layers/gpu/spirv/link.h | 1 + layers/gpu/spirv/module.cpp | 28 ++- layers/gpu/spirv/module.h | 8 + .../spirv/non_bindless_oob_buffer_pass.cpp | 212 ++++++++++++++++++ .../gpu/spirv/non_bindless_oob_buffer_pass.h | 50 +++++ layers/gpu/spirv/pass.cpp | 144 ++++++++++++ layers/gpu/spirv/pass.h | 2 + layers/gpu/spirv/type_manager.cpp | 42 ++++ layers/gpu/spirv/type_manager.h | 2 + layers/state_tracker/descriptor_sets.cpp | 4 +- layers/state_tracker/descriptor_sets.h | 13 +- ...mentation_non_bindless_oob_buffer_comp.cpp | 167 ++++++++++++++ ...rumentation_non_bindless_oob_buffer_comp.h | 30 +++ scripts/generate_source.py | 2 + tests/spirv/instrumentation.cpp | 10 + tests/unit/descriptors.cpp | 47 ++++ tests/unit/gpu_av_oob.cpp | 54 +++++ 29 files changed, 1073 insertions(+), 202 deletions(-) create mode 100644 layers/gpu/shaders/instrumentation/non_bindless_oob_buffer.comp create mode 100644 layers/gpu/spirv/non_bindless_oob_buffer_pass.cpp create mode 100644 layers/gpu/spirv/non_bindless_oob_buffer_pass.h create mode 100644 layers/vulkan/generated/instrumentation_non_bindless_oob_buffer_comp.cpp create mode 100644 layers/vulkan/generated/instrumentation_non_bindless_oob_buffer_comp.h diff --git a/BUILD.gn b/BUILD.gn index 8d04f68ece9..b0229f93143 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -189,6 +189,8 @@ vvl_sources = [ "layers/gpu/shaders/gpu_shaders_constants.h", "layers/gpu/spirv/bindless_descriptor_pass.cpp", "layers/gpu/spirv/bindless_descriptor_pass.h", + "layers/gpu/spirv/non_bindless_oob_buffer_pass.cpp", + "layers/gpu/spirv/non_bindless_oob_buffer_pass.h", "layers/gpu/spirv/buffer_device_address_pass.cpp", "layers/gpu/spirv/buffer_device_address_pass.h", "layers/gpu/spirv/function_basic_block.cpp", @@ -352,6 +354,8 @@ vvl_sources = [ "layers/vulkan/generated/gpu_av_shader_hash.h", "layers/vulkan/generated/instrumentation_bindless_descriptor_comp.cpp", "layers/vulkan/generated/instrumentation_bindless_descriptor_comp.h", + "layers/vulkan/generated/instrumentation_non_bindless_oob_buffer_comp.cpp", + "layers/vulkan/generated/instrumentation_non_bindless_oob_buffer_comp.h", "layers/vulkan/generated/instrumentation_buffer_device_address_comp.cpp", "layers/vulkan/generated/instrumentation_buffer_device_address_comp.h", "layers/vulkan/generated/instrumentation_ray_query_comp.cpp", diff --git a/layers/core_checks/cc_descriptor.cpp b/layers/core_checks/cc_descriptor.cpp index 71f62366398..e2d8bd0ba7a 100644 --- a/layers/core_checks/cc_descriptor.cpp +++ b/layers/core_checks/cc_descriptor.cpp @@ -1813,7 +1813,7 @@ bool CoreChecks::ValidateWriteUpdate(const DescriptorSet &dst_set, const VkWrite } const auto *used_handle = dst_set.InUse(); - if (used_handle && !(dest->IsBindless())) { + if (used_handle && !vvl::IsBindless(dest->binding_flags)) { skip |= LogError("VUID-vkUpdateDescriptorSets-None-03047", objlist, dst_binding_loc, "(%" PRIu32 ") was created with %s, but %s is in use by %s.", update.dstBinding, string_VkDescriptorBindingFlags(dest->binding_flags).c_str(), FormatHandle(update.dstSet).c_str(), diff --git a/layers/gpu/instrumentation/gpu_shader_instrumentor.cpp b/layers/gpu/instrumentation/gpu_shader_instrumentor.cpp index b51ae37e7f3..eca217c75cc 100644 --- a/layers/gpu/instrumentation/gpu_shader_instrumentor.cpp +++ b/layers/gpu/instrumentation/gpu_shader_instrumentor.cpp @@ -23,6 +23,7 @@ #include "gpu/shaders/gpu_error_codes.h" #include "spirv-tools/optimizer.hpp" #include "utils/vk_layer_utils.h" +#include "state_tracker/descriptor_sets.h" #include #include @@ -499,10 +500,24 @@ void GpuShaderInstrumentor::PreCallRecordShaderObjectInstrumentation( unique_shader_id = unique_shader_module_id_++; } + bool has_bindless_descriptors = false; + for (const auto [layout_i, set_layout] : vvl::enumerate(create_info.pSetLayouts, create_info.setLayoutCount)) { + if (auto descriptor_set = Get(*set_layout)) { + for (uint32_t i = 0; i < descriptor_set->GetBindingCount(); i++) { + const VkDescriptorBindingFlags flags = descriptor_set->GetDescriptorBindingFlagsFromIndex(i); + if (vvl::IsBindless(flags)) { + has_bindless_descriptors = true; + break; + } + } + if (has_bindless_descriptors) break; + } + } + if (!cached) { pass = InstrumentShader( vvl::make_span(static_cast(create_info.pCode), create_info.codeSize / sizeof(uint32_t)), - unique_shader_id, create_info_loc, instrumented_spirv); + unique_shader_id, has_bindless_descriptors, create_info_loc, instrumented_spirv); } if (cached || pass) { @@ -939,6 +954,24 @@ void GpuShaderInstrumentor::PreCallRecordPipelineCreationShaderInstrumentation( if (!instrument_shader) return; + // TODO - measure and see if would be better to make a gpuav subclasses of pipeline layout and store this information once there + // (not sure how much pipeline layout re-usage there is) + bool has_bindless_descriptors = false; + if (pipeline_layout) { + for (const auto &descriptor_set : pipeline_layout->set_layouts) { + if (descriptor_set) { + for (uint32_t i = 0; i < descriptor_set->GetBindingCount(); i++) { + const VkDescriptorBindingFlags flags = descriptor_set->GetDescriptorBindingFlagsFromIndex(i); + if (vvl::IsBindless(flags)) { + has_bindless_descriptors = true; + break; + } + } + } + if (has_bindless_descriptors) break; + } + } + for (uint32_t i = 0; i < static_cast(pipeline_state.stage_states.size()); ++i) { const auto &stage_state = pipeline_state.stage_states[i]; auto module_state = std::const_pointer_cast(stage_state.module_state); @@ -980,7 +1013,8 @@ void GpuShaderInstrumentor::PreCallRecordPipelineCreationShaderInstrumentation( unique_shader_id = unique_shader_module_id_++; } if (!cached) { - pass = InstrumentShader(module_state->spirv->words_, unique_shader_id, loc, instrumented_spirv); + pass = + InstrumentShader(module_state->spirv->words_, unique_shader_id, has_bindless_descriptors, loc, instrumented_spirv); } if (cached || pass) { shader_instrumentation_metadata.spirv_unique_id_map[i] = unique_shader_id; @@ -1135,7 +1169,8 @@ static bool GpuValidateShader(const std::vector &input, bool SetRelaxB // Call the SPIR-V Optimizer to run the instrumentation pass on the shader. bool GpuShaderInstrumentor::InstrumentShader(const vvl::span &input_spirv, uint32_t unique_shader_id, - const Location &loc, std::vector &out_instrumented_spirv) { + bool has_bindless_descriptors, const Location &loc, + std::vector &out_instrumented_spirv) { if (input_spirv[0] != spv::MagicNumber) return false; if (gpuav_settings.debug_dump_instrumented_shaders) { @@ -1153,6 +1188,7 @@ bool GpuShaderInstrumentor::InstrumentShader(const vvl::span &in module_settings.max_instrumented_count = gpuav_settings.debug_max_instrumented_count; module_settings.support_int64 = enabled_features.shaderInt64; module_settings.support_memory_model_device_scope = enabled_features.vulkanMemoryModelDeviceScope; + module_settings.has_bindless_descriptors = has_bindless_descriptors; gpu::spirv::Module module(input_spirv, debug_report, module_settings); @@ -1167,6 +1203,7 @@ bool GpuShaderInstrumentor::InstrumentShader(const vvl::span &in // If descriptor indexing is enabled, enable length checks and updated descriptor checks if (shader_instrumentation.bindless_descriptor) { modified |= module.RunPassBindlessDescriptor(); + modified |= module.RunPassNonBindlessOOBBuffer(); } if (shader_instrumentation.buffer_device_address) { diff --git a/layers/gpu/instrumentation/gpu_shader_instrumentor.h b/layers/gpu/instrumentation/gpu_shader_instrumentor.h index a0cb21c649c..c999aaecb04 100644 --- a/layers/gpu/instrumentation/gpu_shader_instrumentor.h +++ b/layers/gpu/instrumentation/gpu_shader_instrumentor.h @@ -203,8 +203,8 @@ class GpuShaderInstrumentor : public ValidationStateTracker { // GPU-AV and DebugPrint are using the same way to do the actual shader instrumentation logic // Returns if shader was instrumented successfully or not - bool InstrumentShader(const vvl::span &input_spirv, uint32_t unique_shader_id, const Location &loc, - std::vector &out_instrumented_spirv); + bool InstrumentShader(const vvl::span &input_spirv, uint32_t unique_shader_id, bool has_bindless_descriptors, + const Location &loc, std::vector &out_instrumented_spirv); VkDescriptorSetLayout GetDebugDescriptorSetLayout() { return debug_desc_layout_; } diff --git a/layers/gpu/instrumentation/gpuav_instrumentation.cpp b/layers/gpu/instrumentation/gpuav_instrumentation.cpp index 1ec736345fb..c664f32b827 100644 --- a/layers/gpu/instrumentation/gpuav_instrumentation.cpp +++ b/layers/gpu/instrumentation/gpuav_instrumentation.cpp @@ -322,6 +322,58 @@ bool LogMessageInstBindlessDescriptor(Validator &gpuav, const uint32_t *error_re return error_found; } +bool LogMessageInstNonBindlessOOB(Validator &gpuav, const uint32_t *error_record, std::string &out_error_msg, + std::string &out_vuid_msg, const std::vector &descriptor_sets, const Location &loc, + bool uses_shader_object, bool &out_oob_access) { + using namespace glsl; + bool error_found = true; + out_oob_access = true; + std::ostringstream strm; + const GpuVuid &vuid = GetGpuVuid(loc.function); + + const uint32_t set_num = error_record[kInstNonBindlessOOBDescSetOffset]; + const uint32_t binding_num = error_record[kInstNonBindlessOOBDescBindingOffset]; + const uint32_t desc_index = error_record[kInstNonBindlessOOBDescIndexOffset]; + + strm << "(set = " << set_num << ", binding = " << binding_num << ", index " << desc_index << ") "; + switch (error_record[kHeaderErrorSubCodeOffset]) { + case kErrorSubCodeNonBindlessOOBBufferArrays: { + const uint32_t desc_array_size = error_record[kInstNonBindlessOOBParamOffset0]; + strm << " access out of bounds. The descriptor buffer array is " << desc_array_size + << " large, but as accessed at index [" << desc_index << "]"; + out_vuid_msg = "UNASSIGNED-Descriptor Buffer index out of bounds"; + } break; + + case kErrorSubCodeNonBindlessOOBBufferBounds: { + const auto *binding_state = descriptor_sets[set_num].state->GetBinding(binding_num); + const vvl::Buffer *buffer_state = + static_cast(binding_state)->descriptors[desc_index].GetBufferState(); + assert(buffer_state); + const uint32_t byte_offset = error_record[kInstNonBindlessOOBParamOffset0]; + const uint32_t resource_size = error_record[kInstNonBindlessOOBParamOffset1]; + + strm << " access out of bounds. The descriptor buffer (" << gpuav.FormatHandle(buffer_state->Handle()) << ") size is " + << buffer_state->create_info.size << " bytes, " << resource_size + << " bytes were bound, and the highest out of bounds access was at [" << byte_offset << "] bytes"; + + if (binding_state->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || + binding_state->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) { + out_vuid_msg = uses_shader_object ? vuid.uniform_access_oob_08612 : vuid.uniform_access_oob_06935; + } else { + out_vuid_msg = uses_shader_object ? vuid.storage_access_oob_08613 : vuid.storage_access_oob_06936; + } + } break; + + default: + error_found = false; + out_oob_access = false; + assert(false); // other OOB checks are not implemented yet + } + + out_error_msg = strm.str(); + return error_found; +} + bool LogMessageInstBufferDeviceAddress(const uint32_t *error_record, std::string &out_error_msg, std::string &out_vuid_msg, bool &out_oob_access) { using namespace glsl; @@ -439,6 +491,10 @@ bool LogInstrumentationError(Validator &gpuav, VkCommandBuffer cmd_buffer, const error_found = LogMessageInstBindlessDescriptor(gpuav, error_record, error_msg, vuid_msg, descriptor_sets, loc, uses_shader_object, oob_access); break; + case glsl::kErrorGroupInstNonBindlessOOB: + error_found = LogMessageInstNonBindlessOOB(gpuav, error_record, error_msg, vuid_msg, descriptor_sets, loc, + uses_shader_object, oob_access); + break; case glsl::kErrorGroupInstBufferDeviceAddress: error_found = LogMessageInstBufferDeviceAddress(error_record, error_msg, vuid_msg, oob_access); break; diff --git a/layers/gpu/instrumentation/gpuav_instrumentation.h b/layers/gpu/instrumentation/gpuav_instrumentation.h index 07ba75c7343..4f3b3728e29 100644 --- a/layers/gpu/instrumentation/gpuav_instrumentation.h +++ b/layers/gpu/instrumentation/gpuav_instrumentation.h @@ -36,6 +36,9 @@ bool LogInstrumentationError(Validator& gpuav, VkCommandBuffer cmd_buffer, const bool LogMessageInstBindlessDescriptor(Validator& gpuav, const uint32_t* error_record, std::string& out_error_msg, std::string& out_vuid_msg, const std::vector& descriptor_sets, const Location& loc, bool uses_shader_object, bool& out_oob_access); +bool LogMessageInstNonBindlessOOB(Validator& gpuav, const uint32_t* error_record, std::string& out_error_msg, + std::string& out_vuid_msg, const std::vector& descriptor_sets, const Location& loc, + bool uses_shader_object, bool& out_oob_access); bool LogMessageInstBufferDeviceAddress(const uint32_t* error_record, std::string& out_error_msg, std::string& out_vuid_msg, bool& out_oob_access); bool LogMessageInstRayQuery(const uint32_t* error_record, std::string& out_error_msg, std::string& out_vuid_msg); diff --git a/layers/gpu/shaders/gpu_error_codes.h b/layers/gpu/shaders/gpu_error_codes.h index 5c80c293c86..9d99fb16201 100644 --- a/layers/gpu/shaders/gpu_error_codes.h +++ b/layers/gpu/shaders/gpu_error_codes.h @@ -34,6 +34,7 @@ const int kErrorGroupGpuPreDraw = 4; const int kErrorGroupGpuPreDispatch = 5; const int kErrorGroupGpuPreTraceRays = 6; const int kErrorGroupGpuCopyBufferToImage = 7; +const int kErrorGroupInstNonBindlessOOB = 8; // Used for MultiEntry and there is no single stage set const int kHeaderStageIdMultiEntryPoint = 0x7fffffff; // same as spv::ExecutionModelMax @@ -46,6 +47,12 @@ const int kErrorSubCodeBindlessDescriptorOOB = 3; const int kErrorSubCodeBindlessDescriptorDestroyed = 4; const int kErrorSubCodeBindlessDescriptorNullPointer = 5; +// Non-Bindless OOB +// +// Buffers +const int kErrorSubCodeNonBindlessOOBBufferArrays = 1; +const int kErrorSubCodeNonBindlessOOBBufferBounds = 2; + // Buffer Device Address // const int kErrorSubCodeBufferDeviceAddressUnallocRef = 1; diff --git a/layers/gpu/shaders/gpu_error_header.h b/layers/gpu/shaders/gpu_error_header.h index d6e3b3b2d10..1c1257038b0 100644 --- a/layers/gpu/shaders/gpu_error_header.h +++ b/layers/gpu/shaders/gpu_error_header.h @@ -111,6 +111,15 @@ const int kInstBindlessBuffOOBDescIndexOffset = kInstBindlessDescIndexOffset; const int kInstBindlessBuffOOBBuffOffOffset = kInstBindlessCustomOffset_0; const int kInstBindlessBuffOOBBuffSizeOffset = kInstBindlessCustomOffset_1; +// Non-Bindless OOB +// --- + +const int kInstNonBindlessOOBDescSetOffset = kHeaderSize; +const int kInstNonBindlessOOBDescBindingOffset = kHeaderSize + 1; +const int kInstNonBindlessOOBDescIndexOffset = kHeaderSize + 2; +const int kInstNonBindlessOOBParamOffset0 = kHeaderSize + 3; +const int kInstNonBindlessOOBParamOffset1 = kHeaderSize + 4; + // Buffer device addresses // --- // A buffer address unalloc error will output the 64-bit pointer in diff --git a/layers/gpu/shaders/instrumentation/non_bindless_oob_buffer.comp b/layers/gpu/shaders/instrumentation/non_bindless_oob_buffer.comp new file mode 100644 index 00000000000..234569d51a4 --- /dev/null +++ b/layers/gpu/shaders/instrumentation/non_bindless_oob_buffer.comp @@ -0,0 +1,135 @@ +// Copyright (c) 2024 The Khronos Group Inc. +// Copyright (c) 2024 Valve Corporation +// Copyright (c) 2024 LunarG, 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. + +// NOTE: This file doesn't contain any entrypoints and should be compiled with then new --no-link option for glslang + +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_buffer_reference : require +#extension GL_EXT_buffer_reference_uvec2 : require +#if defined(GL_ARB_gpu_shader_int64) +#extension GL_ARB_gpu_shader_int64 : require +#else +#error No extension available for 64-bit integers. +#endif + +#include "gpu_error_header.h" +#include "gpu_shaders_constants.h" +#include "common_descriptor_sets.h" + +layout(buffer_reference, buffer_reference_align = 8, std430) buffer DescriptorLayoutData { + // BindingLayout[0] + uint num_bindings; + uint pad; // always zero, used to keep things aligned + + // BindingLayout[1] - BindingLayout[N] + // struct glsl::BindingLayout { + // x: count + // y: state_start + // } + uvec2 data[]; +}; + +layout(buffer_reference, buffer_reference_align = 8, std430) buffer DescriptorSetInData { + // struct glsl::DescriptorState { + // x: id + // y: extra data depending on the descriptor type + // } + uvec2 data[]; +}; + +layout(buffer_reference, buffer_reference_align = 8, std430) buffer GlobalState { + // Maps to DescriptorHeap and used to detect if descriptor is still valid on CPU + uint data[]; +}; + +struct DescriptorSetRecord { + DescriptorLayoutData layout_data; + DescriptorSetInData in_data; + uvec2 out_data; // unused BDA pointer +}; + +layout(set = kInstDefaultDescriptorSet, binding = kBindingInstBindlessDescriptor, std430) buffer BindlessStateBuffer { + GlobalState global_state; + DescriptorSetRecord desc_sets[kDebugInputBindlessMaxDescSets]; +} bindless_state_buffer; + +bool inst_non_bindless_oob_buffer(const uint inst_num, const uvec4 stage_info, const uint desc_array_size, + const uint desc_set, const uint binding, const uint desc_index, const uint byte_offset) { + uint error = 0u; + uint param_0 = 0u; + uint param_1 = 0u; + do { + // For non-array this should hopefully optimized out as "if (0 > 1)" + if (desc_index > desc_array_size) { + error = kErrorSubCodeNonBindlessOOBBufferArrays; + param_0 = desc_array_size; + break; + } + + DescriptorLayoutData layout_data = bindless_state_buffer.desc_sets[desc_set].layout_data; + uvec2 binding_state = layout_data.data[binding]; + + DescriptorSetInData in_data = bindless_state_buffer.desc_sets[desc_set].in_data; + + uint state_index = binding_state.y + desc_index; + + // check that the offset is in bounds + uint resource_size = in_data.data[state_index].y; + if (byte_offset >= resource_size) { + error = kErrorSubCodeNonBindlessOOBBufferBounds; + param_0 = byte_offset; + param_1 = resource_size; + break; + } + } while (false); + + if (0u != error) { + + const uint cmd_id = inst_cmd_resource_index_buffer.index[0]; + const uint cmd_errors_count = atomicAdd(inst_cmd_errors_count_buffer.errors_count[cmd_id], 1); + const bool max_cmd_errors_count_reached = cmd_errors_count >= kMaxErrorsPerCmd; + + if (max_cmd_errors_count_reached) return false; + + uint write_pos = atomicAdd(inst_errors_buffer.written_count, kErrorRecordSize); + const bool errors_buffer_not_filled = (write_pos + kErrorRecordSize) <= uint(inst_errors_buffer.data.length()); + + if (errors_buffer_not_filled) { + inst_errors_buffer.data[write_pos + kHeaderErrorRecordSizeOffset] = kErrorRecordSize; + inst_errors_buffer.data[write_pos + kHeaderShaderIdOffset] = kLinkShaderId; + inst_errors_buffer.data[write_pos + kHeaderInstructionIdOffset] = inst_num; + inst_errors_buffer.data[write_pos + kHeaderStageIdOffset] = stage_info.x; + inst_errors_buffer.data[write_pos + kHeaderStageInfoOffset_0] = stage_info.y; + inst_errors_buffer.data[write_pos + kHeaderStageInfoOffset_1] = stage_info.z; + inst_errors_buffer.data[write_pos + kHeaderStageInfoOffset_2] = stage_info.w; + + inst_errors_buffer.data[write_pos + kHeaderErrorGroupOffset] = kErrorGroupInstNonBindlessOOB; + inst_errors_buffer.data[write_pos + kHeaderErrorSubCodeOffset] = error; + + inst_errors_buffer.data[write_pos + kHeaderActionIdOffset] = inst_action_index_buffer.index[0]; + inst_errors_buffer.data[write_pos + kHeaderCommandResourceIdOffset] = inst_cmd_resource_index_buffer.index[0]; + + inst_errors_buffer.data[write_pos + kInstBindlessDescSetOffset] = desc_set; + inst_errors_buffer.data[write_pos + kInstBindlessDescBindingOffset] = binding; + inst_errors_buffer.data[write_pos + kInstBindlessDescIndexOffset] = desc_index; + inst_errors_buffer.data[write_pos + kInstBindlessCustomOffset_0] = param_0; + inst_errors_buffer.data[write_pos + kInstBindlessCustomOffset_1] = param_1; + } + return false; + } + return true; +} \ No newline at end of file diff --git a/layers/gpu/spirv/CMakeLists.txt b/layers/gpu/spirv/CMakeLists.txt index d20e57ae257..1c862eb6679 100644 --- a/layers/gpu/spirv/CMakeLists.txt +++ b/layers/gpu/spirv/CMakeLists.txt @@ -19,6 +19,8 @@ target_sources(gpu_av_spirv PRIVATE # Passes bindless_descriptor_pass.h bindless_descriptor_pass.cpp + non_bindless_oob_buffer_pass.h + non_bindless_oob_buffer_pass.cpp buffer_device_address_pass.h buffer_device_address_pass.cpp ray_query_pass.h @@ -46,6 +48,8 @@ target_sources(gpu_av_spirv PRIVATE ${VVL_SOURCE_DIR}/layers/${API_TYPE}/generated/instrumentation_bindless_descriptor_comp.h ${VVL_SOURCE_DIR}/layers/${API_TYPE}/generated/instrumentation_bindless_descriptor_comp.cpp + ${VVL_SOURCE_DIR}/layers/${API_TYPE}/generated/instrumentation_non_bindless_oob_buffer_comp.h + ${VVL_SOURCE_DIR}/layers/${API_TYPE}/generated/instrumentation_non_bindless_oob_buffer_comp.cpp ${VVL_SOURCE_DIR}/layers/${API_TYPE}/generated/instrumentation_buffer_device_address_comp.h ${VVL_SOURCE_DIR}/layers/${API_TYPE}/generated/instrumentation_buffer_device_address_comp.cpp ${VVL_SOURCE_DIR}/layers/${API_TYPE}/generated/instrumentation_ray_query_comp.h diff --git a/layers/gpu/spirv/bindless_descriptor_pass.cpp b/layers/gpu/spirv/bindless_descriptor_pass.cpp index e4cac429d1b..09d4e94d845 100644 --- a/layers/gpu/spirv/bindless_descriptor_pass.cpp +++ b/layers/gpu/spirv/bindless_descriptor_pass.cpp @@ -37,190 +37,6 @@ uint32_t BindlessDescriptorPass::GetLinkFunctionId() { return link_function_id; } -uint32_t BindlessDescriptorPass::FindTypeByteSize(uint32_t type_id, uint32_t matrix_stride, bool col_major, bool in_matrix) { - const Type& type = *module_.type_manager_.FindTypeById(type_id); - switch (type.spv_type_) { - case SpvType::kPointer: - return 8; // Assuming PhysicalStorageBuffer pointer - break; - case SpvType::kMatrix: { - if (matrix_stride == 0) { - module_.InternalError("BindlessDescriptorPass", "FindTypeByteSize is missing matrix stride"); - } - if (col_major) { - return type.inst_.Word(3) * matrix_stride; - } else { - const Type* vector_type = module_.type_manager_.FindTypeById(type.inst_.Word(2)); - return vector_type->inst_.Word(3) * matrix_stride; - } - } - case SpvType::kVector: { - uint32_t size = type.inst_.Word(3); - const Type* component_type = module_.type_manager_.FindTypeById(type.inst_.Word(2)); - // if vector in row major matrix, the vector is strided so return the number of bytes spanned by the vector - if (in_matrix && !col_major && matrix_stride > 0) { - return (size - 1) * matrix_stride + FindTypeByteSize(component_type->Id()); - } else if (component_type->spv_type_ == SpvType::kFloat || component_type->spv_type_ == SpvType::kInt) { - const uint32_t width = component_type->inst_.Word(2); - size *= width; - } else { - module_.InternalError("BindlessDescriptorPass", "FindTypeByteSize has unexpected vector type"); - } - return size / 8; - } - case SpvType::kFloat: - case SpvType::kInt: { - const uint32_t width = type.inst_.Word(2); - return width / 8; - } - default: - break; - } - return 1; -} - -// 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 BindlessDescriptorPass::GetLastByte(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; - - if (descriptor_type->spv_type_ == SpvType::kArray || descriptor_type->spv_type_ == SpvType::kRuntimeArray) { - current_type_id = descriptor_type->inst_.Operand(0); - ac_word_index++; - } else if (descriptor_type->spv_type_ == SpvType::kStruct) { - current_type_id = descriptor_type->Id(); - } else { - module_.InternalError("BindlessDescriptorPass", "GetLastByte has unexpected descriptor type"); - return 0; - } - - const Type& uint32_type = module_.type_manager_.GetTypeInt(32, false); - - // instruction that will have calculated the sum of the byte offset - uint32_t sum_id = 0; - - uint32_t matrix_stride = 0; - bool col_major = false; - 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); - uint32_t current_offset_id = 0; - - const Type* current_type = module_.type_manager_.FindTypeById(current_type_id); - switch (current_type->spv_type_) { - case SpvType::kArray: - case SpvType::kRuntimeArray: { - // Get array stride and multiply by current index - uint32_t arr_stride = GetDecoration(current_type_id, spv::DecorationArrayStride)->Word(3); - const uint32_t arr_stride_id = module_.type_manager_.GetConstantUInt32(arr_stride).Id(); - const uint32_t ac_index_id_32 = ConvertTo32(ac_index_id, block, inst_it); - - current_offset_id = module_.TakeNextId(); - block.CreateInstruction(spv::OpIMul, {uint32_type.Id(), current_offset_id, arr_stride_id, ac_index_id_32}, inst_it); - - // Get element type for next step - current_type_id = current_type->inst_.Operand(0); - } break; - case SpvType::kMatrix: { - if (matrix_stride == 0) { - module_.InternalError("BindlessDescriptorPass", "GetLastByte is missing matrix stride"); - } - matrix_stride_id = module_.type_manager_.GetConstantUInt32(matrix_stride).Id(); - uint32_t vec_type_id = current_type->inst_.Operand(0); - - // If column major, multiply column index by matrix stride, otherwise by vector component size and save matrix - // stride for vector (row) index - uint32_t col_stride_id = 0; - if (col_major) { - col_stride_id = matrix_stride_id; - } else { - const uint32_t component_type_id = module_.type_manager_.FindTypeById(vec_type_id)->inst_.Operand(0); - const uint32_t col_stride = FindTypeByteSize(component_type_id); - col_stride_id = module_.type_manager_.GetConstantUInt32(col_stride).Id(); - } - - const uint32_t ac_index_id_32 = ConvertTo32(ac_index_id, block, inst_it); - current_offset_id = module_.TakeNextId(); - block.CreateInstruction(spv::OpIMul, {uint32_type.Id(), current_offset_id, col_stride_id, ac_index_id_32}, inst_it); - - // Get element type for next step - current_type_id = vec_type_id; - in_matrix = true; - } break; - case SpvType::kVector: { - // If inside a row major matrix type, multiply index by matrix stride, - // else multiply by component size - const uint32_t component_type_id = current_type->inst_.Operand(0); - const uint32_t ac_index_id_32 = ConvertTo32(ac_index_id, block, inst_it); - if (in_matrix && !col_major) { - current_offset_id = module_.TakeNextId(); - block.CreateInstruction(spv::OpIMul, {uint32_type.Id(), current_offset_id, matrix_stride_id, ac_index_id_32}, - inst_it); - } else { - const uint32_t component_type_size = FindTypeByteSize(component_type_id); - const uint32_t size_id = module_.type_manager_.GetConstantUInt32(component_type_size).Id(); - - current_offset_id = module_.TakeNextId(); - block.CreateInstruction(spv::OpIMul, {uint32_type.Id(), current_offset_id, size_id, ac_index_id_32}, inst_it); - } - // Get element type for next step - current_type_id = component_type_id; - } break; - case SpvType::kStruct: { - // Get buffer byte offset for the referenced member - const Constant* member_constant = module_.type_manager_.FindConstantById(ac_index_id); - uint32_t member_index = member_constant->inst_.Operand(0); - uint32_t member_offset = GetMemeberDecoration(current_type_id, member_index, spv::DecorationOffset)->Word(4); - current_offset_id = module_.type_manager_.GetConstantUInt32(member_offset).Id(); - - // Look for matrix stride for this member if there is one. The matrix - // stride is not on the matrix type, but in a OpMemberDecorate on the - // enclosing struct type at the member index. If none found, reset - // stride to 0. - const Instruction* decoration_matrix_stride = - GetMemeberDecoration(current_type_id, member_index, spv::DecorationMatrixStride); - matrix_stride = decoration_matrix_stride ? decoration_matrix_stride->Word(4) : 0; - - const Instruction* decoration_col_major = - GetMemeberDecoration(current_type_id, member_index, spv::DecorationColMajor); - col_major = decoration_col_major != nullptr; - - // Get element type for next step - current_type_id = current_type->inst_.Operand(member_index); - } break; - default: { - module_.InternalError("BindlessDescriptorPass", "GetLastByte has unexpected non-composite type"); - } break; - } - - if (sum_id == 0) { - sum_id = current_offset_id; - } else { - const uint32_t new_sum_id = module_.TakeNextId(); - 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++; - } - - // Add in offset of last byte of referenced object - uint32_t bsize = FindTypeByteSize(current_type_id, matrix_stride, col_major, in_matrix); - uint32_t last = bsize - 1; - - const uint32_t last_id = module_.type_manager_.GetConstantUInt32(last).Id(); - - const uint32_t new_sum_id = module_.TakeNextId(); - block.CreateInstruction(spv::OpIAdd, {uint32_type.Id(), new_sum_id, sum_id, last_id}, inst_it); - return new_sum_id; -} - uint32_t BindlessDescriptorPass::CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InjectionData& injection_data) { const Constant& set_constant = module_.type_manager_.GetConstantUInt32(descriptor_set_); @@ -281,7 +97,7 @@ uint32_t BindlessDescriptorPass::CreateFunctionCall(BasicBlock& block, Instructi 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(block, inst_it); // Get Last Byte Index + descriptor_offset_id_ = GetLastByte(*var_inst_, *access_chain_inst_, block, inst_it); // Get Last Byte Index } } @@ -317,6 +133,9 @@ bool BindlessDescriptorPass::AnalyzeInstruction(const Function& function, const const uint32_t opcode = inst.Opcode(); if (opcode == spv::OpLoad || opcode == spv::OpStore) { + // For now only have non-bindless support for buffers + if (!module_.has_bindless_descriptors_) 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)); diff --git a/layers/gpu/spirv/bindless_descriptor_pass.h b/layers/gpu/spirv/bindless_descriptor_pass.h index 487696c785b..4cf07388e53 100644 --- a/layers/gpu/spirv/bindless_descriptor_pass.h +++ b/layers/gpu/spirv/bindless_descriptor_pass.h @@ -34,9 +34,6 @@ class BindlessDescriptorPass : public InjectConditionalFunctionPass { uint32_t CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InjectionData& injection_data) final; void Reset() final; - uint32_t FindTypeByteSize(uint32_t type_id, uint32_t matrix_stride = 0, bool col_major = false, bool in_matrix = false); - uint32_t GetLastByte(BasicBlock& block, InstructionIt* inst_it); - uint32_t link_function_id = 0; uint32_t GetLinkFunctionId(); diff --git a/layers/gpu/spirv/link.h b/layers/gpu/spirv/link.h index 7dca5b21a46..5b061403245 100644 --- a/layers/gpu/spirv/link.h +++ b/layers/gpu/spirv/link.h @@ -23,6 +23,7 @@ namespace spirv { enum class LinkFunctions { inst_buffer_device_address, inst_bindless_descriptor, + inst_non_bindless_oob_buffer, inst_ray_query, }; diff --git a/layers/gpu/spirv/module.cpp b/layers/gpu/spirv/module.cpp index e5b842820e0..aaaf2838199 100644 --- a/layers/gpu/spirv/module.cpp +++ b/layers/gpu/spirv/module.cpp @@ -20,6 +20,7 @@ #include "buffer_device_address_pass.h" #include "bindless_descriptor_pass.h" +#include "non_bindless_oob_buffer_pass.h" #include "ray_query_pass.h" #include "debug_printf_pass.h" @@ -35,6 +36,7 @@ Module::Module(vvl::span words, DebugReport* debug_report, const output_buffer_descriptor_set_(settings.output_buffer_descriptor_set), support_int64_(settings.support_int64), support_memory_model_device_scope_(settings.support_memory_model_device_scope), + has_bindless_descriptors_(settings.has_bindless_descriptors), print_debug_info_(settings.print_debug_info), debug_report_(debug_report) { uint32_t instruction_count = 0; @@ -118,7 +120,22 @@ Module::Module(vvl::span words, DebugReport* debug_report, const } case spv::OpVariable: { const Type* type = type_manager_.FindTypeById(new_inst->TypeId()); - type_manager_.AddVariable(std::move(new_inst), *type); + const Variable& new_var = type_manager_.AddVariable(std::move(new_inst), *type); + + // While adding the global variables, detect if descriptors is bindless or not + spv::StorageClass storage_class = new_var.StorageClass(); + // These are the only storage classes that interface with a descriptor + // see vkspec.html#interfaces-resources-descset + if (storage_class == spv::StorageClassUniform || storage_class == spv::StorageClassUniformConstant || + storage_class == spv::StorageClassStorageBuffer) { + const Type* ptr_type = new_var.PointerType(type_manager_); + // The shader will also have OpCapability RuntimeDescriptorArray + if (ptr_type->spv_type_ == SpvType::kRuntimeArray) { + // TODO - This might not actually need to be marked as bindless + has_bindless_descriptors_ = true; + } + } + break; } default: { @@ -267,6 +284,15 @@ bool Module::RunPassBindlessDescriptor() { return changed; } +bool Module::RunPassNonBindlessOOBBuffer() { + NonBindlessOOBBufferPass pass(*this); + const bool changed = pass.Run(); + if (print_debug_info_) { + pass.PrintDebugInfo(); + } + return changed; +} + bool Module::RunPassBufferDeviceAddress() { BufferDeviceAddressPass pass(*this); const bool changed = pass.Run(); diff --git a/layers/gpu/spirv/module.h b/layers/gpu/spirv/module.h index abf32335b22..a96f1292c67 100644 --- a/layers/gpu/spirv/module.h +++ b/layers/gpu/spirv/module.h @@ -41,6 +41,7 @@ struct Settings { uint32_t max_instrumented_count; bool support_int64; bool support_memory_model_device_scope; + bool has_bindless_descriptors; }; // This is the "brain" of SPIR-V logic, it stores the memory of all the Instructions and is the main context. @@ -83,6 +84,7 @@ class Module { // Passes that can be ran // Return true if code was instrumented bool RunPassBindlessDescriptor(); + bool RunPassNonBindlessOOBBuffer(); bool RunPassBufferDeviceAddress(); bool RunPassRayQuery(); bool RunPassDebugPrintf(uint32_t binding_slot = 0); @@ -108,6 +110,12 @@ class Module { const bool support_int64_; const bool support_memory_model_device_scope_; + // TODO - To make things simple to start, decide if the whole shader has anything bindless or not. The next step will be a + // system to pass in the information from the descriptor set layout to build a LUT of which OpVariable point to bindless + // descriptors. This will require special consideration as it will break a simple way to test standalone version of the + // instrumentation + bool has_bindless_descriptors_ = false; + // Used to help debug const bool print_debug_info_; diff --git a/layers/gpu/spirv/non_bindless_oob_buffer_pass.cpp b/layers/gpu/spirv/non_bindless_oob_buffer_pass.cpp new file mode 100644 index 00000000000..e7848381118 --- /dev/null +++ b/layers/gpu/spirv/non_bindless_oob_buffer_pass.cpp @@ -0,0 +1,212 @@ +/* Copyright (c) 2024 LunarG, 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 "non_bindless_oob_buffer_pass.h" +#include "module.h" +#include +#include + +#include "generated/instrumentation_non_bindless_oob_buffer_comp.h" +#include "gpu/shaders/gpu_shaders_constants.h" + +namespace gpu { +namespace spirv { + +NonBindlessOOBBufferPass::NonBindlessOOBBufferPass(Module& module) : Pass(module) { module.use_bda_ = true; } + +static LinkInfo link_info = {instrumentation_non_bindless_oob_buffer_comp, instrumentation_non_bindless_oob_buffer_comp_size, + LinkFunctions::inst_non_bindless_oob_buffer, 0, "inst_non_bindless_oob_buffer"}; + +// By appending the LinkInfo, it will attempt at linking stage to add the function. +uint32_t NonBindlessOOBBufferPass::GetLinkFunctionId() { + if (link_function_id == 0) { + link_function_id = module_.TakeNextId(); + link_info.function_id = link_function_id; + module_.link_info_.push_back(link_info); + } + return link_function_id; +} + +uint32_t NonBindlessOOBBufferPass::CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, + const InjectionData& injection_data) { + assert(access_chain_inst_ && 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()); + 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 + } else { + descriptor_offset_id_ = module_.type_manager_.GetConstantZeroUint32().Id(); + } + + const uint32_t function_result = module_.TakeNextId(); + const uint32_t function_def = GetLinkFunctionId(); + const uint32_t bool_type = module_.type_manager_.GetTypeBool().Id(); + + block.CreateInstruction( + spv::OpFunctionCall, + {bool_type, function_result, function_def, injection_data.inst_position_id, injection_data.stage_info_id, + descriptor_array_size_id_, set_constant.Id(), binding_constant.Id(), descriptor_index_id, descriptor_offset_id_}, + inst_it); + + return function_result; +} + +void NonBindlessOOBBufferPass::Reset() { + access_chain_inst_ = nullptr; + var_inst_ = nullptr; + target_instruction_ = nullptr; + descriptor_array_size_id_ = 0; + descriptor_set_ = 0; + descriptor_binding_ = 0; + descriptor_index_id_ = 0; + descriptor_offset_id_ = 0; +} + +bool NonBindlessOOBBufferPass::AnalyzeInstruction(const Function& function, const Instruction& inst) { + if (module_.has_bindless_descriptors_) return false; + const uint32_t opcode = inst.Opcode(); + + if (opcode != spv::OpLoad && opcode != spv::OpStore) { + 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) { + return false; + } + + const uint32_t variable_id = access_chain_inst_->Operand(0); + const Variable* variable = module_.type_manager_.FindVariableById(variable_id); + if (!variable) { + return false; + } + var_inst_ = &variable->inst_; + + uint32_t storage_class = variable->StorageClass(); + if (storage_class != spv::StorageClassUniform && storage_class != spv::StorageClassStorageBuffer) { + return false; + } + + const Type* pointer_type = variable->PointerType(module_.type_manager_); + if (pointer_type->inst_.Opcode() == spv::OpTypeRuntimeArray) { + return false; // Currently we mark these are "bindless" + } + + const bool is_descriptor_array = pointer_type->inst_.Opcode() == spv::OpTypeArray; + if (is_descriptor_array) { + const Constant* array_size_const = module_.type_manager_.FindConstantById(pointer_type->inst_.Operand(1)); + if (!array_size_const) { + return false; // TODO - Handle Spec Constants here + } + descriptor_array_size_id_ = array_size_const->Id(); + } else { + descriptor_array_size_id_ = module_.type_manager_.GetConstantUInt32(1).Id(); + } + + // Check for deprecated storage block form + if (storage_class == spv::StorageClassUniform) { + const uint32_t block_type_id = is_descriptor_array ? pointer_type->inst_.Operand(0) : pointer_type->Id(); + assert(module_.type_manager_.FindTypeById(block_type_id)->spv_type_ == SpvType::kStruct && "unexpected block type"); + + const bool block_found = GetDecoration(block_type_id, spv::DecorationBlock) != nullptr; + + // If block decoration not found, verify deprecated form of SSBO + if (!block_found) { + assert(GetDecoration(block_type_id, spv::DecorationBufferBlock) != nullptr && "block decoration not found"); + storage_class = spv::StorageClassStorageBuffer; + } + } + + // 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); + } 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->Word(2) == spv::DecorationDescriptorSet) { + descriptor_set_ = annotation->Word(3); + } else if (annotation->Word(2) == spv::DecorationBinding) { + descriptor_binding_ = annotation->Word(3); + } + } + } + + if (descriptor_set_ >= gpuav::glsl::kDebugInputBindlessMaxDescSets) { + module_.InternalWarning("BindlessDescriptorPass", "Tried to use a descriptor slot over the current max limit"); + return false; + } + + // Save information to be used to make the Function + target_instruction_ = &inst; + + return true; +} + +void NonBindlessOOBBufferPass::PrintDebugInfo() { + std::cout << "NonBindlessOOBBufferPass\n\tinstrumentation count: " << instrumented_count_ << '\n'; +} + +// Created own Run() because need to control finding the largest offset in a given block +bool NonBindlessOOBBufferPass::Run() { + // Can safely loop function list as there is no injecting of new Functions until linking time + for (const auto& function : module_.functions_) { + for (auto block_it = function->blocks_.begin(); block_it != function->blocks_.end(); ++block_it) { + if ((*block_it)->loop_header_) { + continue; // Currently can't properly handle injecting CFG logic into a loop header block + } + auto& block_instructions = (*block_it)->instructions_; + for (auto inst_it = block_instructions.begin(); inst_it != block_instructions.end(); ++inst_it) { + // Every instruction is analyzed by the specific pass and lets us know if we need to inject a function or not + if (!AnalyzeInstruction(*function, *(inst_it->get()))) continue; + + if (module_.max_instrumented_count_ != 0 && instrumented_count_ >= module_.max_instrumented_count_) { + return true; // hit limit + } + instrumented_count_++; + + // Add any debug information to pass into the function call + InjectionData injection_data; + injection_data.stage_info_id = GetStageInfo(*function, block_it, inst_it); + const uint32_t inst_position = target_instruction_->position_index_; + auto inst_position_constant = module_.type_manager_.CreateConstantUInt32(inst_position); + injection_data.inst_position_id = inst_position_constant.Id(); + + // inst_it is updated to the instruction after the new function call, it will not add/remove any Blocks + CreateFunctionCall(**block_it, &inst_it, injection_data); + Reset(); + } + } + } + + return instrumented_count_ != 0; +} + +} // namespace spirv +} // namespace gpu \ No newline at end of file diff --git a/layers/gpu/spirv/non_bindless_oob_buffer_pass.h b/layers/gpu/spirv/non_bindless_oob_buffer_pass.h new file mode 100644 index 00000000000..2a96a5c07c6 --- /dev/null +++ b/layers/gpu/spirv/non_bindless_oob_buffer_pass.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2024 LunarG, 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. + */ +#pragma once + +#include +#include "pass.h" + +namespace gpu { +namespace spirv { + +// Will make sure Buffers (Storage and Uniform Buffers) that are non bindless are not OOB Uses robustBufferAccess to ensure if we +// are OOB that it won't crash and we will return the error safely +class NonBindlessOOBBufferPass : public Pass { + public: + NonBindlessOOBBufferPass(Module& module); + void PrintDebugInfo() final; + bool Run() final; + + private: + bool AnalyzeInstruction(const Function& function, const Instruction& inst); + uint32_t CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InjectionData& injection_data); + void Reset() final; + + uint32_t link_function_id = 0; + uint32_t GetLinkFunctionId(); + + const Instruction* access_chain_inst_ = nullptr; + const Instruction* var_inst_ = nullptr; + + uint32_t descriptor_array_size_id_ = 0; // the size of the descriptor array (size is 1 if no array) + uint32_t descriptor_set_ = 0; + uint32_t descriptor_binding_ = 0; + uint32_t descriptor_index_id_ = 0; // index input the descriptor array + uint32_t descriptor_offset_id_ = 0; +}; + +} // namespace spirv +} // namespace gpu \ No newline at end of file diff --git a/layers/gpu/spirv/pass.cpp b/layers/gpu/spirv/pass.cpp index 4805bb81ecc..1d8c4145715 100644 --- a/layers/gpu/spirv/pass.cpp +++ b/layers/gpu/spirv/pass.cpp @@ -205,6 +205,150 @@ const Instruction* Pass::GetMemeberDecoration(uint32_t id, uint32_t member_index return nullptr; } +// 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, + 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; + + if (descriptor_type->spv_type_ == SpvType::kArray || descriptor_type->spv_type_ == SpvType::kRuntimeArray) { + current_type_id = descriptor_type->inst_.Operand(0); + ac_word_index++; + } else if (descriptor_type->spv_type_ == SpvType::kStruct) { + current_type_id = descriptor_type->Id(); + } else { + // TODO - Add a GetPassName() helper + module_.InternalError("Pass", "GetLastByte has unexpected descriptor type"); + return 0; + } + + const Type& uint32_type = module_.type_manager_.GetTypeInt(32, false); + + // instruction that will have calculated the sum of the byte offset + uint32_t sum_id = 0; + + uint32_t matrix_stride = 0; + bool col_major = false; + 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); + uint32_t current_offset_id = 0; + + const Type* current_type = module_.type_manager_.FindTypeById(current_type_id); + switch (current_type->spv_type_) { + case SpvType::kArray: + case SpvType::kRuntimeArray: { + // Get array stride and multiply by current index + uint32_t arr_stride = GetDecoration(current_type_id, spv::DecorationArrayStride)->Word(3); + const uint32_t arr_stride_id = module_.type_manager_.GetConstantUInt32(arr_stride).Id(); + const uint32_t ac_index_id_32 = ConvertTo32(ac_index_id, block, inst_it); + + current_offset_id = module_.TakeNextId(); + block.CreateInstruction(spv::OpIMul, {uint32_type.Id(), current_offset_id, arr_stride_id, ac_index_id_32}, inst_it); + + // Get element type for next step + current_type_id = current_type->inst_.Operand(0); + } break; + case SpvType::kMatrix: { + if (matrix_stride == 0) { + module_.InternalError("Pass", "GetLastByte is missing matrix stride"); + } + matrix_stride_id = module_.type_manager_.GetConstantUInt32(matrix_stride).Id(); + uint32_t vec_type_id = current_type->inst_.Operand(0); + + // If column major, multiply column index by matrix stride, otherwise by vector component size and save matrix + // stride for vector (row) index + uint32_t col_stride_id = 0; + if (col_major) { + col_stride_id = matrix_stride_id; + } else { + const uint32_t component_type_id = module_.type_manager_.FindTypeById(vec_type_id)->inst_.Operand(0); + const uint32_t col_stride = module_.type_manager_.FindTypeByteSize(component_type_id); + col_stride_id = module_.type_manager_.GetConstantUInt32(col_stride).Id(); + } + + const uint32_t ac_index_id_32 = ConvertTo32(ac_index_id, block, inst_it); + current_offset_id = module_.TakeNextId(); + block.CreateInstruction(spv::OpIMul, {uint32_type.Id(), current_offset_id, col_stride_id, ac_index_id_32}, inst_it); + + // Get element type for next step + current_type_id = vec_type_id; + in_matrix = true; + } break; + case SpvType::kVector: { + // If inside a row major matrix type, multiply index by matrix stride, + // else multiply by component size + const uint32_t component_type_id = current_type->inst_.Operand(0); + const uint32_t ac_index_id_32 = ConvertTo32(ac_index_id, block, inst_it); + if (in_matrix && !col_major) { + current_offset_id = module_.TakeNextId(); + block.CreateInstruction(spv::OpIMul, {uint32_type.Id(), current_offset_id, matrix_stride_id, ac_index_id_32}, + inst_it); + } else { + const uint32_t component_type_size = module_.type_manager_.FindTypeByteSize(component_type_id); + const uint32_t size_id = module_.type_manager_.GetConstantUInt32(component_type_size).Id(); + + current_offset_id = module_.TakeNextId(); + block.CreateInstruction(spv::OpIMul, {uint32_type.Id(), current_offset_id, size_id, ac_index_id_32}, inst_it); + } + // Get element type for next step + current_type_id = component_type_id; + } break; + case SpvType::kStruct: { + // Get buffer byte offset for the referenced member + const Constant* member_constant = module_.type_manager_.FindConstantById(ac_index_id); + uint32_t member_index = member_constant->inst_.Operand(0); + uint32_t member_offset = GetMemeberDecoration(current_type_id, member_index, spv::DecorationOffset)->Word(4); + current_offset_id = module_.type_manager_.GetConstantUInt32(member_offset).Id(); + + // Look for matrix stride for this member if there is one. The matrix + // stride is not on the matrix type, but in a OpMemberDecorate on the + // enclosing struct type at the member index. If none found, reset + // stride to 0. + const Instruction* decoration_matrix_stride = + GetMemeberDecoration(current_type_id, member_index, spv::DecorationMatrixStride); + matrix_stride = decoration_matrix_stride ? decoration_matrix_stride->Word(4) : 0; + + const Instruction* decoration_col_major = + GetMemeberDecoration(current_type_id, member_index, spv::DecorationColMajor); + col_major = decoration_col_major != nullptr; + + // Get element type for next step + current_type_id = current_type->inst_.Operand(member_index); + } break; + default: { + module_.InternalError("Pass", "GetLastByte has unexpected non-composite type"); + } break; + } + + if (sum_id == 0) { + sum_id = current_offset_id; + } else { + const uint32_t new_sum_id = module_.TakeNextId(); + 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++; + } + + // Add in offset of last byte of referenced object + uint32_t bsize = module_.type_manager_.FindTypeByteSize(current_type_id, matrix_stride, col_major, in_matrix); + uint32_t last = bsize - 1; + + const uint32_t last_id = module_.type_manager_.GetConstantUInt32(last).Id(); + + const uint32_t new_sum_id = module_.TakeNextId(); + block.CreateInstruction(spv::OpIAdd, {uint32_type.Id(), new_sum_id, sum_id, last_id}, inst_it); + return new_sum_id; +} + // Generate code to convert integer id to 32bit, if needed. uint32_t Pass::ConvertTo32(uint32_t id, BasicBlock& block, InstructionIt* inst_it) { // Find type doing the indexing into the access chain diff --git a/layers/gpu/spirv/pass.h b/layers/gpu/spirv/pass.h index a737b4df992..17d329e40a4 100644 --- a/layers/gpu/spirv/pass.h +++ b/layers/gpu/spirv/pass.h @@ -48,6 +48,8 @@ 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, + 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 uint32_t ConvertTo32(uint32_t id, BasicBlock& block, InstructionIt* inst_it); diff --git a/layers/gpu/spirv/type_manager.cpp b/layers/gpu/spirv/type_manager.cpp index e604a4691b8..b61638989a5 100644 --- a/layers/gpu/spirv/type_manager.cpp +++ b/layers/gpu/spirv/type_manager.cpp @@ -526,5 +526,47 @@ const Variable* TypeManager::FindVariableById(uint32_t id) const { return (variable == id_to_variable_.end()) ? nullptr : variable->second.get(); } +uint32_t TypeManager::FindTypeByteSize(uint32_t type_id, uint32_t matrix_stride, bool col_major, bool in_matrix) { + const Type& type = *FindTypeById(type_id); + switch (type.spv_type_) { + case SpvType::kPointer: + return 8; // Assuming PhysicalStorageBuffer pointer + break; + case SpvType::kMatrix: { + if (matrix_stride == 0) { + module_.InternalError("BindlessDescriptorPass", "FindTypeByteSize is missing matrix stride"); + } + if (col_major) { + return type.inst_.Word(3) * matrix_stride; + } else { + const Type* vector_type = FindTypeById(type.inst_.Word(2)); + return vector_type->inst_.Word(3) * matrix_stride; + } + } + case SpvType::kVector: { + uint32_t size = type.inst_.Word(3); + const Type* component_type = FindTypeById(type.inst_.Word(2)); + // if vector in row major matrix, the vector is strided so return the number of bytes spanned by the vector + if (in_matrix && !col_major && matrix_stride > 0) { + return (size - 1) * matrix_stride + FindTypeByteSize(component_type->Id()); + } else if (component_type->spv_type_ == SpvType::kFloat || component_type->spv_type_ == SpvType::kInt) { + const uint32_t width = component_type->inst_.Word(2); + size *= width; + } else { + module_.InternalError("BindlessDescriptorPass", "FindTypeByteSize has unexpected vector type"); + } + return size / 8; + } + case SpvType::kFloat: + case SpvType::kInt: { + const uint32_t width = type.inst_.Word(2); + return width / 8; + } + default: + break; + } + return 1; +} + } // namespace spirv } // namespace gpu \ No newline at end of file diff --git a/layers/gpu/spirv/type_manager.h b/layers/gpu/spirv/type_manager.h index adc148a9705..5777446b3d0 100644 --- a/layers/gpu/spirv/type_manager.h +++ b/layers/gpu/spirv/type_manager.h @@ -126,6 +126,8 @@ class TypeManager { const Variable& AddVariable(std::unique_ptr new_inst, const Type& type); const Variable* FindVariableById(uint32_t id) const; + uint32_t FindTypeByteSize(uint32_t type_id, uint32_t matrix_stride = 0, bool col_major = false, bool in_matrix = false); + private: Module& module_; diff --git a/layers/state_tracker/descriptor_sets.cpp b/layers/state_tracker/descriptor_sets.cpp index b182fc5b47d..d2f596a42e5 100644 --- a/layers/state_tracker/descriptor_sets.cpp +++ b/layers/state_tracker/descriptor_sets.cpp @@ -630,7 +630,7 @@ void vvl::DescriptorSet::PerformWriteUpdate(const VkWriteDescriptorSet &update) if (iter.AtEnd() || !orig_binding.IsConsistent(iter.CurrentBinding())) { break; } - iter->WriteUpdate(*this, *state_data_, update, i, iter.CurrentBinding().IsBindless()); + iter->WriteUpdate(*this, *state_data_, update, i, IsBindless(iter.CurrentBinding().binding_flags)); iter.updated(true); } if (update.descriptorCount) { @@ -657,7 +657,7 @@ void vvl::DescriptorSet::PerformCopyUpdate(const VkCopyDescriptorSet &update, co const auto &mutable_src = static_cast(src); type = mutable_src.ActiveType(); } - dst.CopyUpdate(*this, *state_data_, src, src_iter.CurrentBinding().IsBindless(), type); + dst.CopyUpdate(*this, *state_data_, src, IsBindless(src_iter.CurrentBinding().binding_flags), type); some_update_ = true; ++change_count_; dst_iter.updated(true); diff --git a/layers/state_tracker/descriptor_sets.h b/layers/state_tracker/descriptor_sets.h index 2c92a9db870..a288741584c 100644 --- a/layers/state_tracker/descriptor_sets.h +++ b/layers/state_tracker/descriptor_sets.h @@ -45,6 +45,13 @@ class AccelerationStructureNV; class AccelerationStructureKHR; struct AllocateDescriptorSetsData; +// "bindless" does not have a concrete definition, but we use it as means to know: +// "is GPU-AV going to have to validate this or not" +// (see docs/gpu_av_bindless.md for more details) +static inline bool IsBindless(VkDescriptorBindingFlags flags) { + return (flags & (VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT)) != 0; +} + class DescriptorPool : public StateObject { public: DescriptorPool(ValidationStateTracker &dev, const VkDescriptorPool handle, const VkDescriptorPoolCreateInfo *pCreateInfo); @@ -675,10 +682,6 @@ class DescriptorBinding { virtual const Descriptor *GetDescriptor(const uint32_t index) const = 0; virtual Descriptor *GetDescriptor(const uint32_t index) = 0; - bool IsBindless() const { - return (binding_flags & (VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT)) != 0; - } - bool IsVariableCount() const { return (binding_flags & VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT) != 0; } bool IsConsistent(const DescriptorBinding &other) const { @@ -983,7 +986,7 @@ class DescriptorSet : public StateObject { virtual bool SkipBinding(const DescriptorBinding &binding, bool is_dynamic_accessed) const { // core validation case: We check if all parts of the descriptor are statically known, from here spirv-val should have // caught any OOB values. - return binding.IsBindless() || is_dynamic_accessed; + return IsBindless(binding.binding_flags) || is_dynamic_accessed; } protected: diff --git a/layers/vulkan/generated/instrumentation_non_bindless_oob_buffer_comp.cpp b/layers/vulkan/generated/instrumentation_non_bindless_oob_buffer_comp.cpp new file mode 100644 index 00000000000..251fb058bd8 --- /dev/null +++ b/layers/vulkan/generated/instrumentation_non_bindless_oob_buffer_comp.cpp @@ -0,0 +1,167 @@ +// *** THIS FILE IS GENERATED - DO NOT EDIT *** +// See generate_spirv.py for modifications + +/*************************************************************************** + * + * Copyright (c) 2021-2024 The Khronos Group Inc. + * Copyright (c) 2021-2024 Valve Corporation + * Copyright (c) 2021-2024 LunarG, 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 "instrumentation_non_bindless_oob_buffer_comp.h" + +// To view SPIR-V, copy contents of array and paste in https://www.khronos.org/spir/visualizer/ +[[maybe_unused]] const uint32_t instrumentation_non_bindless_oob_buffer_comp_size = 1371; +[[maybe_unused]] const uint32_t instrumentation_non_bindless_oob_buffer_comp[1371] = { + 0x07230203, 0x00010300, 0x0008000b, 0x000000d0, 0x00000000, 0x00020011, 0x00000001, 0x00020011, 0x00000005, 0x00020011, + 0x000014e3, 0x0009000a, 0x5f565053, 0x5f52484b, 0x73796870, 0x6c616369, 0x6f74735f, 0x65676172, 0x6675625f, 0x00726566, + 0x0006000b, 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x000014e4, 0x00000001, 0x00030003, + 0x00000002, 0x000001c2, 0x00070004, 0x415f4c47, 0x675f4252, 0x735f7570, 0x65646168, 0x6e695f72, 0x00343674, 0x00070004, + 0x455f4c47, 0x625f5458, 0x65666675, 0x65725f72, 0x65726566, 0x0065636e, 0x00090004, 0x455f4c47, 0x625f5458, 0x65666675, + 0x65725f72, 0x65726566, 0x5f65636e, 0x63657675, 0x00000032, 0x000a0004, 0x475f4c47, 0x4c474f4f, 0x70635f45, 0x74735f70, + 0x5f656c79, 0x656e696c, 0x7269645f, 0x69746365, 0x00006576, 0x00080004, 0x475f4c47, 0x4c474f4f, 0x6e695f45, 0x64756c63, + 0x69645f65, 0x74636572, 0x00657669, 0x000f0005, 0x0000000d, 0x74736e69, 0x6e6f6e5f, 0x6e69625f, 0x73656c64, 0x6f6f5f73, + 0x75625f62, 0x72656666, 0x3b317528, 0x3b347576, 0x753b3175, 0x31753b31, 0x3b31753b, 0x003b3175, 0x00050005, 0x00000006, + 0x74736e69, 0x6d756e5f, 0x00000000, 0x00050005, 0x00000007, 0x67617473, 0x6e695f65, 0x00006f66, 0x00060005, 0x00000008, + 0x63736564, 0x7272615f, 0x735f7961, 0x00657a69, 0x00050005, 0x00000009, 0x63736564, 0x7465735f, 0x00000000, 0x00040005, + 0x0000000a, 0x646e6962, 0x00676e69, 0x00050005, 0x0000000b, 0x63736564, 0x646e695f, 0x00007865, 0x00050005, 0x0000000c, + 0x65747962, 0x66666f5f, 0x00746573, 0x00040005, 0x00000010, 0x6f727265, 0x00000072, 0x00040005, 0x00000012, 0x61726170, + 0x00305f6d, 0x00040005, 0x00000013, 0x61726170, 0x00315f6d, 0x00080005, 0x00000020, 0x63736544, 0x74706972, 0x614c726f, + 0x74756f79, 0x61746144, 0x00000000, 0x00070006, 0x00000020, 0x00000000, 0x5f6d756e, 0x646e6962, 0x73676e69, 0x00000000, + 0x00040006, 0x00000020, 0x00000001, 0x00646170, 0x00050006, 0x00000020, 0x00000002, 0x61746164, 0x00000000, 0x00070005, + 0x00000025, 0x63736544, 0x74706972, 0x6553726f, 0x63655274, 0x0064726f, 0x00060006, 0x00000025, 0x00000000, 0x6f79616c, + 0x645f7475, 0x00617461, 0x00050006, 0x00000025, 0x00000001, 0x645f6e69, 0x00617461, 0x00060006, 0x00000025, 0x00000002, + 0x5f74756f, 0x61746164, 0x00000000, 0x00070005, 0x00000027, 0x63736544, 0x74706972, 0x6553726f, 0x446e4974, 0x00617461, + 0x00050006, 0x00000027, 0x00000000, 0x61746164, 0x00000000, 0x00070005, 0x0000002a, 0x646e6942, 0x7373656c, 0x74617453, + 0x66754265, 0x00726566, 0x00070006, 0x0000002a, 0x00000000, 0x626f6c67, 0x735f6c61, 0x65746174, 0x00000000, 0x00060006, + 0x0000002a, 0x00000001, 0x63736564, 0x7465735f, 0x00000073, 0x00050005, 0x0000002c, 0x626f6c47, 0x74536c61, 0x00657461, + 0x00050006, 0x0000002c, 0x00000000, 0x61746164, 0x00000000, 0x00080005, 0x0000002e, 0x646e6962, 0x7373656c, 0x6174735f, + 0x625f6574, 0x65666675, 0x00000072, 0x00060005, 0x00000036, 0x646e6962, 0x5f676e69, 0x74617473, 0x00000065, 0x00080005, + 0x00000059, 0x52646d43, 0x756f7365, 0x49656372, 0x7865646e, 0x66667542, 0x00007265, 0x00050006, 0x00000059, 0x00000000, + 0x65646e69, 0x00000078, 0x000a0005, 0x0000005b, 0x74736e69, 0x646d635f, 0x7365725f, 0x6372756f, 0x6e695f65, 0x5f786564, + 0x66667562, 0x00007265, 0x00080005, 0x00000061, 0x45646d43, 0x726f7272, 0x756f4373, 0x7542746e, 0x72656666, 0x00000000, + 0x00070006, 0x00000061, 0x00000000, 0x6f727265, 0x635f7372, 0x746e756f, 0x00000000, 0x000a0005, 0x00000063, 0x74736e69, + 0x646d635f, 0x7272655f, 0x5f73726f, 0x6e756f63, 0x75625f74, 0x72656666, 0x00000000, 0x00060005, 0x00000072, 0x7074754f, + 0x75427475, 0x72656666, 0x00000000, 0x00050006, 0x00000072, 0x00000000, 0x67616c66, 0x00000073, 0x00070006, 0x00000072, + 0x00000001, 0x74697277, 0x5f6e6574, 0x6e756f63, 0x00000074, 0x00050006, 0x00000072, 0x00000002, 0x61746164, 0x00000000, + 0x00070005, 0x00000074, 0x74736e69, 0x7272655f, 0x5f73726f, 0x66667562, 0x00007265, 0x00070005, 0x000000ad, 0x69746341, + 0x6e496e6f, 0x42786564, 0x65666675, 0x00000072, 0x00050006, 0x000000ad, 0x00000000, 0x65646e69, 0x00000078, 0x00090005, + 0x000000af, 0x74736e69, 0x7463615f, 0x5f6e6f69, 0x65646e69, 0x75625f78, 0x72656666, 0x00000000, 0x000c0047, 0x0000000d, + 0x00000029, 0x74736e69, 0x6e6f6e5f, 0x6e69625f, 0x73656c64, 0x6f6f5f73, 0x75625f62, 0x72656666, 0x00000000, 0x00000000, + 0x00040047, 0x0000001f, 0x00000006, 0x00000008, 0x00030047, 0x00000020, 0x00000002, 0x00050048, 0x00000020, 0x00000000, + 0x00000023, 0x00000000, 0x00050048, 0x00000020, 0x00000001, 0x00000023, 0x00000004, 0x00050048, 0x00000020, 0x00000002, + 0x00000023, 0x00000008, 0x00050048, 0x00000025, 0x00000000, 0x00000023, 0x00000000, 0x00050048, 0x00000025, 0x00000001, + 0x00000023, 0x00000008, 0x00050048, 0x00000025, 0x00000002, 0x00000023, 0x00000010, 0x00040047, 0x00000026, 0x00000006, + 0x00000008, 0x00030047, 0x00000027, 0x00000002, 0x00050048, 0x00000027, 0x00000000, 0x00000023, 0x00000000, 0x00040047, + 0x00000029, 0x00000006, 0x00000018, 0x00030047, 0x0000002a, 0x00000002, 0x00050048, 0x0000002a, 0x00000000, 0x00000023, + 0x00000000, 0x00050048, 0x0000002a, 0x00000001, 0x00000023, 0x00000008, 0x00040047, 0x0000002b, 0x00000006, 0x00000004, + 0x00030047, 0x0000002c, 0x00000002, 0x00050048, 0x0000002c, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x0000002e, + 0x00000021, 0x00000001, 0x00040047, 0x0000002e, 0x00000022, 0x00000007, 0x00040047, 0x00000058, 0x00000006, 0x00000004, + 0x00030047, 0x00000059, 0x00000002, 0x00050048, 0x00000059, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x0000005b, + 0x00000021, 0x00000004, 0x00040047, 0x0000005b, 0x00000022, 0x00000007, 0x00040047, 0x00000060, 0x00000006, 0x00000004, + 0x00030047, 0x00000061, 0x00000002, 0x00050048, 0x00000061, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x00000063, + 0x00000021, 0x00000005, 0x00040047, 0x00000063, 0x00000022, 0x00000007, 0x00040047, 0x00000071, 0x00000006, 0x00000004, + 0x00030047, 0x00000072, 0x00000002, 0x00050048, 0x00000072, 0x00000000, 0x00000023, 0x00000000, 0x00050048, 0x00000072, + 0x00000001, 0x00000023, 0x00000004, 0x00050048, 0x00000072, 0x00000002, 0x00000023, 0x00000008, 0x00040047, 0x00000074, + 0x00000021, 0x00000000, 0x00040047, 0x00000074, 0x00000022, 0x00000007, 0x00040047, 0x000000ac, 0x00000006, 0x00000004, + 0x00030047, 0x000000ad, 0x00000002, 0x00050048, 0x000000ad, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x000000af, + 0x00000021, 0x00000003, 0x00040047, 0x000000af, 0x00000022, 0x00000007, 0x00040015, 0x00000002, 0x00000020, 0x00000000, + 0x00040017, 0x00000003, 0x00000002, 0x00000004, 0x00020014, 0x00000004, 0x000a0021, 0x00000005, 0x00000004, 0x00000002, + 0x00000003, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00040020, 0x0000000f, 0x00000007, 0x00000002, + 0x0004002b, 0x00000002, 0x00000011, 0x00000000, 0x0004002b, 0x00000002, 0x0000001b, 0x00000001, 0x00030027, 0x0000001d, + 0x000014e5, 0x00040017, 0x0000001e, 0x00000002, 0x00000002, 0x0003001d, 0x0000001f, 0x0000001e, 0x0005001e, 0x00000020, + 0x00000002, 0x00000002, 0x0000001f, 0x00040020, 0x0000001d, 0x000014e5, 0x00000020, 0x00030027, 0x00000023, 0x000014e5, + 0x00030027, 0x00000024, 0x000014e5, 0x0005001e, 0x00000025, 0x0000001d, 0x00000024, 0x0000001e, 0x0003001d, 0x00000026, + 0x0000001e, 0x0003001e, 0x00000027, 0x00000026, 0x00040020, 0x00000024, 0x000014e5, 0x00000027, 0x0004002b, 0x00000002, + 0x00000028, 0x00000020, 0x0004001c, 0x00000029, 0x00000025, 0x00000028, 0x0004001e, 0x0000002a, 0x00000023, 0x00000029, + 0x0003001d, 0x0000002b, 0x00000002, 0x0003001e, 0x0000002c, 0x0000002b, 0x00040020, 0x00000023, 0x000014e5, 0x0000002c, + 0x00040020, 0x0000002d, 0x0000000c, 0x0000002a, 0x0004003b, 0x0000002d, 0x0000002e, 0x0000000c, 0x00040015, 0x0000002f, + 0x00000020, 0x00000001, 0x0004002b, 0x0000002f, 0x00000030, 0x00000001, 0x0004002b, 0x0000002f, 0x00000031, 0x00000000, + 0x00040020, 0x00000032, 0x0000000c, 0x0000001d, 0x00040020, 0x00000035, 0x00000007, 0x0000001e, 0x0004002b, 0x0000002f, + 0x00000038, 0x00000002, 0x00040020, 0x00000039, 0x000014e5, 0x0000001e, 0x00040020, 0x0000003e, 0x0000000c, 0x00000024, + 0x00040020, 0x00000048, 0x000014e5, 0x00000002, 0x0004002b, 0x00000002, 0x0000004f, 0x00000002, 0x0003002a, 0x00000004, + 0x00000052, 0x0003001d, 0x00000058, 0x00000002, 0x0003001e, 0x00000059, 0x00000058, 0x00040020, 0x0000005a, 0x0000000c, + 0x00000059, 0x0004003b, 0x0000005a, 0x0000005b, 0x0000000c, 0x00040020, 0x0000005c, 0x0000000c, 0x00000002, 0x0003001d, + 0x00000060, 0x00000002, 0x0003001e, 0x00000061, 0x00000060, 0x00040020, 0x00000062, 0x0000000c, 0x00000061, 0x0004003b, + 0x00000062, 0x00000063, 0x0000000c, 0x0004002b, 0x00000002, 0x0000006a, 0x00000006, 0x0003001d, 0x00000071, 0x00000002, + 0x0005001e, 0x00000072, 0x00000002, 0x00000002, 0x00000071, 0x00040020, 0x00000073, 0x0000000c, 0x00000072, 0x0004003b, + 0x00000073, 0x00000074, 0x0000000c, 0x0004002b, 0x00000002, 0x00000076, 0x00000010, 0x0004002b, 0x00000002, 0x00000087, + 0x0dead001, 0x0004002b, 0x00000002, 0x0000008d, 0x00000003, 0x0004002b, 0x00000002, 0x00000092, 0x00000004, 0x0004002b, + 0x00000002, 0x00000097, 0x00000005, 0x0004002b, 0x00000002, 0x000000a0, 0x00000009, 0x0004002b, 0x00000002, 0x000000a2, + 0x00000008, 0x0004002b, 0x00000002, 0x000000a5, 0x0000000a, 0x0004002b, 0x00000002, 0x000000aa, 0x00000007, 0x0003001d, + 0x000000ac, 0x00000002, 0x0003001e, 0x000000ad, 0x000000ac, 0x00040020, 0x000000ae, 0x0000000c, 0x000000ad, 0x0004003b, + 0x000000ae, 0x000000af, 0x0000000c, 0x0004002b, 0x00000002, 0x000000b9, 0x0000000b, 0x0004002b, 0x00000002, 0x000000bd, + 0x0000000c, 0x0004002b, 0x00000002, 0x000000c1, 0x0000000d, 0x0004002b, 0x00000002, 0x000000c5, 0x0000000e, 0x0004002b, + 0x00000002, 0x000000ca, 0x0000000f, 0x00030029, 0x00000004, 0x000000cf, 0x00050036, 0x00000004, 0x0000000d, 0x00000000, + 0x00000005, 0x00030037, 0x00000002, 0x00000006, 0x00030037, 0x00000003, 0x00000007, 0x00030037, 0x00000002, 0x00000008, + 0x00030037, 0x00000002, 0x00000009, 0x00030037, 0x00000002, 0x0000000a, 0x00030037, 0x00000002, 0x0000000b, 0x00030037, + 0x00000002, 0x0000000c, 0x000200f8, 0x0000000e, 0x0004003b, 0x0000000f, 0x00000010, 0x00000007, 0x0004003b, 0x0000000f, + 0x00000012, 0x00000007, 0x0004003b, 0x0000000f, 0x00000013, 0x00000007, 0x0004003b, 0x00000035, 0x00000036, 0x00000007, + 0x0003003e, 0x00000010, 0x00000011, 0x0003003e, 0x00000012, 0x00000011, 0x0003003e, 0x00000013, 0x00000011, 0x000200f9, + 0x00000014, 0x000200f8, 0x00000014, 0x000400f6, 0x00000016, 0x00000017, 0x00000000, 0x000200f9, 0x00000015, 0x000200f8, + 0x00000015, 0x000500ac, 0x00000004, 0x00000018, 0x0000000b, 0x00000008, 0x000300f7, 0x0000001a, 0x00000000, 0x000400fa, + 0x00000018, 0x00000019, 0x0000001a, 0x000200f8, 0x00000019, 0x0003003e, 0x00000010, 0x0000001b, 0x0003003e, 0x00000012, + 0x00000008, 0x000200f9, 0x00000016, 0x000200f8, 0x0000001a, 0x00070041, 0x00000032, 0x00000033, 0x0000002e, 0x00000030, + 0x00000009, 0x00000031, 0x0004003d, 0x0000001d, 0x00000034, 0x00000033, 0x00060041, 0x00000039, 0x0000003a, 0x00000034, + 0x00000038, 0x0000000a, 0x0006003d, 0x0000001e, 0x0000003b, 0x0000003a, 0x00000002, 0x00000008, 0x0003003e, 0x00000036, + 0x0000003b, 0x00070041, 0x0000003e, 0x0000003f, 0x0000002e, 0x00000030, 0x00000009, 0x00000030, 0x0004003d, 0x00000024, + 0x00000040, 0x0000003f, 0x00050041, 0x0000000f, 0x00000042, 0x00000036, 0x0000001b, 0x0004003d, 0x00000002, 0x00000043, + 0x00000042, 0x00050080, 0x00000002, 0x00000044, 0x00000043, 0x0000000b, 0x00070041, 0x00000048, 0x00000049, 0x00000040, + 0x00000031, 0x00000044, 0x0000001b, 0x0006003d, 0x00000002, 0x0000004a, 0x00000049, 0x00000002, 0x00000004, 0x000500ae, + 0x00000004, 0x0000004c, 0x0000000c, 0x0000004a, 0x000300f7, 0x0000004e, 0x00000000, 0x000400fa, 0x0000004c, 0x0000004d, + 0x0000004e, 0x000200f8, 0x0000004d, 0x0003003e, 0x00000010, 0x0000004f, 0x0003003e, 0x00000012, 0x0000000c, 0x0003003e, + 0x00000013, 0x0000004a, 0x000200f9, 0x00000016, 0x000200f8, 0x0000004e, 0x000200f9, 0x00000017, 0x000200f8, 0x00000017, + 0x000400fa, 0x00000052, 0x00000014, 0x00000016, 0x000200f8, 0x00000016, 0x0004003d, 0x00000002, 0x00000053, 0x00000010, + 0x000500ab, 0x00000004, 0x00000054, 0x00000011, 0x00000053, 0x000300f7, 0x00000056, 0x00000000, 0x000400fa, 0x00000054, + 0x00000055, 0x00000056, 0x000200f8, 0x00000055, 0x00060041, 0x0000005c, 0x0000005d, 0x0000005b, 0x00000031, 0x00000031, + 0x0004003d, 0x00000002, 0x0000005e, 0x0000005d, 0x00060041, 0x0000005c, 0x00000065, 0x00000063, 0x00000031, 0x0000005e, + 0x000700ea, 0x00000002, 0x00000066, 0x00000065, 0x0000001b, 0x00000011, 0x0000001b, 0x000500ae, 0x00000004, 0x0000006b, + 0x00000066, 0x0000006a, 0x000300f7, 0x0000006e, 0x00000000, 0x000400fa, 0x0000006b, 0x0000006d, 0x0000006e, 0x000200f8, + 0x0000006d, 0x000200fe, 0x00000052, 0x000200f8, 0x0000006e, 0x00050041, 0x0000005c, 0x00000075, 0x00000074, 0x00000030, + 0x000700ea, 0x00000002, 0x00000077, 0x00000075, 0x0000001b, 0x00000011, 0x00000076, 0x00050080, 0x00000002, 0x0000007a, + 0x00000077, 0x00000076, 0x00050044, 0x00000002, 0x0000007b, 0x00000074, 0x00000002, 0x0004007c, 0x0000002f, 0x0000007c, + 0x0000007b, 0x0004007c, 0x00000002, 0x0000007d, 0x0000007c, 0x000500b2, 0x00000004, 0x0000007e, 0x0000007a, 0x0000007d, + 0x000300f7, 0x00000081, 0x00000000, 0x000400fa, 0x0000007e, 0x00000080, 0x00000081, 0x000200f8, 0x00000080, 0x00060041, + 0x0000005c, 0x00000084, 0x00000074, 0x00000038, 0x00000077, 0x0003003e, 0x00000084, 0x00000076, 0x00050080, 0x00000002, + 0x00000086, 0x00000077, 0x0000001b, 0x00060041, 0x0000005c, 0x00000088, 0x00000074, 0x00000038, 0x00000086, 0x0003003e, + 0x00000088, 0x00000087, 0x00050080, 0x00000002, 0x0000008a, 0x00000077, 0x0000004f, 0x00060041, 0x0000005c, 0x0000008b, + 0x00000074, 0x00000038, 0x0000008a, 0x0003003e, 0x0000008b, 0x00000006, 0x00050080, 0x00000002, 0x0000008e, 0x00000077, + 0x0000008d, 0x00050051, 0x00000002, 0x0000008f, 0x00000007, 0x00000000, 0x00060041, 0x0000005c, 0x00000090, 0x00000074, + 0x00000038, 0x0000008e, 0x0003003e, 0x00000090, 0x0000008f, 0x00050080, 0x00000002, 0x00000093, 0x00000077, 0x00000092, + 0x00050051, 0x00000002, 0x00000094, 0x00000007, 0x00000001, 0x00060041, 0x0000005c, 0x00000095, 0x00000074, 0x00000038, + 0x00000093, 0x0003003e, 0x00000095, 0x00000094, 0x00050080, 0x00000002, 0x00000098, 0x00000077, 0x00000097, 0x00050051, + 0x00000002, 0x00000099, 0x00000007, 0x00000002, 0x00060041, 0x0000005c, 0x0000009a, 0x00000074, 0x00000038, 0x00000098, + 0x0003003e, 0x0000009a, 0x00000099, 0x00050080, 0x00000002, 0x0000009c, 0x00000077, 0x0000006a, 0x00050051, 0x00000002, + 0x0000009d, 0x00000007, 0x00000003, 0x00060041, 0x0000005c, 0x0000009e, 0x00000074, 0x00000038, 0x0000009c, 0x0003003e, + 0x0000009e, 0x0000009d, 0x00050080, 0x00000002, 0x000000a1, 0x00000077, 0x000000a0, 0x00060041, 0x0000005c, 0x000000a3, + 0x00000074, 0x00000038, 0x000000a1, 0x0003003e, 0x000000a3, 0x000000a2, 0x00050080, 0x00000002, 0x000000a6, 0x00000077, + 0x000000a5, 0x0004003d, 0x00000002, 0x000000a7, 0x00000010, 0x00060041, 0x0000005c, 0x000000a8, 0x00000074, 0x00000038, + 0x000000a6, 0x0003003e, 0x000000a8, 0x000000a7, 0x00050080, 0x00000002, 0x000000ab, 0x00000077, 0x000000aa, 0x00060041, + 0x0000005c, 0x000000b0, 0x000000af, 0x00000031, 0x00000031, 0x0004003d, 0x00000002, 0x000000b1, 0x000000b0, 0x00060041, + 0x0000005c, 0x000000b2, 0x00000074, 0x00000038, 0x000000ab, 0x0003003e, 0x000000b2, 0x000000b1, 0x00050080, 0x00000002, + 0x000000b4, 0x00000077, 0x000000a2, 0x00060041, 0x0000005c, 0x000000b5, 0x0000005b, 0x00000031, 0x00000031, 0x0004003d, + 0x00000002, 0x000000b6, 0x000000b5, 0x00060041, 0x0000005c, 0x000000b7, 0x00000074, 0x00000038, 0x000000b4, 0x0003003e, + 0x000000b7, 0x000000b6, 0x00050080, 0x00000002, 0x000000ba, 0x00000077, 0x000000b9, 0x00060041, 0x0000005c, 0x000000bb, + 0x00000074, 0x00000038, 0x000000ba, 0x0003003e, 0x000000bb, 0x00000009, 0x00050080, 0x00000002, 0x000000be, 0x00000077, + 0x000000bd, 0x00060041, 0x0000005c, 0x000000bf, 0x00000074, 0x00000038, 0x000000be, 0x0003003e, 0x000000bf, 0x0000000a, + 0x00050080, 0x00000002, 0x000000c2, 0x00000077, 0x000000c1, 0x00060041, 0x0000005c, 0x000000c3, 0x00000074, 0x00000038, + 0x000000c2, 0x0003003e, 0x000000c3, 0x0000000b, 0x00050080, 0x00000002, 0x000000c6, 0x00000077, 0x000000c5, 0x0004003d, + 0x00000002, 0x000000c7, 0x00000012, 0x00060041, 0x0000005c, 0x000000c8, 0x00000074, 0x00000038, 0x000000c6, 0x0003003e, + 0x000000c8, 0x000000c7, 0x00050080, 0x00000002, 0x000000cb, 0x00000077, 0x000000ca, 0x0004003d, 0x00000002, 0x000000cc, + 0x00000013, 0x00060041, 0x0000005c, 0x000000cd, 0x00000074, 0x00000038, 0x000000cb, 0x0003003e, 0x000000cd, 0x000000cc, + 0x000200f9, 0x00000081, 0x000200f8, 0x00000081, 0x000200fe, 0x00000052, 0x000200f8, 0x00000056, 0x000200fe, 0x000000cf, + 0x00010038, +}; diff --git a/layers/vulkan/generated/instrumentation_non_bindless_oob_buffer_comp.h b/layers/vulkan/generated/instrumentation_non_bindless_oob_buffer_comp.h new file mode 100644 index 00000000000..0729eb939b7 --- /dev/null +++ b/layers/vulkan/generated/instrumentation_non_bindless_oob_buffer_comp.h @@ -0,0 +1,30 @@ +// *** THIS FILE IS GENERATED - DO NOT EDIT *** +// See generate_spirv.py for modifications + +/*************************************************************************** + * + * Copyright (c) 2021-2024 The Khronos Group Inc. + * Copyright (c) 2021-2024 Valve Corporation + * Copyright (c) 2021-2024 LunarG, 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. + * + ****************************************************************************/ + +#pragma once + +#include + +// To view SPIR-V, copy contents of array and paste in https://www.khronos.org/spir/visualizer/ +extern const uint32_t instrumentation_non_bindless_oob_buffer_comp_size; +extern const uint32_t instrumentation_non_bindless_oob_buffer_comp[]; diff --git a/scripts/generate_source.py b/scripts/generate_source.py index e5246678cf0..d307aa59af6 100755 --- a/scripts/generate_source.py +++ b/scripts/generate_source.py @@ -379,6 +379,8 @@ def main(argv): 'instrumentation_buffer_device_address_comp.cpp', 'instrumentation_bindless_descriptor_comp.h', 'instrumentation_bindless_descriptor_comp.cpp', + 'instrumentation_non_bindless_oob_buffer_comp.h', + 'instrumentation_non_bindless_oob_buffer_comp.cpp', 'instrumentation_ray_query_comp.h', 'instrumentation_ray_query_comp.cpp', 'gpu_av_shader_hash.h' diff --git a/tests/spirv/instrumentation.cpp b/tests/spirv/instrumentation.cpp index 4ce2c5f60fa..7496672a1a6 100644 --- a/tests/spirv/instrumentation.cpp +++ b/tests/spirv/instrumentation.cpp @@ -24,6 +24,7 @@ static bool timer = false; static bool print_debug_info = false; static bool all_passes = false; static bool bindless_descriptor_pass = false; +static bool non_bindless_oob_buffer_pass = false; static bool buffer_device_address_pass = false; static bool ray_query_pass = false; static bool debug_printf_pass = false; @@ -41,6 +42,8 @@ USAGE: %s -o Runs all passes together --bindless-descriptor Runs BindlessDescriptorPass + --non-bindless-oob-buffer + Runs NonBindlessOOBBufferPass --buffer-device-address Runs BufferDeviceAddressPass --ray-query @@ -78,6 +81,8 @@ bool ParseFlags(int argc, char** argv, const char** out_file) { all_passes = true; } else if (0 == strcmp(cur_arg, "--bindless-descriptor")) { bindless_descriptor_pass = true; + } else if (0 == strcmp(cur_arg, "--non-bindless-oob-buffer")) { + non_bindless_oob_buffer_pass = true; } else if (0 == strcmp(cur_arg, "--buffer-device-address")) { buffer_device_address_pass = true; } else if (0 == strcmp(cur_arg, "--ray-query")) { @@ -139,11 +144,16 @@ int main(int argc, char** argv) { module_settings.max_instrumented_count = 0; module_settings.support_int64 = true; module_settings.support_memory_model_device_scope = true; + // for all passes, test worst case of using bindless + module_settings.has_bindless_descriptors = all_passes || bindless_descriptor_pass; gpu::spirv::Module module(spirv_data, nullptr, module_settings); if (all_passes || bindless_descriptor_pass) { module.RunPassBindlessDescriptor(); } + if (all_passes || non_bindless_oob_buffer_pass) { + module.RunPassNonBindlessOOBBuffer(); + } if (all_passes || buffer_device_address_pass) { module.RunPassBufferDeviceAddress(); } diff --git a/tests/unit/descriptors.cpp b/tests/unit/descriptors.cpp index 5674c86b624..ae4175b4593 100644 --- a/tests/unit/descriptors.cpp +++ b/tests/unit/descriptors.cpp @@ -1441,6 +1441,53 @@ TEST_F(NegativeDescriptors, BindInvalidPipelineLayout) { m_commandBuffer->end(); } +// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/6944 +TEST_F(NegativeDescriptors, DISABLED_ConstantArrayElementNotBound) { + RETURN_IF_SKIP(Init()); + InitRenderTarget(); + + VkMemoryPropertyFlags reqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + vkt::Buffer offset_buffer(*m_device, 4, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, reqs); + vkt::Buffer write_buffer(*m_device, 16, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, reqs); + + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, + {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2, VK_SHADER_STAGE_ALL, nullptr}}); + const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_}); + descriptor_set.WriteDescriptorBufferInfo(0, offset_buffer.handle(), 0, VK_WHOLE_SIZE); + // Don't bind 2nd element which might be accessed so an error will occur + descriptor_set.WriteDescriptorBufferInfo(1, write_buffer.handle(), 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0); + descriptor_set.UpdateDescriptorSets(); + + const char vs_source[] = R"glsl( + #version 450 + #extension GL_EXT_nonuniform_qualifier : enable + layout(set = 0, binding = 0) uniform ufoo { uint index; }; + layout(set = 0, binding = 1) buffer StorageBuffer { uint data; } Data[2]; + void main() { + Data[index].data = 0xdeadca71; + } + )glsl"; + + VkShaderObj vs(this, vs_source, VK_SHADER_STAGE_VERTEX_BIT); + CreatePipelineHelper pipe(*this); + pipe.shader_stages_[0] = vs.GetStageCreateInfo(); + pipe.gp_ci_.layout = pipeline_layout.handle(); + pipe.CreateGraphicsPipeline(); + + m_commandBuffer->begin(); + m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo); + vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.Handle()); + vk::CmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 0, 1, + &descriptor_set.set_, 0, nullptr); + + m_errorMonitor->SetDesiredError("VUID-vkCmdDraw-None-08114"); + vk::CmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0); + m_errorMonitor->VerifyFound(); + + m_commandBuffer->EndRenderPass(); + m_commandBuffer->end(); +} + TEST_F(NegativeDescriptors, UpdateDescriptorSetMismatchType) { RETURN_IF_SKIP(Init()); diff --git a/tests/unit/gpu_av_oob.cpp b/tests/unit/gpu_av_oob.cpp index 4b5dbdcf987..4435c980b84 100644 --- a/tests/unit/gpu_av_oob.cpp +++ b/tests/unit/gpu_av_oob.cpp @@ -1549,3 +1549,57 @@ TEST_F(NegativeGpuAVOOB, PartialBoundDescriptorCopy) { m_default_queue->Wait(); m_errorMonitor->VerifyFound(); } + +TEST_F(NegativeGpuAVOOB, ConstantArrayOOB) { + AddDisabledFeature(vkt::Feature::robustBufferAccess); + RETURN_IF_SKIP(InitGpuAvFramework()); + RETURN_IF_SKIP(InitState()); + InitRenderTarget(); + + VkMemoryPropertyFlags reqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + vkt::Buffer offset_buffer(*m_device, 4, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, reqs); + vkt::Buffer write_buffer(*m_device, 16, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, reqs); + + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, + {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2, VK_SHADER_STAGE_ALL, nullptr}}); + const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_}); + descriptor_set.WriteDescriptorBufferInfo(0, offset_buffer.handle(), 0, VK_WHOLE_SIZE); + descriptor_set.WriteDescriptorBufferInfo(1, write_buffer.handle(), 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0); + descriptor_set.WriteDescriptorBufferInfo(1, write_buffer.handle(), 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1); + descriptor_set.UpdateDescriptorSets(); + + const char vs_source[] = R"glsl( + #version 450 + #extension GL_EXT_nonuniform_qualifier : enable + layout(set = 0, binding = 0) uniform ufoo { uint index; }; + layout(set = 0, binding = 1) buffer StorageBuffer { uint data; } Data[2]; + void main() { + Data[index].data = 0xdeadca71; + } + )glsl"; + + VkShaderObj vs(this, vs_source, VK_SHADER_STAGE_VERTEX_BIT); + CreatePipelineHelper pipe(*this); + pipe.shader_stages_[0] = vs.GetStageCreateInfo(); + pipe.gp_ci_.layout = pipeline_layout.handle(); + pipe.CreateGraphicsPipeline(); + + m_commandBuffer->begin(); + m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo); + vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.Handle()); + vk::CmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 0, 1, + &descriptor_set.set_, 0, nullptr); + vk::CmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0); + m_commandBuffer->EndRenderPass(); + m_commandBuffer->end(); + + uint32_t *data = (uint32_t *)offset_buffer.memory().map(); + *data = 8; + offset_buffer.memory().unmap(); + + m_errorMonitor->SetDesiredError("UNASSIGNED-Descriptor Buffer index out of bounds", 3); + + m_default_queue->Submit(*m_commandBuffer); + m_default_queue->Wait(); + m_errorMonitor->VerifyFound(); +}