Specialized vertex decoder for GTA

This commit is contained in:
Henrik Rydgård 2023-12-19 14:14:10 +01:00
parent 3e11e54405
commit afca3717ba

View File

@ -21,6 +21,7 @@
#include <emmintrin.h>
#include "Common/CPUDetect.h"
#include "Common/Data/Convert/ColorConv.h"
#include "Core/Config.h"
#include "GPU/GPUState.h"
#include "GPU/Common/VertexDecoderCommon.h"
@ -166,7 +167,114 @@ static const JitLookup jitLookup[] = {
{&VertexDecoder::Step_Color5551Morph, &VertexDecoderJitCache::Jit_Color5551Morph},
};
void GTA_Tu8_C5551_Ps16(const u8 *srcp, u8 *dstp, int count, const UVScale *uvScaleOffset) {
struct GTAVTX {
union {
struct {
u8 u;
u8 v;
};
u16 uv;
};
u16 col;
s16 x;
s16 y;
s16 z;
};
// NOTE: This might be different for different vertex format.
struct OutVTX {
float u;
float v;
uint32_t col;
float x;
float y;
float z;
};
const GTAVTX *src = (const GTAVTX *)srcp;
OutVTX *dst = (OutVTX *)dstp;
float uscale = uvScaleOffset->uScale * (1.0f / 128.0f);
float vscale = uvScaleOffset->vScale * (1.0f / 128.0f);
float uoff = uvScaleOffset->uOff;
float voff = uvScaleOffset->vOff;
u32 alpha = 0xFFFFFFFF;
#if PPSSPP_ARCH(SSE2)
__m128 uvOff = _mm_setr_ps(uoff, voff, uoff, voff);
__m128 uvScale = _mm_setr_ps(uscale, vscale, uscale, vscale);
__m128 posScale = _mm_set1_ps(1.0f / 32768.0f);
__m128i rmask = _mm_set1_epi32(0x001F);
__m128i gmask = _mm_set1_epi32(0x03E0);
__m128i bmask = _mm_set1_epi32(0x7c00);
__m128i amask = _mm_set1_epi32(0x8000);
__m128i lowbits = _mm_set1_epi32(0x00070707);
// Two vertices at a time, we can share some calculations.
for (int i = 0; i < count; i += 2) {
__m128i pos0 = _mm_loadl_epi64((const __m128i *) & src[i].x);
__m128i pos1 = _mm_loadl_epi64((const __m128i *) & src[i + 1].x);
// Translate UV, combined. TODO: Can possibly shuffle UV and col together here
uint32_t uv0 = (uint32_t)src[i].uv | ((uint32_t)src[i + 1].uv << 16);
uint64_t col0 = (uint64_t)src[i].col | ((uint64_t)src[i + 1].col << 32);
__m128i pos0_32 = _mm_srai_epi32(_mm_unpacklo_epi16(pos0, pos0), 16);
__m128i pos1_32 = _mm_srai_epi32(_mm_unpacklo_epi16(pos1, pos1), 16);
__m128 pos0_ext = _mm_mul_ps(_mm_cvtepi32_ps(pos0_32), posScale);
__m128 pos1_ext = _mm_mul_ps(_mm_cvtepi32_ps(pos1_32), posScale);
__m128i uv8 = _mm_set1_epi32(uv0);
__m128i uv16 = _mm_unpacklo_epi8(uv8, uv8);
__m128i uv32 = _mm_srli_epi32(_mm_unpacklo_epi16(uv16, uv16), 24);
__m128d uvf = _mm_castps_pd(_mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(uv32), uvScale), uvOff));
alpha &= col0;
// Combined RGBA
__m128i col = _mm_set1_epi64x(col0);
__m128i r = _mm_slli_epi32(_mm_and_si128(col, rmask), 8 - 5);
__m128i g = _mm_slli_epi32(_mm_and_si128(col, gmask), 16 - 10);
__m128i b = _mm_slli_epi32(_mm_and_si128(col, bmask), 24 - 15);
__m128i a = _mm_srai_epi32(_mm_slli_epi32(_mm_and_si128(col, amask), 16), 7);
col = _mm_or_si128(_mm_or_si128(r, g), b);
col = _mm_or_si128(col, _mm_and_si128(_mm_srli_epi32(col, 5), lowbits));
col = _mm_or_si128(col, a);
// TODO: Mix into fewer stores.
_mm_storeu_ps(&dst[i].x, pos0_ext);
_mm_storeu_ps(&dst[i + 1].x, pos1_ext);
_mm_storel_pd((double *)&dst[i].u, uvf);
_mm_storeh_pd((double *)&dst[i + 1].u, uvf);
dst[i].col = _mm_cvtsi128_si32(col);
dst[i + 1].col = _mm_cvtsi128_si32(_mm_shuffle_epi32(col, _MM_SHUFFLE(1, 1, 1, 1)));
}
alpha = alpha & (alpha >> 16);
#else
for (int i = 0; i < count; i++) {
float u = src[i].u * uscale + uoff;
float v = src[i].v * vscale + voff;
alpha &= src[i].col;
uint32_t color = RGBA5551ToRGBA8888(src[i].col);
float x = src[i].x * (1.0f / 32768.0f);
float y = src[i].y * (1.0f / 32768.0f);
float z = src[i].z * (1.0f / 32768.0f);
dst[i].col = color;
dst[i].u = u;
dst[i].v = v;
dst[i].x = x;
dst[i].y = y;
dst[i].z = z;
}
#endif
gstate_c.vertexFullAlpha = (alpha >> 15) & 1;
}
JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int32_t *jittedSize) {
if ((dec.fmt_ & ~0x04000000) == (GE_VTYPE_TC_8BIT | GE_VTYPE_COL_5551 | GE_VTYPE_POS_16BIT)) {
return &GTA_Tu8_C5551_Ps16;
}
dec_ = &dec;
BeginWrite(4096);
const u8 *start = this->AlignCode16();