Merge pull request #21771 from hrydgard/bbox-debug

Fix culling for draws with 8-bit positions, visualize bbox corners in the ImGe debugger
This commit is contained in:
Henrik Rydgård
2026-06-03 15:54:03 +02:00
committed by GitHub
19 changed files with 419 additions and 159 deletions
+2 -1
View File
@@ -203,7 +203,8 @@ struct Vec4F32 {
static Vec4F32 LoadAligned(const float *src) { return Vec4F32{ _mm_load_ps(src) }; }
static Vec4F32 LoadS8Norm(const int8_t *src) {
__m128i value = _mm_set1_epi32(*((uint32_t *)src));
__m128i value32 = _mm_unpacklo_epi16(_mm_unpacklo_epi8(value, value), value);
__m128i value16 = _mm_unpacklo_epi8(value, value);
__m128i value32 = _mm_unpacklo_epi16(value16, value16);
// Sign extension. A bit ugly without SSE4.
value32 = _mm_srai_epi32(value32, 24);
return Vec4F32 { _mm_mul_ps(_mm_cvtepi32_ps(value32), _mm_set1_ps(1.0f / 128.0f)) };
+11 -16
View File
@@ -179,19 +179,15 @@ void DrawEngineCommon::DispatchSubmitImm(GEPrimitiveType prim, TransformedVertex
// - Only requires six plane evaluations then.
bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int vertexCount, const VertexDecoder *dec, u32 vertType) {
// Grab temp buffer space from large offsets in decoded_. Not exactly safe for large draws.
if (vertexCount > 1024) {
// Although this may lead to drawing that shouldn't happen, the viewport is more complex on VR.
// Let's always say objects are within bounds.
if (vertexCount > 1024 || gstate_c.Use(GPU_USE_VIRTUAL_REALITY)) {
return true;
}
SimpleVertex *corners = (SimpleVertex *)(decoded_ + 65536 * 12);
float *verts = (float *)(decoded_ + 65536 * 18);
// Although this may lead to drawing that shouldn't happen, the viewport is more complex on VR.
// Let's always say objects are within bounds.
if (gstate_c.Use(GPU_USE_VIRTUAL_REALITY)) {
return true;
}
// Try to skip NormalizeVertices if it's pure positions. No need to bother with a vertex decoder
// and a large vertex format.
@@ -333,6 +329,7 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
for (int i = 0; i < countToCheck; i++) {
if (insideCount[i] == 0) {
// All verts were outside one side.
return false;
}
}
@@ -347,8 +344,8 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
//
// NOTE: This doesn't handle through-mode or indexing (morph or skinning can be handled if they're implemented in software during decode).
template<u32 posFmt>
static bool TestBoundingBoxFast(const float *worldViewProj, const void *vdata, int vertexCount, const VertexDecoder *dec, u8 *decoded, ClipInfoFlags *clipInfoFlags) {
Mat4F32 worldViewProjMat(worldViewProj);
static bool TestBoundingBoxFast(const float *cullMatrix, const void *vdata, int vertexCount, const VertexDecoder *dec, ClipInfoFlags *clipInfoFlags) {
Mat4F32 cullMat(cullMatrix);
alignas(16) static const float planesXYData[4] = { 1, -1, 1, -1 };
Vec4F32 planesXY = Vec4F32::LoadAligned(planesXYData);
Vec4S32 insideMaskXY = Vec4S32::Zero();
@@ -358,10 +355,8 @@ static bool TestBoundingBoxFast(const float *worldViewProj, const void *vdata, i
// Used to reduce the Z precision. This effectively implements the small offsets where Z can be very slightly outside -1..1.
// In reality we should probably affect X and Y too, but meh.
alignas(16) static const u32 vertexMaskData[4] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFFFF};
Vec4S32 vertexMask = Vec4S32::LoadAligned((const int *)vertexMaskData);
const int stride = dec->VertexSize();
const int offset = dec->posoff;
const s8 *data = (const s8 *)vdata + offset;
const s8 *data = (const s8 *)vdata + dec->posoff;
const float vpZScale = gstate.getViewportZScale();
@@ -381,7 +376,7 @@ static bool TestBoundingBoxFast(const float *worldViewProj, const void *vdata, i
objPos = Vec4F32::Load((const float *)data);
break;
}
Vec4F32 clipPos = objPos.AsVec3ByMatrix44(worldViewProjMat) & vertexMask; // Not sure we should do the vertex mask thing.
Vec4F32 clipPos = objPos.AsVec3ByMatrix44(cullMat);
Vec4F32 posW = clipPos.ShuffleWWWW();
Vec4F32 posXY = clipPos.ShuffleXXYY();
Vec4F32 planeDistXY = posXY * planesXY + posW;
@@ -457,11 +452,11 @@ bool DrawEngineCommon::TestBoundingBoxFast(const float *cullMatrix, const void *
switch (vertType & GE_VTYPE_POS_MASK) {
case GE_VTYPE_POS_8BIT:
return ::TestBoundingBoxFast<GE_VTYPE_POS_8BIT>(cullMatrix, vdata, vertexCount, dec, decoded_, flags);
return ::TestBoundingBoxFast<GE_VTYPE_POS_8BIT>(cullMatrix, vdata, vertexCount, dec, flags);
case GE_VTYPE_POS_16BIT:
return ::TestBoundingBoxFast<GE_VTYPE_POS_16BIT>(cullMatrix, vdata, vertexCount, dec, decoded_, flags);
return ::TestBoundingBoxFast<GE_VTYPE_POS_16BIT>(cullMatrix, vdata, vertexCount, dec, flags);
case GE_VTYPE_POS_FLOAT:
return ::TestBoundingBoxFast<GE_VTYPE_POS_FLOAT>(cullMatrix, vdata, vertexCount, dec, decoded_, flags);
return ::TestBoundingBoxFast<GE_VTYPE_POS_FLOAT>(cullMatrix, vdata, vertexCount, dec, flags);
default:
// Shouldn't end up here with the checks outside this function.
_dbg_assert_(false);
+33 -30
View File
@@ -37,7 +37,7 @@
static bool ExpandRectangles(int vertexCount, int &numDecodedVerts, int vertsSize, u16 *&inds, int indsSize, const TransformedVertex *transformed, TransformedVertex *transformedExpanded, int *drawIndexCount, bool throughmode, bool *pixelMappedExactly);
static bool ExpandLines(int vertexCount, int &numDecodedVerts, int vertsSize, u16 *&inds, int indsSize, const TransformedVertex *transformed, TransformedVertex *transformedExpanded, int *drawIndexCount, bool throughmode);
static bool ExpandPoints(int vertexCount, int &maxIndex, int vertsSize, u16 *&inds, int indsSize, const TransformedVertex *transformed, TransformedVertex *transformedExpanded, int *drawIndexCount, bool throughmode);
static bool ExpandPoints(int vertexCount, int &maxIndex, int vertsSize, u16 *&inds, int indsSize, const TransformedVertex *transformed, TransformedVertex *transformedExpanded, int *drawIndexCount, bool throughmode, float pointScale);
static SoftwareTransformAction ProjectClipAndExpand(SoftwareTransformParams &params, int prim, int vertexCount, u32 vertType, u16 *&inds, int indsSize, int &numDecodedVerts, int vertsSize, SoftwareTransformResult *result);
// This is the software transform pipeline, which is necessary for supporting RECT
@@ -636,7 +636,7 @@ static SoftwareTransformAction ProjectClipAndExpand(SoftwareTransformParams &par
}
result->pixelMapped = false;
if (!ExpandPoints(vertexCount, numDecodedVerts, vertsSize, inds, indsSize, transformed, transformedExpanded, &drawIndexCount, throughmode)) {
if (!ExpandPoints(vertexCount, numDecodedVerts, vertsSize, inds, indsSize, transformed, transformedExpanded, &drawIndexCount, throughmode, params.pointScale)) {
result->drawVertexCount = 0;
result->drawIndexCount = 0;
result->drawBuffer = nullptr;
@@ -1104,7 +1104,7 @@ static bool ExpandLines(int vertexCount, int &numDecodedVerts, int vertsSize, u1
return true;
}
static bool ExpandPoints(int vertexCount, int &maxIndex, int vertsSize, u16 *&inds, int indsSize, const TransformedVertex *transformed, TransformedVertex *transformedExpanded, int *drawIndexCount, bool throughmode) {
static bool ExpandPoints(int vertexCount, int &maxIndex, int vertsSize, u16 *&inds, int indsSize, const TransformedVertex *transformed, TransformedVertex *transformedExpanded, int *drawIndexCount, bool throughmode, float pointScale) {
// Before we start, do a sanity check - does the output fit?
if (vertexCount * 6 > indsSize) {
// Won't fit, kill the draw.
@@ -1121,21 +1121,22 @@ static bool ExpandPoints(int vertexCount, int &maxIndex, int vertsSize, u16 *&in
u16 *newInds = inds + vertexCount;
u16 *indsOut = newInds;
float dx = 1.0f * gstate_c.vpWidthScale * (1.0f / gstate.getViewportXScale());
float dy = 1.0f * gstate_c.vpHeightScale * (1.0f / gstate.getViewportYScale());
float du = 1.0f / gstate_c.curTextureWidth;
float dv = 1.0f / gstate_c.curTextureHeight;
const float offset = pointScale != 1.0f ? -pointScale * 0.5f : 0.0f;
if (throughmode) {
dx = 1.0f;
dy = 1.0f;
}
const float dx = 1.0f * pointScale;
const float dy = 1.0f * pointScale;
const float du = 1.0f / gstate_c.curTextureWidth;
const float dv = 1.0f / gstate_c.curTextureHeight;
maxIndex = 4 * vertexCount;
for (int i = 0; i < vertexCount; ++i) {
const TransformedVertex &transVtxTL = transformed[indsIn[i]];
TransformedVertex transVtxTL = transformed[indsIn[i]];
// Centering, if the logic below enables it.
transVtxTL.x += offset;
transVtxTL.y += offset;
// Create the bottom right version.
// Create the bottom right corner.
TransformedVertex transVtxBR = transVtxTL;
transVtxBR.x += dx;
transVtxBR.y += dy;
@@ -1173,6 +1174,7 @@ static bool ExpandPoints(int vertexCount, int &maxIndex, int vertsSize, u16 *&in
indsOut[3] = i * 4 + 3;
indsOut[4] = i * 4 + 0;
indsOut[5] = i * 4 + 2;
trans += 4;
indsOut += 6;
}
@@ -1314,7 +1316,7 @@ static Vec3f ScreenToDrawing(const Vec3f& coords) {
// This is really just for vertex preview in the debugger, not for actual rendering!
// TODO: Support tessellation!! That's currently entirely broken (I guess maybe we'll draw the control points as something).
// count is the input vertex count (describes the draw together with prim).
bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType prim, GEPrimitiveType *outPrim, int count, std::vector<GPUDebugVertex> &debugVertices, std::vector<u16> &debugIndices, int *outLowerIndexBound, TransformStats *stats, DebugVertexFlags flags) {
bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GECommand cmd, GEPrimitiveType prim, GEPrimitiveType *outPrim, int count, std::vector<GPUDebugVertex> *debugVertices, std::vector<u16> *debugIndices, int *outLowerIndexBound, TransformStats *stats, DebugVertexFlags flags) {
// This is always for the current vertices.
u16 indexLowerBound = 0;
u16 indexUpperBound = count - 1;
@@ -1377,7 +1379,7 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
if (!(flags & DebugVertexFlags::Transformed)) {
// Output the untransformed vertices (although with skinning and morph baked-in from decode),
// and the original indices. Might be interesting to look at.
debugVertices.resize(verticesToDecode);
debugVertices->resize(verticesToDecode);
VertexReader reader(vertsTemp.data(), dec->GetDecVtxFmt(), vertTypeID);
const u8 defaultColor[4] = {
@@ -1389,7 +1391,7 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
for (int i = 0; i < verticesToDecode; i++) {
reader.Goto(i);
GPUDebugVertex &sv = debugVertices[i];
GPUDebugVertex &sv = (*debugVertices)[i];
sv = {};
if (vertTypeID & GE_VTYPE_TC_MASK) {
reader.ReadUV(&sv.u);
@@ -1421,24 +1423,24 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
// Output the indices straight
switch (vertTypeID & GE_VTYPE_IDX_MASK) {
case GE_VTYPE_IDX_NONE:
debugIndices.clear(); // it's just a sequence, effectively.
debugIndices->clear(); // it's just a sequence, effectively.
break;
case GE_VTYPE_IDX_8BIT:
debugIndices.resize(verticesToDecode);
debugIndices->resize(verticesToDecode);
for (int i = 0; i < verticesToDecode; i++) {
debugIndices[i] = ((const u8 *)indsPtr)[i];
(*debugIndices)[i] = ((const u8 *)indsPtr)[i];
}
break;
case GE_VTYPE_IDX_16BIT:
debugIndices.resize(verticesToDecode);
debugIndices->resize(verticesToDecode);
for (int i = 0; i < verticesToDecode; i++) {
debugIndices[i] = ((const u16_le *)indsPtr)[i];
(*debugIndices)[i] = ((const u16_le *)indsPtr)[i];
}
break;
case GE_VTYPE_IDX_32BIT:
debugIndices.resize(verticesToDecode);
debugIndices->resize(verticesToDecode);
for (int i = 0; i < verticesToDecode; i++) {
debugIndices[i] = ((const u32_le *)indsPtr)[i];
(*debugIndices)[i] = ((const u32_le *)indsPtr)[i];
}
break;
}
@@ -1469,7 +1471,7 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
break;
}
// After this, strips and fans have been collapsed to triangles.
// After index generation, strips and fans have been collapsed to triangles.
switch (prim) {
case GE_PRIM_LINE_STRIP:
prim = GE_PRIM_LINES;
@@ -1492,14 +1494,15 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
params.decoded = vertsTemp.data();
params.transformed = transformed.data();
params.transformedExpanded = transformedExpanded.data();
params.pointScale = cmd == GE_CMD_BOUNDINGBOX ? 4.0f : 1.0f; // Just make them more visible, for eas of debugging.
RunSoftwareTransform(params, prim, vertTypeID, dec->GetDecVtxFmt(), numDecodedVerts, 65536, generatedIndices, inds, (int)indexTemp.size(), &result);
// Output of software transform is always an indexed triangle list (or nothing).
if (result.drawIndexCount == 0) {
// Not a failue, but everything got culled.
debugVertices.clear();
debugIndices.clear();
debugVertices->clear();
debugIndices->clear();
return true;
}
@@ -1513,18 +1516,18 @@ bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType
gstate_c.vertexFullAlpha = savedVertexFullAlpha;
// Supply indices in a correctly-sized vector.
debugIndices.resize(result.drawIndexCount);
memcpy(debugIndices.data(), inds, result.drawIndexCount * sizeof(u16));
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);
debugVertices->resize(result.drawVertexCount);
for (int i = 0; i < result.drawVertexCount; i++) {
const TransformedVertex &vtx = result.drawBuffer[i];
GPUDebugVertex &dv = debugVertices[i];
GPUDebugVertex &dv = (*debugVertices)[i];
dv.x = vtx.x + offsetX;
dv.y = vtx.y + offsetY;
dv.z = vtx.z;
+2 -1
View File
@@ -65,6 +65,7 @@ struct SoftwareTransformParams {
bool allowClear;
bool allowSeparateAlphaClear;
bool everUsedEqualDepth;
float pointScale = 1.0f; // Useful to increase these for debug views of bounding box corners.
};
// Converts an index buffer to make the provoking vertex the last.
@@ -86,4 +87,4 @@ u32 NormalizeVertices(SimpleVertex *sverts, u8 *bufPtr, const u8 *inPtr, int low
// In the returned data, you should subtract the value of lowerIndexBound from the indices to get the actual vertex index in the vertices array.
// This is because some draws in some games use very large indices, but they only use a small range of them in each PRIM submission.
// Additionally, if the transformed flag is set in flags, the indices will be transformed into "generic" types (triangles instead of strips), etc.
bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GEPrimitiveType prim, GEPrimitiveType *outPrim, int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int *lowerIndexBound, TransformStats *stats, DebugVertexFlags flags);
bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, GECommand cmd, GEPrimitiveType prim, GEPrimitiveType *outPrim, int count, std::vector<GPUDebugVertex> *vertices, std::vector<u16> *indices, int *lowerIndexBound, TransformStats *stats, DebugVertexFlags flags);
+33 -32
View File
@@ -805,26 +805,28 @@ static void ExpandSpline(int &count, int op, const std::vector<SimpleVertex> &si
FreeAlignedMemory(cpoints.col);
}
bool GetPrimPreview(u32 op, GEPrimitiveType &prim, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int *lowerIndexBound, bool transformed) {
u32 prim_type = GE_PRIM_INVALID;
bool GetPrimPreview(u32 op, GEPrimitiveType *prim, std::vector<GPUDebugVertex> *vertices, std::vector<u16> *indices, int *lowerIndexBound, bool transformed) {
int count_u = 0;
int count_v = 0;
int count = 0;
const u32 cmd = op >> 24;
const GECommand cmd = static_cast<GECommand>(op >> 24);
if (cmd == GE_CMD_PRIM) {
prim_type = (op >> 16) & 0x7;
*prim = static_cast<GEPrimitiveType>((op >> 16) & 0x7); // irrelevant for bbox
count = op & 0xFFFF;
} else if (cmd == GE_CMD_BOUNDINGBOX) {
*prim = GE_PRIM_POINTS; // There's no set order for these, so drawing lines might look strange. Maybe if we draw lines from every vertex to every other...
count = op & 0xFFFF;
} else {
constexpr GEPrimitiveType primLookup[] = { GE_PRIM_TRIANGLES, GE_PRIM_LINES, GE_PRIM_POINTS, GE_PRIM_POINTS };
prim_type = primLookup[gstate.getPatchPrimitiveType()];
*prim = primLookup[gstate.getPatchPrimitiveType()];
count_u = (op & 0x00FF) >> 0;
count_v = (op & 0xFF00) >> 8;
count = count_u * count_v;
}
if (prim_type >= 7) {
if (*prim >= 7) {
ERROR_LOG(Log::G3D, "Unsupported prim type: %x", op);
return false;
}
@@ -836,8 +838,6 @@ bool GetPrimPreview(u32 op, GEPrimitiveType &prim, std::vector<GPUDebugVertex> &
return false;
}
prim = static_cast<GEPrimitiveType>(prim_type);
DebugVertexFlags flags = DebugVertexFlags::DrawCoords;
if (cmd == GE_CMD_PRIM) {
flags |= DebugVertexFlags::Clipped;
@@ -845,57 +845,58 @@ bool GetPrimPreview(u32 op, GEPrimitiveType &prim, std::vector<GPUDebugVertex> &
flags |= DebugVertexFlags::Transformed;
}
} else {
flags = DebugVertexFlags::Transformed;
flags |= DebugVertexFlags::Transformed;
}
GEPrimitiveType outPrim;
if (!gpu->GetCurrentDrawAsDebugVertices(prim, &outPrim, count, vertices, indices, lowerIndexBound, nullptr, flags)) {
if (!gpu->GetCurrentDrawAsDebugVertices(cmd, *prim, &outPrim, count, vertices, indices, lowerIndexBound, nullptr, flags)) {
ERROR_LOG(Log::G3D, "Vertex preview not yet supported");
return false;
}
// Sanity check the output (for debugging)
if (indices.size()) {
for (int i = 0; i < indices.size(); ++i) {
int offsetIndex = indices[i] - *lowerIndexBound;
if (offsetIndex >= vertices.size() || offsetIndex < 0) {
ERROR_LOG(Log::G3D, "Invalid vertex index %d (%d) (vertex count %d)", indices[i], offsetIndex, (int)vertices.size());
if (indices->size()) {
for (int i = 0; i < indices->size(); ++i) {
int offsetIndex = (*indices)[i] - *lowerIndexBound;
if (offsetIndex >= vertices->size() || offsetIndex < 0) {
ERROR_LOG(Log::G3D, "Invalid vertex index %d (%d) (vertex count %d)", (*indices)[i], offsetIndex, (int)vertices->size());
return false;
}
}
}
prim = outPrim;
*prim = outPrim;
if (cmd != GE_CMD_PRIM) {
if (cmd != GE_CMD_PRIM && cmd != GE_CMD_BOUNDINGBOX) {
static std::vector<SimpleVertex> generatedVerts;
static std::vector<u16> generatedInds;
static std::vector<SimpleVertex> simpleVerts;
simpleVerts.resize(vertices.size());
for (size_t i = 0; i < vertices.size(); ++i) {
simpleVerts.resize(vertices->size());
for (size_t i = 0; i < vertices->size(); ++i) {
// For now, let's just copy back so we can use TessellateBezierPatch/TessellateSplinePatch...
simpleVerts[i].uv[0] = vertices[i].u;
simpleVerts[i].uv[1] = vertices[i].v;
simpleVerts[i].pos = Vec3Packedf(vertices[i].x, vertices[i].y, vertices[i].z);
simpleVerts[i].uv[0] = (*vertices)[i].u;
simpleVerts[i].uv[1] = (*vertices)[i].v;
simpleVerts[i].pos = Vec3Packedf((*vertices)[i].x, (*vertices)[i].y, (*vertices)[i].z);
}
*lowerIndexBound = 0;
if (cmd == GE_CMD_BEZIER) {
ExpandBezier(count, op, simpleVerts, indices, generatedVerts, generatedInds);
ExpandBezier(count, op, simpleVerts, *indices, generatedVerts, generatedInds);
} else if (cmd == GE_CMD_SPLINE) {
ExpandSpline(count, op, simpleVerts, indices, generatedVerts, generatedInds);
ExpandSpline(count, op, simpleVerts, *indices, generatedVerts, generatedInds);
}
vertices.resize(generatedVerts.size());
for (size_t i = 0; i < vertices.size(); ++i) {
vertices[i].u = generatedVerts[i].uv[0];
vertices[i].v = generatedVerts[i].uv[1];
vertices[i].x = generatedVerts[i].pos.x;
vertices[i].y = generatedVerts[i].pos.y;
vertices[i].z = generatedVerts[i].pos.z;
vertices->resize(generatedVerts.size());
for (size_t i = 0; i < vertices->size(); ++i) {
auto vertex = &(*vertices)[i];
vertex->u = generatedVerts[i].uv[0];
vertex->v = generatedVerts[i].uv[1];
vertex->x = generatedVerts[i].pos.x;
vertex->y = generatedVerts[i].pos.y;
vertex->z = generatedVerts[i].pos.z;
}
indices = generatedInds;
*indices = generatedInds;
}
/*
+1 -1
View File
@@ -68,6 +68,6 @@ void FormatVertColDecoded(char *dest, size_t destSize, const GPUDebugVertex &ver
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);
bool GetPrimPreview(u32 op, GEPrimitiveType *prim, std::vector<GPUDebugVertex> *vertices, std::vector<u16> *indices, int *lowerIndexBound, bool transformed);
void DescribePixel(u32 pix, GPUDebugBufferFormat fmt, int x, int y, char desc[256]);
void DescribePixelRGBA(u32 pix, GPUDebugBufferFormat fmt, int x, int y, char desc[256]);
-17
View File
@@ -310,23 +310,6 @@ static void SetMatrix4x3(GLRenderManager *render, GLint *uniform, const float *m
render->SetUniformM4x4(uniform, m4x4);
}
static inline void FlipProjMatrix(Matrix4x4 &in) {
const bool invertedY = gstate_c.vpHeight < 0;
if (invertedY) {
in[1] = -in[1];
in[5] = -in[5];
in[9] = -in[9];
in[13] = -in[13];
}
const bool invertedX = gstate_c.vpWidth < 0;
if (invertedX) {
in[0] = -in[0];
in[4] = -in[4];
in[8] = -in[8];
in[12] = -in[12];
}
}
static inline bool GuessVRDrawingHUD(bool is2D, bool flatScreen) {
bool hud = true;
+22 -13
View File
@@ -1579,23 +1579,29 @@ bool GPUCommon::GetCurrentDisplayList(DisplayList &list) const {
return true;
}
int GPUCommon::GetCurrentPrimCount(GEPrimitiveType *prim) const {
int GPUCommon::GetCurrentPrim(GEPrimitiveType *prim, GECommand *outCmd) const {
DisplayList list;
u32 cmd;
u32 cmdWord;
if (GetCurrentDisplayList(list)) {
cmd = Memory::Read_U32(list.pc);
cmdWord = Memory::Read_U32(list.pc);
} else {
// Current prim value.
cmd = gstate.cmdmem[GE_CMD_PRIM];
cmdWord = gstate.cmdmem[GE_CMD_PRIM];
}
if ((cmd >> 24) == GE_CMD_PRIM || (cmd >> 24) == GE_CMD_BOUNDINGBOX) {
*prim = GEPrimitiveType((cmd >> 16) & 7);
return cmd & 0xFFFF;
} else if ((cmd >> 24) == GE_CMD_BEZIER || (cmd >> 24) == GE_CMD_SPLINE) {
GECommand cmd = static_cast<GECommand>(cmdWord >> 24);
*outCmd = cmd;
if (cmd == GE_CMD_PRIM) {
*prim = GEPrimitiveType((cmdWord >> 16) & 7);
return cmdWord & 0xFFFF;
} else if (cmd == GE_CMD_BOUNDINGBOX) {
*prim = GE_PRIM_POINTS;
return cmdWord & 0xFFFF;
} else if (cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE) {
*prim = GE_PRIM_TRIANGLES; // no correct answer
u32 u = (cmd & 0x00FF) >> 0;
u32 v = (cmd & 0xFF00) >> 8;
u32 u = (cmdWord & 0x00FF) >> 0;
u32 v = (cmdWord & 0xFF00) >> 8;
return u * v;
} else {
// Unknown primitive.
@@ -2028,8 +2034,8 @@ bool GPUCommon::PerformWriteStencilFromMemory(u32 dest, int size, WriteStencil f
return false;
}
bool GPUCommon::GetCurrentDrawAsDebugVertices(GEPrimitiveType prim, GEPrimitiveType *outPrim, int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int *lowerIndexBound, TransformStats *stats, DebugVertexFlags flags) const {
return ::GetCurrentDrawAsDebugVertices(drawEngineCommon_, prim, outPrim, count, vertices, indices, lowerIndexBound, stats, flags);
bool GPUCommon::GetCurrentDrawAsDebugVertices(GECommand cmd, GEPrimitiveType prim, GEPrimitiveType *outPrim, int count, std::vector<GPUDebugVertex> *vertices, std::vector<u16> *indices, int *lowerIndexBound, TransformStats *stats, DebugVertexFlags flags) const {
return ::GetCurrentDrawAsDebugVertices(drawEngineCommon_, cmd, prim, outPrim, count, vertices, indices, lowerIndexBound, stats, flags);
}
bool GPUCommon::DescribeCodePtr(const u8 *ptr, std::string &name) {
@@ -2057,6 +2063,7 @@ void GPUCommon::SetBreakNext(GPUDebug::BreakNext next) {
break;
case GPUDebug::BreakNext::PRIM:
case GPUDebug::BreakNext::COUNT:
breakpoints_.AddCmdBreakpoint(GE_CMD_BOUNDINGBOX, true);
breakpoints_.AddCmdBreakpoint(GE_CMD_PRIM, true);
breakpoints_.AddCmdBreakpoint(GE_CMD_BEZIER, true);
breakpoints_.AddCmdBreakpoint(GE_CMD_SPLINE, true);
@@ -2106,7 +2113,9 @@ GPUDebug::NotifyResult GPUCommon::NotifyCommand(u32 pc, GPUBreakpoints *breakpoi
bool isPrim = false;
bool process = true; // Process is only for the restrictPrimRanges functionality
if (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE || cmd == GE_CMD_VAP || cmd == GE_CMD_TRANSFERSTART) { // VAP is immediate mode prims.
// NOTE: We now consider BBOX a PRIM command.
if (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE || cmd == GE_CMD_VAP || cmd == GE_CMD_TRANSFERSTART || cmd == GE_CMD_BOUNDINGBOX) { // VAP is immediate mode prims.
isPrim = true;
primsThisFrame_++;
+3 -2
View File
@@ -75,8 +75,8 @@ public:
virtual bool GetOutputFramebuffer(GPUDebugBuffer &buffer) { return false; }
bool GetCurrentDisplayList(DisplayList &list) const;
bool GetCurrentDrawAsDebugVertices(GEPrimitiveType prim, GEPrimitiveType *outPrim, int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int *lowerIndexBound, TransformStats *stats, DebugVertexFlags flags) const;
int GetCurrentPrimCount(GEPrimitiveType *prim) const;
bool GetCurrentDrawAsDebugVertices(GECommand cmd, GEPrimitiveType prim, GEPrimitiveType *outPrim, int count, std::vector<GPUDebugVertex> *vertices, std::vector<u16> *indices, int *lowerIndexBound, TransformStats *stats, DebugVertexFlags flags) const;
int GetCurrentPrim(GEPrimitiveType *prim, GECommand *outCmd) const; // Return value has the count.
// FinishInitOnMainThread runs on the main thread, of course.
virtual void FinishInitOnMainThread() {}
@@ -201,6 +201,7 @@ public:
virtual FramebufferManagerCommon *GetFramebufferManagerCommon() { return nullptr; }
virtual TextureCacheCommon *GetTextureCacheCommon() { return nullptr; }
const DrawEngineCommon *GetDrawEngineCommon() const { return drawEngineCommon_; }
virtual std::vector<std::string> DebugGetShaderIDs(DebugShaderType shader) { return std::vector<std::string>(); };
virtual std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) {
+8 -5
View File
@@ -116,7 +116,7 @@ void GPUgstate::Reset() {
savedContextVersion = 1;
gstate_c.Dirty(DIRTY_WORLD_VIEW_PROJ_MATRIX | DIRTY_VIEW_PROJ_MATRIX);
gstate_c.Dirty(DIRTY_WORLD_VIEW_PROJ_MATRIX | DIRTY_VIEW_PROJ_MATRIX | DIRTY_CULL_MATRIX);
}
void GPUgstate::Save(u32_le *ptr) {
@@ -248,7 +248,7 @@ void GPUgstate::Restore(const u32_le *ptr) {
if (gpu)
gpu->ResetMatrices();
gstate_c.Dirty(DIRTY_WORLD_VIEW_PROJ_MATRIX | DIRTY_VIEW_PROJ_MATRIX);
gstate_c.Dirty(DIRTY_WORLD_VIEW_PROJ_MATRIX | DIRTY_VIEW_PROJ_MATRIX | DIRTY_CULL_MATRIX);
}
bool vertTypeIsSkinningEnabled(u32 vertType) {
@@ -341,8 +341,11 @@ void GPUStateCache::DoState(PointerWrap &p) {
Do(p, actualTextureHeight);
// curTextureXOffset and curTextureYOffset don't need to be saved. Well, the above don't either...
Do(p, vpWidth);
Do(p, vpHeight);
// previously vpWidth, vpHeight. Remove in future versions.
float dummy = 0.0f;
Do(p, dummy);
Do(p, dummy);
if (s == 4) {
float oldDepth = 1.0f;
Do(p, oldDepth);
@@ -359,7 +362,7 @@ void GPUStateCache::DoState(PointerWrap &p) {
}
if (p.GetMode() == PointerWrap::MODE_READ) {
gstate_c.Dirty(DIRTY_WORLD_VIEW_PROJ_MATRIX | DIRTY_VIEW_PROJ_MATRIX);
gstate_c.Dirty(DIRTY_WORLD_VIEW_PROJ_MATRIX | DIRTY_VIEW_PROJ_MATRIX | DIRTY_CULL_MATRIX);
}
}
-10
View File
@@ -651,16 +651,6 @@ public:
int curTextureYOffset;
bool curTextureIs3D;
float vpWidth;
float vpHeight;
float vpXOffset;
float vpYOffset;
float vpZOffset;
float vpWidthScale;
float vpHeightScale;
float vpDepthScale;
// Cached 4x4 products of the matrices.
// Useful for culling and extracing the final Z from vertices (so we can check if clipping is needed).
// Most often, world changes the most.
+263
View File
@@ -1,5 +1,268 @@
#include "GPU/ge_constants.h"
const char *GeCmdToString(GECommand cmd) {
switch (cmd) {
case GE_CMD_NOP: return "NOP";
case GE_CMD_VADDR: return "VADDR";
case GE_CMD_IADDR: return "IADDR";
case GE_CMD_PRIM: return "PRIM";
case GE_CMD_BEZIER: return "BEZIER";
case GE_CMD_SPLINE: return "SPLINE";
case GE_CMD_BOUNDINGBOX: return "BOUNDINGBOX";
case GE_CMD_JUMP: return "JUMP";
case GE_CMD_BJUMP: return "BJUMP";
case GE_CMD_CALL: return "CALL";
case GE_CMD_RET: return "RET";
case GE_CMD_END: return "END";
case GE_CMD_SIGNAL: return "SIGNAL";
case GE_CMD_FINISH: return "FINISH";
case GE_CMD_BASE: return "BASE";
case GE_CMD_VERTEXTYPE: return "VERTEXTYPE";
case GE_CMD_OFFSETADDR: return "OFFSETADDR";
case GE_CMD_ORIGIN: return "ORIGIN";
case GE_CMD_REGION1: return "REGION1";
case GE_CMD_REGION2: return "REGION2";
case GE_CMD_LIGHTINGENABLE: return "LIGHTINGENABLE";
case GE_CMD_LIGHTENABLE0: return "LIGHTENABLE0";
case GE_CMD_LIGHTENABLE1: return "LIGHTENABLE1";
case GE_CMD_LIGHTENABLE2: return "LIGHTENABLE2";
case GE_CMD_LIGHTENABLE3: return "LIGHTENABLE3";
case GE_CMD_DEPTHCLIPENABLE: return "DEPTHCLIPENABLE";
case GE_CMD_CULLFACEENABLE: return "CULLFACEENABLE";
case GE_CMD_TEXTUREMAPENABLE: return "TEXTUREMAPENABLE";
case GE_CMD_FOGENABLE: return "FOGENABLE";
case GE_CMD_DITHERENABLE: return "DITHERENABLE";
case GE_CMD_ALPHABLENDENABLE: return "ALPHABLENDENABLE";
case GE_CMD_ALPHATESTENABLE: return "ALPHATESTENABLE";
case GE_CMD_ZTESTENABLE: return "ZTESTENABLE";
case GE_CMD_STENCILTESTENABLE: return "STENCILTESTENABLE";
case GE_CMD_ANTIALIASENABLE: return "ANTIALIASENABLE";
case GE_CMD_PATCHCULLENABLE: return "PATCHCULLENABLE";
case GE_CMD_COLORTESTENABLE: return "COLORTESTENABLE";
case GE_CMD_LOGICOPENABLE: return "LOGICOPENABLE";
case GE_CMD_BONEMATRIXNUMBER: return "BONEMATRIXNUMBER";
case GE_CMD_BONEMATRIXDATA: return "BONEMATRIXDATA";
case GE_CMD_MORPHWEIGHT0: return "MORPHWEIGHT0";
case GE_CMD_MORPHWEIGHT1: return "MORPHWEIGHT1";
case GE_CMD_MORPHWEIGHT2: return "MORPHWEIGHT2";
case GE_CMD_MORPHWEIGHT3: return "MORPHWEIGHT3";
case GE_CMD_MORPHWEIGHT4: return "MORPHWEIGHT4";
case GE_CMD_MORPHWEIGHT5: return "MORPHWEIGHT5";
case GE_CMD_MORPHWEIGHT6: return "MORPHWEIGHT6";
case GE_CMD_MORPHWEIGHT7: return "MORPHWEIGHT7";
case GE_CMD_PATCHDIVISION: return "PATCHDIVISION";
case GE_CMD_PATCHPRIMITIVE: return "PATCHPRIMITIVE";
case GE_CMD_PATCHFACING: return "PATCHFACING";
case GE_CMD_WORLDMATRIXNUMBER: return "WORLDMATRIXNUMBER";
case GE_CMD_WORLDMATRIXDATA: return "WORLDMATRIXDATA";
case GE_CMD_VIEWMATRIXNUMBER: return "VIEWMATRIXNUMBER";
case GE_CMD_VIEWMATRIXDATA: return "VIEWMATRIXDATA";
case GE_CMD_PROJMATRIXNUMBER: return "PROJMATRIXNUMBER";
case GE_CMD_PROJMATRIXDATA: return "PROJMATRIXDATA";
case GE_CMD_TGENMATRIXNUMBER: return "TGENMATRIXNUMBER";
case GE_CMD_TGENMATRIXDATA: return "TGENMATRIXDATA";
case GE_CMD_VIEWPORTXSCALE: return "VIEWPORTXSCALE";
case GE_CMD_VIEWPORTYSCALE: return "VIEWPORTYSCALE";
case GE_CMD_VIEWPORTZSCALE: return "VIEWPORTZSCALE";
case GE_CMD_VIEWPORTXCENTER: return "VIEWPORTXCENTER";
case GE_CMD_VIEWPORTYCENTER: return "VIEWPORTYCENTER";
case GE_CMD_VIEWPORTZCENTER: return "VIEWPORTZCENTER";
case GE_CMD_TEXSCALEU: return "TEXSCALEU";
case GE_CMD_TEXSCALEV: return "TEXSCALEV";
case GE_CMD_TEXOFFSETU: return "TEXOFFSETU";
case GE_CMD_TEXOFFSETV: return "TEXOFFSETV";
case GE_CMD_OFFSETX: return "OFFSETX";
case GE_CMD_OFFSETY: return "OFFSETY";
case GE_CMD_SHADEMODE: return "SHADEMODE"; // flat or gouraud
case GE_CMD_REVERSENORMAL: return "REVERSENORMAL";
case GE_CMD_MATERIALUPDATE: return "MATERIALUPDATE";
case GE_CMD_MATERIALEMISSIVE: return "MATERIALEMISSIVE"; //not sure about these but this makes sense
case GE_CMD_MATERIALAMBIENT: return "MATERIALAMBIENT"; //gotta try enabling lighting and check :)
case GE_CMD_MATERIALDIFFUSE: return "MATERIALDIFFUSE";
case GE_CMD_MATERIALSPECULAR: return "MATERIALSPECULAR";
case GE_CMD_MATERIALALPHA: return "MATERIALALPHA";
case GE_CMD_MATERIALSPECULARCOEF: return "MATERIALSPECULARCOEF";
case GE_CMD_AMBIENTCOLOR: return "AMBIENTCOLOR";
case GE_CMD_AMBIENTALPHA: return "AMBIENTALPHA";
case GE_CMD_LIGHTMODE: return "LIGHTMODE";
case GE_CMD_LIGHTTYPE0: return "LIGHTTYPE0";
case GE_CMD_LIGHTTYPE1: return "LIGHTTYPE1";
case GE_CMD_LIGHTTYPE2: return "LIGHTTYPE2";
case GE_CMD_LIGHTTYPE3: return "LIGHTTYPE3";
case GE_CMD_LX0: return "LX0";
case GE_CMD_LY0: return "LY0";
case GE_CMD_LZ0: return "LZ0";
case GE_CMD_LX1: return "LX1";
case GE_CMD_LY1: return "LY1";
case GE_CMD_LZ1: return "LZ1";
case GE_CMD_LX2: return "LX2";
case GE_CMD_LY2: return "LY2";
case GE_CMD_LZ2: return "LZ2";
case GE_CMD_LX3: return "LX3";
case GE_CMD_LY3: return "LY3";
case GE_CMD_LZ3: return "LZ3";
case GE_CMD_LDX0: return "LDX0";
case GE_CMD_LDY0: return "LDY0";
case GE_CMD_LDZ0: return "LDZ0";
case GE_CMD_LDX1: return "LDX1";
case GE_CMD_LDY1: return "LDY1";
case GE_CMD_LDZ1: return "LDZ1";
case GE_CMD_LDX2: return "LDX2";
case GE_CMD_LDY2: return "LDY2";
case GE_CMD_LDZ2: return "LDZ2";
case GE_CMD_LDX3: return "LDX3";
case GE_CMD_LDY3: return "LDY3";
case GE_CMD_LDZ3: return "LDZ3";
case GE_CMD_LKA0: return "LKA0";
case GE_CMD_LKB0: return "LKB0";
case GE_CMD_LKC0: return "LKC0";
case GE_CMD_LKA1: return "LKA1";
case GE_CMD_LKB1: return "LKB1";
case GE_CMD_LKC1: return "LKC1";
case GE_CMD_LKA2: return "LKA2";
case GE_CMD_LKB2: return "LKB2";
case GE_CMD_LKC2: return "LKC2";
case GE_CMD_LKA3: return "LKA3";
case GE_CMD_LKB3: return "LKB3";
case GE_CMD_LKC3: return "LKC3";
case GE_CMD_LKS0: return "LKS0";
case GE_CMD_LKS1: return "LKS1";
case GE_CMD_LKS2: return "LKS2";
case GE_CMD_LKS3: return "LKS3";
case GE_CMD_LKO0: return "LKO0";
case GE_CMD_LKO1: return "LKO1";
case GE_CMD_LKO2: return "LKO2";
case GE_CMD_LKO3: return "LKO3";
case GE_CMD_LAC0: return "LAC0";
case GE_CMD_LDC0: return "LDC0";
case GE_CMD_LSC0: return "LSC0";
case GE_CMD_LAC1: return "LAC1";
case GE_CMD_LDC1: return "LDC1";
case GE_CMD_LSC1: return "LSC1";
case GE_CMD_LAC2: return "LAC2";
case GE_CMD_LDC2: return "LDC2";
case GE_CMD_LSC2: return "LSC2";
case GE_CMD_LAC3: return "LAC3";
case GE_CMD_LDC3: return "LDC3";
case GE_CMD_LSC3: return "LSC3";
case GE_CMD_CULL: return "CULL";
case GE_CMD_FRAMEBUFPTR: return "FRAMEBUFPTR";
case GE_CMD_FRAMEBUFWIDTH: return "FRAMEBUFWIDTH";
case GE_CMD_ZBUFPTR: return "ZBUFPTR";
case GE_CMD_ZBUFWIDTH: return "ZBUFWIDTH";
case GE_CMD_TEXADDR0: return "TEXADDR0";
case GE_CMD_TEXADDR1: return "TEXADDR1";
case GE_CMD_TEXADDR2: return "TEXADDR2";
case GE_CMD_TEXADDR3: return "TEXADDR3";
case GE_CMD_TEXADDR4: return "TEXADDR4";
case GE_CMD_TEXADDR5: return "TEXADDR5";
case GE_CMD_TEXADDR6: return "TEXADDR6";
case GE_CMD_TEXADDR7: return "TEXADDR7";
case GE_CMD_TEXBUFWIDTH0: return "TEXBUFWIDTH0";
case GE_CMD_TEXBUFWIDTH1: return "TEXBUFWIDTH1";
case GE_CMD_TEXBUFWIDTH2: return "TEXBUFWIDTH2";
case GE_CMD_TEXBUFWIDTH3: return "TEXBUFWIDTH3";
case GE_CMD_TEXBUFWIDTH4: return "TEXBUFWIDTH4";
case GE_CMD_TEXBUFWIDTH5: return "TEXBUFWIDTH5";
case GE_CMD_TEXBUFWIDTH6: return "TEXBUFWIDTH6";
case GE_CMD_TEXBUFWIDTH7: return "TEXBUFWIDTH7";
case GE_CMD_CLUTADDR: return "CLUTADDR";
case GE_CMD_CLUTADDRUPPER: return "CLUTADDRUPPER";
case GE_CMD_TRANSFERSRC: return "TRANSFERSRC";
case GE_CMD_TRANSFERSRCW: return "TRANSFERSRCW";
case GE_CMD_TRANSFERDST: return "TRANSFERDST";
case GE_CMD_TRANSFERDSTW: return "TRANSFERDSTW";
case GE_CMD_TEXSIZE0: return "TEXSIZE0";
case GE_CMD_TEXSIZE1: return "TEXSIZE1";
case GE_CMD_TEXSIZE2: return "TEXSIZE2";
case GE_CMD_TEXSIZE3: return "TEXSIZE3";
case GE_CMD_TEXSIZE4: return "TEXSIZE4";
case GE_CMD_TEXSIZE5: return "TEXSIZE5";
case GE_CMD_TEXSIZE6: return "TEXSIZE6";
case GE_CMD_TEXSIZE7: return "TEXSIZE7";
case GE_CMD_TEXMAPMODE: return "TEXMAPMODE";
case GE_CMD_TEXSHADELS: return "TEXSHADELS";
case GE_CMD_TEXMODE: return "TEXMODE";
case GE_CMD_TEXFORMAT: return "TEXFORMAT";
case GE_CMD_LOADCLUT: return "LOADCLUT";
case GE_CMD_CLUTFORMAT: return "CLUTFORMAT";
case GE_CMD_TEXFILTER: return "TEXFILTER";
case GE_CMD_TEXWRAP: return "TEXWRAP";
case GE_CMD_TEXLEVEL: return "TEXLEVEL";
case GE_CMD_TEXFUNC: return "TEXFUNC";
case GE_CMD_TEXENVCOLOR: return "TEXENVCOLOR";
case GE_CMD_TEXFLUSH: return "TEXFLUSH";
case GE_CMD_TEXSYNC: return "TEXSYNC";
case GE_CMD_FOG1: return "FOG1";
case GE_CMD_FOG2: return "FOG2";
case GE_CMD_FOGCOLOR: return "FOGCOLOR";
case GE_CMD_TEXLODSLOPE: return "TEXLODSLOPE";
case GE_CMD_FRAMEBUFPIXFORMAT: return "FRAMEBUFPIXFORMAT";
case GE_CMD_CLEARMODE: return "CLEARMODE";
case GE_CMD_SCISSOR1: return "SCISSOR1";
case GE_CMD_SCISSOR2: return "SCISSOR2";
case GE_CMD_MINZ: return "MINZ";
case GE_CMD_MAXZ: return "MAXZ";
case GE_CMD_COLORTEST: return "COLORTEST";
case GE_CMD_COLORREF: return "COLORREF";
case GE_CMD_COLORTESTMASK: return "COLORTESTMASK";
case GE_CMD_ALPHATEST: return "ALPHATEST";
case GE_CMD_STENCILTEST: return "STENCILTEST";
case GE_CMD_STENCILOP: return "STENCILOP";
case GE_CMD_ZTEST: return "ZTEST";
case GE_CMD_BLENDMODE: return "BLENDMODE";
case GE_CMD_BLENDFIXEDA: return "BLENDFIXEDA";
case GE_CMD_BLENDFIXEDB: return "BLENDFIXEDB";
case GE_CMD_DITH0: return "DITH0";
case GE_CMD_DITH1: return "DITH1";
case GE_CMD_DITH2: return "DITH2";
case GE_CMD_DITH3: return "DITH3";
case GE_CMD_LOGICOP: return "LOGICOP";
case GE_CMD_ZWRITEDISABLE: return "ZWRITEDISABLE";
case GE_CMD_MASKRGB: return "MASKRGB";
case GE_CMD_MASKALPHA: return "MASKALPHA";
case GE_CMD_TRANSFERSTART: return "TRANSFERSTART";
case GE_CMD_TRANSFERSRCPOS: return "TRANSFERSRCPOS";
case GE_CMD_TRANSFERDSTPOS: return "TRANSFERDSTPOS";
case GE_CMD_TRANSFERSIZE: return "TRANSFERSIZE";
case GE_CMD_VSCX: return "VSCX";
case GE_CMD_VSCY: return "VSCY";
case GE_CMD_VSCZ: return "VSCZ";
case GE_CMD_VTCS: return "VTCS";
case GE_CMD_VTCT: return "VTCT";
case GE_CMD_VTCQ: return "VTCQ";
case GE_CMD_VCV: return "VCV";
case GE_CMD_VAP: return "VAP";
case GE_CMD_VFC: return "VFC";
case GE_CMD_VSCV: return "VSCV";
case GE_CMD_UNKNOWN_03: return "UNKNOWN_03";
case GE_CMD_UNKNOWN_0D: return "UNKNOWN_0D";
case GE_CMD_UNKNOWN_11: return "UNKNOWN_11";
case GE_CMD_UNKNOWN_29: return "UNKNOWN_29";
case GE_CMD_UNKNOWN_34: return "UNKNOWN_34";
case GE_CMD_UNKNOWN_35: return "UNKNOWN_35";
case GE_CMD_UNKNOWN_39: return "UNKNOWN_39";
case GE_CMD_UNKNOWN_4E: return "UNKNOWN_4E";
case GE_CMD_UNKNOWN_4F: return "UNKNOWN_4F";
case GE_CMD_UNKNOWN_52: return "UNKNOWN_52";
case GE_CMD_UNKNOWN_59: return "UNKNOWN_59";
case GE_CMD_UNKNOWN_5A: return "UNKNOWN_5A";
case GE_CMD_UNKNOWN_B6: return "UNKNOWN_B6";
case GE_CMD_UNKNOWN_B7: return "UNKNOWN_B7";
case GE_CMD_UNKNOWN_D1: return "UNKNOWN_D1";
case GE_CMD_UNKNOWN_ED: return "UNKNOWN_ED";
case GE_CMD_UNKNOWN_EF: return "UNKNOWN_EF";
case GE_CMD_UNKNOWN_FA: return "UNKNOWN_FA";
case GE_CMD_UNKNOWN_FB: return "UNKNOWN_FB";
case GE_CMD_UNKNOWN_FC: return "UNKNOWN_FC";
case GE_CMD_UNKNOWN_FD: return "UNKNOWN_FD";
case GE_CMD_UNKNOWN_FE: return "UNKNOWN_FE";
case GE_CMD_NOP_FF: return "NOP_FF";
default:
return "INVALID";
}
}
const char *GePrimTypeToString(GEPrimitiveType prim) {
static constexpr const char * primTypes[8] = {
"POINTS",
+2 -2
View File
@@ -1082,7 +1082,7 @@ void SoftGPU::Execute_WorldMtxData(u32 op, u32 diff) {
if (newVal != *target) {
*target = newVal;
dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX;
gstate_c.Dirty(DIRTY_WORLD_VIEW_PROJ_MATRIX | DIRTY_VIEW_PROJ_MATRIX);
gstate_c.Dirty(DIRTY_WORLD_VIEW_PROJ_MATRIX | DIRTY_VIEW_PROJ_MATRIX | DIRTY_CULL_MATRIX);
}
}
@@ -1103,7 +1103,7 @@ void SoftGPU::Execute_ViewMtxData(u32 op, u32 diff) {
if (newVal != *target) {
*target = newVal;
dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX;
gstate_c.Dirty(DIRTY_WORLD_VIEW_PROJ_MATRIX | DIRTY_VIEW_PROJ_MATRIX);
gstate_c.Dirty(DIRTY_WORLD_VIEW_PROJ_MATRIX | DIRTY_VIEW_PROJ_MATRIX | DIRTY_CULL_MATRIX);
}
}
+3 -1
View File
@@ -278,6 +278,8 @@ enum GECommand : uint8_t {
GE_CMD_NOP_FF = 0xFF,
};
const char *GeCmdToString(GECommand cmd);
#define GE_VTYPE_TRANSFORM (0<<23)
#define GE_VTYPE_THROUGH (1<<23)
#define GE_VTYPE_THROUGH_MASK (1<<23)
@@ -304,7 +306,7 @@ enum GECommand : uint8_t {
#define GE_VTYPE_NRM_MASK (3<<5)
#define GE_VTYPE_NRM_SHIFT 5
//#define GE_VTYPE_POSITION_NONE (0<<5)
// No NONE, there is always a position.
#define GE_VTYPE_POS_8BIT (1<<7)
#define GE_VTYPE_POS_16BIT (2<<7)
#define GE_VTYPE_POS_FLOAT (3<<7)
+23 -25
View File
@@ -11,6 +11,7 @@
#include "GPU/Common/TextureCacheCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
#include "GPU/Common/DrawEngineCommon.h"
#include "Core/HLE/sceDisplay.h"
#include "Core/HW/Display.h"
@@ -891,7 +892,7 @@ static const char *DLStateToString(DisplayListState state) {
}
// TODO: Backport this to the Win32 debugger (ugh).
static void DrawPreviewPrimitive(ImDrawList *drawList, ImVec2 p0, GEPrimitiveType prim, const std::vector<u16> &indices, const std::vector<GPUDebugVertex> &verts, int indexOffset, bool transformed, bool uvToPos, float sx, float sy) {
static void DrawPreviewPrimitive(ImDrawList *drawList, ImVec2 p0, GECommand cmd, GEPrimitiveType prim, const std::vector<u16> &indices, const std::vector<GPUDebugVertex> &verts, int indexOffset, bool transformed, bool uvToPos, float sx, float sy) {
// These wrappers are to either draw using the positions or the UV coordinates.
auto x = [sx, uvToPos](const GPUDebugVertex &vert) {
return sx * (uvToPos ? vert.u : vert.x);
@@ -1208,34 +1209,25 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUCommon *gpuD
u32 op = 0;
DisplayList list;
bool isOnBlockTransfer = false;
if (gpuDebug->GetCurrentDisplayList(list)) {
_dbg_assert_(Memory::IsValid4AlignedAddress(list.pc));
op = Memory::ReadUnchecked_U32(list.pc);
GECommand cmd = static_cast<GECommand>(op >> 24);
previewCmd_ = cmd;
// TODO: Also add support for block transfer previews!
bool isOnPrim = false;
switch (op >> 24) {
case GE_CMD_PRIM:
isOnPrim = true;
if (cmd == GE_CMD_PRIM || cmd == GE_CMD_BOUNDINGBOX) {
if (reloadPreview_) {
GetPrimPreview(op, previewPrim_, previewVertices_, previewIndices_, &previewIndexOffset_, previewTransformed_);
GetPrimPreview(op, &previewPrim_, &previewVertices_, &previewIndices_, &previewIndexOffset_, previewTransformed_);
reloadPreview_ = false;
}
break;
case GE_CMD_TRANSFERSTART:
isOnBlockTransfer = true;
break;
default:
} else {
// Disable the current preview.
previewVertices_.clear();
previewIndices_.clear();
previewIndexOffset_ = 0;
previewTransformed_ = true;
break;
}
}
ImGui::BeginChild("texture/fb view");
@@ -1243,7 +1235,7 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUCommon *gpuD
ImDrawList *drawList = ImGui::GetWindowDrawList();
if (coreState == CORE_STEPPING_GE) {
if (isOnBlockTransfer) {
if (previewCmd_ == GE_CMD_TRANSFERSTART) {
ImGui::Text("Block transfer! Proper preview coming in the future.\n");
ImGui::Text("%08x -> %08x, %d bpp (strides: %d, %d)", gstate.getTransferSrcAddress(), gstate.getTransferDstAddress(), gstate.getTransferBpp(), gstate.getTransferSrcStride(), gstate.getTransferDstStride());
ImGui::Text("%dx%d pixels", gstate.getTransferWidth(), gstate.getTransferHeight());
@@ -1318,7 +1310,7 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUCommon *gpuD
// Draw vertex preview on top!
if (!previewVertices_.empty()) {
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewIndexOffset_, previewTransformed_, false, scale * previewZoom_, scale * previewZoom_);
DrawPreviewPrimitive(drawList, p0, previewCmd_, previewPrim_, previewIndices_, previewVertices_, previewIndexOffset_, previewTransformed_, false, scale * previewZoom_, scale * previewZoom_);
}
drawList->PopClipRect();
@@ -1347,7 +1339,7 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUCommon *gpuD
ImGui::Text("(clear mode - texturing not used)");
} else if (!gstate.isTextureMapEnabled()) {
ImGui::Text("(texturing not enabled");
} else {
} else { // We don't bother with the texture if previewCmd is BOUNDING_BOX - no texturing happens there.
TextureCacheCommon *texcache = gpuDebug->GetTextureCacheCommon();
TexCacheEntry *tex = texcache ? texcache->SetTexture() : nullptr;
if (tex) {
@@ -1369,8 +1361,11 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUCommon *gpuD
ImGui::Image(texId, ImVec2(texW, texH));
// Show the preview as texcoords. TODO: We should probably use unclipped data here?
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewIndexOffset_, previewTransformed_, true, texW, texH);
// Don't bother with trying to preview bounding box here, it only has positions, no texcoords.
if (previewCmd_ == GE_CMD_PRIM || previewCmd_ == GE_CMD_BEZIER || previewCmd_ == GE_CMD_SPLINE) {
// Show the preview as texcoords. TODO: We should probably use unclipped data here?
DrawPreviewPrimitive(drawList, p0, previewCmd_, previewPrim_, previewIndices_, previewVertices_, previewIndexOffset_, previewTransformed_, true, texW, texH);
}
drawList->PopClipRect();
@@ -1705,7 +1700,7 @@ void ImGeStateWindow::Draw(ImConfig &cfg, ImControl &control, GPUCommon *gpuDebu
ImGui::End();
}
void DrawImGeVertsWindow(ImConfig &cfg, ImControl &control, GPUCommon *gpuDebug) {
void DrawImGeVertsWindow(ImConfig &cfg, ImControl &control, GPUCommon *gpu) {
ImGui::SetNextWindowSize(ImVec2(300, 500), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("GE Vertices", &cfg.geVertsOpen)) {
ImGui::End();
@@ -1724,7 +1719,7 @@ void DrawImGeVertsWindow(ImConfig &cfg, ImControl &control, GPUCommon *gpuDebug)
ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollY;
if (ImGui::BeginTabBar("vertexmode", ImGuiTabBarFlags_None)) {
auto state = gpuDebug->GetGState();
auto state = gpu->GetGState();
auto buildVertexTable = [&](bool transformed) {
// Let's see if it's fast enough to just do all this each frame.
GEPrimitiveType prim;
@@ -1740,11 +1735,14 @@ void DrawImGeVertsWindow(ImConfig &cfg, ImControl &control, GPUCommon *gpuDebug)
flags |= DebugVertexFlags::Clipped;
}
int inputVertexCount = gpuDebug->GetCurrentPrimCount(&prim);
GECommand cmd;
int inputVertexCount = gpu->GetCurrentPrim(&prim, &cmd);
// TODO: If cmd is BOUNDING_BOX, actually test the bounding box here and show the result.
// 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)) {
if (!gpu->GetCurrentDrawAsDebugVertices(cmd, prim, &prim, inputVertexCount, &vertices, &indices, &indexOffset, &stats, flags)) {
inputVertexCount = 0;
}
@@ -1752,7 +1750,7 @@ void DrawImGeVertsWindow(ImConfig &cfg, ImControl &control, GPUCommon *gpuDebug)
char statusLine[256];
StringWriter w(statusLine);
w.F("%s: ", GePrimTypeToString(prim));
w.F("%s - %s: ", GeCmdToString(cmd), GePrimTypeToString(prim));
char fmtTemp[256];
FormatStateRow(fmtTemp, sizeof(fmtTemp), CMD_FMT_VERTEXTYPE, state.vertType, true, false, false);
w.F("%s", fmtTemp);
+1
View File
@@ -158,6 +158,7 @@ private:
ImGePixelViewer swViewer_;
int showBannerInFrames_ = 0;
bool reloadPreview_ = false;
GECommand previewCmd_{};
GEPrimitiveType previewPrim_ = GEPrimitiveType::GE_PRIM_TRIANGLES;
std::vector<u16> previewIndices_;
std::vector<GPUDebugVertex> previewVertices_;
+3 -2
View File
@@ -200,7 +200,8 @@ int CtrlVertexList::GetRowCount() {
auto state = gpu->GetGState();
GEPrimitiveType prim;
rowCount_ = gpu->GetCurrentPrimCount(&prim);
GECommand cmd;
rowCount_ = gpu->GetCurrentPrim(&prim, &cmd);
previewIndexOffset_ = 0;
@@ -213,7 +214,7 @@ int CtrlVertexList::GetRowCount() {
}
TransformStats stats;
if (!gpu->GetCurrentDrawAsDebugVertices(prim, &prim, rowCount_, vertices, indices, &previewIndexOffset_, &stats, flags)) {
if (!gpu->GetCurrentDrawAsDebugVertices(cmd, prim, &prim, rowCount_, &vertices, &indices, &previewIndexOffset_, &stats, flags)) {
rowCount_ = 0;
}
+1 -1
View File
@@ -99,7 +99,7 @@ void CGEDebugger::UpdatePrimPreview(u32 op, int which) {
GEPrimitiveType prim;
int previewIndexOffset;
bool previewTransformed;
if (!GetPrimPreview(op, prim, vertices, indices, &previewIndexOffset, &previewTransformed)) {
if (!GetPrimPreview(op, &prim, &vertices, &indices, &previewIndexOffset, &previewTransformed)) {
return;
}
+8
View File
@@ -1219,6 +1219,14 @@ bool TestCrossSIMD() {
return false;
}
s8 values[4] = {-1, -128, 127, 45};
float fvalues[4];
Vec4F32::LoadS8Norm(values).Store(fvalues);
static const float known_s8norm_result[4] = {(float)values[0]/128.0f, (float)values[1]/128.0f, (float)values[2]/128.0f, (float)values[3]/128.0f,};
if (!CompareFloats(fvalues, known_s8norm_result, ARRAY_SIZE(known_s8norm_result), __LINE__)) {
return false;
}
// PrintFloats(result, 16);
return true;