mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Well, it builds, but doesn't work yet.
This commit is contained in:
@@ -245,9 +245,7 @@ struct Vec4F32 {
|
||||
return Vec4F32{ _mm_or_ps(_mm_and_ps(value, _mm_load_ps((const float *)mask)), _mm_load_ps(onelane3)) };
|
||||
}
|
||||
|
||||
static Vec4F32 LoadF24x3_DontCare(const uint32_t *src) {
|
||||
alignas(16) static const uint32_t mask[4] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0};
|
||||
|
||||
static Vec4F32 LoadF24x4(const uint32_t *src) {
|
||||
return Vec4F32{_mm_castsi128_ps(_mm_slli_epi32(_mm_loadu_si128((const __m128i *)src), 8))};
|
||||
}
|
||||
|
||||
@@ -619,7 +617,7 @@ struct Vec4F32 {
|
||||
static Vec4F32 LoadF24x3_One(const uint32_t *src) {
|
||||
return Vec4F32{ vsetq_lane_f32(1.0f, vreinterpretq_f32_u32(vshlq_n_u32(vld1q_u32(src), 8)), 3) };
|
||||
}
|
||||
static Vec4F32 LoadF24x3_DontCare(const uint32_t *src) {
|
||||
static Vec4F32 LoadF24x4(const uint32_t *src) {
|
||||
return Vec4F32{vreinterpretq_f32_u32(vshlq_n_u32(vld1q_u32(src), 8))};
|
||||
}
|
||||
|
||||
@@ -1082,7 +1080,7 @@ struct Vec4F32 {
|
||||
value = __lsx_vinsgr2vr_w(value, kOneF32Bits, 3);
|
||||
return Vec4F32{ (__m128)value };
|
||||
}
|
||||
static Vec4F32 LoadF24x3_DontCare(const uint32_t *src) {
|
||||
static Vec4F32 LoadF24x4(const uint32_t *src) {
|
||||
return Vec4F32{(__m128)__lsx_vslli_w(__lsx_vld(src, 0), 8)};
|
||||
}
|
||||
|
||||
@@ -1530,7 +1528,7 @@ struct Vec4F32 {
|
||||
return temp;
|
||||
}
|
||||
|
||||
static Vec4F32 LoadF24x3_DontCare(const uint32_t *src) {
|
||||
static Vec4F32 LoadF24x4(const uint32_t *src) {
|
||||
return LoadR24x3_One(src);
|
||||
}
|
||||
|
||||
|
||||
@@ -202,18 +202,34 @@ private:
|
||||
int scaleFactor_ = 0;
|
||||
};
|
||||
|
||||
// TODO: Replace with TransformedVertex?
|
||||
struct GPUDebugVertex {
|
||||
float u;
|
||||
float v;
|
||||
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
u8 c[4];
|
||||
float w;
|
||||
|
||||
union {
|
||||
u8 c[4];
|
||||
u32 color0_32;
|
||||
};
|
||||
|
||||
float fog;
|
||||
|
||||
float nx;
|
||||
float ny;
|
||||
float nz;
|
||||
};
|
||||
|
||||
enum class DebugVertexFlags {
|
||||
Transformed = 1 << 0,
|
||||
Clipped = 1 << 1,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(DebugVertexFlags);
|
||||
|
||||
class StringWriter;
|
||||
class GPUCommon;
|
||||
|
||||
|
||||
@@ -97,8 +97,8 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool useBuffe
|
||||
|
||||
if (dirtyUniforms & DIRTY_VIEWPORT_UNIFORMS) {
|
||||
// TODO: This should be a couple of SIMD instructions.
|
||||
Vec4F32 vpScale = Vec4F32::LoadF24x3_DontCare(&gstate.viewportxscale);
|
||||
Vec4F32 vpOffset = Vec4F32::LoadF24x3_DontCare(&gstate.viewportxcenter);
|
||||
Vec4F32 vpScale = Vec4F32::LoadF24x4(&gstate.viewportxscale);
|
||||
Vec4F32 vpOffset = Vec4F32::LoadF24x4(&gstate.viewportxcenter);
|
||||
vpScale.Store(ub->vpScale);
|
||||
vpOffset.Store(ub->vpOffset);
|
||||
ub->NaN = std::numeric_limits<float>::quiet_NaN(); // Used in the shader for range culling.
|
||||
|
||||
@@ -418,8 +418,8 @@ static void ProjectVertices(const GPUgstate &gstate, TransformedVertex *transfor
|
||||
transformed[i].z = xyz.z;
|
||||
}
|
||||
#else
|
||||
const Vec4F32 vpOffset = GetViewportOffsetVec(gstate);
|
||||
const Vec4F32 vpScale = GetViewportScaleVec(gstate);
|
||||
const Vec4F32 vpOffset = LoadViewportOffsetVec(gstate);
|
||||
const Vec4F32 vpScale = LoadViewportScaleVec(gstate);
|
||||
// CrossSIMD implementation.
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
Vec4F32 xyzw = Vec4F32::Load(&transformed[i].x);
|
||||
@@ -461,9 +461,8 @@ inline void LerpTransformedVertex(TransformedVertex *dest, TransformedVertex &a,
|
||||
static void ClipTrianglesAgainstNearPlane(
|
||||
TransformedVertex *transformed, int &transformedCount, int maxTransformed,
|
||||
u16 *indicesIn, int numIndicesIn,
|
||||
u16 *indicesOut, int &numIndicesOut, int maxIndicesOut
|
||||
u16 *indicesOut, int &numIndicesOut, int maxIndicesOut, TransformStats *stats
|
||||
) {
|
||||
int clipCount = 0;
|
||||
// Process one triangle (3 indices) at a time
|
||||
for (size_t i = 0; i < numIndicesIn; i += 3) {
|
||||
const u16 idx0 = indicesIn[i];
|
||||
@@ -494,16 +493,18 @@ static void ClipTrianglesAgainstNearPlane(
|
||||
// Case 2: Entirely clipped / behind near plane
|
||||
else if (insideCount == 0) {
|
||||
// Cull, no clipping needed.
|
||||
stats->culledTrianglesNear++;
|
||||
continue;
|
||||
}
|
||||
// Case 3: Entirely beyond far plane
|
||||
else if (insideFarCount == 0) {
|
||||
// All are beyond the far plane. Cull.
|
||||
stats->culledTrianglesFar++;
|
||||
continue;
|
||||
}
|
||||
// Case 3: Partially clipped
|
||||
else {
|
||||
clipCount++;
|
||||
stats->clippedTriangles++;
|
||||
|
||||
// We need to organize vertices cleanly to calculate intersections.
|
||||
// We will create a local polygon array of the inside/outside states.
|
||||
@@ -591,7 +592,6 @@ static SoftwareTransformAction ProjectClipAndExpand(SoftwareTransformParams &par
|
||||
bool throughmode = (vertType & GE_VTYPE_THROUGH_MASK) != 0;
|
||||
|
||||
// Step 2: expand and process primitives.
|
||||
result->drawBuffer = transformed;
|
||||
int drawIndexCount = 0;
|
||||
|
||||
// NOTE: ExpandRectanges/lines/etc should do clipping while they're at it.
|
||||
@@ -607,6 +607,7 @@ static SoftwareTransformAction ProjectClipAndExpand(SoftwareTransformParams &par
|
||||
result->drawVertexCount = 0;
|
||||
result->drawIndexCount = 0;
|
||||
result->pixelMapped = false;
|
||||
result->drawBuffer = nullptr;
|
||||
return SW_CULLED;
|
||||
}
|
||||
result->drawBuffer = transformedExpanded;
|
||||
@@ -635,6 +636,7 @@ static SoftwareTransformAction ProjectClipAndExpand(SoftwareTransformParams &par
|
||||
if (!ExpandPoints(vertexCount, numDecodedVerts, vertsSize, inds, indsSize, transformed, transformedExpanded, &drawIndexCount, throughmode)) {
|
||||
result->drawVertexCount = 0;
|
||||
result->drawIndexCount = 0;
|
||||
result->drawBuffer = nullptr;
|
||||
return SW_CULLED;
|
||||
}
|
||||
result->drawBuffer = transformedExpanded;
|
||||
@@ -650,16 +652,21 @@ static SoftwareTransformAction ProjectClipAndExpand(SoftwareTransformParams &par
|
||||
if (!ExpandLines(vertexCount, numDecodedVerts, vertsSize, inds, indsSize, transformed, transformedExpanded, &drawIndexCount, throughmode)) {
|
||||
result->drawVertexCount = 0;
|
||||
result->drawIndexCount = 0;
|
||||
result->drawBuffer = nullptr;
|
||||
return SW_CULLED;
|
||||
}
|
||||
result->drawBuffer = transformedExpanded;
|
||||
} else if (prim == GE_PRIM_TRIANGLES) {
|
||||
// Triangles. We can simply draw the unexpanded buffer, although we do also take the opportunity to perform culling.
|
||||
result->drawBuffer = transformed;
|
||||
// We might actually write more vertics at the end of transformed.
|
||||
|
||||
drawIndexCount = vertexCount;
|
||||
result->pixelMapped = false;
|
||||
|
||||
if (throughmode) {
|
||||
// Nothing to do, pass the vertices right through as-is. Well, we can go look for pixel mapping, but we don't do any culling or clipping.
|
||||
// Nothing to do, pass the vertices right through as-is. Well, we can go look for pixel mapping,
|
||||
// but we don't do any projection, culling or clipping.
|
||||
if (g_Config.bSmart2DTexFiltering && !gstate_c.textureIsVideo) {
|
||||
// We check some common cases for pixel mapping.
|
||||
// TODO: It's not really optimal that some previous step has removed the triangle strip.
|
||||
@@ -711,7 +718,7 @@ static SoftwareTransformAction ProjectClipAndExpand(SoftwareTransformParams &par
|
||||
if (gstate.isDepthClipEnabled()) {
|
||||
const u16 *indsIn = (const u16 *)inds;
|
||||
int newIndexCount = 0;
|
||||
ClipTrianglesAgainstNearPlane(transformed, numDecodedVerts, 65536, inds, vertexCount, indsOut, newIndexCount, 65336);
|
||||
ClipTrianglesAgainstNearPlane(transformed, numDecodedVerts, 65536, inds, vertexCount, indsOut, newIndexCount, 65336, &result->stats);
|
||||
drawIndexCount = newIndexCount;
|
||||
} else {
|
||||
std::vector<int> outsideZ;
|
||||
@@ -1300,127 +1307,202 @@ static Vec3f ScreenToDrawing(const Vec3f& coords) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: This probably is not the best interface.
|
||||
// drawEngine is just for the vertex decoder lookup.
|
||||
// This is really just for vertex preview in the debugger, not for actual rendering!
|
||||
bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) {
|
||||
// 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) {
|
||||
// This is always for the current vertices.
|
||||
u16 indexLowerBound = 0;
|
||||
u16 indexUpperBound = count - 1;
|
||||
|
||||
if (!Memory::IsValidAddress(gstate_c.vertexAddr) || count == 0)
|
||||
if (!Memory::IsValidAddress(gstate_c.vertexAddr) || count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE && !Memory::IsValidAddress(gstate_c.indexAddr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 vertTypeID = GetVertTypeID(gstate.vertType, gstate.getUVGenMode(), true);
|
||||
bool savedVertexFullAlpha = gstate_c.vertexFullAlpha;
|
||||
|
||||
if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
const u8 *inds = Memory::GetPointer(gstate_c.indexAddr);
|
||||
const u16_le *inds16 = (const u16_le *)inds;
|
||||
const u32_le *inds32 = (const u32_le *)inds;
|
||||
// Points is the only primitive that generates 6x as many vertices as input indices (2 triangles per point).
|
||||
std::vector<u16> indexTemp;
|
||||
indexTemp.resize(65536); // (prim == GEPrimitiveType::GE_PRIM_POINTS ? count * 6 : count * 3) * 4);
|
||||
|
||||
if (inds) {
|
||||
GetIndexBounds(inds, count, gstate.vertType, &indexLowerBound, &indexUpperBound);
|
||||
indices.resize(count);
|
||||
switch (gstate.vertType & GE_VTYPE_IDX_MASK) {
|
||||
case GE_VTYPE_IDX_8BIT:
|
||||
for (int i = 0; i < count; ++i) {
|
||||
indices[i] = inds[i];
|
||||
}
|
||||
break;
|
||||
case GE_VTYPE_IDX_16BIT:
|
||||
for (int i = 0; i < count; ++i) {
|
||||
indices[i] = inds16[i];
|
||||
}
|
||||
break;
|
||||
case GE_VTYPE_IDX_32BIT:
|
||||
for (int i = 0; i < count; ++i) {
|
||||
// These are rare. Only the bottom 16 bits are used.
|
||||
indices[i] = (u16)inds32[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
// First, inspect the indices to find the range we need to decode.
|
||||
const u8 *indsPtr = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
if ((vertTypeID & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
const u16_le *inds16 = (const u16_le *)indsPtr;
|
||||
const u32_le *inds32 = (const u32_le *)indsPtr;
|
||||
if (indsPtr) {
|
||||
GetIndexBounds(indsPtr, count, vertTypeID, &indexLowerBound, &indexUpperBound);
|
||||
} else {
|
||||
indices.clear();
|
||||
// Bad index buffer.
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
indices.clear();
|
||||
// No indices, so we just use the count as the upper bound.
|
||||
indexUpperBound = count - 1;
|
||||
indexLowerBound = 0;
|
||||
}
|
||||
|
||||
static std::vector<u32> temp_buffer;
|
||||
static std::vector<SimpleVertex> simpleVertices;
|
||||
temp_buffer.resize(std::max((int)indexUpperBound, 8192) * 128 / sizeof(u32));
|
||||
simpleVertices.resize(indexUpperBound + 1);
|
||||
int verticesToDecode = indexUpperBound + 1 - indexLowerBound;
|
||||
|
||||
// We always want "applyskinindecode" here, faster than letting NormalizeVertices handle it.
|
||||
const u32 vertTypeID = GetVertTypeID(gstate.vertType, gstate.getUVGenMode(), true);
|
||||
const u8 *verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
|
||||
|
||||
std::vector<u8> vertsTemp;
|
||||
|
||||
// Next, run the vertex decoder. We enforce software skinning here.
|
||||
VertexDecoder *dec = drawEngine->GetVertexDecoder(vertTypeID);
|
||||
NormalizeVertices(&simpleVertices[0], (u8 *)(&temp_buffer[0]), Memory::GetPointerUnchecked(gstate_c.vertexAddr), indexLowerBound, indexUpperBound, dec, gstate.vertType);
|
||||
const int stride = (int)dec->GetDecVtxFmt().stride;
|
||||
vertsTemp.resize(stride * verticesToDecode + 32); // Add some padding bytes for "over-writes".
|
||||
|
||||
float world[16];
|
||||
float view[16];
|
||||
float worldview[16];
|
||||
float worldviewproj[16];
|
||||
ConvertMatrix4x3To4x4(world, gstate.worldMatrix);
|
||||
ConvertMatrix4x3To4x4(view, gstate.viewMatrix);
|
||||
Matrix4ByMatrix4(worldview, world, view);
|
||||
Matrix4ByMatrix4(worldviewproj, worldview, gstate.projMatrix);
|
||||
UVScale uvScale{};
|
||||
LoadUVScaleOffsetVec(gstate).Store(&uvScale.uScale);
|
||||
|
||||
// This transforms the vertices.
|
||||
// NOTE: We really should just run the full software transform?
|
||||
const u8 *startPos = verts + indexLowerBound * dec->VertexSize();
|
||||
dec->DecodeVerts(vertsTemp.data(), startPos, &uvScale, verticesToDecode);
|
||||
|
||||
vertices.resize(indexUpperBound + 1);
|
||||
uint32_t vertType = gstate.vertType;
|
||||
for (int i = indexLowerBound; i <= indexUpperBound; ++i) {
|
||||
const SimpleVertex &vert = simpleVertices[i];
|
||||
int numDecodedVerts = verticesToDecode;
|
||||
u16 *inds = indexTemp.data();
|
||||
|
||||
if ((vertType & GE_VTYPE_THROUGH) != 0) {
|
||||
if (vertType & GE_VTYPE_TC_MASK) {
|
||||
vertices[i].u = vert.uv[0];
|
||||
vertices[i].v = vert.uv[1];
|
||||
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);
|
||||
VertexReader reader(vertsTemp.data(), dec->GetDecVtxFmt(), vertTypeID);
|
||||
|
||||
const u8 defaultColor[4] = {
|
||||
(u8)gstate.getMaterialAmbientR(),
|
||||
(u8)gstate.getMaterialAmbientG(),
|
||||
(u8)gstate.getMaterialAmbientB(),
|
||||
(u8)gstate.getMaterialAmbientA(),
|
||||
};
|
||||
|
||||
for (int i = 0; i < verticesToDecode; i++) {
|
||||
reader.Goto(i);
|
||||
GPUDebugVertex &sv = debugVertices[i];
|
||||
if (vertTypeID & GE_VTYPE_TC_MASK) {
|
||||
reader.ReadUV(&sv.u);
|
||||
} else {
|
||||
vertices[i].u = 0.0f;
|
||||
vertices[i].v = 0.0f;
|
||||
sv.u = 0.0f;
|
||||
sv.v = 0.0f;
|
||||
}
|
||||
vertices[i].x = vert.pos.x;
|
||||
vertices[i].y = vert.pos.y;
|
||||
vertices[i].z = vert.pos.z;
|
||||
if (vertType & GE_VTYPE_COL_MASK) {
|
||||
memcpy(vertices[i].c, vert.color, sizeof(vertices[i].c));
|
||||
if (vertTypeID & GE_VTYPE_COL_MASK) {
|
||||
sv.color0_32 = reader.ReadColor0_8888();
|
||||
} else {
|
||||
memset(vertices[i].c, 0, sizeof(vertices[i].c));
|
||||
memcpy(sv.c, defaultColor, 4);
|
||||
}
|
||||
vertices[i].nx = 0.0f; // No meaningful normals in through mode
|
||||
vertices[i].ny = 0.0f;
|
||||
vertices[i].nz = 1.0f;
|
||||
} else {
|
||||
float clipPos[4];
|
||||
Vec3ByMatrix44(clipPos, vert.pos.AsArray(), worldviewproj);
|
||||
Vec3f screenPos = ClipToScreen(clipPos);
|
||||
Vec3f drawPos = ScreenToDrawing(screenPos);
|
||||
|
||||
if (vertType & GE_VTYPE_TC_MASK) {
|
||||
vertices[i].u = vert.uv[0] * (float)gstate.getTextureWidth(0);
|
||||
vertices[i].v = vert.uv[1] * (float)gstate.getTextureHeight(0);
|
||||
if (vertTypeID & GE_VTYPE_NRM_MASK) {
|
||||
reader.ReadNrm((float *)&sv.nx);
|
||||
} else {
|
||||
vertices[i].u = 0.0f;
|
||||
vertices[i].v = 0.0f;
|
||||
sv.nx = 0.0f;
|
||||
sv.ny = 0.0f;
|
||||
sv.nz = 1.0f;
|
||||
}
|
||||
// Should really have separate coordinates for before and after transform.
|
||||
vertices[i].x = drawPos.x;
|
||||
vertices[i].y = drawPos.y;
|
||||
vertices[i].z = drawPos.z;
|
||||
if (vertType & GE_VTYPE_COL_MASK) {
|
||||
memcpy(vertices[i].c, vert.color, sizeof(vertices[i].c));
|
||||
} else {
|
||||
memset(vertices[i].c, 0, sizeof(vertices[i].c));
|
||||
}
|
||||
vertices[i].nx = vert.nrm.x;
|
||||
vertices[i].ny = vert.nrm.y;
|
||||
vertices[i].nz = vert.nrm.z;
|
||||
reader.ReadPosAuto((float *)&sv.x);
|
||||
}
|
||||
|
||||
// Output the indices straight
|
||||
switch (vertTypeID & GE_VTYPE_IDX_MASK) {
|
||||
case GE_VTYPE_IDX_NONE:
|
||||
debugIndices.clear(); // it's just a sequence, effectively.
|
||||
break;
|
||||
case GE_VTYPE_IDX_8BIT:
|
||||
debugIndices.resize(verticesToDecode);
|
||||
for (int i = 0; i < verticesToDecode; i++) {
|
||||
debugIndices[i] = ((const u8 *)indsPtr)[i];
|
||||
}
|
||||
break;
|
||||
case GE_VTYPE_IDX_16BIT:
|
||||
debugIndices.resize(verticesToDecode);
|
||||
for (int i = 0; i < verticesToDecode; i++) {
|
||||
debugIndices[i] = ((const u16_le *)indsPtr)[i];
|
||||
}
|
||||
break;
|
||||
case GE_VTYPE_IDX_32BIT:
|
||||
debugIndices.resize(verticesToDecode);
|
||||
for (int i = 0; i < verticesToDecode; i++) {
|
||||
debugIndices[i] = ((const u32_le *)indsPtr)[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
*outLowerIndexBound = indexLowerBound;
|
||||
*outPrim = prim; // before it changes.
|
||||
return true;
|
||||
}
|
||||
|
||||
IndexGenerator indexGen;
|
||||
indexGen.Setup(indexTemp.data());
|
||||
const int indexOffset = -indexLowerBound;
|
||||
|
||||
// This corresponds to DecodeInds in DrawEngineCommon.
|
||||
const bool clockwise = true;
|
||||
switch ((vertTypeID & GE_VTYPE_IDX_MASK)) {
|
||||
case GE_VTYPE_IDX_NONE:
|
||||
indexGen.AddPrim(prim, count, indexOffset, true);
|
||||
break;
|
||||
case GE_VTYPE_IDX_8BIT:
|
||||
indexGen.TranslatePrim(prim, count, (const u8 *)indsPtr, indexOffset, clockwise);
|
||||
break;
|
||||
case GE_VTYPE_IDX_16BIT:
|
||||
indexGen.TranslatePrim(prim, count, (const u16_le *)indsPtr, indexOffset, clockwise);
|
||||
break;
|
||||
case GE_VTYPE_IDX_32BIT:
|
||||
indexGen.TranslatePrim(prim, count, (const u32_le *)indsPtr, indexOffset, clockwise);
|
||||
break;
|
||||
}
|
||||
|
||||
int generatedIndices = indexGen.VertexCount();
|
||||
|
||||
// We need two temp buffers, transformed and transformedExpanded (the latter is only used for non-triangle primitives).
|
||||
std::vector<TransformedVertex> transformed(65536);
|
||||
std::vector<TransformedVertex> transformedExpanded(65536);
|
||||
// OK, time to run the software transform on these.
|
||||
SoftwareTransformParams params{};
|
||||
SoftwareTransformResult result{};
|
||||
params.allowClear = false;
|
||||
params.decoded = vertsTemp.data();
|
||||
params.transformed = transformed.data();
|
||||
params.transformedExpanded = transformedExpanded.data();
|
||||
|
||||
RunSoftwareTransform(params, prim, verticesToDecode, dec->GetDecVtxFmt(), numDecodedVerts, 65536, generatedIndices, inds, (int)indexTemp.size(), &result);
|
||||
|
||||
// Output of software transform is always triangles.
|
||||
prim = GE_PRIM_TRIANGLES;
|
||||
|
||||
if (stats) {
|
||||
*stats = result.stats;
|
||||
}
|
||||
// Convert the transformed outputs.
|
||||
|
||||
gstate_c.vertexFullAlpha = savedVertexFullAlpha;
|
||||
|
||||
// Supply indices in a correctly-sized vector.
|
||||
debugIndices.resize(result.drawIndexCount);
|
||||
memcpy(debugIndices.data(), inds, result.drawIndexCount * sizeof(u16));
|
||||
|
||||
// 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.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;
|
||||
}
|
||||
*outPrim = prim;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,12 @@ enum SoftwareTransformAction {
|
||||
SW_CULLED, // don't draw
|
||||
};
|
||||
|
||||
struct TransformStats {
|
||||
u16 culledTrianglesNear;
|
||||
u16 culledTrianglesFar;
|
||||
u16 clippedTriangles; // Only near plane is clipped against
|
||||
};
|
||||
|
||||
struct SoftwareTransformResult {
|
||||
u32 color;
|
||||
float depth;
|
||||
@@ -48,6 +54,8 @@ struct SoftwareTransformResult {
|
||||
int drawVertexCount;
|
||||
int drawIndexCount;
|
||||
bool pixelMapped;
|
||||
|
||||
TransformStats stats;
|
||||
};
|
||||
|
||||
struct SoftwareTransformParams {
|
||||
@@ -66,10 +74,15 @@ void IndexBufferProvokingLastToFirst(int prim, u16 *inds, int indsSize);
|
||||
|
||||
// indsSize is in indices, not bytes.
|
||||
// NOTE: In case of clipping, this might write extra vertices after params.transformed.
|
||||
// NOTE2: The output is ALWAYS an indexed triangle list, no matter the input primitive.
|
||||
SoftwareTransformAction RunSoftwareTransform(SoftwareTransformParams ¶ms, int prim, u32 vertexType, const DecVtxFormat &decVtxFormat, int &numDecodedVerts, int vertsSize, int vertexCount, u16 *&inds, int indsSize, SoftwareTransformResult *result);
|
||||
|
||||
class DrawEngineCommon;
|
||||
|
||||
// Slow. See description in the cpp file.
|
||||
u32 NormalizeVertices(SimpleVertex *sverts, u8 *bufPtr, const u8 *inPtr, int lowerBound, int upperBound, const VertexDecoder *dec, u32 vertType);
|
||||
bool GetCurrentDrawAsDebugVertices(DrawEngineCommon *drawEngine, int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices);
|
||||
|
||||
// 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);
|
||||
|
||||
+35
-25
@@ -12,7 +12,7 @@
|
||||
#include "GPU/Common/SplineCommon.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
void FormatStateRow(GPUCommon *gpudebug, char *dest, size_t destSize, CmdFormatType fmt, u32 value, bool enabled, u32 otherValue, u32 otherValue2) {
|
||||
void FormatStateRow(char *dest, size_t destSize, CmdFormatType fmt, u32 value, bool enabled, u32 otherValue, u32 otherValue2) {
|
||||
value &= 0xFFFFFF;
|
||||
otherValue &= 0xFFFFFF;
|
||||
otherValue2 &= 0xFFFFFF;
|
||||
@@ -545,6 +545,7 @@ void FormatVertCol(char *dest, size_t destSize, const GPUDebugVertex &vert, int
|
||||
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:
|
||||
@@ -553,6 +554,7 @@ void FormatVertCol(char *dest, size_t destSize, const GPUDebugVertex &vert, int
|
||||
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;
|
||||
|
||||
default:
|
||||
truncate_cpy(dest, destSize, "Invalid");
|
||||
@@ -834,11 +836,13 @@ 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 &count) {
|
||||
bool GetPrimPreview(u32 op, GEPrimitiveType &prim, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int *lowerIndexBound, bool transformed) {
|
||||
u32 prim_type = GE_PRIM_INVALID;
|
||||
int count_u = 0;
|
||||
int count_v = 0;
|
||||
|
||||
int count = 0;
|
||||
|
||||
const u32 cmd = op >> 24;
|
||||
if (cmd == GE_CMD_PRIM) {
|
||||
prim_type = (op >> 16) & 0x7;
|
||||
@@ -865,11 +869,35 @@ bool GetPrimPreview(u32 op, GEPrimitiveType &prim, std::vector<GPUDebugVertex> &
|
||||
|
||||
prim = static_cast<GEPrimitiveType>(prim_type);
|
||||
|
||||
if (!gpu->GetCurrentDrawAsDebugVertices(count, vertices, indices)) {
|
||||
DebugVertexFlags flags{};
|
||||
if (cmd == GE_CMD_PRIM) {
|
||||
flags |= DebugVertexFlags::Clipped;
|
||||
if (transformed) {
|
||||
flags |= DebugVertexFlags::Transformed;
|
||||
}
|
||||
} else {
|
||||
flags = DebugVertexFlags::Transformed;
|
||||
}
|
||||
|
||||
GEPrimitiveType outPrim;
|
||||
if (!gpu->GetCurrentDrawAsDebugVertices(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());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prim = outPrim;
|
||||
|
||||
if (cmd != GE_CMD_PRIM) {
|
||||
static std::vector<SimpleVertex> generatedVerts;
|
||||
static std::vector<u16> generatedInds;
|
||||
@@ -882,7 +910,8 @@ bool GetPrimPreview(u32 op, GEPrimitiveType &prim, std::vector<GPUDebugVertex> &
|
||||
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);
|
||||
} else if (cmd == GE_CMD_SPLINE) {
|
||||
@@ -900,26 +929,7 @@ bool GetPrimPreview(u32 op, GEPrimitiveType &prim, std::vector<GPUDebugVertex> &
|
||||
indices = generatedInds;
|
||||
}
|
||||
|
||||
if (prim == GE_PRIM_RECTANGLES) {
|
||||
ExpandRectangles(vertices, indices, count, gpu->GetGState().isModeThrough());
|
||||
}
|
||||
|
||||
// TODO: Probably there's a better way and place to do this.
|
||||
u16 minIndex = 0;
|
||||
u16 maxIndex = count - 1;
|
||||
if (!indices.empty()) {
|
||||
minIndex = 0xFFFF;
|
||||
maxIndex = 0;
|
||||
for (int i = 0; i < std::min((size_t)count, indices.size()); ++i) {
|
||||
if (minIndex > indices[i]) {
|
||||
minIndex = indices[i];
|
||||
}
|
||||
if (maxIndex < indices[i]) {
|
||||
maxIndex = indices[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
auto wrapCoord = [](float &coord) {
|
||||
if (coord < 0.0f) {
|
||||
coord += ceilf(-coord);
|
||||
@@ -941,7 +951,7 @@ bool GetPrimPreview(u32 op, GEPrimitiveType &prim, std::vector<GPUDebugVertex> &
|
||||
if (!clampT)
|
||||
wrapCoord(vertices[i].v);
|
||||
}
|
||||
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,17 +14,17 @@ 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,
|
||||
};
|
||||
|
||||
class GPUCommon;
|
||||
|
||||
extern const GECommand g_stateFlagsRows[];
|
||||
extern const GECommand g_stateLightingRows[];
|
||||
extern const GECommand g_stateTextureRows[];
|
||||
@@ -37,11 +37,11 @@ extern const size_t g_stateSettingsRowsSize;
|
||||
struct GPUDebugVertex;
|
||||
class VertexDecoder;
|
||||
|
||||
void FormatStateRow(GPUCommon *debug, char *dest, size_t destSize, CmdFormatType fmt, u32 value, bool enabled, u32 otherValue, u32 otherValue2);
|
||||
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);
|
||||
|
||||
// These are utilities used by the debugger vertex preview.
|
||||
bool GetPrimPreview(u32 op, GEPrimitiveType &prim, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int &count);
|
||||
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]);
|
||||
|
||||
+21
-16
@@ -1569,7 +1569,7 @@ void GPUCommon::SyncEnd(GPUSyncType waitType, int listid, bool wokeThreads) {
|
||||
}
|
||||
}
|
||||
|
||||
bool GPUCommon::GetCurrentDisplayList(DisplayList &list) {
|
||||
bool GPUCommon::GetCurrentDisplayList(DisplayList &list) const {
|
||||
if (!currentList) {
|
||||
return false;
|
||||
}
|
||||
@@ -1577,25 +1577,31 @@ bool GPUCommon::GetCurrentDisplayList(DisplayList &list) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int GPUCommon::GetCurrentPrimCount() {
|
||||
int GPUCommon::GetCurrentPrimCount(GEPrimitiveType *prim) const {
|
||||
DisplayList list;
|
||||
u32 cmd;
|
||||
if (GetCurrentDisplayList(list)) {
|
||||
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;
|
||||
cmd = Memory::Read_U32(list.pc);
|
||||
} else {
|
||||
// Current prim value.
|
||||
return gstate.cmdmem[GE_CMD_PRIM] & 0xFFFF;
|
||||
cmd = 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) {
|
||||
*prim = GE_PRIM_TRIANGLES; // no correct answer
|
||||
u32 u = (cmd & 0x00FF) >> 0;
|
||||
u32 v = (cmd & 0xFF00) >> 8;
|
||||
return u * v;
|
||||
} else {
|
||||
// Unknown primitive.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DisplayList> GPUCommon::ActiveDisplayLists() {
|
||||
std::vector<DisplayList> GPUCommon::ActiveDisplayLists() const {
|
||||
std::vector<DisplayList> result;
|
||||
result.reserve(dlQueue.size());
|
||||
|
||||
@@ -2020,9 +2026,8 @@ bool GPUCommon::PerformWriteStencilFromMemory(u32 dest, int size, WriteStencil f
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GPUCommon::GetCurrentDrawAsDebugVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) {
|
||||
gstate_c.UpdateUVScaleOffset();
|
||||
return ::GetCurrentDrawAsDebugVertices(drawEngineCommon_, count, vertices, indices);
|
||||
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::DescribeCodePtr(const u8 *ptr, std::string &name) {
|
||||
|
||||
+6
-4
@@ -52,6 +52,7 @@ inline bool IsTrianglePrim(GEPrimitiveType prim) {
|
||||
return prim > GE_PRIM_LINE_STRIP && prim != GE_PRIM_RECTANGLES;
|
||||
}
|
||||
|
||||
struct TransformStats;
|
||||
class GPUCommon {
|
||||
public:
|
||||
// The constructor might run on the loader thread.
|
||||
@@ -72,9 +73,10 @@ public:
|
||||
virtual bool GetCurrentTexture(GPUDebugBuffer &buffer, int level, bool *isFramebuffer) { return false; }
|
||||
virtual bool GetCurrentClut(GPUDebugBuffer &buffer) { return false;}
|
||||
virtual bool GetOutputFramebuffer(GPUDebugBuffer &buffer) { return false; }
|
||||
bool GetCurrentDisplayList(DisplayList &list);
|
||||
virtual bool GetCurrentDrawAsDebugVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices);
|
||||
int GetCurrentPrimCount();
|
||||
|
||||
bool GetCurrentDisplayList(DisplayList &list) const;
|
||||
virtual 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;
|
||||
|
||||
// FinishInitOnMainThread runs on the main thread, of course.
|
||||
virtual void FinishInitOnMainThread() {}
|
||||
@@ -207,7 +209,7 @@ public:
|
||||
|
||||
virtual bool DescribeCodePtr(const u8 *ptr, std::string &name);
|
||||
|
||||
std::vector<DisplayList> ActiveDisplayLists();
|
||||
std::vector<DisplayList> ActiveDisplayLists() const;
|
||||
void ResetListPC(int listID, u32 pc);
|
||||
void ResetListStall(int listID, u32 stall);
|
||||
void ResetListState(int listID, DisplayListState state);
|
||||
|
||||
@@ -3,9 +3,14 @@
|
||||
#include "Common/Math/CrossSIMD.h"
|
||||
#include "GPU/GPUState.h"
|
||||
|
||||
inline Vec4F32 GetViewportOffsetVec(const GPUgstate &gstate) {
|
||||
return Vec4F32::LoadF24x3_DontCare(&gstate.viewportxcenter);
|
||||
inline Vec4F32 LoadViewportOffsetVec(const GPUgstate &gstate) {
|
||||
// We ignore the last member.
|
||||
return Vec4F32::LoadF24x4(&gstate.viewportxcenter);
|
||||
}
|
||||
inline Vec4F32 GetViewportScaleVec(const GPUgstate &gstate) {
|
||||
return Vec4F32::LoadF24x3_DontCare(&gstate.viewportxscale);
|
||||
inline Vec4F32 LoadViewportScaleVec(const GPUgstate &gstate) {
|
||||
// We ignore the last member.
|
||||
return Vec4F32::LoadF24x4(&gstate.viewportxscale);
|
||||
}
|
||||
inline Vec4F32 LoadUVScaleOffsetVec(const GPUgstate &gstate) {
|
||||
return Vec4F32::LoadF24x4(&gstate.texscaleu);
|
||||
}
|
||||
|
||||
@@ -1440,9 +1440,13 @@ bool SoftGPU::GetCurrentClut(GPUDebugBuffer &buffer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoftGPU::GetCurrentDrawAsDebugVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) {
|
||||
bool SoftGPU::GetCurrentDrawAsDebugVertices(GEPrimitiveType prim, GEPrimitiveType *outPrim, int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int *lowerIndexBound, TransformStats *stats, DebugVertexFlags flags) const {
|
||||
gstate_c.UpdateUVScaleOffset();
|
||||
return drawEngine_->transformUnit.GetCurrentDrawAsDebugVertices(count, vertices, indices);
|
||||
|
||||
// We just pad out the vertex array here.
|
||||
*lowerIndexBound = 0;
|
||||
*outPrim = prim;
|
||||
return drawEngine_->transformUnit.GetCurrentDrawAsDebugVertices(count, vertices, indices, flags);
|
||||
}
|
||||
|
||||
bool SoftGPU::DescribeCodePtr(const u8 *ptr, std::string &name) {
|
||||
|
||||
@@ -172,7 +172,7 @@ public:
|
||||
bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer) override;
|
||||
bool GetCurrentTexture(GPUDebugBuffer &buffer, int level, bool *isFramebuffer) override;
|
||||
bool GetCurrentClut(GPUDebugBuffer &buffer) override;
|
||||
bool GetCurrentDrawAsDebugVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) override;
|
||||
bool GetCurrentDrawAsDebugVertices(GEPrimitiveType prim, GEPrimitiveType *outPrim, int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int *lowerIndexBound, TransformStats *stats, DebugVertexFlags flags) const override;
|
||||
|
||||
bool DescribeCodePtr(const u8 *ptr, std::string &name) override;
|
||||
|
||||
|
||||
@@ -909,9 +909,8 @@ void TransformUnit::NotifyClutUpdate(const void *src) {
|
||||
binner_->UpdateClut(src);
|
||||
}
|
||||
|
||||
// TODO: This probably is not the best interface.
|
||||
// Also, we should try to merge this into the similar function in DrawEngineCommon.
|
||||
bool TransformUnit::GetCurrentDrawAsDebugVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) {
|
||||
// We should try to merge this into the similar function in SoftwareTransformCommon. It's really backend-independent (or should be...)
|
||||
bool TransformUnit::GetCurrentDrawAsDebugVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, DebugVertexFlags flags) {
|
||||
// This is always for the current vertices.
|
||||
u16 indexLowerBound = 0;
|
||||
u16 indexUpperBound = count - 1;
|
||||
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
void SubmitPrimitive(const void* vertices, const void* indices, GEPrimitiveType prim_type, int vertex_count, u32 vertex_type, int *bytesRead, SoftwareDrawEngine *drawEngine);
|
||||
void SubmitImmVertex(const ClipVertexData &vert, SoftwareDrawEngine *drawEngine);
|
||||
|
||||
static bool GetCurrentDrawAsDebugVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices);
|
||||
static bool GetCurrentDrawAsDebugVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, DebugVertexFlags flags);
|
||||
|
||||
void Flush(GPUCommon *common, const char *reason);
|
||||
void FlushIfOverlap(GPUCommon *common, const char *reason, bool modifying, uint32_t addr, uint32_t stride, uint32_t w, uint32_t h);
|
||||
|
||||
@@ -121,6 +121,8 @@ struct ImConfig {
|
||||
int requesterToken;
|
||||
|
||||
bool sasShowAllVoices = false;
|
||||
bool vertexListTransformed = false;
|
||||
bool vertexListClipped = false;
|
||||
|
||||
float fbViewerZoom = 1.0f;
|
||||
|
||||
|
||||
+134
-84
@@ -10,6 +10,7 @@
|
||||
#include "GPU/Common/FramebufferManagerCommon.h"
|
||||
#include "GPU/Common/TextureCacheCommon.h"
|
||||
#include "GPU/Common/VertexDecoderCommon.h"
|
||||
#include "GPU/Common/SoftwareTransformCommon.h"
|
||||
|
||||
#include "Core/HLE/sceDisplay.h"
|
||||
#include "Core/HW/Display.h"
|
||||
@@ -889,88 +890,109 @@ static const char *DLStateToString(DisplayListState state) {
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawPreviewPrimitive(ImDrawList *drawList, ImVec2 p0, GEPrimitiveType prim, const std::vector<u16> &indices, const std::vector<GPUDebugVertex> &verts, int count, bool uvToPos, float sx = 1.0f, float sy = 1.0f) {
|
||||
if (count) {
|
||||
auto x = [sx, uvToPos](const GPUDebugVertex &vert) {
|
||||
return sx * (uvToPos ? vert.u : vert.x);
|
||||
};
|
||||
auto y = [sy, uvToPos](const GPUDebugVertex &vert) {
|
||||
return sy * (uvToPos ? vert.v : vert.y);
|
||||
};
|
||||
// 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 = 1.0f, float sy = 1.0f) {
|
||||
auto x = [sx, uvToPos](const GPUDebugVertex &vert) {
|
||||
return sx * (uvToPos ? vert.u : vert.x);
|
||||
};
|
||||
auto y = [sy, uvToPos](const GPUDebugVertex &vert) {
|
||||
return sy * (uvToPos ? vert.v : vert.y);
|
||||
};
|
||||
|
||||
// TODO: Maybe not the best idea to use the heavy AddTriangleFilled API instead of just adding raw triangles.
|
||||
switch (prim) {
|
||||
case GE_PRIM_TRIANGLES:
|
||||
case GE_PRIM_RECTANGLES:
|
||||
{
|
||||
for (int i = 0; i < count - 2; i += 3) {
|
||||
const auto &v1 = indices.empty() ? verts[i] : verts[indices[i]];
|
||||
const auto &v2 = indices.empty() ? verts[i + 1] : verts[indices[i + 1]];
|
||||
const auto &v3 = indices.empty() ? verts[i + 2] : verts[indices[i + 2]];
|
||||
drawList->AddTriangleFilled(
|
||||
ImVec2(p0.x + x(v1), p0.y + y(v1)),
|
||||
ImVec2(p0.x + x(v2), p0.y + y(v2)),
|
||||
ImVec2(p0.x + x(v3), p0.y + y(v3)), ImColor(0x600000FF));
|
||||
}
|
||||
break;
|
||||
// TODO: Maybe not the best idea to use the heavy AddTriangleFilled API instead of just adding raw triangles.
|
||||
|
||||
// TODO: If vertices are transformed, we can look for clipped triangles (by checking if any point equals w=-z)
|
||||
// and color them differently.
|
||||
|
||||
// TODO: If untransformed, we can apply a transformation here so we can let the user spin around the untransformed mesh data
|
||||
// if desired.
|
||||
|
||||
constexpr u32 defaultColor = 0x600000FF;
|
||||
constexpr u32 clippedColor = 0x60FF0000;
|
||||
|
||||
int count = indices.empty() ? verts.size() : indices.size();
|
||||
|
||||
switch (prim) {
|
||||
case GE_PRIM_TRIANGLES:
|
||||
{
|
||||
for (int i = 0; i < count; i += 3) {
|
||||
const auto &v1 = indices.empty() ? verts[i + 0] : verts[indices[indexOffset + i + 0]];
|
||||
const auto &v2 = indices.empty() ? verts[i + 1] : verts[indices[indexOffset + i + 1]];
|
||||
const auto &v3 = indices.empty() ? verts[i + 2] : verts[indices[indexOffset + i + 2]];
|
||||
drawList->AddTriangleFilled(
|
||||
ImVec2(p0.x + x(v1), p0.y + y(v1)),
|
||||
ImVec2(p0.x + x(v2), p0.y + y(v2)),
|
||||
ImVec2(p0.x + x(v3), p0.y + y(v3)), ImColor(defaultColor));
|
||||
}
|
||||
case GE_PRIM_TRIANGLE_FAN:
|
||||
{
|
||||
for (int i = 0; i < count - 2; i++) {
|
||||
const auto &v1 = indices.empty() ? verts[0] : verts[indices[0]];
|
||||
const auto &v2 = indices.empty() ? verts[i + 1] : verts[indices[i + 1]];
|
||||
const auto &v3 = indices.empty() ? verts[i + 2] : verts[indices[i + 2]];
|
||||
drawList->AddTriangleFilled(
|
||||
ImVec2(p0.x + x(v1), p0.y + y(v1)),
|
||||
ImVec2(p0.x + x(v2), p0.y + y(v2)),
|
||||
ImVec2(p0.x + x(v3), p0.y + y(v3)), ImColor(0x600000FF));
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case GE_PRIM_RECTANGLES:
|
||||
{
|
||||
for (int i = 0; i < count - 2; i += 3) {
|
||||
const auto &tl = indices.empty() ? verts[i] : verts[indices[indexOffset + i]];
|
||||
const auto &br = indices.empty() ? verts[i + 1] : verts[indices[indexOffset + i + 1]];
|
||||
drawList->AddRectFilled(
|
||||
ImVec2(p0.x + x(tl), p0.y + y(tl)),
|
||||
ImVec2(p0.x + x(br), p0.y + y(br)), ImColor(defaultColor));
|
||||
}
|
||||
case GE_PRIM_TRIANGLE_STRIP:
|
||||
{
|
||||
int t = 2;
|
||||
for (int i = 0; i < count - 2; i++) {
|
||||
int i0 = i;
|
||||
int i1 = i + t;
|
||||
int i2 = i + (t ^ 3);
|
||||
const auto &v1 = indices.empty() ? verts[i0] : verts[indices[i0]];
|
||||
const auto &v2 = indices.empty() ? verts[i1] : verts[indices[i1]];
|
||||
const auto &v3 = indices.empty() ? verts[i2] : verts[indices[i2]];
|
||||
drawList->AddTriangleFilled(
|
||||
ImVec2(p0.x + x(v1), p0.y + y(v1)),
|
||||
ImVec2(p0.x + x(v2), p0.y + y(v2)),
|
||||
ImVec2(p0.x + x(v3), p0.y + y(v3)), ImColor(0x600000FF));
|
||||
t ^= 3;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case GE_PRIM_TRIANGLE_FAN:
|
||||
{
|
||||
for (int i = 0; i < count - 2; i++) {
|
||||
const auto &v1 = indices.empty() ? verts[0] : verts[indices[indexOffset + 0]];
|
||||
const auto &v2 = indices.empty() ? verts[i + 1] : verts[indices[indexOffset + i + 1]];
|
||||
const auto &v3 = indices.empty() ? verts[i + 2] : verts[indices[indexOffset + i + 2]];
|
||||
drawList->AddTriangleFilled(
|
||||
ImVec2(p0.x + x(v1), p0.y + y(v1)),
|
||||
ImVec2(p0.x + x(v2), p0.y + y(v2)),
|
||||
ImVec2(p0.x + x(v3), p0.y + y(v3)), ImColor(defaultColor));
|
||||
}
|
||||
case GE_PRIM_LINES:
|
||||
{
|
||||
for (int i = 0; i < count - 1; i += 2) {
|
||||
const auto &v1 = indices.empty() ? verts[i] : verts[indices[i]];
|
||||
const auto &v2 = indices.empty() ? verts[i + 1] : verts[indices[i + 1]];
|
||||
drawList->AddLine(
|
||||
ImVec2(p0.x + x(v1), p0.y + y(v1)),
|
||||
ImVec2(p0.x + x(v2), p0.y + y(v2)), ImColor(0x600000FF));
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case GE_PRIM_TRIANGLE_STRIP:
|
||||
{
|
||||
int t = 2;
|
||||
for (int i = 0; i < count - 2; i++) {
|
||||
int i0 = i;
|
||||
int i1 = i + t;
|
||||
int i2 = i + (t ^ 3);
|
||||
const auto &v1 = indices.empty() ? verts[i0] : verts[indices[indexOffset + i0]];
|
||||
const auto &v2 = indices.empty() ? verts[i1] : verts[indices[indexOffset + i1]];
|
||||
const auto &v3 = indices.empty() ? verts[i2] : verts[indices[indexOffset + i2]];
|
||||
drawList->AddTriangleFilled(
|
||||
ImVec2(p0.x + x(v1), p0.y + y(v1)),
|
||||
ImVec2(p0.x + x(v2), p0.y + y(v2)),
|
||||
ImVec2(p0.x + x(v3), p0.y + y(v3)), ImColor(defaultColor));
|
||||
t ^= 3;
|
||||
}
|
||||
case GE_PRIM_LINE_STRIP:
|
||||
{
|
||||
for (int i = 0; i < count - 2; i++) {
|
||||
const auto &v1 = indices.empty() ? verts[i] : verts[indices[i]];
|
||||
const auto &v2 = indices.empty() ? verts[i + 1] : verts[indices[i + 1]];
|
||||
drawList->AddLine(
|
||||
ImVec2(p0.x + x(v1), p0.y + y(v1)),
|
||||
ImVec2(p0.x + x(v2), p0.y + y(v2)), ImColor(0x600000FF));
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case GE_PRIM_LINES:
|
||||
{
|
||||
for (int i = 0; i < count - 1; i += 2) {
|
||||
const auto &v1 = indices.empty() ? verts[i] : verts[indices[indexOffset + i]];
|
||||
const auto &v2 = indices.empty() ? verts[i + 1] : verts[indices[indexOffset + i + 1]];
|
||||
drawList->AddLine(
|
||||
ImVec2(p0.x + x(v1), p0.y + y(v1)),
|
||||
ImVec2(p0.x + x(v2), p0.y + y(v2)), ImColor(defaultColor));
|
||||
}
|
||||
default:
|
||||
// TODO: Support lines etc.
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case GE_PRIM_LINE_STRIP:
|
||||
{
|
||||
for (int i = 0; i < count - 2; i++) {
|
||||
const auto &v1 = indices.empty() ? verts[i] : verts[indices[indexOffset + i]];
|
||||
const auto &v2 = indices.empty() ? verts[i + 1] : verts[indices[indexOffset + i + 1]];
|
||||
drawList->AddLine(
|
||||
ImVec2(p0.x + x(v1), p0.y + y(v1)),
|
||||
ImVec2(p0.x + x(v2), p0.y + y(v2)), ImColor(defaultColor));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// TODO: Support lines etc.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1197,7 +1219,7 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUCommon *gpuD
|
||||
case GE_CMD_PRIM:
|
||||
isOnPrim = true;
|
||||
if (reloadPreview_) {
|
||||
GetPrimPreview(op, previewPrim_, previewVertices_, previewIndices_, previewCount_);
|
||||
GetPrimPreview(op, previewPrim_, previewVertices_, previewIndices_, &previewIndexOffset_, previewTransformed_);
|
||||
reloadPreview_ = false;
|
||||
}
|
||||
break;
|
||||
@@ -1206,7 +1228,10 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUCommon *gpuD
|
||||
break;
|
||||
default:
|
||||
// Disable the current preview.
|
||||
previewCount_ = 0;
|
||||
previewVertices_.clear();
|
||||
previewIndices_.clear();
|
||||
previewIndexOffset_ = 0;
|
||||
previewTransformed_ = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1291,7 +1316,7 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUCommon *gpuD
|
||||
}
|
||||
|
||||
// Draw vertex preview on top!
|
||||
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewCount_, false, scale * previewZoom_, scale * previewZoom_);
|
||||
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewIndexOffset_, previewTransformed_, scale * previewZoom_, scale * previewZoom_);
|
||||
|
||||
drawList->PopClipRect();
|
||||
|
||||
@@ -1340,7 +1365,9 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUCommon *gpuD
|
||||
drawList->PushClipRect(p0, p1, true);
|
||||
|
||||
ImGui::Image(texId, ImVec2(texW, texH));
|
||||
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewCount_, true, texW, texH);
|
||||
|
||||
// Show the preview as texcoords. TODO: We should probably use unclipped data here?
|
||||
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewIndexOffset_, previewTransformed_, texW, texH);
|
||||
|
||||
drawList->PopClipRect();
|
||||
|
||||
@@ -1604,11 +1631,11 @@ void ImGeStateWindow::Draw(ImConfig &cfg, ImControl &control, GPUCommon *gpuDebu
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("w=%d", otherValue & 0xFFFF);
|
||||
} else {
|
||||
FormatStateRow(gpuDebug, temp, sizeof(temp), info.fmt, value, true, otherValue, gstate.cmdmem[info.otherCmd2]);
|
||||
FormatStateRow(temp, sizeof(temp), info.fmt, value, true, otherValue, gstate.cmdmem[info.otherCmd2]);
|
||||
ImGui::TextUnformatted(temp);
|
||||
}
|
||||
if (diff && ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort)) {
|
||||
FormatStateRow(gpuDebug, temp2, sizeof(temp2), info.fmt, lastState.cmdmem[info.cmd], true, lastState.cmdmem[info.otherCmd], lastState.cmdmem[info.otherCmd2]);
|
||||
FormatStateRow(temp2, sizeof(temp2), info.fmt, lastState.cmdmem[info.cmd], true, lastState.cmdmem[info.otherCmd], lastState.cmdmem[info.otherCmd2]);
|
||||
ImGui::SetTooltip("Previous: %s", temp2);
|
||||
}
|
||||
}
|
||||
@@ -1681,18 +1708,38 @@ void DrawImGeVertsWindow(ImConfig &cfg, ImControl &control, GPUCommon *gpuDebug)
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::Checkbox("Transformed", &cfg.vertexListTransformed);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Clipped", &cfg.vertexListClipped);
|
||||
|
||||
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);
|
||||
FormatStateRow(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();
|
||||
GEPrimitiveType prim;
|
||||
int rowCount_ = gpuDebug->GetCurrentPrimCount(&prim);
|
||||
std::vector<GPUDebugVertex> vertices;
|
||||
std::vector<u16> indices;
|
||||
if (!gpuDebug->GetCurrentDrawAsDebugVertices(rowCount_, vertices, indices)) {
|
||||
int previewIndexOffset_ = 0;
|
||||
|
||||
DebugVertexFlags flags{};
|
||||
if (cfg.vertexListTransformed) {
|
||||
flags |= DebugVertexFlags::Transformed;
|
||||
}
|
||||
if (cfg.vertexListClipped) {
|
||||
flags |= DebugVertexFlags::Clipped;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -1708,12 +1755,14 @@ void DrawImGeVertsWindow(ImConfig &cfg, ImControl &control, GPUCommon *gpuDebug)
|
||||
"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);
|
||||
@@ -1756,6 +1805,7 @@ void DrawImGeVertsWindow(ImConfig &cfg, ImControl &control, GPUCommon *gpuDebug)
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Transformed")) {
|
||||
ImGui::Text("Culled far: %d near: %d Clipped: %d", stats.culledTrianglesFar, stats.culledTrianglesNear, stats.clippedTriangles);
|
||||
buildVertexTable(false);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
@@ -161,7 +161,8 @@ private:
|
||||
GEPrimitiveType previewPrim_ = GEPrimitiveType::GE_PRIM_TRIANGLES;
|
||||
std::vector<u16> previewIndices_;
|
||||
std::vector<GPUDebugVertex> previewVertices_;
|
||||
int previewCount_ = 0;
|
||||
int previewIndexOffset_ = 0;
|
||||
bool previewTransformed_ = true;
|
||||
Draw::Aspect selectedAspect_;
|
||||
float previewZoom_ = 1.0f;
|
||||
};
|
||||
|
||||
@@ -121,7 +121,7 @@ void ImJitViewerWindow::Draw(ImConfig &cfg, ImControl &control) {
|
||||
if (delta == 0) {
|
||||
return a.addr < b.addr;
|
||||
}
|
||||
if (spec->SortDirection == ImGuiSortDirection_Ascending) {
|
||||
if (!spec || spec->SortDirection == ImGuiSortDirection_Ascending) {
|
||||
return delta < 0;
|
||||
} else {
|
||||
return delta > 0;
|
||||
|
||||
@@ -310,13 +310,13 @@ void CtrlStateValues::GetColumnText(wchar_t *dest, size_t destSize, int row, int
|
||||
}
|
||||
|
||||
const auto info = GECmdInfoByCmd(rows_[row]);
|
||||
const auto state = gpu->GetGState();
|
||||
const auto &state = gpu->GetGState();
|
||||
const bool enabled = info.enableCmd == 0 || (state.cmdmem[info.enableCmd] & 1) == 1;
|
||||
const u32 value = state.cmdmem[info.cmd] & 0xFFFFFF;
|
||||
const u32 otherValue = state.cmdmem[info.otherCmd] & 0xFFFFFF;
|
||||
const u32 otherValue2 = state.cmdmem[info.otherCmd2] & 0xFFFFFF;
|
||||
char temp[256];
|
||||
FormatStateRow(gpu, temp, sizeof(temp), info.fmt, value, enabled, otherValue, otherValue2);
|
||||
FormatStateRow(temp, sizeof(temp), info.fmt, value, enabled, otherValue, otherValue2);
|
||||
ConvertUTF8ToWString(dest, destSize, temp);
|
||||
break;
|
||||
}
|
||||
@@ -444,7 +444,7 @@ void CtrlStateValues::OnRightClick(int row, int column, const POINT &point) {
|
||||
const u32 otherValue2 = state.cmdmem[info.otherCmd2] & 0xFFFFFF;
|
||||
|
||||
char dest[512];
|
||||
FormatStateRow(gpu, dest, sizeof(dest), info.fmt, value, enabled, otherValue, otherValue2);
|
||||
FormatStateRow(dest, sizeof(dest), info.fmt, value, enabled, otherValue, otherValue2);
|
||||
System_CopyStringToClipboard(dest);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -169,8 +169,10 @@ int CtrlVertexList::GetRowCount() {
|
||||
// TODO: Maybe there are smarter ways? Also, is this the best place to recalc?
|
||||
auto state = gpu->GetGState();
|
||||
|
||||
rowCount_ = gpu->GetCurrentPrimCount();
|
||||
if (!gpu->GetCurrentDrawAsDebugVertices(rowCount_, vertices, indices)) {
|
||||
GEPrimitiveType prim;
|
||||
rowCount_ = gpu->GetCurrentPrimCount(&prim);
|
||||
|
||||
if (!gpu->GetCurrentDrawAsDebugVertices(prim, &prim, rowCount_, vertices, indices, &previewIndexOffset_, nullptr, DebugVertexFlags::Transformed)) {
|
||||
rowCount_ = 0;
|
||||
}
|
||||
VertexDecoderOptions options{};
|
||||
|
||||
@@ -43,6 +43,7 @@ private:
|
||||
std::vector<GPUDebugVertex> vertices;
|
||||
std::vector<u16> indices;
|
||||
int rowCount_;
|
||||
int previewIndexOffset_;
|
||||
bool raw_;
|
||||
VertexDecoder *decoder;
|
||||
};
|
||||
|
||||
@@ -96,9 +96,10 @@ void CGEDebugger::UpdatePrimPreview(u32 op, int which) {
|
||||
static std::vector<GPUDebugVertex> vertices;
|
||||
static std::vector<u16> indices;
|
||||
|
||||
int count = 0;
|
||||
GEPrimitiveType prim;
|
||||
if (!GetPrimPreview(op, prim, vertices, indices, count)) {
|
||||
int previewIndexOffset;
|
||||
bool previewTransformed;
|
||||
if (!GetPrimPreview(op, prim, vertices, indices, &previewIndexOffset, &previewTransformed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -163,9 +164,9 @@ void CGEDebugger::UpdatePrimPreview(u32 op, int which) {
|
||||
}
|
||||
|
||||
if (indices.empty()) {
|
||||
glDrawArrays(glprim[prim], 0, count);
|
||||
glDrawArrays(glprim[prim], 0, (GLsizei)vertices.size());
|
||||
} else {
|
||||
glDrawElements(glprim[prim], count, GL_UNSIGNED_SHORT, previewVao != 0 ? 0 : indices.data());
|
||||
glDrawElements(glprim[prim], (GLsizei)indices.size(), GL_UNSIGNED_SHORT, previewVao != 0 ? 0 : indices.data());
|
||||
}
|
||||
|
||||
if (previewVao == 0) {
|
||||
@@ -230,9 +231,9 @@ void CGEDebugger::UpdatePrimPreview(u32 op, int which) {
|
||||
}
|
||||
|
||||
if (indices.empty()) {
|
||||
glDrawArrays(glprim[prim], 0, count);
|
||||
glDrawArrays(glprim[prim], 0, vertices.size());
|
||||
} else {
|
||||
glDrawElements(glprim[prim], count, GL_UNSIGNED_SHORT, texPreviewVao != 0 ? 0 : indices.data());
|
||||
glDrawElements(glprim[prim], indices.size(), GL_UNSIGNED_SHORT, texPreviewVao != 0 ? 0 : indices.data());
|
||||
}
|
||||
|
||||
if (texPreviewVao == 0) {
|
||||
|
||||
Reference in New Issue
Block a user