| /* 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. |
| */ |
| |
| #include "containers/range.h" |
| #include "error_message/error_strings.h" |
| #include "gpu_dump_state.h" |
| #include "gpu_dump.h" |
| #include <vulkan/vk_enum_string_helper.h> |
| #include <vulkan/vulkan_core.h> |
| #include <cstdint> |
| #include <iostream> |
| #include <sstream> |
| #include "state_tracker/buffer_state.h" |
| #include "state_tracker/cmd_buffer_state.h" |
| #include "state_tracker/device_generated_commands_state.h" |
| #include "state_tracker/last_bound_state.h" |
| |
| namespace gpudump { |
| |
| void CommandBufferSubState::DumpDeviceGeneratedCommands(const VkGeneratedCommandsInfoEXT& info, VkPipelineBindPoint bind_point, |
| const Location& loc) const { |
| std::ostringstream ss; |
| ss << "[Dump Device Generated Commands]\n"; |
| |
| bool found_warning = false; |
| const LastBound& last_bound = base.lastBound[ConvertToVvlBindPoint(bind_point)]; |
| const bool has_pipeline = last_bound.pipeline_state != nullptr; |
| |
| ss << "maxDrawCount = " << info.maxDrawCount << ", maxSequenceCount = " << info.maxSequenceCount << "\n"; |
| if (info.sequenceCountAddress != 0) { |
| vvl::range<VkDeviceAddress> sequence_range{info.sequenceCountAddress, info.sequenceCountAddress + sizeof(uint32_t)}; |
| ss << "Sequence count found at " << vvl::string_range_hex(sequence_range) << "\n"; |
| auto buffer_states = dev_data.GetBuffersByAddress(info.sequenceCountAddress); |
| for (auto& buffer_state : buffer_states) { |
| ss << " - " << buffer_state->Describe(dev_data) << "\n"; |
| } |
| if (buffer_states.empty()) { |
| ss << " - [WARNING] No VkBuffer found at 0x" << std::hex << info.sequenceCountAddress << "\n"; |
| found_warning = true; |
| } |
| } |
| |
| if (info.preprocessAddress != 0) { |
| vvl::range<VkDeviceAddress> preprocess_range{info.preprocessAddress, info.preprocessAddress + info.preprocessSize}; |
| ss << "Preprocess at " << vvl::string_range_hex(preprocess_range) << "\n"; |
| auto buffer_states = dev_data.GetBuffersByAddress(info.preprocessAddress); |
| for (auto& buffer_state : buffer_states) { |
| ss << " - " << buffer_state->Describe(dev_data) << "\n"; |
| } |
| if (buffer_states.empty()) { |
| ss << " - [WARNING] No VkBuffer found at 0x" << std::hex << info.preprocessAddress << "\n"; |
| found_warning = true; |
| } |
| } |
| |
| { |
| vvl::range<VkDeviceAddress> indirect_range{info.indirectAddress, info.indirectAddress + info.indirectAddressSize}; |
| ss << "Indirect buffer at " << vvl::string_range_hex(indirect_range) << "\n"; |
| auto buffer_states = dev_data.GetBuffersByAddress(info.indirectAddress); |
| for (auto& buffer_state : buffer_states) { |
| ss << " - " << buffer_state->Describe(dev_data) << "\n"; |
| } |
| if (buffer_states.empty()) { |
| ss << " - [WARNING] No VkBuffer found at 0x" << std::hex << info.indirectAddress << "\n"; |
| found_warning = true; |
| } |
| } |
| |
| const auto indirect_commands_layout = dev_data.Get<vvl::IndirectCommandsLayout>(info.indirectCommandsLayout); |
| ss << "Tokens:\n"; |
| for (uint32_t i = 0; i < indirect_commands_layout->create_info.tokenCount; i++) { |
| const VkIndirectCommandsLayoutTokenEXT& token = indirect_commands_layout->create_info.pTokens[i]; |
| ss << " - Token [" << i << "] "; |
| switch (token.type) { |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT: |
| ss << "[EXECUTION_SET] " << (has_pipeline ? "pipeline" : "shader object") << ", stage " |
| << string_VkShaderStageFlags(token.data.pExecutionSet->shaderStages); |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT: |
| ss << "[PUSH_CONSTANT] " << string_VkPushConstantRange(token.data.pPushConstant->updateRange); |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT: |
| ss << "[SEQUENCE_INDEX] " << string_VkPushConstantRange(token.data.pPushConstant->updateRange); |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_EXT: |
| ss << "[INDEX_BUFFER] mode = " << string_VkIndirectCommandsInputModeFlagBitsEXT(token.data.pIndexBuffer->mode); |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT: |
| ss << "[VERTEX_BUFFER] vertexBindingUnit = " << token.data.pVertexBuffer->vertexBindingUnit; |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_EXT: |
| ss << "[DRAW_INDEXED] VkDrawIndexedIndirectCommand"; |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT: |
| ss << "[DRAW] VkDrawIndirectCommand"; |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_COUNT_EXT: |
| ss << "[DRAW_INDEXED_COUNT] VkDrawIndirectCountIndirectCommandEXT"; |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_COUNT_EXT: |
| ss << "[DRAW_COUNT] VkDrawIndirectCountIndirectCommandEXT"; |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT: |
| ss << "[DISPATCH] VkDispatchIndirectCommand"; |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_DATA_EXT: |
| ss << "[PUSH_DATA] " << string_VkPushConstantRange(token.data.pPushConstant->updateRange); |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_DATA_SEQUENCE_INDEX_EXT: |
| ss << "[PUSH_DATA_SEQUENCE_INDEX] " << string_VkPushConstantRange(token.data.pPushConstant->updateRange); |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_NV_EXT: |
| ss << "[DRAW_MESH_TASKS_NV] VkDrawMeshTasksIndirectCommandNV"; |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_NV_EXT: |
| ss << "[DRAW_MESH_TASKS_COUNT_NV] VkDrawIndirectCountIndirectCommandEXT"; |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_EXT: |
| ss << "[DRAW_MESH_TASKS] VkDrawMeshTasksIndirectCommandEXT"; |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_EXT: |
| ss << "[DRAW_MESH_TASKS_COUNT] VkDrawIndirectCountIndirectCommandEXT"; |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_TRACE_RAYS2_EXT: |
| ss << "[TRACE_RAYS2] VkTraceRaysIndirectCommand2KHR"; |
| break; |
| case VK_INDIRECT_COMMANDS_TOKEN_TYPE_MAX_ENUM_EXT: |
| break; |
| } |
| ss << " (offset " << std::dec << token.offset << ")\n"; |
| } |
| |
| if (dev_data.gpu_dump_settings.to_stdout) { |
| std::cout << "GPU-DUMP " << ss.str() << '\n'; |
| } else { |
| const VkFlags log_level = found_warning ? kWarningBit : kInformationBit; |
| // Don't provide a LogObjectList, embed it into the message instead to keep things cleaner |
| // (because the default callback will list them at the bottom) |
| dev_data.debug_report->LogMessage(log_level, "GPU-DUMP", {}, loc, ss.str().c_str()); |
| } |
| } |
| |
| } // namespace gpudump |