Vertex decoder: Build the skin matrix without a branch per weight on ARM64

The steps skipped bones with a zero weight. With weights that vary from vertex to vertex that
branch mispredicts a lot, and it saves only a few multiplies. Now every weight is applied, with
fused multiply-adds, as the vertex JIT does. Without the vertex JIT (as on iOS): Tekken 4.5% fewer
cycles, God Eater 2.6%, God of War 1.7%.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-15 17:05:52 +02:00
1 parent 4dedfa2b8b
commit bf7e32cdae
1 file changed
+8
+8
View File
@@ -247,12 +247,20 @@ void VertexDecoder::ComputeSkinMatrix(const float weights[8]) const {
float32x4_t skin2 = vdupq_n_f32(0.0f);
for (int j = 0; j < count; j++) {
const float *bone = &gstate.boneMatrix[j * 12];
#if PPSSPP_ARCH(ARM64_NEON)
// Every weight, fused, like the vertex JIT (and no branch on varying weights).
const float32x4_t w = vdupq_n_f32(weights[j]);
skin0 = vfmaq_f32(skin0, vld1q_f32(bone), w);
skin1 = vfmaq_f32(skin1, vld1q_f32(bone + 4), w);
skin2 = vfmaq_f32(skin2, vld1q_f32(bone + 8), w);
#else
if (weights[j] != 0.0f) {
float32x4_t w = vdupq_n_f32(weights[j]);
skin0 = vaddq_f32(skin0, vmulq_f32(vld1q_f32(bone), w));
skin1 = vaddq_f32(skin1, vmulq_f32(vld1q_f32(bone + 4), w));
skin2 = vaddq_f32(skin2, vmulq_f32(vld1q_f32(bone + 8), w));
}
#endif
}
vst1q_f32(skinMatrix, skin0);
vst1q_f32(skinMatrix + 4, skin1);