mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Prepare TestBoundingBoxFast for optimization
This commit is contained in:
@@ -328,9 +328,10 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
|
||||
// corners. That way we can cull more draws quite cheaply.
|
||||
// We could take the min/max during the regular vertex decode, and just skip the draw call if it's trivially culled.
|
||||
// This would help games like Midnight Club (that one does a lot of out-of-bounds drawing) immensely.
|
||||
bool DrawEngineCommon::TestBoundingBoxFast(const float *worldViewProj, const void *vdata, int vertexCount, const VertexDecoder *dec, u32 vertType) {
|
||||
SimpleVertex *corners = (SimpleVertex *)(decoded_ + 65536 * 12);
|
||||
float *verts = (float *)(decoded_ + 65536 * 18);
|
||||
template<u32 posFmt>
|
||||
static bool TestBoundingBoxFast(const float *worldViewProj, const void *vdata, int vertexCount, const VertexDecoder *dec, u32 vertType, u8 *decoded) {
|
||||
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.
|
||||
@@ -339,8 +340,8 @@ bool DrawEngineCommon::TestBoundingBoxFast(const float *worldViewProj, const voi
|
||||
}
|
||||
|
||||
// Simple, most common case.
|
||||
int stride = dec->VertexSize();
|
||||
int offset = dec->posoff;
|
||||
const int stride = dec->VertexSize();
|
||||
const int offset = dec->posoff;
|
||||
int vertStride = 3;
|
||||
|
||||
// TODO: Possibly do the plane tests directly against the source formats instead of converting.
|
||||
@@ -423,50 +424,6 @@ bool DrawEngineCommon::TestBoundingBoxFast(const float *worldViewProj, const voi
|
||||
break;
|
||||
}
|
||||
|
||||
// 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
|
||||
// processing effects, and we don't want to cull those.
|
||||
// Potentially we should cache this transform matrix too, but hopefully this is not a bottleneck.
|
||||
// I guess we could also do this directly when computing worldviewproj...
|
||||
|
||||
const float vpXCenter = gstate.getViewportXCenter();
|
||||
const float vpYCenter = gstate.getViewportYCenter();
|
||||
const float vpXScale = gstate.getViewportXScale();
|
||||
const float vpYScale = gstate.getViewportYScale();
|
||||
const int scissorX2 = gstate.getScissorX2();
|
||||
const int scissorY2 = gstate.getScissorY2();
|
||||
|
||||
// Check for weird scaling that can make graphics extend beyond the viewport.
|
||||
// NOTE: These checks are not bullet proof.
|
||||
float mtx[16];
|
||||
if (vpXCenter != 2048.0f || vpYCenter != 2048.0f || vpXScale < ((scissorX2 + 1) >> 1) && vpYScale < ((scissorY2 + 1) >> 1)) {
|
||||
// Note that the PSP does not clip against the viewport.
|
||||
const Vec2f baseOffset = Vec2f(gstate.getOffsetX(), gstate.getOffsetY());
|
||||
// Region1 (rate) is used as an X1/Y1 here, matching PSP behavior.
|
||||
Vec2f minOffset = baseOffset + Vec2f(std::max(gstate.getRegionX1(), gstate.getScissorX1()), std::max(gstate.getRegionY1(), gstate.getScissorY1()));
|
||||
Vec2f maxOffset = baseOffset + Vec2f(std::min(gstate.getRegionX2(), gstate.getScissorX2()), std::min(gstate.getRegionY2(), gstate.getScissorY2()));
|
||||
|
||||
// Now let's apply the viewport to our scissor/region + offset range.
|
||||
Vec2f inverseViewportScale = Vec2f(1.0f / gstate.getViewportXScale(), 1.0f / gstate.getViewportYScale());
|
||||
Vec2f minViewport = (minOffset - Vec2f(gstate.getViewportXCenter(), gstate.getViewportYCenter())) * inverseViewportScale;
|
||||
Vec2f maxViewport = (maxOffset - Vec2f(gstate.getViewportXCenter(), gstate.getViewportYCenter())) * inverseViewportScale;
|
||||
|
||||
Vec2f viewportInvSize = Vec2f(1.0f / (maxViewport.x - minViewport.x), 1.0f / (maxViewport.y - minViewport.y));
|
||||
|
||||
Lin::Matrix4x4 applyViewport{};
|
||||
// Scale to the viewport's size.
|
||||
applyViewport.xx = 2.0f * viewportInvSize.x;
|
||||
applyViewport.yy = 2.0f * viewportInvSize.y;
|
||||
applyViewport.zz = 1.0f;
|
||||
applyViewport.ww = 1.0f;
|
||||
// And offset to the viewport's centers.
|
||||
applyViewport.wx = -(maxViewport.x + minViewport.x) * viewportInvSize.x;
|
||||
applyViewport.wy = -(maxViewport.y + minViewport.y) * viewportInvSize.y;
|
||||
|
||||
// TODO: Optimize.
|
||||
Matrix4ByMatrix4(mtx, worldViewProj, applyViewport.m);
|
||||
}
|
||||
|
||||
// We only check the 4 sides. Near/far won't likely make a huge difference.
|
||||
// We test one vertex against 4 planes to get some SIMD. Vertices need to be transformed to world space
|
||||
// for testing, don't want to re-do that, so we have to use that "pivot" of the data.
|
||||
@@ -604,6 +561,66 @@ bool DrawEngineCommon::TestBoundingBoxFast(const float *worldViewProj, const voi
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrawEngineCommon::TestBoundingBoxFast(const float *worldViewProj, const void *vdata, int vertexCount, const VertexDecoder *dec, u32 vertType) {
|
||||
// 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
|
||||
// processing effects, and we don't want to cull those.
|
||||
// Potentially we should cache this transform matrix too, but hopefully this is not a bottleneck.
|
||||
// I guess we could also do this directly when computing worldviewproj...
|
||||
|
||||
const float vpXCenter = gstate.getViewportXCenter();
|
||||
const float vpYCenter = gstate.getViewportYCenter();
|
||||
const float vpXScale = gstate.getViewportXScale();
|
||||
const float vpYScale = gstate.getViewportYScale();
|
||||
const int scissorX2 = gstate.getScissorX2();
|
||||
const int scissorY2 = gstate.getScissorY2();
|
||||
|
||||
// Check for weird scaling that can make graphics extend beyond the viewport.
|
||||
// NOTE: These checks are not bullet proof.
|
||||
float mtx[16];
|
||||
if (vpXCenter != 2048.0f || vpYCenter != 2048.0f || vpXScale < ((scissorX2 + 1) >> 1) && vpYScale < ((scissorY2 + 1) >> 1)) {
|
||||
// Note that the PSP does not clip against the viewport.
|
||||
const Vec2f baseOffset = Vec2f(gstate.getOffsetX(), gstate.getOffsetY());
|
||||
// Region1 (rate) is used as an X1/Y1 here, matching PSP behavior.
|
||||
Vec2f minOffset = baseOffset + Vec2f(std::max(gstate.getRegionX1(), gstate.getScissorX1()), std::max(gstate.getRegionY1(), gstate.getScissorY1()));
|
||||
Vec2f maxOffset = baseOffset + Vec2f(std::min(gstate.getRegionX2(), gstate.getScissorX2()), std::min(gstate.getRegionY2(), gstate.getScissorY2()));
|
||||
|
||||
// Now let's apply the viewport to our scissor/region + offset range.
|
||||
Vec2f inverseViewportScale = Vec2f(1.0f / gstate.getViewportXScale(), 1.0f / gstate.getViewportYScale());
|
||||
Vec2f minViewport = (minOffset - Vec2f(gstate.getViewportXCenter(), gstate.getViewportYCenter())) * inverseViewportScale;
|
||||
Vec2f maxViewport = (maxOffset - Vec2f(gstate.getViewportXCenter(), gstate.getViewportYCenter())) * inverseViewportScale;
|
||||
|
||||
Vec2f viewportInvSize = Vec2f(1.0f / (maxViewport.x - minViewport.x), 1.0f / (maxViewport.y - minViewport.y));
|
||||
|
||||
Lin::Matrix4x4 applyViewport{};
|
||||
// Scale to the viewport's size.
|
||||
applyViewport.xx = 2.0f * viewportInvSize.x;
|
||||
applyViewport.yy = 2.0f * viewportInvSize.y;
|
||||
applyViewport.zz = 1.0f;
|
||||
applyViewport.ww = 1.0f;
|
||||
// And offset to the viewport's centers.
|
||||
applyViewport.wx = -(maxViewport.x + minViewport.x) * viewportInvSize.x;
|
||||
applyViewport.wy = -(maxViewport.y + minViewport.y) * viewportInvSize.y;
|
||||
|
||||
// TODO: Optimize.
|
||||
Matrix4ByMatrix4(mtx, worldViewProj, applyViewport.m);
|
||||
worldViewProj = mtx;
|
||||
}
|
||||
|
||||
switch (vertType & GE_VTYPE_POS_MASK) {
|
||||
case GE_VTYPE_POS_8BIT:
|
||||
return ::TestBoundingBoxFast<GE_VTYPE_POS_8BIT>(worldViewProj, vdata, vertexCount, dec, vertType, decoded_);
|
||||
case GE_VTYPE_POS_16BIT:
|
||||
return ::TestBoundingBoxFast<GE_VTYPE_POS_16BIT>(worldViewProj, vdata, vertexCount, dec, vertType, decoded_);
|
||||
case GE_VTYPE_POS_FLOAT:
|
||||
return ::TestBoundingBoxFast<GE_VTYPE_POS_FLOAT>(worldViewProj, vdata, vertexCount, dec, vertType, decoded_);
|
||||
default:
|
||||
// Shouldn't end up here with the checks outside this function.
|
||||
_dbg_assert_(false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 2D bounding box test against scissor. No indexing yet.
|
||||
// Only supports non-indexed draws with float positions.
|
||||
bool DrawEngineCommon::TestBoundingBoxThrough(const void *vdata, int vertexCount, const VertexDecoder *dec, u32 vertType, int *bytesRead) {
|
||||
|
||||
@@ -1232,7 +1232,9 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
|
||||
WRITE(p, " float projZ = (projPos.z - u_depthRange.z) * u_depthRange.w;\n");
|
||||
|
||||
if (!bugs.Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL)) {
|
||||
// Vertex range culling doesn't happen when Z clips, note sign of w is important.
|
||||
// Vertex range culling always happens, even when Z clips - but since we let the host GPU clip
|
||||
// here afterwards, and never actually perform the clipping, we have to disable range culling here if we
|
||||
// detect clipping, otherwise we're checking unclipped coordinates.
|
||||
WRITE(p, " if (u_cullRangeMin.w <= 0.0 || projZ * outPos.w > -outPos.w) {\n");
|
||||
const char *outMin = "projPos.x < u_cullRangeMin.x || projPos.y < u_cullRangeMin.y";
|
||||
const char *outMax = "projPos.x > u_cullRangeMax.x || projPos.y > u_cullRangeMax.y";
|
||||
|
||||
Reference in New Issue
Block a user