mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Apply triangle near-clipping in software transform.
Fixes, but only in SW transform, #10914 Re-enable range culling, oops.
This commit is contained in:
1 parent
280ddbf86c
commit
8b84fd555d
10 files changed
+237
-71
No files matched your search
+1
-1
@@ -284,7 +284,7 @@ public:
|
||||
|
||||
bool bSoftwareRendering;
|
||||
bool bSoftwareRenderingJit;
|
||||
bool bHardwareTransform; // only used in the GLES backend
|
||||
bool bHardwareTransform;
|
||||
bool bSoftwareSkinning;
|
||||
bool bVendorBugChecksEnabled;
|
||||
bool bUseGeometryShader;
|
||||
|
||||
@@ -61,7 +61,7 @@ std::string VertexShaderDesc(const VShaderID &id) {
|
||||
if (id.Bit(VS_BIT_HAS_TEXCOORD_TESS)) desc << "TessT ";
|
||||
if (id.Bit(VS_BIT_HAS_NORMAL_TESS)) desc << "TessN ";
|
||||
if (id.Bit(VS_BIT_NORM_REVERSE_TESS)) desc << "TessRevN ";
|
||||
if (id.Bit(VS_BIT_VERTEX_RANGE_CULLING)) desc << "Cull ";
|
||||
if (id.Bit(VS_BIT_VERTEX_RANGE_CULLING)) desc << "RangeCull ";
|
||||
|
||||
if (id.Bit(VS_BIT_SIMPLE_STEREO)) desc << "SimpleStereo ";
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "GPU/Common/TransformCommon.h"
|
||||
#include "GPU/Common/VertexDecoderCommon.h"
|
||||
#include "GPU/Common/DrawEngineCommon.h"
|
||||
#include "GPU/Software/Clipper.h"
|
||||
|
||||
// This is the software transform pipeline, which is necessary for supporting RECT
|
||||
// primitives correctly without geometry shaders, and may be easier to use for
|
||||
@@ -112,8 +113,6 @@ static bool IsReallyAClear(const TransformedVertex *transformed, int numVerts, f
|
||||
|
||||
// At the end, this calls ProjectClipAndExpand which will expand rectangles as necessary, or apply culling.
|
||||
SoftwareTransformAction SoftwareTransform::Transform(SoftwareTransformParams ¶ms, int prim, u32 vertType, const DecVtxFormat &decVtxFormat, int &numDecodedVerts, int vertsSize, int vertexCount, u16 *&inds, int indsSize, SoftwareTransformResult *result) {
|
||||
u8 *decoded = params.decoded;
|
||||
TransformedVertex *transformed = params.transformed;
|
||||
bool throughmode = (vertType & GE_VTYPE_THROUGH_MASK) != 0;
|
||||
bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled();
|
||||
|
||||
@@ -143,7 +142,8 @@ SoftwareTransformAction SoftwareTransform::Transform(SoftwareTransformParams &pa
|
||||
fog_slope = std::signbit(fog_slope) ? -largeFogValue : largeFogValue;
|
||||
}
|
||||
|
||||
VertexReader reader(decoded, decVtxFormat, vertType);
|
||||
TransformedVertex *transformed = params.transformed;
|
||||
VertexReader reader(params.decoded, decVtxFormat, vertType);
|
||||
if (throughmode) {
|
||||
const u32 materialAmbientRGBA = gstate.getMaterialAmbientRGBA();
|
||||
const bool hasColor = reader.hasColor0();
|
||||
@@ -151,7 +151,6 @@ SoftwareTransformAction SoftwareTransform::Transform(SoftwareTransformParams &pa
|
||||
for (int index = 0; index < numDecodedVerts; index++) {
|
||||
// Do not touch the coordinates or the colors. No lighting.
|
||||
reader.Goto(index);
|
||||
// TODO: Write to a flexible buffer, we don't always need all four components.
|
||||
TransformedVertex &vert = transformed[index];
|
||||
reader.ReadPosThrough(vert.pos);
|
||||
vert.pos_w = 1.0f;
|
||||
@@ -378,6 +377,7 @@ SoftwareTransformAction SoftwareTransform::Transform(SoftwareTransformParams &pa
|
||||
}
|
||||
|
||||
// TODO: This doesn't seem to be a very good check, but let's leave it for now.
|
||||
|
||||
// Detect full screen "clears" that might not be so obvious, to set the safe size if possible.
|
||||
if (!result->setSafeSize && prim == GE_PRIM_RECTANGLES && numDecodedVerts == 2 && throughmode) {
|
||||
bool clearingColor = gstate.isModeClear() && (gstate.isClearModeColorMask() || gstate.isClearModeAlphaMask());
|
||||
@@ -428,6 +428,142 @@ void SoftwareTransform::ProjectVertices(TransformedVertex *transformed, int vert
|
||||
#endif
|
||||
}
|
||||
|
||||
// Helper to check if a vertex is inside the near plane (z >= -w)
|
||||
// We add a tiny epsilon to prevent floating-point precision issues at the exact boundary
|
||||
inline bool IsInsideNearPlane(const TransformedVertex& v) {
|
||||
return v.z >= -v.pos_w;
|
||||
}
|
||||
|
||||
inline bool IsInsideFarPlane(const TransformedVertex& v) {
|
||||
return v.z <= v.pos_w;
|
||||
}
|
||||
|
||||
// Generated by Gemini, and adapted to fit.
|
||||
void ClipTrianglesAgainstNearPlane(
|
||||
TransformedVertex *transformed, int &transformedCount, int maxTransformed,
|
||||
u16 *indicesIn, int numIndicesIn,
|
||||
u16 *indicesOut, int &numIndicesOut, int maxIndicesOut
|
||||
) {
|
||||
int clipCount = 0;
|
||||
// Process one triangle (3 indices) at a time
|
||||
for (size_t i = 0; i < numIndicesIn; i += 3) {
|
||||
u16 idx0 = indicesIn[i];
|
||||
u16 idx1 = indicesIn[i + 1];
|
||||
u16 idx2 = indicesIn[i + 2];
|
||||
|
||||
TransformedVertex& v0 = transformed[idx0];
|
||||
TransformedVertex& v1 = transformed[idx1];
|
||||
TransformedVertex& v2 = transformed[idx2];
|
||||
|
||||
bool in0 = IsInsideNearPlane(v0);
|
||||
bool in1 = IsInsideNearPlane(v1);
|
||||
bool in2 = IsInsideNearPlane(v2);
|
||||
|
||||
bool inFar0 = IsInsideFarPlane(v0);
|
||||
bool inFar1 = IsInsideFarPlane(v1);
|
||||
bool inFar2 = IsInsideFarPlane(v2);
|
||||
|
||||
int insideCount = (in0 ? 1 : 0) + (in1 ? 1 : 0) + (in2 ? 1 : 0);
|
||||
int insideFarCount = (inFar0 ? 1 : 0) + (inFar1 ? 1 : 0) + (inFar2 ? 1 : 0);
|
||||
|
||||
// Case 1: Entirely visible
|
||||
if (insideCount == 3) {
|
||||
indicesOut[numIndicesOut++] = idx0;
|
||||
indicesOut[numIndicesOut++] = idx1;
|
||||
indicesOut[numIndicesOut++] = idx2;
|
||||
}
|
||||
// Case 2: Entirely clipped / behind near plane
|
||||
else if (insideCount == 0) {
|
||||
// Cull, no clipping needed.
|
||||
continue;
|
||||
}
|
||||
// Case 3: Entirely beyond far plane
|
||||
else if (insideFarCount == 0) {
|
||||
// All are beyond the far plane. Cull.
|
||||
continue;
|
||||
}
|
||||
// Case 3: Partially clipped
|
||||
else {
|
||||
clipCount++;
|
||||
|
||||
// We need to organize vertices cleanly to calculate intersections.
|
||||
// We will create a local polygon array of the inside/outside states.
|
||||
u16 triIdx[3] = {idx0, idx1, idx2};
|
||||
bool triIn[3] = {in0, in1, in2};
|
||||
|
||||
// Output generated vertex indices for this clipped polygon
|
||||
u16 polyIndices[4];
|
||||
int polyLength = 0;
|
||||
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
int next = (j + 1) % 3;
|
||||
|
||||
u16 currIdx = triIdx[j];
|
||||
u16 nextIdx = triIdx[next];
|
||||
|
||||
// If current vertex is inside, it stays a part of the output polygon
|
||||
if (triIn[j]) {
|
||||
polyIndices[polyLength++] = currIdx;
|
||||
}
|
||||
|
||||
// If we cross the clipping plane line (inside->outside or outside->inside)
|
||||
if (triIn[j] != triIn[next]) {
|
||||
/* const */ TransformedVertex& a = transformed[currIdx];
|
||||
/* const */ TransformedVertex& b = transformed[nextIdx];
|
||||
|
||||
// Find interpolation factor 't' where: z_interpolated = -w_interpolated
|
||||
// Lerp formulation:
|
||||
// z = a.z + t*(b.z - a.z)
|
||||
// w = a.w + t*(b.w - a.w)
|
||||
// Set z = -w => a.z + t*(b.z - a.z) = -(a.pos_w + t*(b.pos_w - a.pos_w))
|
||||
// Solve for t:
|
||||
float denominator = (b.z - a.z) + (b.pos_w - a.pos_w);
|
||||
float t = 0.0f;
|
||||
if (fabsf(denominator) > 0.000001f) {
|
||||
t = (-a.z - a.pos_w) / denominator;
|
||||
}
|
||||
// Clamp safely due to float precision
|
||||
if (t < 0.0f) t = 0.0f;
|
||||
if (t > 1.0f) t = 1.0f;
|
||||
|
||||
// Generate new vertex at the intersection point
|
||||
TransformedVertex newVertex;
|
||||
TransformedVertex::Lerp(&newVertex, const_cast<TransformedVertex&>(a), const_cast<TransformedVertex&>(b), t);
|
||||
|
||||
// Force exact intersection to eliminate precision creeping down the pipeline
|
||||
newVertex.z = -newVertex.pos_w;
|
||||
newVertex.color0_32 = 0xFFFF00FF;
|
||||
// a.color0_32 = 0xFFFF00FF;
|
||||
// b.color0_32 = 0xFFFF00FF;
|
||||
|
||||
// Append to global vertex buffer
|
||||
transformed[transformedCount++] = newVertex;
|
||||
u16 newIdx = static_cast<u16>(transformedCount - 1);
|
||||
polyIndices[polyLength++] = newIdx;
|
||||
}
|
||||
}
|
||||
|
||||
// Triangulate the resulting polygon array (will be either 3 or 4 vertices)
|
||||
if (polyLength == 3) {
|
||||
indicesOut[numIndicesOut++] = polyIndices[0];
|
||||
indicesOut[numIndicesOut++] = polyIndices[1];
|
||||
indicesOut[numIndicesOut++] = polyIndices[2];
|
||||
} else if (polyLength == 4) {
|
||||
// Triangle 1
|
||||
indicesOut[numIndicesOut++] = polyIndices[0];
|
||||
indicesOut[numIndicesOut++] = polyIndices[1];
|
||||
indicesOut[numIndicesOut++] = polyIndices[2];
|
||||
// Triangle 2
|
||||
indicesOut[numIndicesOut++] = polyIndices[0];
|
||||
indicesOut[numIndicesOut++] = polyIndices[2];
|
||||
indicesOut[numIndicesOut++] = polyIndices[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpuStats.perFrame.numSoftClippedTriangles++;
|
||||
}
|
||||
|
||||
SoftwareTransformAction SoftwareTransform::ProjectClipAndExpand(SoftwareTransformParams ¶ms, int prim, int vertexCount, u32 vertType, u16 *&inds, int indsSize, int &numDecodedVerts, int vertsSize, SoftwareTransformResult *result) {
|
||||
TransformedVertex *transformed = params.transformed;
|
||||
TransformedVertex *transformedExpanded = params.transformedExpanded;
|
||||
@@ -440,6 +576,7 @@ SoftwareTransformAction SoftwareTransform::ProjectClipAndExpand(SoftwareTransfor
|
||||
FramebufferManagerCommon *fbman = params.fbman;
|
||||
bool useBufferedRendering = fbman->UseBufferedRendering();
|
||||
|
||||
// NOTE: ExpandRectanges/lines/etc should do clipping while they're at it.
|
||||
if (prim == GE_PRIM_RECTANGLES) {
|
||||
// TODO: We should cull rectangles outzide -W<Z<W here if *both* points are outside in the same direction,
|
||||
// like we do with triangles.
|
||||
@@ -538,29 +675,7 @@ SoftwareTransformAction SoftwareTransform::ProjectClipAndExpand(SoftwareTransfor
|
||||
// Culling and clipping needs to be done here, it doesn't happen in the shader in the case of software transform.
|
||||
// However, fast culling should already have taken care of the Z<-W and Z>W culling, but we check for it on a per-triangle
|
||||
// basis here anyway.
|
||||
|
||||
const u16 *indsIn = (const u16 *)inds;
|
||||
u16 *newInds = inds + vertexCount;
|
||||
u16 *indsOut = newInds;
|
||||
|
||||
std::vector<int> outsideZ;
|
||||
outsideZ.resize(vertexCount);
|
||||
|
||||
// First, check inside/outside directions for each index.
|
||||
// We are still in clip space here, so we can cull aggressively in Z.
|
||||
// TODO: This is so cheap now that we can probably avoid the buffer and just do the work below.
|
||||
for (int i = 0; i < vertexCount; ++i) {
|
||||
float z = transformed[indsIn[i]].z;
|
||||
float w = transformed[indsIn[i]].pos_w;
|
||||
if (z > w) {
|
||||
outsideZ[i] = 1;
|
||||
} else if (z < -w) {
|
||||
outsideZ[i] = -1;
|
||||
} else {
|
||||
outsideZ[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
u16 *origInds = inds;
|
||||
|
||||
// TODO: We should either merge the two loops, or avoid the second loop if no culling is needed.
|
||||
@@ -568,20 +683,34 @@ SoftwareTransformAction SoftwareTransform::ProjectClipAndExpand(SoftwareTransfor
|
||||
// Now, for each triangle, throw away the indices if:
|
||||
// - Depth clip/clamp on, and ALL verts are outside *in the same direction*.
|
||||
// - Depth clip/clamp off, and ANY vert is outside.
|
||||
|
||||
u16 *newInds = inds + vertexCount;
|
||||
u16 *indsOut = newInds;
|
||||
|
||||
if (gstate.isDepthClipEnabled()) {
|
||||
numTrans = 0;
|
||||
for (int i = 0; i < vertexCount - 2; i += 3) {
|
||||
if (outsideZ[i + 0] != 0 && outsideZ[i + 0] == outsideZ[i + 1] && outsideZ[i + 0] == outsideZ[i + 2]) {
|
||||
// All outside, and all the same direction. Nuke this triangle.
|
||||
continue;
|
||||
const u16 *indsIn = (const u16 *)inds;
|
||||
int newIndexCount = 0;
|
||||
ClipTrianglesAgainstNearPlane(transformed, numDecodedVerts, 65536, inds, vertexCount, indsOut, newIndexCount, 65336);
|
||||
numTrans = newIndexCount;
|
||||
} else {
|
||||
std::vector<int> outsideZ;
|
||||
outsideZ.resize(vertexCount);
|
||||
|
||||
// First, check inside/outside directions for each index.
|
||||
// We are still in clip space here, so we can cull aggressively in Z.
|
||||
// TODO: This is so cheap now that we can probably avoid the buffer and just do the work below.
|
||||
for (int i = 0; i < vertexCount; ++i) {
|
||||
float z = transformed[indsIn[i]].z;
|
||||
float w = transformed[indsIn[i]].pos_w;
|
||||
if (z > w) {
|
||||
outsideZ[i] = 1;
|
||||
} else if (z < -w) {
|
||||
outsideZ[i] = -1;
|
||||
} else {
|
||||
outsideZ[i] = 0;
|
||||
}
|
||||
memcpy(indsOut, indsIn + i, 3 * sizeof(uint16_t));
|
||||
indsOut += 3;
|
||||
numTrans += 3;
|
||||
}
|
||||
|
||||
inds = newInds;
|
||||
} else if (prim == GE_PRIM_TRIANGLES) {
|
||||
numTrans = 0;
|
||||
for (int i = 0; i < vertexCount - 2; i += 3) {
|
||||
if (outsideZ[i + 0] != 0 || outsideZ[i + 1] != 0 || outsideZ[i + 2] != 0) {
|
||||
@@ -593,10 +722,10 @@ SoftwareTransformAction SoftwareTransform::ProjectClipAndExpand(SoftwareTransfor
|
||||
indsOut += 3;
|
||||
numTrans += 3;
|
||||
}
|
||||
|
||||
inds = newInds;
|
||||
}
|
||||
|
||||
inds = newInds;
|
||||
|
||||
// Now that we're done culling and generating clipped vertices if needed (not yet implemented), we go ahead and project.
|
||||
ProjectVertices(transformed, numDecodedVerts);
|
||||
|
||||
@@ -605,33 +734,44 @@ SoftwareTransformAction SoftwareTransform::ProjectClipAndExpand(SoftwareTransfor
|
||||
// If not we technically need to cut it in two parts to clamp accurately.
|
||||
// However, in most cases that matter (such as missing skies, etc), this is fine.
|
||||
const int maxZInt = gstate.getDepthRangeMax();
|
||||
float maxZ = maxZInt / 65535.0f;
|
||||
// float maxZ = maxZInt / 65535.0f;
|
||||
const int minZInt = gstate.getDepthRangeMin();
|
||||
float minZ = minZInt / 65535.0f;
|
||||
// float minZ = minZInt / 65535.0f;
|
||||
// We only need to clamp if minZ and maxZ aren't at the extreme in each direction, as otherwise
|
||||
// minZ and maxZ will cut things off.
|
||||
if (!gstate_c.Use(GPU_USE_DEPTH_CLAMP) && gstate.isDepthClipEnabled() && (maxZInt == 0 || maxZInt == 65535)) {
|
||||
for (int i = 0; i < vertexCount - 2; i += 3) {
|
||||
|
||||
// If gstate_c.Use(GPU_USE_DEPTH_CLAMP), we can theoretically skip this. However, to keep behavior the same regardless of the flag, we won't.
|
||||
if (gstate.isDepthClipEnabled() && (maxZInt == 0 || maxZInt == 65535)) {
|
||||
for (int i = 0; i < numTrans - 2; i += 3) {
|
||||
TransformedVertex &v0 = transformed[indsIn[i]];
|
||||
TransformedVertex &v1 = transformed[indsIn[i + 1]];
|
||||
TransformedVertex &v2 = transformed[indsIn[i + 2]];
|
||||
if (v0.x < 0.0f || v0.x > 4096.0f ||
|
||||
v1.x < 0.0f || v1.y > 4096.0f ||
|
||||
v2.x < 0.0f || v2.y > 4096.0f) {
|
||||
// If it's outside the viewport, we might as well skip the clamping, as it won't be visible anyway.
|
||||
// continue;
|
||||
}
|
||||
if (minZInt == 0) {
|
||||
bool v0InFront = transformed[indsIn[i]].z < 0.0f;
|
||||
bool v1InFront = transformed[indsIn[i + 1]].z < 0.0f;
|
||||
bool v2InFront = transformed[indsIn[i + 2]].z < 0.0f;
|
||||
bool v0InFront = v0.z < 0.0f;
|
||||
bool v1InFront = v1.z < 0.0f;
|
||||
bool v2InFront = v2.z < 0.0f;
|
||||
if (v0InFront && v1InFront && v2InFront) {
|
||||
transformed[indsIn[i]].z = 0.0f;
|
||||
transformed[indsIn[i + 1]].z = 0.0f;
|
||||
transformed[indsIn[i + 2]].z = 0.0f;
|
||||
v0.z = 0.0f;
|
||||
v1.z = 0.0f;
|
||||
v2.z = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxZInt == 65535) {
|
||||
bool v0Beyond = transformed[indsIn[i]].z >= 65535.0f;
|
||||
bool v1Beyond = transformed[indsIn[i + 1]].z >= 65535.0f;
|
||||
bool v2Beyond = transformed[indsIn[i + 2]].z >= 65535.0f;
|
||||
bool v0Beyond = v0.z >= 65535.0f;
|
||||
bool v1Beyond = v1.z >= 65535.0f;
|
||||
bool v2Beyond = v2.z >= 65535.0f;
|
||||
|
||||
if (v0Beyond && v1Beyond && v2Beyond) {
|
||||
transformed[indsIn[i]].z = 65535.0f;
|
||||
transformed[indsIn[i + 1]].z = 65535.0f;
|
||||
transformed[indsIn[i + 2]].z = 65535.0f;
|
||||
v0.z = 65535.0f;
|
||||
v1.z = 65535.0f;
|
||||
v2.z = 65535.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ void IndexBufferProvokingLastToFirst(int prim, u16 *inds, int indsSize);
|
||||
class SoftwareTransform {
|
||||
public:
|
||||
// indsSize is in indices, not bytes.
|
||||
// NOTE: In case of clipping, this might write extra vertices after params.transformed.
|
||||
static SoftwareTransformAction Transform(SoftwareTransformParams ¶ms, int prim, u32 vertexType, const DecVtxFormat &decVtxFormat, int &numDecodedVerts, int vertsSize, int vertexCount, u16 *&inds, int indsSize, SoftwareTransformResult *result);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -80,6 +80,15 @@ inline float roundToFloat24(float f) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
inline float truncateToFloat24(float f) {
|
||||
unsigned int i;
|
||||
memcpy(&i, &f, 4);
|
||||
i &= 0xFFFFFF00;
|
||||
float retval;
|
||||
memcpy(&retval, &i, 4);
|
||||
return retval;
|
||||
}
|
||||
|
||||
// TODO: Possibly use macros to disable expensive parts of stats tracking in release builds.
|
||||
struct GPUStatsPerFrame {
|
||||
// Per frame statistics
|
||||
@@ -89,6 +98,7 @@ struct GPUStatsPerFrame {
|
||||
int numDrawSyncs;
|
||||
int numListSyncs;
|
||||
int numFlushes;
|
||||
int numSoftClippedTriangles;
|
||||
int numBBOXJumps;
|
||||
int numVertsSubmitted;
|
||||
int numVertsDecoded;
|
||||
|
||||
+6
-1
@@ -624,6 +624,10 @@ u32 GPUCommonHW::CheckGPUFeatures() const {
|
||||
features |= GPU_USE_FRAGMENT_UBERSHADER;
|
||||
}
|
||||
|
||||
if (!draw_->GetBugs().Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL)) {
|
||||
features |= GPU_USE_VS_RANGE_CULLING;
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
@@ -1801,7 +1805,7 @@ void GPUCommonHW::FormatGPUStatsCommon(StringWriter &w) {
|
||||
w.F(
|
||||
"DL processing time: %0.2f ms, %d drawsync, %d listsync\n"
|
||||
"Draw: %d (%d dec, %d culled), flushes %d, clears %d, bbox jumps %d\n"
|
||||
"Vertices: %d dec: %d drawn: %d\n"
|
||||
"Vertices: %d dec: %d drawn: %d clipped tris: %d\n"
|
||||
"FBOs active: %d (evaluations: %d, created %d)\n"
|
||||
"Textures: %d, dec: %d, invalidated: %d, hashed: %d kB, clut %d\n"
|
||||
"readbacks %d (%d non-block), upload %d (cached %d), depal %d\n"
|
||||
@@ -1823,6 +1827,7 @@ void GPUCommonHW::FormatGPUStatsCommon(StringWriter &w) {
|
||||
gpuStats.perFrame.numVertsSubmitted,
|
||||
gpuStats.perFrame.numVertsDecoded,
|
||||
gpuStats.perFrame.numUncachedVertsDrawn,
|
||||
gpuStats.perFrame.numSoftClippedTriangles,
|
||||
(int)framebufferManager_->NumVFBs(),
|
||||
gpuStats.perFrame.numFramebufferEvaluations,
|
||||
gpuStats.perFrame.numFBOsCreated,
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/Data/Color/RGBAUtil.h"
|
||||
#include "Core/MemMap.h"
|
||||
|
||||
|
||||
@@ -217,4 +218,19 @@ struct TransformedVertex {
|
||||
this->y = other.y + yoff;
|
||||
memcpy(&this->z, &other.z, sizeof(*this) - sizeof(float) * 2);
|
||||
}
|
||||
|
||||
static void Lerp(TransformedVertex *dest, TransformedVertex &a, TransformedVertex &b, float t) {
|
||||
dest->x = a.x + (b.x - a.x) * t;
|
||||
dest->y = a.y + (b.y - a.y) * t;
|
||||
dest->z = a.z + (b.z - a.z) * t;
|
||||
dest->pos_w = a.pos_w + (b.pos_w - a.pos_w) * t;
|
||||
dest->u = a.u + (b.u - a.u) * t;
|
||||
dest->v = a.v + (b.v - a.v) * t;
|
||||
dest->uv_w = a.uv_w + (b.uv_w - a.uv_w) * t;
|
||||
dest->fog = a.fog + (b.fog - a.fog) * t;
|
||||
|
||||
// note: colorBlend is backwards.
|
||||
dest->color0_32 = colorBlend(b.color0_32, a.color0_32, t);
|
||||
dest->color1_32 = colorBlend(b.color1_32, a.color1_32, t);
|
||||
}
|
||||
};
|
||||
@@ -31,4 +31,4 @@ void ProcessLine(const ClipVertexData &v0, const ClipVertexData &v1, BinManager
|
||||
void ProcessTriangle(const ClipVertexData &v0, const ClipVertexData &v1, const ClipVertexData &v2, const ClipVertexData &provoking, BinManager &binner);
|
||||
void ProcessRect(const ClipVertexData &v0, const ClipVertexData &v1, BinManager &binner);
|
||||
|
||||
}
|
||||
} // namespace
|
||||
@@ -153,8 +153,6 @@ WorldCoords TransformUnit::ModelToWorldNormal(const ModelCoords &coords) {
|
||||
|
||||
template <bool depthClamp, bool alwaysCheckRange>
|
||||
static ScreenCoords ClipToScreenInternal(Vec3f scaled, const ClipCoords &coords, bool *outside_range_flag) {
|
||||
ScreenCoords ret;
|
||||
|
||||
// Account for rounding for X and Y.
|
||||
// TODO: Validate actual rounding range.
|
||||
constexpr float SCREEN_BOUND = 4095.0f + (15.5f / 16.0f);
|
||||
|
||||
@@ -43,10 +43,9 @@ enum class CullType {
|
||||
OFF = 2,
|
||||
};
|
||||
|
||||
struct ScreenCoords
|
||||
{
|
||||
ScreenCoords() {}
|
||||
ScreenCoords(int x, int y, u16 z) : x(x), y(y), z(z) {}
|
||||
struct ScreenCoords {
|
||||
ScreenCoords() = default;
|
||||
ScreenCoords(int _x, int _y, u16 _z) : x(_x), y(_y), z(_z) {}
|
||||
|
||||
int x;
|
||||
int y;
|
||||
@@ -54,24 +53,21 @@ struct ScreenCoords
|
||||
|
||||
Vec2<int> xy() const { return Vec2<int>(x, y); }
|
||||
|
||||
ScreenCoords operator * (const float t) const
|
||||
{
|
||||
ScreenCoords operator * (const float t) const {
|
||||
return ScreenCoords((int)(x * t), (int)(y * t), (u16)(z * t));
|
||||
}
|
||||
|
||||
ScreenCoords operator / (const int t) const
|
||||
{
|
||||
ScreenCoords operator / (const int t) const {
|
||||
return ScreenCoords(x / t, y / t, z / t);
|
||||
}
|
||||
|
||||
ScreenCoords operator + (const ScreenCoords& oth) const
|
||||
{
|
||||
ScreenCoords operator + (const ScreenCoords& oth) const {
|
||||
return ScreenCoords(x + oth.x, y + oth.y, z + oth.z);
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawingCoords {
|
||||
DrawingCoords() {}
|
||||
DrawingCoords() = default;
|
||||
DrawingCoords(s16 x, s16 y) : x(x), y(y) {}
|
||||
|
||||
s16 x;
|
||||
|
||||
Reference in new issue
Block a user