From 253aba2a6041a040eb204ac40573ae8149116fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Oct 2026 12:32:03 +0200 Subject: [PATCH] Vertex decoder: Use the handwritten decoders with every UV generation mode They were only used for UV from texture coordinates. With the texture matrix or environment map, UV isn't prescaled, which is the same as a scale of 1 and no offset, so NoPrescale versions pass that. God of War draws 5 million of its vertices per 10 seconds this way, which without the vertex JIT took 9.6 ns each through the steps and now 2.4. Co-Authored-By: Claude Opus 5.5 (1M context) --- GPU/Common/VertexDecoderCommon.cpp | 15 +++++++++------ GPU/Common/VertexDecoderHandwritten.cpp | 12 ++++++++++++ GPU/Common/VertexDecoderHandwritten.h | 4 ++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index eed7049be8..390d24cfcd 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -1363,14 +1363,17 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options, // Only use the handwritten decoders if we have SSE or NEON. Don't want to use these on RISC-V, probably? #if PPSSPP_ARCH(ARM_NEON) || PPSSPP_ARCH(SSE2) - // See GetVertTypeID - uint32_t fmtWithoutSkinFlag = (fmt_ & ~0x04000000); - if (fmtWithoutSkinFlag == (GE_VTYPE_TC_8BIT | GE_VTYPE_COL_5551 | GE_VTYPE_POS_16BIT)) { + // See GetVertTypeID. The UV generation modes that don't prescale UV convert it the same way with a + // scale of 1 and no offset, which the NoPrescale versions pass. + const uint32_t vtype = fmt_ & 0x00FFFFFF; + const GETexMapMode uvGenMode = VertTypeIDUVGenMode(fmt_); + const bool prescale = uvGenMode == GE_TEXMAP_TEXTURE_COORDS || uvGenMode == GE_TEXMAP_UNKNOWN; + if (vtype == (GE_VTYPE_TC_8BIT | GE_VTYPE_COL_5551 | GE_VTYPE_POS_16BIT)) { // Can skip looking up in the JIT. - jitted_ = &VtxDec_Tu8_C5551_Ps16; + jitted_ = prescale ? &VtxDec_Tu8_C5551_Ps16 : &VtxDec_Tu8_C5551_Ps16_NoPrescale; return; - } else if (!options.expand8BitNormalsToFloat && fmtWithoutSkinFlag == (GE_VTYPE_TC_16BIT | GE_VTYPE_NRM_8BIT | GE_VTYPE_COL_8888 | GE_VTYPE_POS_FLOAT)) { - jitted_ = &VtxDec_Tu16_C8888_Pfloat; + } else if (!options.expand8BitNormalsToFloat && vtype == (GE_VTYPE_TC_16BIT | GE_VTYPE_NRM_8BIT | GE_VTYPE_COL_8888 | GE_VTYPE_POS_FLOAT)) { + jitted_ = prescale ? &VtxDec_Tu16_C8888_Pfloat : &VtxDec_Tu16_C8888_Pfloat_NoPrescale; return; } #endif diff --git a/GPU/Common/VertexDecoderHandwritten.cpp b/GPU/Common/VertexDecoderHandwritten.cpp index 896423b286..41d3ea6b77 100644 --- a/GPU/Common/VertexDecoderHandwritten.cpp +++ b/GPU/Common/VertexDecoderHandwritten.cpp @@ -324,3 +324,15 @@ void VtxDec_Tu8_C5551_Ps16(const u8 *srcp, u8 *dstp, int numVerts, const UVScale gstate_c.vertexFullAlpha = (alpha >> 15) & 1; } + +// Converting UV without a prescale is the same as with a scale of 1 and no offset: the division by a power +// of two in the scale is exact either way. +static const UVScale identityUVScale = { 1.0f, 1.0f, 0.0f, 0.0f }; + +void VtxDec_Tu16_C8888_Pfloat_NoPrescale(const u8 *srcp, u8 *dstp, int count, const UVScale *uvScaleOffset) { + VtxDec_Tu16_C8888_Pfloat(srcp, dstp, count, &identityUVScale); +} + +void VtxDec_Tu8_C5551_Ps16_NoPrescale(const u8 *srcp, u8 *dstp, int count, const UVScale *uvScaleOffset) { + VtxDec_Tu8_C5551_Ps16(srcp, dstp, count, &identityUVScale); +} diff --git a/GPU/Common/VertexDecoderHandwritten.h b/GPU/Common/VertexDecoderHandwritten.h index fdf666cc85..a14679d917 100644 --- a/GPU/Common/VertexDecoderHandwritten.h +++ b/GPU/Common/VertexDecoderHandwritten.h @@ -5,3 +5,7 @@ void VtxDec_Tu16_C8888_Pfloat(const u8 *srcp, u8 *dstp, int count, const UVScale *uvScaleOffset); void VtxDec_Tu8_C5551_Ps16(const u8 *srcp, u8 *dstp, int count, const UVScale *uvScaleOffset); + +// For the UV generation modes that don't prescale. +void VtxDec_Tu16_C8888_Pfloat_NoPrescale(const u8 *srcp, u8 *dstp, int count, const UVScale *uvScaleOffset); +void VtxDec_Tu8_C5551_Ps16_NoPrescale(const u8 *srcp, u8 *dstp, int count, const UVScale *uvScaleOffset);