From cbccc785cc45eda064eb6e0c7c3beb5ed85e58ee Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sun, 1 May 2016 10:15:33 -0500 Subject: [PATCH] TraceViewer: Build a tree of all command buffers and display that instead of a flat list. --- src/xenia/gpu/trace_player.cc | 24 ++++--- src/xenia/gpu/trace_player.h | 4 +- src/xenia/gpu/trace_reader.cc | 22 ++++++ src/xenia/gpu/trace_reader.h | 42 ++++++++++++ src/xenia/gpu/trace_viewer.cc | 124 ++++++++++++++++++++++++---------- src/xenia/gpu/trace_viewer.h | 2 + 6 files changed, 171 insertions(+), 47 deletions(-) diff --git a/src/xenia/gpu/trace_player.cc b/src/xenia/gpu/trace_player.cc index 54c199736..b79b49df2 100644 --- a/src/xenia/gpu/trace_player.cc +++ b/src/xenia/gpu/trace_player.cc @@ -51,7 +51,7 @@ void TracePlayer::SeekFrame(int target_frame) { assert_true(frame->start_ptr <= frame->end_ptr); PlayTrace(frame->start_ptr, frame->end_ptr - frame->start_ptr, - TracePlaybackMode::kBreakOnSwap); + TracePlaybackMode::kBreakOnSwap, false); } void TracePlayer::SeekCommand(int target_command) { @@ -71,11 +71,11 @@ void TracePlayer::SeekCommand(int target_command) { const auto& previous_command = frame->commands[previous_command_index]; PlayTrace(previous_command.end_ptr, command.end_ptr - previous_command.end_ptr, - TracePlaybackMode::kBreakOnSwap); + TracePlaybackMode::kBreakOnSwap, false); } else { // Full playback from frame start. PlayTrace(frame->start_ptr, command.end_ptr - frame->start_ptr, - TracePlaybackMode::kBreakOnSwap); + TracePlaybackMode::kBreakOnSwap, true); } } @@ -84,19 +84,25 @@ void TracePlayer::WaitOnPlayback() { } void TracePlayer::PlayTrace(const uint8_t* trace_data, size_t trace_size, - TracePlaybackMode playback_mode) { - graphics_system_->command_processor()->CallInThread( - [this, trace_data, trace_size, playback_mode]() { - PlayTraceOnThread(trace_data, trace_size, playback_mode); - }); + TracePlaybackMode playback_mode, + bool clear_caches) { + playing_trace_ = true; + graphics_system_->command_processor()->CallInThread([=]() { + PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches); + }); } void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data, size_t trace_size, - TracePlaybackMode playback_mode) { + TracePlaybackMode playback_mode, + bool clear_caches) { auto memory = graphics_system_->memory(); auto command_processor = graphics_system_->command_processor(); + if (clear_caches) { + command_processor->ClearCaches(); + } + command_processor->set_swap_mode(SwapMode::kIgnored); playback_percent_ = 0; auto trace_end = trace_data + trace_size; diff --git a/src/xenia/gpu/trace_player.h b/src/xenia/gpu/trace_player.h index d3926d460..0c3c6571a 100644 --- a/src/xenia/gpu/trace_player.h +++ b/src/xenia/gpu/trace_player.h @@ -50,9 +50,9 @@ class TracePlayer : public TraceReader { private: void PlayTrace(const uint8_t* trace_data, size_t trace_size, - TracePlaybackMode playback_mode); + TracePlaybackMode playback_mode, bool clear_caches); void PlayTraceOnThread(const uint8_t* trace_data, size_t trace_size, - TracePlaybackMode playback_mode); + TracePlaybackMode playback_mode, bool clear_caches); xe::ui::Loop* loop_; GraphicsSystem* graphics_system_; diff --git a/src/xenia/gpu/trace_reader.cc b/src/xenia/gpu/trace_reader.cc index fb58c436b..16980c28c 100644 --- a/src/xenia/gpu/trace_reader.cc +++ b/src/xenia/gpu/trace_reader.cc @@ -75,6 +75,10 @@ void TraceReader::ParseTrace() { const uint8_t* packet_start_ptr = nullptr; const uint8_t* last_ptr = trace_ptr; bool pending_break = false; + auto current_command_buffer = new CommandBuffer(); + current_frame.command_tree = + std::unique_ptr(current_command_buffer); + while (trace_ptr < trace_data_ + trace_size_) { ++current_frame.command_count; auto type = static_cast(xe::load(trace_ptr)); @@ -94,11 +98,24 @@ void TraceReader::ParseTrace() { auto cmd = reinterpret_cast(trace_ptr); trace_ptr += sizeof(*cmd) + cmd->count * 4; + + // Traverse down a level. + auto sub_command_buffer = new CommandBuffer(); + sub_command_buffer->parent = current_command_buffer; + current_command_buffer->commands.push_back( + CommandBuffer::Command(sub_command_buffer)); + current_command_buffer = sub_command_buffer; break; } case TraceCommandType::kIndirectBufferEnd: { auto cmd = reinterpret_cast(trace_ptr); trace_ptr += sizeof(*cmd); + + // Go back up a level. If parent is null, this frame started in an + // indirect buffer. + if (current_command_buffer->parent) { + current_command_buffer = current_command_buffer->parent; + } break; } case TraceCommandType::kPacketStart: { @@ -125,6 +142,8 @@ void TraceReader::ParseTrace() { command.end_ptr = trace_ptr; current_frame.commands.push_back(std::move(command)); last_ptr = trace_ptr; + current_command_buffer->commands.push_back(CommandBuffer::Command( + uint32_t(current_frame.commands.size() - 1))); break; } case PacketCategory::kSwap: @@ -136,6 +155,9 @@ void TraceReader::ParseTrace() { if (pending_break) { current_frame.end_ptr = trace_ptr; frames_.push_back(std::move(current_frame)); + current_command_buffer = new CommandBuffer(); + current_frame.command_tree = + std::unique_ptr(current_command_buffer); current_frame.start_ptr = trace_ptr; current_frame.end_ptr = nullptr; current_frame.command_count = 0; diff --git a/src/xenia/gpu/trace_reader.h b/src/xenia/gpu/trace_reader.h index 5445bd1f9..b3245da46 100644 --- a/src/xenia/gpu/trace_reader.h +++ b/src/xenia/gpu/trace_reader.h @@ -11,6 +11,7 @@ #define XENIA_GPU_TRACE_READER_H_ #include +#include #include "xenia/base/mapped_memory.h" #include "xenia/gpu/trace_protocol.h" @@ -51,6 +52,42 @@ namespace gpu { class TraceReader { public: + struct CommandBuffer { + struct Command { + enum class Type { + kCommand, + kBuffer, + }; + + Command() {} + Command(Command&& other) { + type = other.type; + command_id = other.command_id; + command_subtree = std::move(other.command_subtree); + } + Command(CommandBuffer* buf) { + type = Type::kBuffer; + command_subtree = std::unique_ptr(buf); + } + Command(uint32_t id) { + type = Type::kCommand; + command_id = id; + } + ~Command() = default; + + Type type; + uint32_t command_id = -1; + std::unique_ptr command_subtree = nullptr; + }; + + CommandBuffer() {} + ~CommandBuffer() {} + + // Parent command buffer, if one exists. + CommandBuffer* parent = nullptr; + std::vector commands; + }; + struct Frame { struct Command { enum class Type { @@ -74,7 +111,12 @@ class TraceReader { const uint8_t* start_ptr = nullptr; const uint8_t* end_ptr = nullptr; int command_count = 0; + + // Flat list of all commands in this frame. std::vector commands; + + // Tree of all command buffers + std::unique_ptr command_tree; }; TraceReader() = default; diff --git a/src/xenia/gpu/trace_viewer.cc b/src/xenia/gpu/trace_viewer.cc index 7ce20c7ca..8079631f5 100644 --- a/src/xenia/gpu/trace_viewer.cc +++ b/src/xenia/gpu/trace_viewer.cc @@ -390,6 +390,66 @@ void TraceViewer::DrawPacketDisassemblerUI() { ImGui::End(); } +int TraceViewer::RecursiveDrawCommandBufferUI( + const TraceReader::Frame* frame, TraceReader::CommandBuffer* buffer) { + int selected_id = -1; + int column_width = int(ImGui::GetContentRegionMax().x); + + for (size_t i = 0; i < buffer->commands.size(); i++) { + switch (buffer->commands[i].type) { + case TraceReader::CommandBuffer::Command::Type::kBuffer: { + auto subtree = buffer->commands[i].command_subtree.get(); + if (!subtree->commands.size()) { + continue; + } + + ImGui::PushID(int(i)); + if (ImGui::TreeNode((void*)0, "Indirect Buffer %d", i)) { + ImGui::Indent(); + auto id = RecursiveDrawCommandBufferUI( + frame, buffer->commands[i].command_subtree.get()); + ImGui::Unindent(); + ImGui::TreePop(); + + if (id != -1) { + selected_id = id; + } + } + ImGui::PopID(); + } break; + + case TraceReader::CommandBuffer::Command::Type::kCommand: { + uint32_t command_id = buffer->commands[i].command_id; + + const auto& command = frame->commands[command_id]; + bool is_selected = command_id == player_->current_command_index(); + const char* label; + switch (command.type) { + case TraceReader::Frame::Command::Type::kDraw: + label = "Draw"; + break; + case TraceReader::Frame::Command::Type::kSwap: + label = "Swap"; + break; + } + + ImGui::PushID(command_id); + if (ImGui::Selectable(label, &is_selected)) { + selected_id = command_id; + } + ImGui::SameLine(column_width - 60.0f); + ImGui::Text("%d", command_id); + ImGui::PopID(); + // if (did_seek && target_command == i) { + // ImGui::SetScrollPosHere(); + // } + } break; + } + } + + return selected_id; +} + void TraceViewer::DrawCommandListUI() { ImGui::SetNextWindowPos(ImVec2(5, 70), ImGuiSetCond_FirstUseEver); if (!ImGui::Begin("Command List", nullptr, ImVec2(200, 640))) { @@ -473,31 +533,12 @@ void TraceViewer::DrawCommandListUI() { ImGui::SetScrollPosHere(); } - for (int i = 0; i < int(frame->commands.size()); ++i) { - ImGui::PushID(i); - is_selected = i == player_->current_command_index(); - const auto& command = frame->commands[i]; - const char* label; - switch (command.type) { - case TraceReader::Frame::Command::Type::kDraw: - label = "Draw"; - break; - case TraceReader::Frame::Command::Type::kSwap: - label = "Swap"; - break; - } - if (ImGui::Selectable(label, &is_selected)) { - if (!player_->is_playing_trace()) { - player_->SeekCommand(i); - } - } - ImGui::SameLine(column_width - 60.0f); - ImGui::Text("%d", i); - ImGui::PopID(); - if (did_seek && target_command == i) { - ImGui::SetScrollPosHere(); - } + auto id = RecursiveDrawCommandBufferUI(frame, frame->command_tree.get()); + if (id != -1 && id != player_->current_command_index() && + !player_->is_playing_trace()) { + player_->SeekCommand(id); } + ImGui::EndChild(); ImGui::End(); } @@ -639,8 +680,8 @@ void TraceViewer::DrawTextureInfo( ImGui::Columns(2); ImVec2 button_size(256, 256); - if (ImGui::ImageButton(ImTextureID(texture | ui::ImGuiDrawer::kIgnoreAlpha), - button_size, ImVec2(0, 0), ImVec2(1, 1))) { + if (ImGui::ImageButton(ImTextureID(texture), button_size, ImVec2(0, 0), + ImVec2(1, 1))) { // show viewer } ImGui::NextColumn(); @@ -1108,11 +1149,14 @@ void TraceViewer::DrawStateUI() { ((window_scissor_br >> 16) & 0x7FFF) - ((window_scissor_tl >> 16) & 0x7FFF)); uint32_t surface_info = regs[XE_GPU_REG_RB_SURFACE_INFO].u32; + uint32_t surface_actual = (surface_info >> 18) & 0x3FFF; uint32_t surface_pitch = surface_info & 0x3FFF; auto surface_msaa = (surface_info >> 16) & 0x3; static const char* kMsaaNames[] = { "1X", "2X", "4X", }; + ImGui::BulletText("Surface Pitch - Actual: %d - %d", surface_pitch, + surface_actual); ImGui::BulletText("Surface MSAA: %s", kMsaaNames[surface_msaa]); uint32_t vte_control = regs[XE_GPU_REG_PA_CL_VTE_CNTL].u32; bool vport_xscale_enable = (vte_control & (1 << 0)) > 0; @@ -1124,6 +1168,9 @@ void TraceViewer::DrawStateUI() { assert_true(vport_xscale_enable == vport_yscale_enable == vport_zscale_enable == vport_xoffset_enable == vport_yoffset_enable == vport_zoffset_enable); + if (!vport_xscale_enable) { + ImGui::PushStyleColor(ImGuiCol_Text, kColorIgnored); + } ImGui::BulletText( "Viewport Offset: %f, %f, %f", vport_xoffset_enable ? regs[XE_GPU_REG_PA_CL_VPORT_XOFFSET].f32 : 0, @@ -1134,6 +1181,10 @@ void TraceViewer::DrawStateUI() { vport_xscale_enable ? regs[XE_GPU_REG_PA_CL_VPORT_XSCALE].f32 : 1, vport_yscale_enable ? regs[XE_GPU_REG_PA_CL_VPORT_YSCALE].f32 : 1, vport_zscale_enable ? regs[XE_GPU_REG_PA_CL_VPORT_ZSCALE].f32 : 1); + if (!vport_xscale_enable) { + ImGui::PopStyleColor(); + } + ImGui::BulletText("Vertex Format: %s, %s, %s, %s", ((vte_control >> 8) & 0x1) ? "x/w0" : "x", ((vte_control >> 8) & 0x1) ? "y/w0" : "y", @@ -1318,7 +1369,7 @@ void TraceViewer::DrawStateUI() { if (write_mask) { auto color_target = GetColorRenderTarget(surface_pitch, surface_msaa, color_base, color_format); - tex = ImTextureID(color_target | ui::ImGuiDrawer::kIgnoreAlpha); + tex = ImTextureID(color_target); if (ImGui::ImageButton(tex, button_size, ImVec2(0, 0), ImVec2(1, 1))) { // show viewer @@ -1330,10 +1381,9 @@ void TraceViewer::DrawStateUI() { } if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::Text( - "Color Target %d (%s), base %.4X, pitch %d, msaa %d, format %d", - i, write_mask ? "enabled" : "disabled", color_base, surface_pitch, - surface_msaa, color_format); + ImGui::Text("Color Target %d (%s), base %.4X, pitch %d, format %d", i, + write_mask ? "enabled" : "disabled", color_base, + surface_pitch, color_format); if (tex) { ImVec2 rel_pos; @@ -1407,17 +1457,19 @@ void TraceViewer::DrawStateUI() { auto button_pos = ImGui::GetCursorScreenPos(); ImVec2 button_size(256, 256); - ImGui::ImageButton( - ImTextureID(depth_target | ui::ImGuiDrawer::kIgnoreAlpha), - button_size, ImVec2(0, 0), ImVec2(1, 1)); + ImGui::ImageButton(ImTextureID(depth_target), button_size, ImVec2(0, 0), + ImVec2(1, 1)); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); + ImGui::Text("Depth Target: base %.4X, pitch %d, format %d", depth_base, + surface_pitch, depth_format); + ImVec2 rel_pos; rel_pos.x = ImGui::GetMousePos().x - button_pos.x; rel_pos.y = ImGui::GetMousePos().y - button_pos.y; - ZoomedImage(ImTextureID(depth_target | ui::ImGuiDrawer::kIgnoreAlpha), - rel_pos, button_size, 32.f, ImVec2(256, 256)); + ZoomedImage(ImTextureID(depth_target), rel_pos, button_size, 32.f, + ImVec2(256, 256)); ImGui::EndTooltip(); } diff --git a/src/xenia/gpu/trace_viewer.h b/src/xenia/gpu/trace_viewer.h index 6f7c900fc..7e82ad831 100644 --- a/src/xenia/gpu/trace_viewer.h +++ b/src/xenia/gpu/trace_viewer.h @@ -80,6 +80,8 @@ class TraceViewer { void DrawUI(); void DrawControllerUI(); void DrawPacketDisassemblerUI(); + int RecursiveDrawCommandBufferUI(const TraceReader::Frame* frame, + TraceReader::CommandBuffer* buffer); void DrawCommandListUI(); void DrawStateUI();