TraceViewer: Build a tree of all command buffers and display that instead of a flat list.

This commit is contained in:
Dr. Chat 2016-05-01 10:15:33 -05:00
parent 6101b70641
commit cbccc785cc
6 changed files with 171 additions and 47 deletions

View File

@ -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;

View File

@ -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_;

View File

@ -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<CommandBuffer>(current_command_buffer);
while (trace_ptr < trace_data_ + trace_size_) {
++current_frame.command_count;
auto type = static_cast<TraceCommandType>(xe::load<uint32_t>(trace_ptr));
@ -94,11 +98,24 @@ void TraceReader::ParseTrace() {
auto cmd =
reinterpret_cast<const IndirectBufferStartCommand*>(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<const IndirectBufferEndCommand*>(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<CommandBuffer>(current_command_buffer);
current_frame.start_ptr = trace_ptr;
current_frame.end_ptr = nullptr;
current_frame.command_count = 0;

View File

@ -11,6 +11,7 @@
#define XENIA_GPU_TRACE_READER_H_
#include <string>
#include <vector>
#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<CommandBuffer>(buf);
}
Command(uint32_t id) {
type = Type::kCommand;
command_id = id;
}
~Command() = default;
Type type;
uint32_t command_id = -1;
std::unique_ptr<CommandBuffer> command_subtree = nullptr;
};
CommandBuffer() {}
~CommandBuffer() {}
// Parent command buffer, if one exists.
CommandBuffer* parent = nullptr;
std::vector<Command> 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<Command> commands;
// Tree of all command buffers
std::unique_ptr<CommandBuffer> command_tree;
};
TraceReader() = default;

View File

@ -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();
}

View File

@ -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();