mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
BBOX: Avoid z clipping issues by culling in clip space.
This commit is contained in:
@@ -194,8 +194,15 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
|
||||
|
||||
// Try to skip NormalizeVertices if it's pure positions. No need to bother with a vertex decoder
|
||||
// and a large vertex format.
|
||||
|
||||
// BBOX games:
|
||||
// - Outrun 2006
|
||||
// - Tekken 6 (FLOAT only)
|
||||
// - Smash Court Tennis 3 (All formats)
|
||||
// - Need for Speed Carbon
|
||||
|
||||
if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_FLOAT && !inds) {
|
||||
// Most games that use bbox use floating point bboxes (Outrun, Tekken 6 etc).
|
||||
// Most games that use bbox use floating point bboxes (Outrun, Tekken 6, Smash Court Tennis 3, Need for Speed Carbon etc).
|
||||
// memcpy(verts, vdata, sizeof(float) * 3 * vertexCount);
|
||||
verts = (float *)vdata;
|
||||
} else if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_8BIT && !inds) {
|
||||
@@ -213,6 +220,8 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
|
||||
u8 *temp_buffer = decoded_ + 65536 * 24;
|
||||
|
||||
if ((inds || (vertType & (GE_VTYPE_WEIGHT_MASK | GE_VTYPE_MORPHCOUNT_MASK)))) {
|
||||
// Need for Speed Carbon ends up on this path! With a single bone weight.
|
||||
|
||||
u16 indexLowerBound = 0;
|
||||
u16 indexUpperBound = (u16)vertexCount - 1;
|
||||
|
||||
@@ -259,9 +268,9 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
|
||||
}
|
||||
|
||||
// Unclear why the top/left is off by a pixel.
|
||||
const int left = gstate.getOffsetX() + std::max(gstate.getRegionX1(), gstate.getScissorX1()) - 1;
|
||||
const int top = gstate.getOffsetY() + std::max(gstate.getRegionY1(), gstate.getScissorY1()) - 1;
|
||||
const int right = gstate.getOffsetX() + std::min(gstate.getRegionX2(), gstate.getScissorX2()) + 1;
|
||||
const int left = gstate.getOffsetX() + std::max(gstate.getRegionX1(), gstate.getScissorX1()) - 1;
|
||||
const int top = gstate.getOffsetY() + std::max(gstate.getRegionY1(), gstate.getScissorY1()) - 1;
|
||||
const int right = gstate.getOffsetX() + std::min(gstate.getRegionX2(), gstate.getScissorX2()) + 1;
|
||||
const int bottom = gstate.getOffsetY() + std::min(gstate.getRegionY2(), gstate.getScissorY2()) + 1;
|
||||
|
||||
// This is strange, it seems if the draw box is at all outside the 4096x4096 coordinate space, all checks pass.
|
||||
@@ -271,6 +280,7 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
|
||||
}
|
||||
|
||||
// TODO: How accurate should we be?
|
||||
// TODO: Use CrossSIMD.
|
||||
int insideCount[6] = {0};
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
// Complete the transform to see if the vertex should be ignored. Not sure if we need to go to these lengths...
|
||||
@@ -286,26 +296,27 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
|
||||
insideCount[5]++;
|
||||
}
|
||||
|
||||
const float invW = 1.0f / projpos[3];
|
||||
const float w = projpos[3];
|
||||
// const float invW = 1.0f / w;
|
||||
const float screenpos[3] = {
|
||||
projpos[0] * gstate.getViewportXScale() * invW + gstate.getViewportXCenter(),
|
||||
projpos[1] * gstate.getViewportYScale() * invW + gstate.getViewportYCenter(),
|
||||
projpos[2] * gstate.getViewportZScale() * invW + gstate.getViewportZCenter(),
|
||||
(projpos[0] * gstate.getViewportXScale()) + gstate.getViewportXCenter() * w,
|
||||
(projpos[1] * gstate.getViewportYScale()) + gstate.getViewportYCenter() * w,
|
||||
(projpos[2] * gstate.getViewportZScale()) + gstate.getViewportZCenter() * w,
|
||||
};
|
||||
|
||||
const float drawX = screenpos[0];
|
||||
const float drawY = screenpos[1];
|
||||
|
||||
if (drawX >= left) {
|
||||
if (drawX >= left * w) {
|
||||
insideCount[0]++;
|
||||
}
|
||||
if (drawX <= right) {
|
||||
if (drawX <= right * w) {
|
||||
insideCount[1]++;
|
||||
}
|
||||
if (drawY >= top) {
|
||||
if (drawY >= top * w) {
|
||||
insideCount[2]++;
|
||||
}
|
||||
if (drawY <= bottom) {
|
||||
if (drawY <= bottom * w) {
|
||||
insideCount[3]++;
|
||||
}
|
||||
}
|
||||
@@ -329,7 +340,7 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
|
||||
return true;
|
||||
}
|
||||
|
||||
// This optionally culls collections of points against the four side planes, and always computes the min and max of Z and W.
|
||||
// This optionally culls collections of points against the six planes, and always computes the min and max of Z and W.
|
||||
//
|
||||
// The result of that is then used to determine if we need to drop down to software transform+clip or we can hand
|
||||
// off to hardware, with whatever capabilities are available.
|
||||
|
||||
+12
-7
@@ -7,6 +7,7 @@
|
||||
#include "Common/GraphicsContext.h"
|
||||
#include "Common/LogReporting.h"
|
||||
#include "Common/Math/SIMDHeaders.h"
|
||||
#include "Common/Math/CrossSIMD.h"
|
||||
#include "Common/Serialize/Serializer.h"
|
||||
#include "Common/Serialize/SerializeFuncs.h"
|
||||
#include "Common/Serialize/SerializeList.h"
|
||||
@@ -1232,6 +1233,7 @@ void GPUCommon::Execute_BoundingBox(u32 op, u32 diff) {
|
||||
int checkSize = count - 0x100;
|
||||
currentList->bboxResult = drawEngineCommon_->TestBoundingBox(control_points, inds, checkSize, dec, vertType);
|
||||
} else {
|
||||
// This is the normal case that pretty much always happens, the others are esoteric.
|
||||
currentList->bboxResult = drawEngineCommon_->TestBoundingBox(control_points, inds, count, dec, vertType);
|
||||
}
|
||||
AdvanceVerts(gstate.vertType, count, bytesRead);
|
||||
@@ -2197,18 +2199,21 @@ bool GPUCommon::SetRestrictPrims(std::string_view rule) {
|
||||
}
|
||||
|
||||
void GPUCommon::UpdateMatrixProducts() {
|
||||
// We clean the dirty flags at the end.
|
||||
|
||||
// Compute any dirty product matrices.
|
||||
if (gstate_c.IsDirty(DIRTY_VIEW_PROJ_MATRIX)) {
|
||||
float view[4 * 4];
|
||||
ConvertMatrix4x3To4x4(view, gstate.viewMatrix);
|
||||
Matrix4ByMatrix4(gstate_c.viewproj, view, gstate.projMatrix);
|
||||
gstate_c.Clean(DIRTY_VIEW_PROJ_MATRIX);
|
||||
Mat4x3F32 view(gstate.viewMatrix);
|
||||
Mat4F32 proj(gstate.projMatrix);
|
||||
Mul4x3By4x4(view, proj).Store(gstate_c.viewproj);
|
||||
}
|
||||
|
||||
if (gstate_c.IsDirty(DIRTY_WORLD_VIEW_PROJ_MATRIX)) {
|
||||
float world[4 * 4];
|
||||
ConvertMatrix4x3To4x4(world, gstate.worldMatrix);
|
||||
Matrix4ByMatrix4(gstate_c.worldviewproj, world, gstate_c.viewproj);
|
||||
Mat4x3F32 world(gstate.worldMatrix);
|
||||
Mat4F32 viewproj(gstate_c.viewproj);
|
||||
Mul4x3By4x4(world, viewproj).Store(gstate_c.worldviewproj);
|
||||
}
|
||||
|
||||
if (gstate_c.IsDirty(DIRTY_CULL_MATRIX)) {
|
||||
// Modify the transform matrix to take the viewport into account before culling. This is not necessary
|
||||
// for most games, but there are games that rely on outside-viewport draws (such as Dante's Inferno)'s post
|
||||
|
||||
Reference in New Issue
Block a user