diff --git a/BUILD.gn b/BUILD.gn index 2f3564b8d42..52fa9a05339 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -200,6 +200,8 @@ vvl_sources = [ "layers/gpuav/core/gpuav_validation_pipeline.cpp", "layers/gpuav/debug_printf/debug_printf.cpp", "layers/gpuav/debug_printf/debug_printf.h", + "layers/gpuav/debug_descriptor/debug_descriptor.cpp", + "layers/gpuav/debug_descriptor/debug_descriptor.h", "layers/gpuav/descriptor_validation/gpuav_descriptor_set.cpp", "layers/gpuav/descriptor_validation/gpuav_descriptor_set.h", "layers/gpuav/descriptor_validation/gpuav_descriptor_validation.cpp", @@ -263,6 +265,8 @@ vvl_sources = [ "layers/gpuav/spirv/sanitizer_pass.h", "layers/gpuav/spirv/shared_memory_data_race_pass.h", "layers/gpuav/spirv/shared_memory_data_race_pass.cpp", + "layers/gpuav/spirv/debug_descriptor_pass.cpp", + "layers/gpuav/spirv/debug_descriptor_pass.h", "layers/gpuav/spirv/debug_printf_pass.cpp", "layers/gpuav/spirv/debug_printf_pass.h", "layers/gpuav/spirv/type_manager.cpp", diff --git a/layers/CMakeLists.txt b/layers/CMakeLists.txt index 63cd7e425cc..1c11258b3a3 100644 --- a/layers/CMakeLists.txt +++ b/layers/CMakeLists.txt @@ -329,6 +329,8 @@ target_sources(vvl PRIVATE gpuav/descriptor_validation/gpuav_descriptor_set.h gpuav/debug_printf/debug_printf.cpp gpuav/debug_printf/debug_printf.h + gpuav/debug_descriptor/debug_descriptor.cpp + gpuav/debug_descriptor/debug_descriptor.h gpuav/instrumentation/gpuav_shader_instrumentor.cpp gpuav/instrumentation/gpuav_shader_instrumentor.h gpuav/instrumentation/gpuav_instrumentation.h diff --git a/layers/VkLayer_khronos_validation.json.in b/layers/VkLayer_khronos_validation.json.in index 46260a42035..df2ec8b8144 100644 --- a/layers/VkLayer_khronos_validation.json.in +++ b/layers/VkLayer_khronos_validation.json.in @@ -86,6 +86,7 @@ { "key": "thread_safety", "value": false }, { "key": "validate_sync", "value": false }, { "key": "printf_enable", "value": false }, + { "key": "debug_descriptor", "value": false }, { "key": "gpuav_enable", "value": false }, { "key": "validate_best_practices", "value": false }, { "key": "report_flags", "value": [ "error", "warn" ] }, @@ -113,6 +114,7 @@ { "key": "thread_safety", "value": false }, { "key": "validate_sync", "value": false }, { "key": "printf_enable", "value": false }, + { "key": "debug_descriptor", "value": false }, { "key": "gpuav_enable", "value": false }, { "key": "validate_best_practices", "value": false }, { "key": "report_flags", "value": [ "error" ] }, @@ -140,6 +142,7 @@ { "key": "thread_safety", "value": false }, { "key": "validate_sync", "value": false }, { "key": "printf_enable", "value": false }, + { "key": "debug_descriptor", "value": false }, { "key": "gpuav_enable", "value": false }, { "key": "validate_best_practices", "value": true }, { "key": "report_flags", "value": [ "error", "warn", "perf" ] }, @@ -167,6 +170,7 @@ { "key": "thread_safety", "value": true }, { "key": "validate_sync", "value": true }, { "key": "printf_enable", "value": false }, + { "key": "debug_descriptor", "value": false }, { "key": "gpuav_enable", "value": false }, { "key": "validate_best_practices", "value": false }, { "key": "report_flags", "value": [ "error" ] }, @@ -194,6 +198,7 @@ { "key": "thread_safety", "value": false }, { "key": "validate_sync", "value": false }, { "key": "printf_enable", "value": false }, + { "key": "debug_descriptor", "value": false }, { "key": "gpuav_enable", "value": true }, { "key": "gpuav_shader_instrumentation", "value": true }, { "key": "gpuav_select_instrumented_shaders", "value": false }, @@ -225,6 +230,7 @@ { "key": "validate_sync", "value": false }, { "key": "gpuav_enable", "value": false }, { "key": "printf_enable", "value": true }, + { "key": "debug_descriptor", "value": false }, { "key": "validate_best_practices", "value": false }, { "key": "report_flags", "value": [ "error", "warn", "info" ] }, { "key": "debug_action", "value": [] }, @@ -252,6 +258,7 @@ { "key": "validate_sync", "value": false }, { "key": "gpuav_enable", "value": false }, { "key": "printf_enable", "value": false }, + { "key": "debug_descriptor", "value": false }, { "key": "validate_best_practices", "value": false }, { "key": "report_flags", "value": [ "warn" ] }, { "key": "debug_action", "value": [ "VK_DBG_LAYER_ACTION_LOG_MSG", "VK_DBG_LAYER_ACTION_DEBUG_OUTPUT" ] }, @@ -631,6 +638,79 @@ } ] }, + { + "key": "debug_descriptor", + "label": "Debug Descriptor", + "description": "[Used for VK_EXT_descriptor_buffer and VK_EXT_descriptor_heap] Enable DebugDescriptor and will print anything that uses NonSemantic.DebugDescriptor in their SPIR-V", + "view": "DEBUG", + "type": "BOOL", + "default": false, + "platforms": [ "WINDOWS", "LINUX" ], + "settings": [ + { + "key": "debug_descriptor_to_stdout", + "label": "Redirect DebugDescriptor messages to stdout", + "description": "Enable redirection of Debug Descriptor messages from the debug callback to stdout", + "view": "DEBUG", + "type": "BOOL", + "default": true, + "dependence": { + "mode": "ALL", + "settings": [ + { "key": "debug_descriptor", "value": true } + ] + }, + "messages": [ + { + "key": "debug_descriptor_msg3", + "title": "Debug Descriptor without 'info' level message severity flag", + "version": 1, + "description": "Enabling Debug Descriptor output redirection to the debug callback, but 'info' level message severity is disabled, so messages won't be shown.", + "informative": "Adding 'info' level to 'Message Severity'", + "severity": "INFORMATION", + "conditions": [ + { "setting": { "key": "debug_descriptor", "value": true }}, + { "setting": { "key": "debug_descriptor_to_stdout", "value": false }}, + { "setting": { "key": "report_flags", "value": ["info"]}, "operator": "NOT" } + ], + "actions": { + "default": "BUTTON0", + "BUTTON0": { + "type": "OK", + "changes": [ + { "setting": { "key": "report_flags", "value": ["info"]}, "operator": "APPEND" } + ] + } + } + } + ] + } + ], + "messages": [ + { + "key": "debug_descriptor_msg1", + "title": "Warning: Debug Descriptor without info level message severity flag", + "version": 1, + "description": "Enabling Debug Descriptor, with output directed to the debug callback, but info level message severity is disabled, so messages won't be shown.", + "informative": "Adding 'info' level to 'Message Severity'", + "severity": "INFORMATION", + "conditions": [ + { "setting": { "key": "debug_descriptor", "value": true }}, + { "setting": { "key": "debug_descriptor_to_stdout", "value": false }}, + { "setting": { "key": "report_flags", "value": ["info"]}, "operator": "NOT" } + ], + "actions": { + "default": "BUTTON0", + "BUTTON0": { + "type": "OK", + "changes": [ + { "setting": { "key": "report_flags", "value": ["info"] }, "operator": "APPEND" } + ] + } + } + } + ] + }, { "key": "gpuav_enable", "label": "GPU Assisted Validation", diff --git a/layers/gpuav/core/gpuav_features.cpp b/layers/gpuav/core/gpuav_features.cpp index b2da5700dfb..e7040b0019d 100644 --- a/layers/gpuav/core/gpuav_features.cpp +++ b/layers/gpuav/core/gpuav_features.cpp @@ -375,10 +375,11 @@ void Instance::AddFeatures(VkPhysicalDevice physical_device, vku::safe_VkDeviceC } } - if (gpuav_settings.debug_printf_enabled) { + if (gpuav_settings.debug_printf_enabled || gpuav_settings.debug_descriptor_enabled) { if (!IsExtensionAvailable(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, available_extensions)) { adjustment_warnings += - "\tVK_KHR_shader_non_semantic_info is not available on selected device, Debug Printf may produce SPIR-V " + "\tVK_KHR_shader_non_semantic_info is not available on selected device, DebugPrintf/DebugDescriptor may produce " + "SPIR-V " "that could fail to compile the shader\n"; } else { vku::AddExtension(*modified_create_info, VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME); diff --git a/layers/gpuav/core/gpuav_record.cpp b/layers/gpuav/core/gpuav_record.cpp index 1194044ae03..3bf2453726e 100644 --- a/layers/gpuav/core/gpuav_record.cpp +++ b/layers/gpuav/core/gpuav_record.cpp @@ -18,6 +18,7 @@ #include "chassis/chassis_modification_state.h" #include "gpuav/core/gpuav.h" #include "gpuav/core/gpuav_constants.h" +#include "gpuav/debug_descriptor/debug_descriptor.h" #include "gpuav/debug_printf/debug_printf.h" #include "gpuav/descriptor_validation/gpuav_descriptor_validation.h" #include "gpuav/instrumentation/descriptor_checks.h" @@ -168,6 +169,7 @@ void Validator::PreCallRecordBeginCommandBuffer(VkCommandBuffer commandBuffer, c RegisterSharedMemoryDataRaceValidation(*this, gpuav_cb_state); RegisterSanitizer(*this, gpuav_cb_state); debug_printf::RegisterDebugPrintf(*this, gpuav_cb_state); + debug_descriptor::RegisterDebugDescriptor(*this, gpuav_cb_state); } // Dedicated warning VUID that likely can be ignored. diff --git a/layers/gpuav/core/gpuav_settings.cpp b/layers/gpuav/core/gpuav_settings.cpp index 96d4f4c3a16..792f66b600b 100644 --- a/layers/gpuav/core/gpuav_settings.cpp +++ b/layers/gpuav/core/gpuav_settings.cpp @@ -35,7 +35,9 @@ bool GpuAVSettings::IsShaderInstrumentationEnabled() const { shader_instrumentation.post_process_descriptor_indexing || shader_instrumentation.vertex_attribute_fetch_oob || shader_instrumentation.sanitizer || shader_instrumentation.shared_memory_data_race; } -bool GpuAVSettings::IsSpirvModified() const { return IsShaderInstrumentationEnabled() || debug_printf_enabled; } +bool GpuAVSettings::IsSpirvModified() const { + return IsShaderInstrumentationEnabled() || debug_printf_enabled || debug_descriptor_enabled; +} // Also disables shader caching and select shader instrumentation void GpuAVSettings::DisableShaderInstrumentationAndOptions() { @@ -137,6 +139,8 @@ void GpuAVSettings::TracyLogSettings() const { VVL_TRACY_PRINT_GPUAV_SETTING(debug_printf_to_stdout); VVL_TRACY_PRINT_GPUAV_SETTING(debug_printf_verbose); VVL_TRACY_PRINT_GPUAV_SETTING(debug_printf_buffer_size); + VVL_TRACY_PRINT_GPUAV_SETTING(debug_descriptor_enabled); + VVL_TRACY_PRINT_GPUAV_SETTING(debug_descriptor_to_stdout); #undef VVL_TRACY_PRINT_GPUAV_SETTING #undef VVL_TRACY_PRINT_INSTRUMENTATION_SETTING #endif diff --git a/layers/gpuav/core/gpuav_settings.h b/layers/gpuav/core/gpuav_settings.h index f4f74a5cbe9..b50340941da 100644 --- a/layers/gpuav/core/gpuav_settings.h +++ b/layers/gpuav/core/gpuav_settings.h @@ -90,5 +90,8 @@ struct GpuAVSettings { bool debug_printf_verbose = false; uint32_t debug_printf_buffer_size = 1024; + bool debug_descriptor_enabled = false; + bool debug_descriptor_to_stdout = false; + void TracyLogSettings() const; }; diff --git a/layers/gpuav/core/gpuav_setup.cpp b/layers/gpuav/core/gpuav_setup.cpp index 7be35f72507..abe073d5b28 100644 --- a/layers/gpuav/core/gpuav_setup.cpp +++ b/layers/gpuav/core/gpuav_setup.cpp @@ -231,6 +231,8 @@ void Validator::FinishDeviceSetup(const VkDeviceCreateInfo* pCreateInfo, const L {glsl::kBindingInstCmdErrorsCount, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, // Vertex attribute fetch limits {glsl::kBindingInstVertexAttributeFetchLimits, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, nullptr}, + // DebugDescriptor Output buffer + {glsl::kBindingInstDebugDescriptor, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, }; assert(instrumentation_bindings_.size() == glsl::kTotalBindings); @@ -470,6 +472,14 @@ void Validator::InitSettings(const Location& loc) { gpuav_settings.DisableShaderInstrumentationAndOptions(); } + if (gpuav_settings.debug_descriptor_enabled && !IsExtEnabled(extensions.vk_ext_descriptor_buffer) && + !IsExtEnabled(extensions.vk_ext_descriptor_heap)) { + AdjustmentWarning(device, loc, + "VK_EXT_descriptor_buffer and VK_EXT_descriptor_heap were both not enabled, there is no need/reason to " + "use DebugDescriptor. [Disabling debug_descriptor_enabled]"); + gpuav_settings.debug_descriptor_enabled = false; + } + // If we have turned off all the possible things to instrument, turn off everything fully if (!gpuav_settings.IsShaderInstrumentationEnabled()) { gpuav_settings.DisableShaderInstrumentationAndOptions(); diff --git a/layers/gpuav/debug_descriptor/debug_descriptor.cpp b/layers/gpuav/debug_descriptor/debug_descriptor.cpp new file mode 100644 index 00000000000..ae28ea3c8d7 --- /dev/null +++ b/layers/gpuav/debug_descriptor/debug_descriptor.cpp @@ -0,0 +1,36 @@ +/* Copyright (c) 2020-2026 The Khronos Group Inc. + * Copyright (c) 2020-2026 Valve Corporation + * Copyright (c) 2020-2026 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 +#include "gpuav/debug_descriptor/debug_descriptor.h" +#include "gpuav/resources/gpuav_state_trackers.h" +#include "gpuav/core/gpuav.h" + +namespace gpuav { +namespace debug_descriptor { + +void RegisterDebugDescriptor(Validator& gpuav, CommandBufferSubState& cb_state) { + if (!gpuav.gpuav_settings.debug_descriptor_enabled) { + return; + } + + // TODO + printf("WORKING\n"); +} + +} // namespace debug_descriptor +} // namespace gpuav diff --git a/layers/gpuav/debug_descriptor/debug_descriptor.h b/layers/gpuav/debug_descriptor/debug_descriptor.h new file mode 100644 index 00000000000..c94529ec002 --- /dev/null +++ b/layers/gpuav/debug_descriptor/debug_descriptor.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2026 The Khronos Group Inc. + * Copyright (c) 2026 Valve Corporation + * Copyright (c) 2026 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 + +namespace gpuav { +class CommandBufferSubState; +class Validator; + +namespace debug_descriptor { +void RegisterDebugDescriptor(Validator& gpuav, CommandBufferSubState& cb_state); +} // namespace debug_descriptor + +} // namespace gpuav diff --git a/layers/gpuav/instrumentation/gpuav_shader_instrumentor.cpp b/layers/gpuav/instrumentation/gpuav_shader_instrumentor.cpp index a17ed25f40a..8d018745d41 100644 --- a/layers/gpuav/instrumentation/gpuav_shader_instrumentor.cpp +++ b/layers/gpuav/instrumentation/gpuav_shader_instrumentor.cpp @@ -53,6 +53,7 @@ #include "gpuav/spirv/shared_memory_data_race_pass.h" #include "gpuav/spirv/mesh_shading_pass.h" #include "gpuav/spirv/debug_printf_pass.h" +#include "gpuav/spirv/debug_descriptor_pass.h" #include "gpuav/spirv/post_process_descriptor_indexing_pass.h" #include "gpuav/spirv/vertex_attribute_fetch_oob_pass.h" #include "gpuav/spirv/sanitizer_pass.h" @@ -1711,6 +1712,13 @@ bool GpuShaderInstrumentor::InstrumentShader(const vvl::span& in modified |= log_error_pass.Run(); } + // Currently we don't link anything in this pass, could move after linking if desired + // If we do decide to link things, we need to add ABOVE the LogErrorPass + if (gpuav_settings.debug_descriptor_enabled) { + spirv::DebugDescriptorPass pass(module, glsl::kBindingInstDebugDescriptor); + modified |= pass.Run(); + } + // If there were GLSL written function injected, we will grab them and link them in here for (const auto& info : module.link_infos_) { module.LinkFunctions(info); diff --git a/layers/gpuav/shaders/gpuav_shaders_constants.h b/layers/gpuav/shaders/gpuav_shaders_constants.h index de5c8a10bdb..bab9d3d8db6 100644 --- a/layers/gpuav/shaders/gpuav_shaders_constants.h +++ b/layers/gpuav/shaders/gpuav_shaders_constants.h @@ -62,7 +62,8 @@ const int kBindingInstActionIndex = 5; const int kBindingInstCmdResourceIndex = 6; const int kBindingInstCmdErrorsCount = 7; const int kBindingInstVertexAttributeFetchLimits = 8; -const int kTotalBindings = 9; +const int kBindingInstDebugDescriptor = 9; +const int kTotalBindings = 10; // Validation pipelines // --- diff --git a/layers/gpuav/spirv/CMakeLists.txt b/layers/gpuav/spirv/CMakeLists.txt index b1983f4f18f..7b6cf83e44e 100644 --- a/layers/gpuav/spirv/CMakeLists.txt +++ b/layers/gpuav/spirv/CMakeLists.txt @@ -33,6 +33,8 @@ target_sources(gpu_av_spirv PRIVATE sanitizer_pass.cpp shared_memory_data_race_pass.h shared_memory_data_race_pass.cpp + debug_descriptor_pass.h + debug_descriptor_pass.cpp debug_printf_pass.h debug_printf_pass.cpp post_process_descriptor_indexing_pass.h diff --git a/layers/gpuav/spirv/debug_descriptor_pass.cpp b/layers/gpuav/spirv/debug_descriptor_pass.cpp new file mode 100644 index 00000000000..058e6dcd499 --- /dev/null +++ b/layers/gpuav/spirv/debug_descriptor_pass.cpp @@ -0,0 +1,108 @@ +/* Copyright (c) 2026 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 "debug_descriptor_pass.h" +#include "module.h" +#include +#include +#include +#include + +namespace gpuav { +namespace spirv { + +// Replace when we have spirv header +[[maybe_unused]] static const uint32_t debug_descriptor_encoding = 1; +[[maybe_unused]] static const uint32_t debug_descriptor_all_encoding = 2; +[[maybe_unused]] static const uint32_t debug_options_none = 0; +[[maybe_unused]] static const uint32_t debug_options_return_early = 1; +[[maybe_unused]] static const uint32_t debug_options_shader_abort = 2; +[[maybe_unused]] static const uint32_t debug_options_use_null_value = 3; + +bool DebugDescriptorPass::RequiresInstrumentation(const Instruction& inst, InstructionMeta& meta) { + if (inst.Opcode() == spv::OpExtInst && inst.Word(3) == ext_import_id_) { + meta.target_instruction = &inst; + meta.dump_all = inst.Word(4) == debug_descriptor_all_encoding; + return true; + } + return false; +} + +void DebugDescriptorPass::CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InstructionMeta& meta) { + (void)block; + (void)inst_it; + (void)meta; + + uint32_t slot = binding_slot_; + (void)slot; // to silent warning until used +} + +bool DebugDescriptorPass::Instrument() { + for (const auto& inst : module_.ext_inst_imports_) { + const char* import_string = inst->GetAsString(2); + if (strcmp(import_string, "NonSemantic.DebugDescriptor") == 0) { + ext_import_id_ = inst->ResultId(); + break; + } + } + + if (ext_import_id_ == 0) { + return false; // no debug descriptor strings found, early return + } + + for (Function& function : module_.functions_) { + if (!function.called_from_target_) { + continue; + } + + for (auto block_it = function.blocks_.begin(); block_it != function.blocks_.end(); ++block_it) { + BasicBlock& current_block = **block_it; + + cf_.Update(current_block); + if (debug_disable_loops_ && cf_.in_loop) { + continue; + } + + auto& block_instructions = current_block.instructions_; + + for (auto inst_it = block_instructions.begin(); inst_it != block_instructions.end(); ++inst_it) { + InstructionMeta meta; + if (!RequiresInstrumentation(*(inst_it->get()), meta)) { + continue; + } + + instrumentations_count_++; + + CreateFunctionCall(current_block, &inst_it, meta); + } + } + } + + return instrumentations_count_ != 0; +} + +void DebugDescriptorPass::PrintDebugInfo() const { + std::cout << "DebugDescriptorPass instrumentation count: " << instrumentations_count_ << '\n'; +} + +bool DebugDescriptorPass::Validate(const Function& current_function, const InstructionMeta& meta) { + // TODO - Decide if there is stuff we want to validate here or not + (void)current_function; + (void)meta; + return true; +} + +} // namespace spirv +} // namespace gpuav diff --git a/layers/gpuav/spirv/debug_descriptor_pass.h b/layers/gpuav/spirv/debug_descriptor_pass.h new file mode 100644 index 00000000000..22e034650f3 --- /dev/null +++ b/layers/gpuav/spirv/debug_descriptor_pass.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2026 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 gpuav { +namespace spirv { + +struct Type; + +// Create a pass to instrument NonSemantic.DebugDebug (GL_EXT_debug_descriptor) instructions +class DebugDescriptorPass : public Pass { + public: + DebugDescriptorPass(Module& module, uint32_t binding_slot) : Pass(module, kNullOffline), binding_slot_(binding_slot) {} + const char* Name() const final { return "DebugDescriptorPass"; } + + bool Instrument() final; + void PrintDebugInfo() const final; + + private: + // This is metadata tied to a single instruction gathered during RequiresInstrumentation() to be used later + struct InstructionMeta { + const Instruction* target_instruction = nullptr; + bool dump_all = false; + }; + + bool RequiresInstrumentation(const Instruction& inst, InstructionMeta& meta); + void CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InstructionMeta& meta); + + bool Validate(const Function& current_function, const InstructionMeta& meta); + + const uint32_t binding_slot_; + uint32_t ext_import_id_ = 0; +}; + +} // namespace spirv +} // namespace gpuav diff --git a/layers/gpuav/spirv/debug_printf_pass.h b/layers/gpuav/spirv/debug_printf_pass.h index e66162624e9..b0694867acc 100644 --- a/layers/gpuav/spirv/debug_printf_pass.h +++ b/layers/gpuav/spirv/debug_printf_pass.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2024-2025 LunarG, Inc. +/* Copyright (c) 2024-2026 LunarG, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,6 @@ namespace gpuav { namespace spirv { -// This pass has no linking, it is all done inplace at runtime -static const OfflineModule kNullOffline{nullptr, 0}; - struct Type; // Create a pass to instrument NonSemantic.DebugPrintf (GL_EXT_debug_printf) instructions diff --git a/layers/gpuav/spirv/pass.h b/layers/gpuav/spirv/pass.h index 7db8a61cdce..fbbfb03806e 100644 --- a/layers/gpuav/spirv/pass.h +++ b/layers/gpuav/spirv/pass.h @@ -42,6 +42,9 @@ struct InjectConditionalData { BasicBlockIt merge_block_it; }; +// If the pass has no linking and it is all done inplace at instrumentation time +[[maybe_unused]] static const OfflineModule kNullOffline{nullptr, 0}; + // Common helpers for all passes // The pass takes the Module object and modifies it as needed class Pass { diff --git a/layers/layer_options.cpp b/layers/layer_options.cpp index 5705c0e7000..031021fc765 100644 --- a/layers/layer_options.cpp +++ b/layers/layer_options.cpp @@ -186,6 +186,13 @@ const char* VK_LAYER_PRINTF_TO_STDOUT = "printf_to_stdout"; const char* VK_LAYER_PRINTF_VERBOSE = "printf_verbose"; const char* VK_LAYER_PRINTF_BUFFER_SIZE = "printf_buffer_size"; +// DebugDescriptor +// --- +// TODO - Add a VK_LAYER_PRINTF_ONLY_PRESET equivalent +// These only start with "debug" because it matches the "NonSemantic.Debug" naming scheme +const char* VK_LAYER_DEBUG_DESCRIPTOR = "debug_descriptor"; +const char* VK_LAYER_DEBUG_DESCRIPTOR_TO_STDOUT = "debug_descriptor_to_stdout"; + // GPU-AV // --- const char* VK_LAYER_GPUAV_ENABLE = "gpuav_enable"; @@ -720,6 +727,27 @@ static void ProcessDebugReportSettings(ConfigAndEnvSettings* settings_data, VkuL } } + if (settings_data->gpuav_settings->debug_descriptor_enabled) { + if (settings_data->gpuav_settings->debug_descriptor_to_stdout && (debug_action & VK_DBG_LAYER_ACTION_LOG_MSG)) { + if (is_stdout) { + setting_warnings.emplace_back( + "The debug callback is already logging to stdout, but " + std::string(VK_LAYER_DEBUG_DESCRIPTOR_TO_STDOUT) + + " is also enabled. DebugDescriptor will skip the debug callback in favor of a direct stdout write."); + } else { + setting_warnings.emplace_back("The logging to " + log_filename + + " will not contain any DebugDescriptor info because " + + std::string(VK_LAYER_DEBUG_DESCRIPTOR_TO_STDOUT) + " is enabled."); + } + } + if (!settings_data->gpuav_settings->debug_descriptor_to_stdout && ((report_flags & kInformationBit) == 0)) { + setting_warnings.emplace_back( + "DebugDescriptor logs to the Information message severity, enabling Information level logging otherwise the " + "message " + "will not be seen."); + report_flags |= kInformationBit; + } + } + if (settings_data->enabled[legacy_detection] && ((report_flags & kWarningBit) == 0)) { setting_warnings.emplace_back( "Legacy Detection logs to the Warning message severity, enabling Warning level logging otherwise the message " @@ -1158,6 +1186,14 @@ void ProcessConfigAndEnvSettings(ConfigAndEnvSettings* settings_data) { } } + if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_DEBUG_DESCRIPTOR)) { + vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_DEBUG_DESCRIPTOR, gpuav_settings.debug_descriptor_enabled); + } + + if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_DEBUG_DESCRIPTOR_TO_STDOUT)) { + vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_DEBUG_DESCRIPTOR_TO_STDOUT, gpuav_settings.debug_descriptor_to_stdout); + } + SyncValSettings& syncval_settings = *settings_data->syncval_settings; if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_SYNCVAL_SUBMIT_TIME_VALIDATION)) { vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_SYNCVAL_SUBMIT_TIME_VALIDATION, @@ -1305,6 +1341,12 @@ void ProcessConfigAndEnvSettings(ConfigAndEnvSettings* settings_data) { "errors in Core Check are solved, please disable, then only use GPU-AV for best performance."); } + // This has got a bit tricky, but if using DebugDescriptor, we want to turn on GPU-AV, but in a way it doesn't trip up the above + // logic/warnigs + if (gpuav_settings.debug_descriptor_enabled) { + settings_data->enabled[gpu_validation] = true; + } + // Set at the end once we decide what settings are actually on if (settings_data->disabled[shader_validation] || settings_data->disabled[core_checks]) { // only is used for core validation checks diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4baf715e52c..adab9c5363a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -49,6 +49,7 @@ target_sources(vk_layer_validation_tests PRIVATE unit/data_graph_positive.cpp unit/debug_extensions.cpp unit/debug_extensions_positive.cpp + unit/debug_descriptor.cpp unit/debug_printf.cpp unit/debug_printf_shader_debug_info.cpp unit/debug_printf_ray_tracing.cpp diff --git a/tests/unit/debug_descriptor.cpp b/tests/unit/debug_descriptor.cpp new file mode 100644 index 00000000000..01ca72a94d6 --- /dev/null +++ b/tests/unit/debug_descriptor.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2020-2026 The Khronos Group Inc. + * Copyright (c) 2020-2026 Valve Corporation + * Copyright (c) 2020-2026 LunarG, Inc. + * Copyright (c) 2020-2026 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include +#include +#include "../framework/layer_validation_tests.h" +#include "../framework/pipeline_helper.h" +#include "../framework/descriptor_helper.h" +#include "../framework/gpu_av_helper.h" + +// Until we land the glsl/spirv tooling, these are based on +// +// layout (set = 0, binding = 0) buffer SSBO { +// uint data; +// } x; +// +// layout (set = 0, binding = 1) uniform UBO { +// uint data; +// } y; +// +// void main() { +// uint a = y.data; +// x.data = a; +// } +// + +// void main() { +// DebugDescriptor(x); +// uint a = y.data; +// x.data = a; +// } +[[maybe_unused]] static const uint32_t spv_simple_size = 168; +[[maybe_unused]] static const uint32_t spv_simple_data[168] = { + 0x07230203, 0x00010500, 0x00070000, 0x00000018, 0x00000000, 0x00020011, 0x00000001, 0x0008000a, 0x5f565053, 0x5f52484b, + 0x5f6e6f6e, 0x616d6573, 0x6369746e, 0x666e695f, 0x0000006f, 0x0009000b, 0x00000001, 0x536e6f4e, 0x6e616d65, 0x2e636974, + 0x75626544, 0x73654467, 0x70697263, 0x00726f74, 0x0003000e, 0x00000000, 0x00000001, 0x0007000f, 0x00000005, 0x00000002, + 0x6e69616d, 0x00000000, 0x00000003, 0x00000004, 0x00060010, 0x00000002, 0x00000011, 0x00000001, 0x00000001, 0x00000001, + 0x00030047, 0x00000005, 0x00000002, 0x00050048, 0x00000005, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x00000003, + 0x00000021, 0x00000001, 0x00040047, 0x00000003, 0x00000022, 0x00000000, 0x00030047, 0x00000006, 0x00000002, 0x00050048, + 0x00000006, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x00000004, 0x00000021, 0x00000000, 0x00040047, 0x00000004, + 0x00000022, 0x00000000, 0x00020013, 0x00000007, 0x00030021, 0x00000008, 0x00000007, 0x00040015, 0x00000009, 0x00000020, + 0x00000000, 0x00040020, 0x0000000a, 0x00000007, 0x00000009, 0x0003001e, 0x00000005, 0x00000009, 0x00040020, 0x0000000b, + 0x00000002, 0x00000005, 0x0004003b, 0x0000000b, 0x00000003, 0x00000002, 0x00040015, 0x0000000c, 0x00000020, 0x00000001, + 0x0004002b, 0x0000000c, 0x0000000d, 0x00000000, 0x00040020, 0x0000000e, 0x00000002, 0x00000009, 0x0003001e, 0x00000006, + 0x00000009, 0x00040020, 0x0000000f, 0x0000000c, 0x00000006, 0x0004003b, 0x0000000f, 0x00000004, 0x0000000c, 0x00040020, + 0x00000010, 0x0000000c, 0x00000009, 0x00050036, 0x00000007, 0x00000002, 0x00000000, 0x00000008, 0x000200f8, 0x00000011, + 0x0004003b, 0x0000000a, 0x00000012, 0x00000007, 0x0008000c, 0x00000007, 0x00000013, 0x00000001, 0x00000001, 0x00000004, + 0x0000000d, 0x0000000d, 0x00050041, 0x0000000e, 0x00000014, 0x00000003, 0x0000000d, 0x0004003d, 0x00000009, 0x00000015, + 0x00000014, 0x0003003e, 0x00000012, 0x00000015, 0x0004003d, 0x00000009, 0x00000016, 0x00000012, 0x00050041, 0x00000010, + 0x00000017, 0x00000004, 0x0000000d, 0x0003003e, 0x00000017, 0x00000016, 0x000100fd, 0x00010038}; + + +// void main() { +// uint a = y.data; +// x.data = a; +// DebugDescriptorAll(); +// } +[[maybe_unused]] static const uint32_t spv_simple_all_size = 166; +[[maybe_unused]] static const uint32_t spv_simple_all_data[166] = { + 0x07230203, 0x00010500, 0x00070000, 0x00000018, 0x00000000, 0x00020011, 0x00000001, 0x0008000a, 0x5f565053, 0x5f52484b, + 0x5f6e6f6e, 0x616d6573, 0x6369746e, 0x666e695f, 0x0000006f, 0x0009000b, 0x00000001, 0x536e6f4e, 0x6e616d65, 0x2e636974, + 0x75626544, 0x73654467, 0x70697263, 0x00726f74, 0x0003000e, 0x00000000, 0x00000001, 0x0007000f, 0x00000005, 0x00000002, + 0x6e69616d, 0x00000000, 0x00000003, 0x00000004, 0x00060010, 0x00000002, 0x00000011, 0x00000001, 0x00000001, 0x00000001, + 0x00030047, 0x00000005, 0x00000002, 0x00050048, 0x00000005, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x00000003, + 0x00000021, 0x00000001, 0x00040047, 0x00000003, 0x00000022, 0x00000000, 0x00030047, 0x00000006, 0x00000002, 0x00050048, + 0x00000006, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x00000004, 0x00000021, 0x00000000, 0x00040047, 0x00000004, + 0x00000022, 0x00000000, 0x00020013, 0x00000007, 0x00030021, 0x00000008, 0x00000007, 0x00040015, 0x00000009, 0x00000020, + 0x00000000, 0x00040020, 0x0000000a, 0x00000007, 0x00000009, 0x0003001e, 0x00000005, 0x00000009, 0x00040020, 0x0000000b, + 0x00000002, 0x00000005, 0x0004003b, 0x0000000b, 0x00000003, 0x00000002, 0x00040015, 0x0000000c, 0x00000020, 0x00000001, + 0x0004002b, 0x0000000c, 0x0000000d, 0x00000000, 0x00040020, 0x0000000e, 0x00000002, 0x00000009, 0x0003001e, 0x00000006, + 0x00000009, 0x00040020, 0x0000000f, 0x0000000c, 0x00000006, 0x0004003b, 0x0000000f, 0x00000004, 0x0000000c, 0x00040020, + 0x00000010, 0x0000000c, 0x00000009, 0x00050036, 0x00000007, 0x00000002, 0x00000000, 0x00000008, 0x000200f8, 0x00000011, + 0x0004003b, 0x0000000a, 0x00000012, 0x00000007, 0x00050041, 0x0000000e, 0x00000013, 0x00000003, 0x0000000d, 0x0004003d, + 0x00000009, 0x00000014, 0x00000013, 0x0003003e, 0x00000012, 0x00000014, 0x0004003d, 0x00000009, 0x00000015, 0x00000012, + 0x00050041, 0x00000010, 0x00000016, 0x00000004, 0x0000000d, 0x0003003e, 0x00000016, 0x00000015, 0x0006000c, 0x00000007, + 0x00000017, 0x00000001, 0x00000002, 0x0000000d, 0x000100fd, 0x00010038}; + +// void main() { +// DebugDescriptor(x, gl_earlyReturn); +// uint a = y.data; +// x.data = a; +// } +[[maybe_unused]] static const uint32_t spv_return_early_size = 172; +[[maybe_unused]] static const uint32_t spv_return_early_data[172] = { + 0x07230203, 0x00010500, 0x00070000, 0x00000019, 0x00000000, 0x00020011, 0x00000001, 0x0008000a, 0x5f565053, 0x5f52484b, + 0x5f6e6f6e, 0x616d6573, 0x6369746e, 0x666e695f, 0x0000006f, 0x0009000b, 0x00000001, 0x536e6f4e, 0x6e616d65, 0x2e636974, + 0x75626544, 0x73654467, 0x70697263, 0x00726f74, 0x0003000e, 0x00000000, 0x00000001, 0x0007000f, 0x00000005, 0x00000002, + 0x6e69616d, 0x00000000, 0x00000003, 0x00000004, 0x00060010, 0x00000002, 0x00000011, 0x00000001, 0x00000001, 0x00000001, + 0x00030047, 0x00000005, 0x00000002, 0x00050048, 0x00000005, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x00000003, + 0x00000021, 0x00000001, 0x00040047, 0x00000003, 0x00000022, 0x00000000, 0x00030047, 0x00000006, 0x00000002, 0x00050048, + 0x00000006, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x00000004, 0x00000021, 0x00000000, 0x00040047, 0x00000004, + 0x00000022, 0x00000000, 0x00020013, 0x00000007, 0x00030021, 0x00000008, 0x00000007, 0x00040015, 0x00000009, 0x00000020, + 0x00000000, 0x00040020, 0x0000000a, 0x00000007, 0x00000009, 0x0003001e, 0x00000005, 0x00000009, 0x00040020, 0x0000000b, + 0x00000002, 0x00000005, 0x0004003b, 0x0000000b, 0x00000003, 0x00000002, 0x00040015, 0x0000000c, 0x00000020, 0x00000001, + 0x0004002b, 0x0000000c, 0x0000000d, 0x00000000, 0x0004002b, 0x0000000c, 0x0000000e, 0x00000001, 0x00040020, 0x0000000f, + 0x00000002, 0x00000009, 0x0003001e, 0x00000006, 0x00000009, 0x00040020, 0x00000010, 0x0000000c, 0x00000006, 0x0004003b, + 0x00000010, 0x00000004, 0x0000000c, 0x00040020, 0x00000011, 0x0000000c, 0x00000009, 0x00050036, 0x00000007, 0x00000002, + 0x00000000, 0x00000008, 0x000200f8, 0x00000012, 0x0004003b, 0x0000000a, 0x00000013, 0x00000007, 0x0008000c, 0x00000007, + 0x00000014, 0x00000001, 0x00000001, 0x00000004, 0x0000000d, 0x0000000e, 0x00050041, 0x0000000f, 0x00000015, 0x00000003, + 0x0000000d, 0x0004003d, 0x00000009, 0x00000016, 0x00000015, 0x0003003e, 0x00000013, 0x00000016, 0x0004003d, 0x00000009, + 0x00000017, 0x00000013, 0x00050041, 0x00000011, 0x00000018, 0x00000004, 0x0000000d, 0x0003003e, 0x00000018, 0x00000017, + 0x000100fd, 0x00010038}; + +// void main() { +// DebugDescriptorAll(gl_earlyReturn); +// uint a = y.data; +// x.data = a; +// } +[[maybe_unused]] static const uint32_t spv_return_early_all_size = 170; +[[maybe_unused]] static const uint32_t spv_return_early_all_data[170] = { + 0x07230203, 0x00010500, 0x00070000, 0x00000019, 0x00000000, 0x00020011, 0x00000001, 0x0008000a, 0x5f565053, 0x5f52484b, + 0x5f6e6f6e, 0x616d6573, 0x6369746e, 0x666e695f, 0x0000006f, 0x0009000b, 0x00000001, 0x536e6f4e, 0x6e616d65, 0x2e636974, + 0x75626544, 0x73654467, 0x70697263, 0x00726f74, 0x0003000e, 0x00000000, 0x00000001, 0x0007000f, 0x00000005, 0x00000002, + 0x6e69616d, 0x00000000, 0x00000003, 0x00000004, 0x00060010, 0x00000002, 0x00000011, 0x00000001, 0x00000001, 0x00000001, + 0x00030047, 0x00000005, 0x00000002, 0x00050048, 0x00000005, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x00000003, + 0x00000021, 0x00000001, 0x00040047, 0x00000003, 0x00000022, 0x00000000, 0x00030047, 0x00000006, 0x00000002, 0x00050048, + 0x00000006, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x00000004, 0x00000021, 0x00000000, 0x00040047, 0x00000004, + 0x00000022, 0x00000000, 0x00020013, 0x00000007, 0x00030021, 0x00000008, 0x00000007, 0x00040015, 0x00000009, 0x00000020, + 0x00000000, 0x00040020, 0x0000000a, 0x00000007, 0x00000009, 0x0003001e, 0x00000005, 0x00000009, 0x00040020, 0x0000000b, + 0x00000002, 0x00000005, 0x0004003b, 0x0000000b, 0x00000003, 0x00000002, 0x00040015, 0x0000000c, 0x00000020, 0x00000001, + 0x0004002b, 0x0000000c, 0x0000000d, 0x00000000, 0x0004002b, 0x0000000c, 0x0000000e, 0x00000001, 0x00040020, 0x0000000f, + 0x00000002, 0x00000009, 0x0003001e, 0x00000006, 0x00000009, 0x00040020, 0x00000010, 0x0000000c, 0x00000006, 0x0004003b, + 0x00000010, 0x00000004, 0x0000000c, 0x00040020, 0x00000011, 0x0000000c, 0x00000009, 0x00050036, 0x00000007, 0x00000002, + 0x00000000, 0x00000008, 0x000200f8, 0x00000012, 0x0004003b, 0x0000000a, 0x00000013, 0x00000007, 0x0006000c, 0x00000007, + 0x00000014, 0x00000001, 0x00000002, 0x0000000e, 0x00050041, 0x0000000f, 0x00000015, 0x00000003, 0x0000000d, 0x0004003d, + 0x00000009, 0x00000016, 0x00000015, 0x0003003e, 0x00000013, 0x00000016, 0x0004003d, 0x00000009, 0x00000017, 0x00000013, + 0x00050041, 0x00000011, 0x00000018, 0x00000004, 0x0000000d, 0x0003003e, 0x00000018, 0x00000017, 0x000100fd, 0x00010038}; + +static const VkLayerSettingEXT kDescriptorSetting = {OBJECT_LAYER_NAME, "debug_descriptor", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, + &kVkTrue}; +static VkLayerSettingsCreateInfoEXT kDescriptorSettingCreateInfo = {VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT, nullptr, 1, + &kDescriptorSetting}; + +class NegativeDebugDescriptor : public VkLayerTest { + public: + void InitDebugDescriptor(); +}; + +void NegativeDebugDescriptor::InitDebugDescriptor() { + SetTargetApiVersion(VK_API_VERSION_1_1); + AddRequiredExtensions(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME); + RETURN_IF_SKIP(InitFramework(&kDescriptorSettingCreateInfo)); + RETURN_IF_SKIP(InitState()); + + if (!CanEnableGpuAV(*this)) { + GTEST_SKIP() << "Requirements for GPU-AV are not met"; + } +} + +TEST_F(NegativeDebugDescriptor, BufferBasic) { + SetTargetApiVersion(VK_API_VERSION_1_2); + AddRequiredExtensions(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME); + AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::descriptorBuffer); + AddRequiredFeature(vkt::Feature::bufferDeviceAddress); + RETURN_IF_SKIP(InitDebugDescriptor()); + + VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer_properties = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(descriptor_buffer_properties); + + vkt::Buffer ssbo_buffer(*m_device, 64, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, vkt::device_address); + vkt::Buffer ubo_buffer(*m_device, 64, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, vkt::device_address); + + std::vector bindings = {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, + {1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}; + vkt::DescriptorSetLayout ds_layout(*m_device, bindings, VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT); + vkt::PipelineLayout pipeline_layout(*m_device, {&ds_layout}); + + VkDeviceSize ds_layout_size = ds_layout.GetDescriptorBufferSize(); + vkt::Buffer descriptor_buffer(*m_device, ds_layout_size * 4, VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT, + vkt::device_address); + + vkt::DescriptorGetInfo get_info_ssbo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, ssbo_buffer, 32); + uint8_t* mapped_descriptor_data = (uint8_t*)descriptor_buffer.Memory().Map(); + vk::GetDescriptorEXT(device(), get_info_ssbo, descriptor_buffer_properties.storageBufferDescriptorSize, + mapped_descriptor_data + ds_layout.GetDescriptorBufferBindingOffset(0)); + + vkt::DescriptorGetInfo get_info_ubo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, ubo_buffer, 16); + vk::GetDescriptorEXT(device(), get_info_ubo, descriptor_buffer_properties.uniformBufferDescriptorSize, + mapped_descriptor_data + ds_layout.GetDescriptorBufferBindingOffset(1)); + + VkShaderModuleCreateInfo module_ci = vku::InitStructHelper(); + module_ci.codeSize = spv_simple_size * sizeof(uint32_t); + module_ci.pCode = spv_simple_data; + + // spirv-val will not be happy without our spirv headers added + m_errorMonitor->SetAllowedFailureMsg("VUID-VkShaderModuleCreateInfo-pCode-08737"); + vkt::ShaderModule module(*m_device, module_ci); + + VkPipelineShaderStageCreateInfo stage_info = vku::InitStructHelper(); + stage_info.flags = 0; + stage_info.stage = VK_SHADER_STAGE_COMPUTE_BIT; + stage_info.module = module; + stage_info.pName = "main"; + + CreateComputePipelineHelper pipe(*this); + pipe.cp_ci_.flags |= VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; + pipe.cp_ci_.layout = pipeline_layout; + pipe.cp_ci_.stage = stage_info; + pipe.CreateComputePipeline(false); + + m_command_buffer.Begin(); + vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); + + VkDescriptorBufferBindingInfoEXT descriptor_buffer_binding_info = vku::InitStructHelper(); + descriptor_buffer_binding_info.address = descriptor_buffer.Address(); + descriptor_buffer_binding_info.usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT; + vk::CmdBindDescriptorBuffersEXT(m_command_buffer, 1, &descriptor_buffer_binding_info); + + uint32_t buffer_index = 0; + VkDeviceSize buffer_offset = 0; + vk::CmdSetDescriptorBufferOffsetsEXT(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, 0, 1, &buffer_index, + &buffer_offset); + + vk::CmdDispatch(m_command_buffer, 1, 1, 1); + m_command_buffer.End(); + + m_default_queue->SubmitAndWait(m_command_buffer); +}