TraceViewer: Build a tree of all command buffers and display that instead of a flat list.
This commit is contained in:
parent
6101b70641
commit
cbccc785cc
|
@ -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;
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue