blob: 393dfb247ca67d5f9ba5369d81cf585e9529f1a9 [file]
/* 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