Skip to content

Commit

Permalink
Merge pull request #19702 from hrydgard/ge-debugger-vertices
Browse files Browse the repository at this point in the history
ImDebugger GE: Add vertex data viewer
  • Loading branch information
hrydgard authored Dec 6, 2024
2 parents dab40ab + 5f83e48 commit 246e269
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 78 deletions.
3 changes: 3 additions & 0 deletions GPU/Common/DrawEngineCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,9 @@ bool DrawEngineCommon::GetCurrentSimpleVertices(int count, std::vector<GPUDebugV
Matrix4ByMatrix4(worldview, world, view);
Matrix4ByMatrix4(worldviewproj, worldview, gstate.projMatrix);

// This transforms the vertices.
// NOTE: We really should just run the full software transform?

vertices.resize(indexUpperBound + 1);
uint32_t vertType = gstate.vertType;
for (int i = indexLowerBound; i <= indexUpperBound; ++i) {
Expand Down
5 changes: 4 additions & 1 deletion GPU/Common/GPUDebugInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#pragma once

#include <vector>
#include <list>
#include <string>

#include "Common/Math/expression_parser.h"
Expand Down Expand Up @@ -200,7 +201,7 @@ class GPUDebugInterface {
public:
virtual ~GPUDebugInterface() {}
virtual bool GetCurrentDisplayList(DisplayList &list) = 0;
virtual bool GetCurrentCommand(u32 *cmd) = 0;
virtual int GetCurrentPrimCount() = 0;
virtual std::vector<DisplayList> ActiveDisplayLists() = 0;
virtual void ResetListPC(int listID, u32 pc) = 0;
virtual void ResetListStall(int listID, u32 stall) = 0;
Expand Down Expand Up @@ -239,6 +240,8 @@ class GPUDebugInterface {
virtual std::vector<std::string> DebugGetShaderIDs(DebugShaderType shader) = 0;
virtual std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) = 0;
virtual bool DescribeCodePtr(const u8 *ptr, std::string &name) = 0;
virtual const std::list<int> &GetDisplayListQueue() = 0;
virtual const DisplayList &GetDisplayList(int index) = 0;

virtual bool GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) {
return false;
Expand Down
2 changes: 1 addition & 1 deletion GPU/Debugger/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ enum VertexListCols {
VERTEXLIST_COL_NX,
VERTEXLIST_COL_NY,
VERTEXLIST_COL_NZ,
VERTEXLIST_COL_COUNT,
};

class GPUDebugInterface;


struct TabStateRow {
std::string_view title;
uint8_t cmd;
Expand Down
64 changes: 11 additions & 53 deletions GPU/GPUCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

#include <algorithm>

#include "ext/imgui/imgui.h"
#include "Common/Profiler/Profiler.h"

#include "Common/GraphicsContext.h"
Expand Down Expand Up @@ -1581,13 +1580,21 @@ bool GPUCommon::GetCurrentDisplayList(DisplayList &list) {
return true;
}

bool GPUCommon::GetCurrentCommand(u32 *cmd) {
int GPUCommon::GetCurrentPrimCount() {
DisplayList list;
if (GetCurrentDisplayList(list)) {
*cmd = Memory::Read_U32(list.pc);
u32 cmd = Memory::Read_U32(list.pc);
if ((cmd >> 24) == GE_CMD_PRIM || (cmd >> 24) == GE_CMD_BOUNDINGBOX) {
return cmd & 0xFFFF;
} else if ((cmd >> 24) == GE_CMD_BEZIER || (cmd >> 24) == GE_CMD_SPLINE) {
u32 u = (cmd & 0x00FF) >> 0;
u32 v = (cmd & 0xFF00) >> 8;
return u * v;
}
return true;
} else {
return false;
// Current prim value.
return gstate.cmdmem[GE_CMD_PRIM] & 0xFFFF;
}
}

Expand Down Expand Up @@ -2004,52 +2011,3 @@ bool GPUCommon::DescribeCodePtr(const u8 *ptr, std::string &name) {
return drawEngineCommon_->DescribeCodePtr(ptr, name);
}

void GPUCommon::DrawImGuiDebugger() {
if (ImGui::Button("Run/Resume")) {
Core_Resume();
}
ImGui::SameLine();
ImGui::TextUnformatted("Break:");
ImGui::SameLine();
if (ImGui::Button("Frame")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::FRAME);
}
ImGui::SameLine();
if (ImGui::Button("Tex")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::TEX);
}
ImGui::SameLine();
if (ImGui::Button("NonTex")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::NONTEX);
}
ImGui::SameLine();
if (ImGui::Button("Prim")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::PRIM);
}
ImGui::SameLine();
if (ImGui::Button("Draw")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::DRAW);
}
ImGui::SameLine();
if (ImGui::Button("Curve")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::CURVE);
}
ImGui::SameLine();
if (ImGui::Button("Single step")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::OP);
}

// Let's display the current CLUT.

// First, let's list any active display lists.
ImGui::Text("Next list ID: %d", nextListID);
for (auto index : dlQueue) {
const auto &list = dls[index];
char title[64];
snprintf(title, sizeof(title), "List %d", list.id);
if (ImGui::CollapsingHeader(title, ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text("PC: %08x (start: %08x)", list.pc, list.startpc);
ImGui::Text("BBOX result: %d", (int)list.bboxResult);
}
}
}
11 changes: 6 additions & 5 deletions GPU/GPUCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ class GPUCommon : public GPUDebugInterface {
u32 DequeueList(int listid);
virtual int ListSync(int listid, int mode);
virtual u32 DrawSync(int mode);
int GetStack(int index, u32 stackPtr);
int GetStack(int index, u32 stackPtr);
virtual bool GetMatrix24(GEMatrixType type, u32_le *result, u32 cmdbits);
virtual void ResetMatrices();
virtual void DoState(PointerWrap &p);
Expand All @@ -265,8 +265,6 @@ class GPUCommon : public GPUDebugInterface {
// For example, a debugger is active.
bool ShouldSplitOverGe() const;

void DrawImGuiDebugger();

uint32_t SetAddrTranslation(uint32_t value) override;
uint32_t GetAddrTranslation() override;

Expand Down Expand Up @@ -328,7 +326,7 @@ class GPUCommon : public GPUDebugInterface {
// From GPUDebugInterface.
bool GetCurrentDisplayList(DisplayList &list) override;
bool GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) override;
bool GetCurrentCommand(u32 *cmd) override;
int GetCurrentPrimCount() override;
FramebufferManagerCommon *GetFramebufferManagerCommon() override {
return nullptr;
}
Expand Down Expand Up @@ -364,9 +362,12 @@ class GPUCommon : public GPUDebugInterface {
return &dls[listid];
}

const std::list<int>& GetDisplayLists() {
const std::list<int> &GetDisplayListQueue() override {
return dlQueue;
}
const DisplayList &GetDisplayList(int index) override {
return dls[index];
}

s64 GetListTicks(int listid) const {
if (listid >= 0 && listid < DisplayListMaxCount) {
Expand Down
2 changes: 1 addition & 1 deletion UI/ImDebugger/ImDebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu
}

if (cfg_.geDebuggerOpen) {
DrawGeDebuggerWindow(cfg_);
DrawGeDebuggerWindow(cfg_, gpuDebug);
}

if (cfg_.geStateOpen) {
Expand Down
141 changes: 138 additions & 3 deletions UI/ImDebugger/ImGe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
#include "GPU/Common/GPUDebugInterface.h"
#include "GPU/Common/FramebufferManagerCommon.h"
#include "GPU/Common/TextureCacheCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"

#include "Core/HLE/sceDisplay.h"
#include "Core/HW/Display.h"
#include "GPU/Debugger/State.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/GPUState.h"

void DrawFramebuffersWindow(ImConfig &cfg, FramebufferManagerCommon *framebufferManager) {
Expand Down Expand Up @@ -77,15 +79,61 @@ void DrawDebugStatsWindow(ImConfig &cfg) {
}

// Stub
void DrawGeDebuggerWindow(ImConfig &cfg) {
void DrawGeDebuggerWindow(ImConfig &cfg, GPUDebugInterface *gpuDebug) {
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("GE Debugger", &cfg.geDebuggerOpen)) {
ImGui::End();
return;
}

gpu->DrawImGuiDebugger();
if (ImGui::Button("Run/Resume")) {
Core_Resume();
}
ImGui::SameLine();
ImGui::TextUnformatted("Break:");
ImGui::SameLine();
if (ImGui::Button("Frame")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::FRAME);
}
ImGui::SameLine();
if (ImGui::Button("Tex")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::TEX);
}
ImGui::SameLine();
if (ImGui::Button("NonTex")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::NONTEX);
}
ImGui::SameLine();
if (ImGui::Button("Prim")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::PRIM);
}
ImGui::SameLine();
if (ImGui::Button("Draw")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::DRAW);
}
ImGui::SameLine();
if (ImGui::Button("Curve")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::CURVE);
}
ImGui::SameLine();
if (ImGui::Button("Single step")) {
GPUDebug::SetBreakNext(GPUDebug::BreakNext::OP);
}

// Let's display the current CLUT.

// First, let's list any active display lists.
// ImGui::Text("Next list ID: %d", nextListID);

for (auto index : gpuDebug->GetDisplayListQueue()) {
const auto &list = gpuDebug->GetDisplayList(index);
char title[64];
snprintf(title, sizeof(title), "List %d", list.id);
if (ImGui::CollapsingHeader(title, ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text("PC: %08x (start: %08x)", list.pc, list.startpc);
ImGui::Text("BBOX result: %d", (int)list.bboxResult);
}
}
ImGui::End();
}

Expand All @@ -96,7 +144,6 @@ void DrawGeStateWindow(ImConfig &cfg, GPUDebugInterface *gpuDebug) {
ImGui::End();
return;
}

if (ImGui::BeginTabBar("GeRegs", ImGuiTabBarFlags_None)) {
auto buildStateTab = [&](const char *tabName, const TabStateRow *rows, size_t numRows) {
if (ImGui::BeginTabItem(tabName)) {
Expand Down Expand Up @@ -135,6 +182,94 @@ void DrawGeStateWindow(ImConfig &cfg, GPUDebugInterface *gpuDebug) {
buildStateTab("Texture", g_stateTextureRows, g_stateTextureRowsSize);
buildStateTab("Settings", g_stateSettingsRows, g_stateSettingsRowsSize);

// Do a vertex tab (maybe later a separate window)
if (ImGui::BeginTabItem("Vertices")) {
const ImGuiTableFlags tableFlags =
ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollY;
if (ImGui::BeginTabBar("vertexmode", ImGuiTabBarFlags_None)) {
auto state = gpuDebug->GetGState();
char fmtTemp[256];
FormatStateRow(gpuDebug, fmtTemp, sizeof(fmtTemp), CMD_FMT_VERTEXTYPE, state.vertType, true, false, false);
ImGui::TextUnformatted(fmtTemp);
// Let's see if it's fast enough to just do all this each frame.
int rowCount_ = gpuDebug->GetCurrentPrimCount();
std::vector<GPUDebugVertex> vertices;
std::vector<u16> indices;
if (!gpuDebug->GetCurrentSimpleVertices(rowCount_, vertices, indices)) {
rowCount_ = 0;
}
VertexDecoderOptions options{};
// TODO: Maybe an option?
options.applySkinInDecode = true;

auto buildVertexTable = [&](bool raw) {
// Ignore indices for now.
if (ImGui::BeginTable("rawverts", VERTEXLIST_COL_COUNT + 1, tableFlags)) {
static VertexDecoder decoder;
decoder.SetVertexType(state.vertType, options);

static const char *colNames[] = {
"Index",
"X",
"Y",
"Z",
"U",
"V",
"Color",
"NX",
"NY",
"NZ",
};
for (int i = 0; i < ARRAY_SIZE(colNames); i++) {
ImGui::TableSetupColumn(colNames[i], ImGuiTableColumnFlags_WidthFixed, 0.0f, i);
}
ImGui::TableSetupScrollFreeze(0, 1); // Make header row always visible
ImGui::TableHeadersRow();

ImGuiListClipper clipper;
_dbg_assert_(rowCount_ >= 0);
clipper.Begin(rowCount_);
while (clipper.Step()) {
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
int index = indices.empty() ? i : indices[i];
ImGui::PushID(i);

ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%d", index);
for (int column = 0; column < VERTEXLIST_COL_COUNT; column++) {
ImGui::TableNextColumn();
char temp[36];
if (raw) {
FormatVertColRaw(&decoder, temp, sizeof(temp), index, column);
} else {
FormatVertCol(temp, sizeof(temp), vertices[index], column);
}
ImGui::TextUnformatted(temp);
}
ImGui::PopID();
}
}
clipper.End();

ImGui::EndTable();
}
};

if (ImGui::BeginTabItem("Raw")) {
buildVertexTable(true);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Transformed")) {
buildVertexTable(false);
ImGui::EndTabItem();
}
// TODO: Let's not include columns for which we have no data.
ImGui::EndTabBar();
}
ImGui::EndTabItem();
}

ImGui::EndTabBar();
}
ImGui::End();
Expand Down
2 changes: 1 addition & 1 deletion UI/ImDebugger/ImGe.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ void DrawFramebuffersWindow(ImConfig &cfg, FramebufferManagerCommon *framebuffer
void DrawTexturesWindow(ImConfig &cfg, TextureCacheCommon *textureCache);
void DrawDisplayWindow(ImConfig &cfg, FramebufferManagerCommon *framebufferManager);
void DrawDebugStatsWindow(ImConfig &cfg);
void DrawGeDebuggerWindow(ImConfig &cfg);
void DrawGeDebuggerWindow(ImConfig &cfg, GPUDebugInterface *gpuDebug);
void DrawGeStateWindow(ImConfig &cfg, GPUDebugInterface *gpuDebug);

class ImGeDebugger {
Expand Down
15 changes: 2 additions & 13 deletions Windows/GEDebugger/TabVertices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,19 +168,8 @@ int CtrlVertexList::GetRowCount() {

// TODO: Maybe there are smarter ways? Also, is this the best place to recalc?
auto state = gpuDebug->GetGState();
rowCount_ = state.prim & 0xFFFF;

// Override if we're on a prim command.
u32 cmd;
if (gpuDebug->GetCurrentCommand(&cmd)) {
if ((cmd >> 24) == GE_CMD_PRIM || (cmd >> 24) == GE_CMD_BOUNDINGBOX) {
rowCount_ = cmd & 0xFFFF;
} else if ((cmd >> 24) == GE_CMD_BEZIER || (cmd >> 24) == GE_CMD_SPLINE) {
u32 u = (cmd & 0x00FF) >> 0;
u32 v = (cmd & 0xFF00) >> 8;
rowCount_ = u * v;
}
}

int rowCount_ = gpuDebug->GetCurrentPrimCount();
if (!gpuDebug->GetCurrentSimpleVertices(rowCount_, vertices, indices)) {
rowCount_ = 0;
}
Expand Down

0 comments on commit 246e269

Please sign in to comment.