mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
SIMD-optimize BBox vertex decoding
This commit is contained in:
@@ -228,7 +228,6 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
|
||||
|
||||
SimpleVertex *corners = (SimpleVertex *)(decoded_ + 65536 * 12);
|
||||
float *verts = (float *)(decoded_ + 65536 * 18);
|
||||
float *vertsTransformed = (float *)(decoded_ + 65536 * 24);
|
||||
|
||||
// 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.
|
||||
@@ -243,19 +242,28 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
|
||||
gstate_c.Clean(DIRTY_CULL_PLANES);
|
||||
}
|
||||
|
||||
// Pretransform the verts on load so we don't have to do it inside the loop.
|
||||
// We do this differently in the fast version below since we skip the max/minOffset checks there
|
||||
// making it easier to get the whole thing ready for SIMD.
|
||||
|
||||
// Try to skip NormalizeVertices if it's pure positions. No need to bother with a vertex decoder
|
||||
// and a large vertex format.
|
||||
Mat4F32 world = Mat4F32::Load4x3(gstate.worldMatrix);
|
||||
|
||||
if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_FLOAT && !inds) {
|
||||
memcpy(verts, vdata, sizeof(float) * 3 * vertexCount);
|
||||
const float *vdata_pos = (const float *)vdata;
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
Vec4F32::Load(&vdata_pos[i * 3]).AsVec3ByMatrix44(world).Store(&verts[i * 3]);
|
||||
}
|
||||
} else if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_8BIT && !inds) {
|
||||
const s8 *vtx = (const s8 *)vdata;
|
||||
for (int i = 0; i < vertexCount * 3; i++) {
|
||||
verts[i] = vtx[i] * (1.0f / 128.0f);
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
Vec4F32::LoadS8Norm(&vtx[i * 3]).AsVec3ByMatrix44(world).Store(&verts[i * 3]);
|
||||
}
|
||||
} else if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_16BIT && !inds) {
|
||||
const s16 *vtx = (const s16 *)vdata;
|
||||
for (int i = 0; i < vertexCount * 3; i++) {
|
||||
verts[i] = vtx[i] * (1.0f / 32768.0f);
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
Vec4F32::LoadS16Norm(&vtx[i * 3]).AsVec3ByMatrix44(world).Store(&verts[i * 3]);
|
||||
}
|
||||
} else {
|
||||
// Simplify away indices, bones, and morph before proceeding.
|
||||
@@ -274,49 +282,36 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
|
||||
::NormalizeVertices(corners, temp_buffer, (const u8 *)vdata, indexLowerBound, indexUpperBound, dec, vertType);
|
||||
IndexConverter conv(vertType, inds);
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
verts[i * 3] = corners[conv(i)].pos.x;
|
||||
verts[i * 3 + 1] = corners[conv(i)].pos.y;
|
||||
verts[i * 3 + 2] = corners[conv(i)].pos.z;
|
||||
int index = conv(i);
|
||||
Vec4F32::Load(corners[index].pos.AsArray()).AsVec3ByMatrix44(world).Store(&verts[i * 3]);
|
||||
}
|
||||
} else {
|
||||
// Simple, most common case.
|
||||
// Simple, most common case that's not just a pure position.
|
||||
int stride = dec->VertexSize();
|
||||
int offset = dec->posoff;
|
||||
switch (vertType & GE_VTYPE_POS_MASK) {
|
||||
case GE_VTYPE_POS_8BIT:
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
const s8 *data = (const s8 *)vdata + i * stride + offset;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
verts[i * 3 + j] = data[j] * (1.0f / 128.0f);
|
||||
}
|
||||
Vec4F32::LoadS8Norm(data).AsVec3ByMatrix44(world).Store(&verts[i * 3]);
|
||||
}
|
||||
break;
|
||||
case GE_VTYPE_POS_16BIT:
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
const s16 *data = ((const s16 *)((const s8 *)vdata + i * stride + offset));
|
||||
for (int j = 0; j < 3; j++) {
|
||||
verts[i * 3 + j] = data[j] * (1.0f / 32768.0f);
|
||||
}
|
||||
Vec4F32::LoadS16Norm(data).AsVec3ByMatrix44(world).Store(&verts[i * 3]);
|
||||
}
|
||||
break;
|
||||
case GE_VTYPE_POS_FLOAT:
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
memcpy(&verts[i * 3], (const u8 *)vdata + stride * i + offset, sizeof(float) * 3);
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
const float *data = (const float *)((const u8 *)vdata + stride * i + offset);
|
||||
Vec4F32::Load(data).AsVec3ByMatrix44(world).Store(&verts[i * 3]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pretransform the verts so we don't have to do it inside the loop.
|
||||
// We do this differently in the fast version below since we skip the max/minOffset checks there
|
||||
// making it easier to get the whole thing ready for SIMD.
|
||||
|
||||
// TODO: Merge this into the decoder loops above?
|
||||
Mat4F32 world = Mat4F32::Load4x3(gstate.worldMatrix);
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
Vec4F32::Load(&verts[i * 3]).AsVec3ByMatrix44(world).Store3(&vertsTransformed[i * 3]);
|
||||
}
|
||||
|
||||
// Note: near/far are not checked without clamp/clip enabled, so we skip those planes.
|
||||
int totalPlanes = gstate.isDepthClampEnabled() ? 6 : 4;
|
||||
for (int plane = 0; plane < totalPlanes; plane++) {
|
||||
@@ -326,7 +321,7 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int
|
||||
// Test against the frustum planes, and count.
|
||||
// TODO: We should test 4 vertices at a time using SIMD.
|
||||
// I guess could also test one vertex against 4 planes at a time, though a lot of waste at the common case of 6.
|
||||
const float *worldpos = vertsTransformed + i * 3;
|
||||
const float *worldpos = verts + i * 3;
|
||||
float value = planes_.Test(plane, worldpos);
|
||||
if (value <= -FLT_EPSILON) // Not sure why we use exactly this value. Probably '< 0' would do.
|
||||
out++;
|
||||
|
||||
Reference in New Issue
Block a user