Fix the ImGe debugger vertex list

This commit is contained in:
Henrik Rydgård
2026-06-02 14:51:38 +02:00
parent bb573e6e0c
commit c46774c923
12 changed files with 237 additions and 200 deletions
+6
View File
@@ -22,6 +22,12 @@ public:
std::string_view as_view() const {
return std::string_view(start_, p_ - start_);
}
const char *begin() const {
return start_;
}
const char *end() {
return p_;
}
size_t size() const {
return p_ - start_;
+9 -2
View File
@@ -202,7 +202,7 @@ private:
int scaleFactor_ = 0;
};
// TODO: Replace with TransformedVertex?
// This is used to represent both pre- and post transformed vertices, so is a bit of a mess.
struct GPUDebugVertex {
float u;
float v;
@@ -213,20 +213,27 @@ struct GPUDebugVertex {
float w;
union {
u8 c[4];
u8 c0[4];
u32 color0_32;
};
union {
u8 c1[4];
u32 color1_32;
};
float fog;
float nx;
float ny;
float nz;
float weights[8];
};
enum class DebugVertexFlags {
Transformed = 1 << 0,
Clipped = 1 << 1,
DrawCoords = 1 << 2, // if not set, output X/Y will be in screen coords.
};
ENUM_CLASS_BITOPS(DebugVertexFlags);
+27 -9
View File
@@ -1325,7 +1325,7 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
}
const u32 vertTypeID = GetVertTypeID(gstate.vertType, gstate.getUVGenMode(), true);
bool savedVertexFullAlpha = gstate_c.vertexFullAlpha;
const bool throughMode = (vertTypeID & GE_VTYPE_THROUGH) != 0;
// Points is the only primitive that generates 6x as many vertices as input indices (2 triangles per point).
std::vector<u16> indexTemp;
@@ -1363,7 +1363,10 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
LoadUVScaleOffsetVec(gstate).Store(&uvScale.uScale);
const u8 *startPos = verts + indexLowerBound * dec->VertexSize();
bool savedVertexFullAlpha = gstate_c.vertexFullAlpha;
dec->DecodeVerts(vertsTemp.data(), startPos, &uvScale, verticesToDecode);
gstate_c.vertexFullAlpha = savedVertexFullAlpha;
int numDecodedVerts = verticesToDecode;
u16 *inds = indexTemp.data();
@@ -1384,6 +1387,7 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
for (int i = 0; i < verticesToDecode; i++) {
reader.Goto(i);
GPUDebugVertex &sv = debugVertices[i];
sv = {};
if (vertTypeID & GE_VTYPE_TC_MASK) {
reader.ReadUV(&sv.u);
} else {
@@ -1393,7 +1397,7 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
if (vertTypeID & GE_VTYPE_COL_MASK) {
sv.color0_32 = reader.ReadColor0_8888();
} else {
memcpy(sv.c, defaultColor, 4);
memcpy(sv.c0, defaultColor, 4);
}
if (vertTypeID & GE_VTYPE_NRM_MASK) {
@@ -1403,6 +1407,11 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
sv.ny = 0.0f;
sv.nz = 1.0f;
}
if (vertTypeID & GE_VTYPE_WEIGHT_MASK) {
reader.ReadWeights(sv.weights);
}
reader.ReadPosAuto((float *)&sv.x);
}
@@ -1470,7 +1479,7 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
params.transformed = transformed.data();
params.transformedExpanded = transformedExpanded.data();
RunSoftwareTransform(params, prim, verticesToDecode, dec->GetDecVtxFmt(), numDecodedVerts, 65536, generatedIndices, inds, (int)indexTemp.size(), &result);
RunSoftwareTransform(params, prim, vertTypeID, dec->GetDecVtxFmt(), numDecodedVerts, 65536, generatedIndices, inds, (int)indexTemp.size(), &result);
// Output of software transform is always triangles.
prim = GE_PRIM_TRIANGLES;
@@ -1486,22 +1495,31 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
debugIndices.resize(result.drawIndexCount);
memcpy(debugIndices.data(), inds, result.drawIndexCount * sizeof(u16));
const bool applyOffset = (flags & DebugVertexFlags::DrawCoords) && !throughMode;
const float offsetX = applyOffset ? -gstate.getOffsetX() : 0.0f;
const float offsetY = applyOffset ? -gstate.getOffsetY() : 0.0f;
// Convert the transformed vertices to the debug vertex format.
debugVertices.resize(result.drawVertexCount);
for (int i = 0; i < result.drawVertexCount; i++) {
const TransformedVertex &vtx = result.drawBuffer[i];
GPUDebugVertex &dv = debugVertices[i];
dv.x = vtx.x;
dv.y = vtx.y;
dv.x = vtx.x + offsetX;
dv.y = vtx.y + offsetY;
dv.z = vtx.z;
dv.w = vtx.pos_w;
dv.u = vtx.u;
dv.v = vtx.v;
dv.fog = vtx.fog;
dv.c[0] = (vtx.color0_32 >> 24) & 0xFF;
dv.c[1] = (vtx.color0_32 >> 16) & 0xFF;
dv.c[2] = (vtx.color0_32 >> 8) & 0xFF;
dv.c[3] = vtx.color0_32 & 0xFF;
dv.c0[0] = (vtx.color0_32 >> 24) & 0xFF;
dv.c0[1] = (vtx.color0_32 >> 16) & 0xFF;
dv.c0[2] = (vtx.color0_32 >> 8) & 0xFF;
dv.c0[3] = vtx.color0_32 & 0xFF;
dv.c1[0] = (vtx.color1_32 >> 24) & 0xFF;
dv.c1[1] = (vtx.color1_32 >> 16) & 0xFF;
dv.c1[2] = (vtx.color1_32 >> 8) & 0xFF;
dv.c1[3] = vtx.color1_32 & 0xFF;
}
*outPrim = prim;
return true;
+64 -95
View File
@@ -12,6 +12,38 @@
#include "GPU/Common/SplineCommon.h"
#include "Core/System.h"
const char *const g_vertexListDecodedColNames[] = {
"X",
"Y",
"Z",
"U",
"V",
"Color",
"NX",
"NY",
"NZ",
"W0",
"W1",
"W2",
"W3",
"W4",
"W5",
"W6",
"W7",
};
const char *const g_vertexListTransformedColNames[] = {
"X",
"Y",
"Z",
"W",
"U",
"V",
"Color0",
"Color1",
"Fog",
};
void FormatStateRow(char *dest, size_t destSize, CmdFormatType fmt, u32 value, bool enabled, u32 otherValue, u32 otherValue2) {
value &= 0xFFFFFF;
otherValue &= 0xFFFFFF;
@@ -540,122 +572,59 @@ void FormatStateRow(char *dest, size_t destSize, CmdFormatType fmt, u32 value, b
}
}
void FormatVertCol(char *dest, size_t destSize, const GPUDebugVertex &vert, int col) {
void FormatVertColTransformed(char *dest, size_t destSize, const GPUDebugVertex &vert, VertexListTransformedCol col) {
switch (col) {
case VERTEXLIST_COL_X: snprintf(dest, destSize, "%f", vert.x); break;
case VERTEXLIST_COL_Y: snprintf(dest, destSize, "%f", vert.y); break;
case VERTEXLIST_COL_Z: snprintf(dest, destSize, "%f", vert.z); break;
case VERTEXLIST_COL_W: snprintf(dest, destSize, "%f", vert.w); break;
case VERTEXLIST_COL_U: snprintf(dest, destSize, "%f", vert.u); break;
case VERTEXLIST_COL_V: snprintf(dest, destSize, "%f", vert.v); break;
case VERTEXLIST_COL_COLOR:
snprintf(dest, destSize, "%02x%02x%02x%02x", vert.c[0], vert.c[1], vert.c[2], vert.c[3]);
case VertexListTransformedCol::X: snprintf(dest, destSize, "%f", vert.x); break;
case VertexListTransformedCol::Y: snprintf(dest, destSize, "%f", vert.y); break;
case VertexListTransformedCol::Z: snprintf(dest, destSize, "%f", vert.z); break;
case VertexListTransformedCol::W: snprintf(dest, destSize, "%f", vert.w); break;
case VertexListTransformedCol::U: snprintf(dest, destSize, "%f", vert.u); break;
case VertexListTransformedCol::V: snprintf(dest, destSize, "%f", vert.v); break;
case VertexListTransformedCol::COLOR0:
snprintf(dest, destSize, "%02x%02x%02x%02x", vert.c0[0], vert.c0[1], vert.c0[2], vert.c0[3]);
break;
case VERTEXLIST_COL_NX: snprintf(dest, destSize, "%f", vert.nx); break;
case VERTEXLIST_COL_NY: snprintf(dest, destSize, "%f", vert.ny); break;
case VERTEXLIST_COL_NZ: snprintf(dest, destSize, "%f", vert.nz); break;
case VERTEXLIST_COL_FOG: snprintf(dest, destSize, "%f", vert.fog); break;
case VertexListTransformedCol::COLOR1:
snprintf(dest, destSize, "%02x%02x%02x%02x", vert.c1[0], vert.c1[1], vert.c1[2], vert.c1[3]);
break;
case VertexListTransformedCol::FOG: snprintf(dest, destSize, "%f", vert.fog); break;
default:
truncate_cpy(dest, destSize, "Invalid");
break;
}
}
static void FormatVertColRawType(char *dest, size_t destSize, const void *data, int type, int offset);
static void FormatVertColRawColor(char *dest, size_t destSize, const void *data, int type);
void FormatVertColRaw(VertexDecoder *decoder, char *dest, size_t destSize, int row, int col) {
void FormatVertColDecoded(char *dest, size_t destSize, const GPUDebugVertex &vert, VertexListDecodedCol col) {
if (PSP_GetBootState() != BootState::Complete) {
truncate_cpy(dest, destSize, "Invalid");
return;
}
// We could use the vertex decoder and reader, but those already do some minor adjustments.
// There's only a few values - let's just go after them directly.
const u8 *vert = Memory::GetPointer(gpu->GetVertexAddress()) + row * decoder->size;
const u8 *pos = vert + decoder->posoff;
const u8 *tc = vert + decoder->tcoff;
const u8 *color = vert + decoder->coloff;
const u8 *norm = vert + decoder->nrmoff;
switch (col) {
case VERTEXLIST_COL_X:
FormatVertColRawType(dest, destSize, pos, decoder->pos, 0);
case VertexListDecodedCol::X: snprintf(dest, destSize, "%f", vert.x); break;
case VertexListDecodedCol::Y: snprintf(dest, destSize, "%f", vert.y); break;
case VertexListDecodedCol::Z: snprintf(dest, destSize, "%f", vert.z); break;
case VertexListDecodedCol::U: snprintf(dest, destSize, "%f", vert.u); break;
case VertexListDecodedCol::V: snprintf(dest, destSize, "%f", vert.v); break;
case VertexListDecodedCol::COLOR:
snprintf(dest, destSize, "%02x%02x%02x%02x", vert.c0[0], vert.c0[1], vert.c0[2], vert.c0[3]);
break;
case VERTEXLIST_COL_Y:
FormatVertColRawType(dest, destSize, pos, decoder->pos, 1);
break;
case VERTEXLIST_COL_Z:
FormatVertColRawType(dest, destSize, pos, decoder->pos, 2);
break;
case VERTEXLIST_COL_U:
FormatVertColRawType(dest, destSize, tc, decoder->tc, 0);
break;
case VERTEXLIST_COL_V:
FormatVertColRawType(dest, destSize, tc, decoder->tc, 1);
break;
case VERTEXLIST_COL_COLOR:
FormatVertColRawColor(dest, destSize, color, decoder->col);
break;
case VERTEXLIST_COL_NX: FormatVertColRawType(dest, destSize, norm, decoder->nrm, 0); break;
case VERTEXLIST_COL_NY: FormatVertColRawType(dest, destSize, norm, decoder->nrm, 1); break;
case VERTEXLIST_COL_NZ: FormatVertColRawType(dest, destSize, norm, decoder->nrm, 2); break;
case VertexListDecodedCol::NX: snprintf(dest, destSize, "%f", vert.nx); break;
case VertexListDecodedCol::NY: snprintf(dest, destSize, "%f", vert.ny); break;
case VertexListDecodedCol::NZ: snprintf(dest, destSize, "%f", vert.nz); break;
case VertexListDecodedCol::W0: snprintf(dest, destSize, "%f", vert.weights[0]); break;
case VertexListDecodedCol::W1: snprintf(dest, destSize, "%f", vert.weights[1]); break;
case VertexListDecodedCol::W2: snprintf(dest, destSize, "%f", vert.weights[2]); break;
case VertexListDecodedCol::W3: snprintf(dest, destSize, "%f", vert.weights[3]); break;
case VertexListDecodedCol::W4: snprintf(dest, destSize, "%f", vert.weights[4]); break;
case VertexListDecodedCol::W5: snprintf(dest, destSize, "%f", vert.weights[5]); break;
case VertexListDecodedCol::W6: snprintf(dest, destSize, "%f", vert.weights[6]); break;
case VertexListDecodedCol::W7: snprintf(dest, destSize, "%f", vert.weights[7]); break;
default:
truncate_cpy(dest, destSize, "Invalid");
break;
}
}
static void FormatVertColRawType(char *dest, size_t destSize, const void *data, int type, int offset) {
switch (type) {
case 0:
truncate_cpy(dest, destSize, "-");
break;
case 1: // 8-bit
snprintf(dest, destSize, "%02x", ((const u8 *)data)[offset]);
break;
case 2: // 16-bit
snprintf(dest, destSize, "%04x", ((const u16_le *)data)[offset]);
break;
case 3: // float
snprintf(dest, destSize, "%f", ((const float *)data)[offset]);
break;
default:
truncate_cpy(dest, destSize, "Invalid");
break;
}
}
static void FormatVertColRawColor(char *dest, size_t destSize, const void *data, int type) {
switch (type) {
case GE_VTYPE_COL_NONE >> GE_VTYPE_COL_SHIFT:
truncate_cpy(dest, destSize, "-");
break;
case GE_VTYPE_COL_565 >> GE_VTYPE_COL_SHIFT:
case GE_VTYPE_COL_5551 >> GE_VTYPE_COL_SHIFT:
case GE_VTYPE_COL_4444 >> GE_VTYPE_COL_SHIFT:
snprintf(dest, destSize, "%04x", *(const u16_le *)data);
break;
case GE_VTYPE_COL_8888 >> GE_VTYPE_COL_SHIFT:
snprintf(dest, destSize, "%08x", *(const u32_le *)data);
break;
default:
truncate_cpy(dest, destSize, "Invalid");
break;
}
}
static void SwapUVs(GPUDebugVertex &a, GPUDebugVertex &b) {
float tempu = a.u;
float tempv = a.v;
+41 -15
View File
@@ -10,20 +10,45 @@
#include "GPU/Common/SplineCommon.h"
#include "GPU/GPUCommon.h"
enum VertexListCols {
VERTEXLIST_COL_X,
VERTEXLIST_COL_Y,
VERTEXLIST_COL_Z,
VERTEXLIST_COL_W,
VERTEXLIST_COL_U,
VERTEXLIST_COL_V,
VERTEXLIST_COL_COLOR,
VERTEXLIST_COL_NX,
VERTEXLIST_COL_NY,
VERTEXLIST_COL_NZ,
VERTEXLIST_COL_FOG,
VERTEXLIST_COL_COUNT,
// TODO: Add back a true raw vertex list?
enum class VertexListDecodedCol {
X,
Y,
Z,
U,
V,
COLOR,
NX,
NY,
NZ,
W0,
W1,
W2,
W3,
W4,
W5,
W6,
W7,
COUNT,
};
extern const char *const g_vertexListDecodedColNames[];
enum class VertexListTransformedCol {
X,
Y,
Z,
W,
U,
V,
COLOR0,
COLOR1,
FOG,
COUNT,
};
// Indexed by the above enum;
extern const char *const g_vertexListTransformedColNames[];
extern const GECommand g_stateFlagsRows[];
extern const GECommand g_stateLightingRows[];
@@ -38,8 +63,9 @@ struct GPUDebugVertex;
class VertexDecoder;
void FormatStateRow(char *dest, size_t destSize, CmdFormatType fmt, u32 value, bool enabled, u32 otherValue, u32 otherValue2);
void FormatVertCol(char *dest, size_t destSize, const GPUDebugVertex &vert, int col);
void FormatVertColRaw(VertexDecoder *decoder, char *dest, size_t destSize, int row, int col);
void FormatVertColDecoded(char *dest, size_t destSize, const GPUDebugVertex &vert, VertexListDecodedCol col);
void FormatVertColTransformed(char *dest, size_t destSize, const GPUDebugVertex &vert, VertexListTransformedCol col);
// These are utilities used by the debugger vertex preview.
bool GetPrimPreview(u32 op, GEPrimitiveType &prim, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int *lowerIndexBound, bool transformed);
+17
View File
@@ -1,5 +1,22 @@
#include "GPU/ge_constants.h"
const char *GePrimTypeToString(GEPrimitiveType prim) {
static constexpr const char * primTypes[8] = {
"POINTS",
"LINES",
"LINE_STRIP",
"TRI_LIST",
"TRI_STRIP",
"TRI_FAN",
"RECTS",
"CONTINUE_PREV",
};
const int p = static_cast<int>(prim);
if (p < 0 || p >= 8)
return "INVALID";
return primTypes[p];
}
const char *GeBufferFormatToString(GEBufferFormat fmt) {
switch (fmt) {
case GE_FORMAT_4444: return "4444";
+3 -14
View File
@@ -94,17 +94,6 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
u32 cmd = op >> 24;
u32 data = op & 0xFFFFFF;
static constexpr const char * primTypes[8] = {
"POINTS",
"LINES",
"LINE_STRIP",
"TRIANGLES",
"TRIANGLE_STRIP",
"TRIANGLE_FAN",
"RECTANGLES",
"CONTINUE_PREVIOUS",
};
// Handle control and drawing commands here directly. The others we delegate.
switch (cmd)
{
@@ -144,9 +133,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
u32 count = data & 0xFFFF;
u32 type = (data >> 16) & 7;
if (gstate.vertType & GE_VTYPE_IDX_MASK)
snprintf(buffer, bufsize, "DRAW PRIM %s: count= %i vaddr= %08x, iaddr= %08x", type < 7 ? primTypes[type] : "INVALID", count, gstate_c.vertexAddr, gstate_c.indexAddr);
snprintf(buffer, bufsize, "DRAW PRIM %s: count= %i vaddr= %08x, iaddr= %08x", GePrimTypeToString(static_cast<GEPrimitiveType>(type)), count, gstate_c.vertexAddr, gstate_c.indexAddr);
else
snprintf(buffer, bufsize, "DRAW PRIM %s: count= %i vaddr= %08x", type < 7 ? primTypes[type] : "INVALID", count, gstate_c.vertexAddr);
snprintf(buffer, bufsize, "DRAW PRIM %s: count= %i vaddr= %08x", GePrimTypeToString(static_cast<GEPrimitiveType>(type)), count, gstate_c.vertexAddr);
}
break;
@@ -1365,7 +1354,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
bool texturing = (data & GE_IMM_TEXTURE) != 0;
bool dither = (data & GE_IMM_DITHER) != 0;
char *p = buffer;
p += snprintf(p, bufsize - (p - buffer), "Vertex draw: alpha=%02x, prim=%s", data & 0xFF, primTypes[(data >> 8) & 7]);
p += snprintf(p, bufsize - (p - buffer), "Vertex draw: alpha=%02x, prim=%s", data & 0xFF, GePrimTypeToString(static_cast<GEPrimitiveType>((data >> 8) & 7)));
if (antialias)
p += snprintf(p, bufsize - (p - buffer), ", antialias");
if (clip != 0)
+2 -2
View File
@@ -1016,9 +1016,9 @@ bool TransformUnit::GetCurrentDrawAsDebugVertices(int count, std::vector<GPUDebu
}
if (gstate.vertType & GE_VTYPE_COL_MASK) {
memcpy(vertices[i].c, vert.color, sizeof(vertices[i].c));
memcpy(vertices[i].c0, vert.color, sizeof(vertices[i].c0));
} else {
memset(vertices[i].c, 0, sizeof(vertices[i].c));
memset(vertices[i].c0, 0, sizeof(vertices[i].c0));
}
vertices[i].nx = vert.nrm.x;
vertices[i].ny = vert.nrm.y;
+2
View File
@@ -595,6 +595,8 @@ enum GEPrimitiveType : int8_t {
GE_PRIM_INVALID = -1,
};
const char *GePrimTypeToString(GEPrimitiveType prim);
enum GELogicOp : uint8_t {
GE_LOGIC_CLEAR = 0,
GE_LOGIC_AND = 1,
-1
View File
@@ -121,7 +121,6 @@ struct ImConfig {
int requesterToken;
bool sasShowAllVoices = false;
bool vertexListTransformed = false;
bool vertexListClipped = false;
float fbViewerZoom = 1.0f;
+63 -58
View File
@@ -1709,85 +1709,91 @@ void DrawImGeVertsWindow(ImConfig &cfg, ImControl &control, GPUCommon *gpuDebug)
return;
}
ImGui::Checkbox("Transformed", &cfg.vertexListTransformed);
ImGui::SameLine();
if (gstate.isModeThrough()) {
ImGui::BeginDisabled();
}
ImGui::Checkbox("Clipped", &cfg.vertexListClipped);
if (gstate.isModeThrough()) {
ImGui::EndDisabled();
}
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(fmtTemp, sizeof(fmtTemp), CMD_FMT_VERTEXTYPE, state.vertType, true, false, false);
ImGui::TextUnformatted(fmtTemp);
auto buildVertexTable = [&](bool transformed) {
// Let's see if it's fast enough to just do all this each frame.
GEPrimitiveType prim;
std::vector<GPUDebugVertex> vertices;
std::vector<u16> indices;
int indexOffset = 0;
// Let's see if it's fast enough to just do all this each frame.
GEPrimitiveType prim;
int rowCount_ = gpuDebug->GetCurrentPrimCount(&prim);
std::vector<GPUDebugVertex> vertices;
std::vector<u16> indices;
int previewIndexOffset_ = 0;
DebugVertexFlags flags{};
if (transformed) {
flags |= DebugVertexFlags::Transformed | DebugVertexFlags::DrawCoords;
}
if (cfg.vertexListClipped && !gstate.isModeThrough()) {
flags |= DebugVertexFlags::Clipped;
}
DebugVertexFlags flags{};
if (cfg.vertexListTransformed) {
flags |= DebugVertexFlags::Transformed;
}
if (cfg.vertexListClipped) {
flags |= DebugVertexFlags::Clipped;
}
int inputVertexCount = gpuDebug->GetCurrentPrimCount(&prim);
// This performs software transform, if transformed is checked. We might want to cache it? Although, it's only for a single draw...
TransformStats stats;
if (!gpuDebug->GetCurrentDrawAsDebugVertices(prim, &prim, rowCount_, vertices, indices, &previewIndexOffset_, &stats, flags)) {
rowCount_ = 0;
}
auto buildVertexTable = [&](bool raw) {
// Ignore indices for now.
if (ImGui::BeginTable("rawverts", VERTEXLIST_COL_COUNT + 1, tableFlags)) {
static VertexDecoder decoder;
u32 vertTypeID = GetVertTypeID(state.vertType, state.getUVGenMode(), true);
VertexDecoderOptions options{};
decoder.SetVertexType(vertTypeID, options);
// This performs software transform, if transformed is checked. We might want to cache it? Although, it's only for a single draw...
TransformStats stats;
if (!gpuDebug->GetCurrentDrawAsDebugVertices(prim, &prim, inputVertexCount, vertices, indices, &indexOffset, &stats, flags)) {
inputVertexCount = 0;
}
static const char * const colNames[] = {
"Index",
"X",
"Y",
"Z",
"W",
"U",
"V",
"Color",
"NX",
"NY",
"NZ",
"Fog",
};
for (int i = 0; i < ARRAY_SIZE(colNames); i++) {
ImGui::TableSetupColumn(colNames[i], ImGuiTableColumnFlags_WidthFixed, 0.0f, i);
const int rowCount = indices.empty() ? (int)vertices.size() : (int)indices.size();
char statusLine[256];
StringWriter w(statusLine);
w.F("%s: ", GePrimTypeToString(prim));
char fmtTemp[256];
FormatStateRow(fmtTemp, sizeof(fmtTemp), CMD_FMT_VERTEXTYPE, state.vertType, true, false, false);
w.F("%s", fmtTemp);
if (gstate.isModeClear()) {
w.C(" (clearmode)");
}
ImGui::TextUnformatted(w.begin(), w.end());
if (transformed) {
ImGui::Text("Culled far: %d near: %d Clipped: %d", stats.culledTrianglesFar, stats.culledTrianglesNear, stats.clippedTriangles);
}
const int colCount = transformed ? (int)VertexListTransformedCol::COUNT : (int)VertexListDecodedCol::COUNT;
// + 1 for the index column.
if (ImGui::BeginTable("rawverts", colCount + 1, tableFlags)) {
const char *const *colNames = transformed ? g_vertexListTransformedColNames : g_vertexListDecodedColNames;
ImGui::TableSetupColumn("Index", ImGuiTableColumnFlags_WidthFixed, 0.0f, 0);
for (int i = 0; i < colCount; i++) {
ImGui::TableSetupColumn(colNames[i], ImGuiTableColumnFlags_WidthFixed, 0.0f, i + 1);
}
ImGui::TableSetupScrollFreeze(0, 1); // Make header row always visible
ImGui::TableHeadersRow();
ImGuiListClipper clipper;
_dbg_assert_(rowCount_ >= 0);
clipper.Begin(rowCount_);
_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];
int index = indices.empty() ? i : (indices[i] - indexOffset);
ImGui::PushID(i);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushID(i);
ImGui::Text("%d", index);
for (int column = 0; column < VERTEXLIST_COL_COUNT; column++) {
for (int column = 0; column < colCount; column++) {
ImGui::TableNextColumn();
char temp[36];
if (raw) {
FormatVertColRaw(&decoder, temp, sizeof(temp), index, column);
if (transformed) {
FormatVertColTransformed(temp, sizeof(temp), vertices[index], (VertexListTransformedCol)column);
} else {
FormatVertCol(temp, sizeof(temp), vertices[index], column);
FormatVertColDecoded(temp, sizeof(temp), vertices[index], (VertexListDecodedCol)column);
}
ImGui::TextUnformatted(temp);
}
@@ -1800,13 +1806,12 @@ void DrawImGeVertsWindow(ImConfig &cfg, ImControl &control, GPUCommon *gpuDebug)
}
};
if (ImGui::BeginTabItem("Raw")) {
buildVertexTable(true);
if (ImGui::BeginTabItem("Decoded")) {
buildVertexTable(false);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Transformed")) {
ImGui::Text("Culled far: %d near: %d Clipped: %d", stats.culledTrianglesFar, stats.culledTrianglesNear, stats.clippedTriangles);
buildVertexTable(false);
buildVertexTable(true);
ImGui::EndTabItem();
}
// TODO: Let's not include columns for which we have no data.
+3 -4
View File
@@ -138,19 +138,18 @@ void CtrlVertexList::GetColumnText(wchar_t *dest, size_t destSize, int row, int
swprintf(dest, destSize, L"Invalid index %d", row);
return;
}
row = indices[row];
row = indices[row - previewIndexOffset_];
}
char temp[256];
if (raw_) {
FormatVertColRaw(decoder, temp, sizeof(temp), row, col);
FormatVertColDecoded(temp, sizeof(temp), vertices[row], (VertexListDecodedCol)col);
} else {
if (row >= (int)vertices.size()) {
swprintf(dest, destSize, L"Invalid vertex %d", row);
return;
}
FormatVertCol(temp, sizeof(temp), vertices[row], col);
FormatVertColTransformed(temp, sizeof(temp), vertices[row], (VertexListTransformedCol)col);
}
ConvertUTF8ToWString(dest, destSize, temp);
}