diff --git a/Common/Arm64Emitter.cpp b/Common/Arm64Emitter.cpp index f33f7452a1..ffabf83a3a 100644 --- a/Common/Arm64Emitter.cpp +++ b/Common/Arm64Emitter.cpp @@ -2080,6 +2080,7 @@ void ARM64FloatEmitter::EmitLoadStoreImmediate(u8 size, u32 opc, IndexType type, imm >>= 3; else if (size == 128) imm >>= 4; + _dbg_assert_((imm & ~0xFFF) == 0); encoded_imm = (imm & 0xFFF); } else diff --git a/Common/Arm64Emitter.h b/Common/Arm64Emitter.h index 81a8325c4b..5cf73ba4db 100644 --- a/Common/Arm64Emitter.h +++ b/Common/Arm64Emitter.h @@ -793,10 +793,13 @@ public: // Loadstore single structure void LD1(u8 size, ARM64Reg Rt, u8 index, ARM64Reg Rn); void LD1(u8 size, ARM64Reg Rt, u8 index, ARM64Reg Rn, ARM64Reg Rm); + + // Replicate void LD1R(u8 size, ARM64Reg Rt, ARM64Reg Rn); void LD2R(u8 size, ARM64Reg Rt, ARM64Reg Rn); void LD1R(u8 size, ARM64Reg Rt, ARM64Reg Rn, ARM64Reg Rm); void LD2R(u8 size, ARM64Reg Rt, ARM64Reg Rn, ARM64Reg Rm); + void ST1(u8 size, ARM64Reg Rt, u8 index, ARM64Reg Rn); void ST1(u8 size, ARM64Reg Rt, u8 index, ARM64Reg Rn, ARM64Reg Rm); diff --git a/GPU/Common/VertexDecoderArm.cpp b/GPU/Common/VertexDecoderArm.cpp index 29c4368ad2..4974443518 100644 --- a/GPU/Common/VertexDecoderArm.cpp +++ b/GPU/Common/VertexDecoderArm.cpp @@ -156,6 +156,15 @@ static const JitLookup jitLookup[] = { {&VertexDecoder::Step_Color4444Morph, &VertexDecoderJitCache::Jit_Color4444Morph}, {&VertexDecoder::Step_Color565Morph, &VertexDecoderJitCache::Jit_Color565Morph}, {&VertexDecoder::Step_Color5551Morph, &VertexDecoderJitCache::Jit_Color5551Morph}, + + /* + {&VertexDecoder::Step_TcU8MorphToFloat, &VertexDecoderJitCache::Jit_TcU8MorphToFloat}, + {&VertexDecoder::Step_TcU16MorphToFloat, &VertexDecoderJitCache::Jit_TcU16MorphToFloat}, + {&VertexDecoder::Step_TcFloatMorph, &VertexDecoderJitCache::Jit_TcFloatMorph}, + {&VertexDecoder::Step_TcU8PrescaleMorph, &VertexDecoderJitCache::Jit_TcU8PrescaleMorph}, + {&VertexDecoder::Step_TcU16PrescaleMorph, &VertexDecoderJitCache::Jit_TcU16PrescaleMorph}, + {&VertexDecoder::Step_TcFloatPrescaleMorph, &VertexDecoderJitCache::Jit_TcFloatPrescaleMorph}, + */ }; JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int32_t *jittedSize) { @@ -1021,7 +1030,7 @@ void VertexDecoderJitCache::Jit_AnyS8Morph(int srcoff, int dstoff) { } ADDI2R(tempReg1, dstReg, dstoff, scratchReg); - // TODO: Is it okay that we're over-writing by 4 bytes? Probably... + // It's OK that we're over-writing by 4 bytes, not worth avoiding. VSTMIA(tempReg1, false, D4, 2); } @@ -1054,7 +1063,7 @@ void VertexDecoderJitCache::Jit_AnyS16Morph(int srcoff, int dstoff) { } ADDI2R(tempReg1, dstReg, dstoff, scratchReg); - // TODO: Is it okay that we're over-writing by 4 bytes? Probably... + // It's OK that we're over-writing by 4 bytes, not worth avoiding. VSTMIA(tempReg1, false, D4, 2); } @@ -1080,7 +1089,7 @@ void VertexDecoderJitCache::Jit_AnyFloatMorph(int srcoff, int dstoff) { } ADDI2R(tempReg1, dstReg, dstoff, scratchReg); - // TODO: Is it okay that we're over-writing by 4 bytes? Probably... + // It's OK that we're over-writing by 4 bytes, not worth avoiding. VSTMIA(tempReg1, false, D4, 2); } diff --git a/GPU/Common/VertexDecoderArm64.cpp b/GPU/Common/VertexDecoderArm64.cpp index 9c322247b8..7bd263ae7a 100644 --- a/GPU/Common/VertexDecoderArm64.cpp +++ b/GPU/Common/VertexDecoderArm64.cpp @@ -26,7 +26,7 @@ #include "GPU/GPUState.h" #include "GPU/Common/VertexDecoderCommon.h" -alignas(16) static float bones[16 * 8]; // First four are kept in registers +alignas(16) static float bones[16 * 8]; // First four are kept in registers Q16->Q31 using namespace Arm64Gen; @@ -69,7 +69,8 @@ static const ARM64Reg src[2] = {S2, S3}; static const ARM64Reg srcD = D2; static const ARM64Reg srcQ = Q2; -static const ARM64Reg srcNEON = Q8; +static const ARM64Reg weightQ = Q3; + static const ARM64Reg accNEON = Q9; static const ARM64Reg neonWeightRegsQ[2] = { Q3, Q2 }; // reverse order to prevent clash with neonScratchReg in Jit_WeightsU*Skin. @@ -77,6 +78,7 @@ static const ARM64Reg neonWeightRegsQ[2] = { Q3, Q2 }; // reverse order to prev // Q4-Q7 is the generated matrix that we multiply things by. // Q8,Q9 are accumulators/scratch for matrix mul. // Q10, Q11 are more scratch for matrix mul. +// Q12, Q13 are morph weights. // Q16+ are free-for-all for matrices. In 16 registers, we can fit 4 4x4 matrices. static const JitLookup jitLookup[] = { @@ -125,7 +127,6 @@ static const JitLookup jitLookup[] = { {&VertexDecoder::Step_PosS16Skin, &VertexDecoderJitCache::Jit_PosS16Skin}, {&VertexDecoder::Step_PosFloatSkin, &VertexDecoderJitCache::Jit_PosFloatSkin}, - /* {&VertexDecoder::Step_NormalS8Morph, &VertexDecoderJitCache::Jit_NormalS8Morph}, {&VertexDecoder::Step_NormalS16Morph, &VertexDecoderJitCache::Jit_NormalS16Morph}, {&VertexDecoder::Step_NormalFloatMorph, &VertexDecoderJitCache::Jit_NormalFloatMorph}, @@ -134,11 +135,22 @@ static const JitLookup jitLookup[] = { {&VertexDecoder::Step_PosS16Morph, &VertexDecoderJitCache::Jit_PosS16Morph}, {&VertexDecoder::Step_PosFloatMorph, &VertexDecoderJitCache::Jit_PosFloatMorph}, - {&VertexDecoder::Step_Color8888Morph, &VertexDecoderJitCache::Jit_Color8888Morph}, + /* {&VertexDecoder::Step_Color4444Morph, &VertexDecoderJitCache::Jit_Color4444Morph}, {&VertexDecoder::Step_Color565Morph, &VertexDecoderJitCache::Jit_Color565Morph}, {&VertexDecoder::Step_Color5551Morph, &VertexDecoderJitCache::Jit_Color5551Morph}, */ + + {&VertexDecoder::Step_Color8888Morph, &VertexDecoderJitCache::Jit_Color8888Morph}, + + /* + {&VertexDecoder::Step_TcU8MorphToFloat, &VertexDecoderJitCache::Jit_TcU8MorphToFloat}, + {&VertexDecoder::Step_TcU16MorphToFloat, &VertexDecoderJitCache::Jit_TcU16MorphToFloat}, + {&VertexDecoder::Step_TcFloatMorph, &VertexDecoderJitCache::Jit_TcFloatMorph}, + {&VertexDecoder::Step_TcU8PrescaleMorph, &VertexDecoderJitCache::Jit_TcU8PrescaleMorph}, + {&VertexDecoder::Step_TcU16PrescaleMorph, &VertexDecoderJitCache::Jit_TcU16PrescaleMorph}, + {&VertexDecoder::Step_TcFloatPrescaleMorph, &VertexDecoderJitCache::Jit_TcFloatPrescaleMorph}, + */ }; @@ -248,6 +260,12 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int LDRH(INDEX_UNSIGNED, boundsMaxVReg, scratchReg64, offsetof(KnownVertexBounds, maxV)); } + if (dec.morphcount != 0) { + MOVP2R(scratchReg64, &gstate_c.morphWeights); + // TODO: Only load the first 2 or 4 if morphWeights is small. + fp.LDP(128, INDEX_SIGNED, Q12, Q13, scratchReg64, 0); + } + const u8 *loopStart = NopAlignCode16(); for (int i = 0; i < dec.numSteps_; i++) { if (!CompileStep(dec, i)) { @@ -492,6 +510,98 @@ void VertexDecoderJitCache::Jit_Color8888() { STR(INDEX_UNSIGNED, tempReg1, dstReg, dec_->decFmt.c0off); } +void VertexDecoderJitCache::Jit_Color8888Morph() { + // The morph weights are pre-loaded into Q12, Q13. + // We accumulate into neonScratchRegQ2 + for (int n = 0; n < dec_->morphcount; ++n) { + const ARM64Reg weightReg = n >= 4 ? Q13 : Q12; + const int weightIndex = n & 3; + // TODO: Does the offset grow too large? + fp.LDR(32, INDEX_UNSIGNED, neonScratchRegD, srcReg, dec_->onesize_ * n + dec_->coloff); break; + fp.UXTL(8, neonScratchRegQ, neonScratchRegD); + fp.UXTL(16, neonScratchRegQ, neonScratchRegD); + fp.UCVTF(32, neonScratchRegQ, neonScratchRegQ, 7); + if (n == 0) { + fp.FMUL(32, neonScratchReg2Q, neonScratchRegQ, weightReg, weightIndex); + } else { + fp.FMLA(32, neonScratchReg2Q, neonScratchRegQ, weightReg, weightIndex); + } + } + Jit_WriteMorphColor(dec_->decFmt.c0off); +} + +void VertexDecoderJitCache::Jit_AnyS8Morph(int srcoff, int dstoff) { + // The morph weights are pre-loaded into Q12, Q13. + // We accumulate into neonScratchRegQ2 + for (int n = 0; n < dec_->morphcount; ++n) { + const ARM64Reg weightReg = n >= 4 ? Q13 : Q12; + const int weightIndex = n & 3; + fp.LDR(32, INDEX_UNSIGNED, neonScratchRegD, srcReg, dec_->onesize_ * n + srcoff); break; + fp.SXTL(8, neonScratchRegQ, neonScratchRegD); + fp.SXTL(16, neonScratchRegQ, neonScratchRegD); + fp.SCVTF(32, neonScratchRegQ, neonScratchRegQ, 7); + if (n == 0) { + fp.FMUL(32, neonScratchReg2Q, neonScratchRegQ, weightReg, weightIndex); + } else { + fp.FMLA(32, neonScratchReg2Q, neonScratchRegQ, weightReg, weightIndex); + } + } + fp.STUR(128, neonScratchReg2Q, dstReg, dstoff); +} + +void VertexDecoderJitCache::Jit_AnyS16Morph(int srcoff, int dstoff) { + // The morph weights are pre-loaded into Q12, Q13. + // We accumulate into neonScratchRegQ2 + for (int n = 0; n < dec_->morphcount; ++n) { + const ARM64Reg weightReg = n >= 4 ? Q13 : Q12; + const int weightIndex = n & 3; + fp.LDR(64, INDEX_UNSIGNED, neonScratchRegD, srcReg, dec_->onesize_ * n + srcoff); break; + fp.SXTL(16, neonScratchRegQ, neonScratchRegD); + fp.SCVTF(32, neonScratchRegQ, neonScratchRegQ, 7); + if (n == 0) { + fp.FMUL(32, neonScratchReg2Q, neonScratchRegQ, weightReg, weightIndex); + } else { + fp.FMLA(32, neonScratchReg2Q, neonScratchRegQ, weightReg, weightIndex); + } + } + fp.STUR(128, neonScratchReg2Q, dstReg, dstoff); +} + +void VertexDecoderJitCache::Jit_AnyFloatMorph(int srcoff, int dstoff) { + // The morph weights are pre-loaded into Q12, Q13. + // We accumulate into neonScratchRegQ2 + for (int n = 0; n < dec_->morphcount; ++n) { + const ARM64Reg weightReg = n >= 4 ? Q13 : Q12; + const int weightIndex = n & 3; + fp.LDR(128, INDEX_UNSIGNED, neonScratchRegD, srcReg, dec_->onesize_ * n + srcoff); break; + if (n == 0) { + fp.FMUL(32, neonScratchReg2Q, neonScratchRegQ, weightReg, weightIndex); + } else { + fp.FMLA(32, neonScratchReg2Q, neonScratchRegQ, weightReg, weightIndex); + } + } + fp.STUR(128, neonScratchReg2Q, dstReg, dstoff); +} + +// Incoming value in 2Q +void VertexDecoderJitCache::Jit_WriteMorphColor(int outOff, bool checkAlpha) { + // TODO: Keep zero and 255 around? + fp.MOVI2FDUP(neonScratchRegQ, 0.0f, scratchReg); + fp.FMAX(neonScratchReg2Q, neonScratchReg2Q, neonScratchRegQ); + fp.MOVI2FDUP(neonScratchRegQ, 255.0f, scratchReg); + fp.FMIN(neonScratchReg2Q, neonScratchReg2Q, neonScratchRegQ); + + fp.FCVTU(neonScratchRegQ, neonScratchReg2Q, RoundingMode::ROUND_Z); + fp.XTN(16, neonScratchRegQ, neonScratchRegQ); + fp.XTN(8, neonScratchRegQ, neonScratchRegQ); + + fp.MOV(tempReg1, neonScratchRegD); + + ORN(alphaNonFullReg, alphaNonFullReg, tempReg1, ArithOption(tempReg1, ST_ASR, 24)); + + STR(INDEX_UNSIGNED, tempReg1, dstReg, outOff); +} + void VertexDecoderJitCache::Jit_Color4444() { LDRH(INDEX_UNSIGNED, tempReg1, srcReg, dec_->coloff); @@ -785,4 +895,28 @@ void VertexDecoderJitCache::Jit_WriteMatrixMul(int outOff, bool pos) { fp.STUR(128, accNEON, dstReg, outOff); } +void VertexDecoderJitCache::Jit_PosS8Morph() { + Jit_AnyS8Morph(dec_->posoff, dec_->decFmt.posoff); +} + +void VertexDecoderJitCache::Jit_PosS16Morph() { + Jit_AnyS16Morph(dec_->posoff, dec_->decFmt.posoff); +} + +void VertexDecoderJitCache::Jit_PosFloatMorph() { + Jit_AnyFloatMorph(dec_->posoff, dec_->decFmt.posoff); +} + +void VertexDecoderJitCache::Jit_NormalS8Morph() { + Jit_AnyS8Morph(dec_->nrmoff, dec_->decFmt.nrmoff); +} + +void VertexDecoderJitCache::Jit_NormalS16Morph() { + Jit_AnyS16Morph(dec_->nrmoff, dec_->decFmt.nrmoff); +} + +void VertexDecoderJitCache::Jit_NormalFloatMorph() { + Jit_AnyFloatMorph(dec_->nrmoff, dec_->decFmt.nrmoff); +} + #endif // PPSSPP_ARCH(ARM64) diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 37756baf1c..7d29398eb0 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -943,9 +943,9 @@ void VertexDecoder::Step_PosS8Morph(const VertexDecoder *dec, const u8 *ptr, u8 // that it's 1.0 in that case, but that seems dangerous. void VertexDecoder::Step_PosS16Morph(const VertexDecoder *dec, const u8 *ptr, u8 *decoded) { const int onesize = dec->onesize_; + const int morphcount = dec->morphcount; #ifdef CROSSSIMD_SLOW float acc[3]{}; - const int morphcount = dec->morphcount; for (int n = 0; n < morphcount; n++) { const float multiplier = 1.0f / 32768.0f; const s16_le *sv = (const s16_le *)(ptr + onesize * n + dec->posoff); @@ -957,7 +957,6 @@ void VertexDecoder::Step_PosS16Morph(const VertexDecoder *dec, const u8 *ptr, u8 #else Vec4F32 sum = Vec4F32::Zero(); const float *weights = gstate_c.morphWeights; - const int morphcount = dec->morphcount; const s8 *bv = (const s8 *)(ptr + dec->posoff); for (int n = 0; n < morphcount; n++) { Vec4F32 w = Vec4F32::Splat(weights[n]);