From 583f20a4909e912725074c8cac5f74496655a21a Mon Sep 17 00:00:00 2001 From: Lin Runze Date: Fri, 18 Jul 2025 05:10:36 +0800 Subject: [PATCH 1/2] loongarch: Implement Morph in VertexJIT --- GPU/Common/VertexDecoderLoongArch64.cpp | 416 +++++++++++++++++++++++- 1 file changed, 414 insertions(+), 2 deletions(-) diff --git a/GPU/Common/VertexDecoderLoongArch64.cpp b/GPU/Common/VertexDecoderLoongArch64.cpp index a13c822b96..a607f386a2 100644 --- a/GPU/Common/VertexDecoderLoongArch64.cpp +++ b/GPU/Common/VertexDecoderLoongArch64.cpp @@ -66,6 +66,8 @@ static const LoongArch64Reg fpScratchReg4 = F7; static const LoongArch64Reg lsxScratchReg = V2; static const LoongArch64Reg lsxScratchReg2 = V3; +static const LoongArch64Reg lsxScratchReg3 = V10; +static const LoongArch64Reg lsxScratchReg4 = V11; static const LoongArch64Reg fpSrc[4] = {F2, F3, F10, F11}; @@ -109,6 +111,13 @@ static const JitLookup jitLookup[] = { {&VertexDecoder::Step_TcFloatThrough, &VertexDecoderJitCache::Jit_TcFloatThrough}, {&VertexDecoder::Step_TcU16ThroughToFloat, &VertexDecoderJitCache::Jit_TcU16ThroughToFloat}, + {&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}, + {&VertexDecoder::Step_NormalS8, &VertexDecoderJitCache::Jit_NormalS8}, {&VertexDecoder::Step_NormalS16, &VertexDecoderJitCache::Jit_NormalS16}, {&VertexDecoder::Step_NormalFloat, &VertexDecoderJitCache::Jit_NormalFloat}, @@ -134,20 +143,24 @@ 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}, + {&VertexDecoder::Step_NormalS8MorphSkin, &VertexDecoderJitCache::Jit_NormalS8MorphSkin}, + {&VertexDecoder::Step_NormalS16MorphSkin, &VertexDecoderJitCache::Jit_NormalS16MorphSkin}, + {&VertexDecoder::Step_NormalFloatMorphSkin, &VertexDecoderJitCache::Jit_NormalFloatMorphSkin}, {&VertexDecoder::Step_PosS8Morph, &VertexDecoderJitCache::Jit_PosS8Morph}, {&VertexDecoder::Step_PosS16Morph, &VertexDecoderJitCache::Jit_PosS16Morph}, {&VertexDecoder::Step_PosFloatMorph, &VertexDecoderJitCache::Jit_PosFloatMorph}, + {&VertexDecoder::Step_PosS8MorphSkin, &VertexDecoderJitCache::Jit_PosS8MorphSkin}, + {&VertexDecoder::Step_PosS16MorphSkin, &VertexDecoderJitCache::Jit_PosS16MorphSkin}, + {&VertexDecoder::Step_PosFloatMorphSkin, &VertexDecoderJitCache::Jit_PosFloatMorphSkin}, {&VertexDecoder::Step_Color8888Morph, &VertexDecoderJitCache::Jit_Color8888Morph}, {&VertexDecoder::Step_Color4444Morph, &VertexDecoderJitCache::Jit_Color4444Morph}, {&VertexDecoder::Step_Color565Morph, &VertexDecoderJitCache::Jit_Color565Morph}, {&VertexDecoder::Step_Color5551Morph, &VertexDecoderJitCache::Jit_Color5551Morph}, - */ }; JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int32_t *jittedSize) { @@ -660,6 +673,197 @@ void VertexDecoderJitCache::Jit_Color5551() { ST_W(tempReg1, dstReg, dec_->decFmt.c0off); } +void VertexDecoderJitCache::Jit_Color8888Morph() { + LI(tempReg1, &gstate_c.morphWeights[0]); + VXOR_V(lsxScratchReg4, lsxScratchReg4, lsxScratchReg4); + + bool first = true; + for (int n = 0; n < dec_->morphcount; ++n) { + const LoongArch64Reg reg = first ? lsxScratchReg : lsxScratchReg2; + FLD_S((LoongArch64Reg)(DecodeReg(reg) + F0), srcReg, dec_->onesize_ * n + dec_->coloff); + + VILVL_B(reg, lsxScratchReg4, reg); + VILVL_H(reg, lsxScratchReg4, reg); + VFFINT_S_W(reg, reg); + + // And now the weight. + VLDREPL_W(lsxScratchReg3, tempReg1, n * sizeof(float)); + VFMUL_S(reg, reg, lsxScratchReg3); + + if (!first) { + VFADD_S(lsxScratchReg, lsxScratchReg,lsxScratchReg2); + } else { + first = false; + } + } + + Jit_WriteMorphColor(dec_->decFmt.c0off); +} + +void VertexDecoderJitCache::Jit_Color4444Morph() { + LI(tempReg1, &gstate_c.morphWeights[0]); + VXOR_V(lsxScratchReg4, lsxScratchReg4, lsxScratchReg4); + + LI(tempReg2, 0xf00ff00f); // color 4444 mask + VREPLGR2VR_W(V8, tempReg2); + LI(tempReg3, 255.0f / 15.0f); // by color 4444 + VREPLGR2VR_W(V9, tempReg2); + + bool first = true; + for (int n = 0; n < dec_->morphcount; ++n) { + const LoongArch64Reg reg = first ? lsxScratchReg : lsxScratchReg2; + FLD_S((LoongArch64Reg)(DecodeReg(reg) + F0), srcReg, dec_->onesize_ * n + dec_->coloff); + VILVL_B(reg, reg, reg); + VAND_V(reg, reg, V8); + VEXTRINS_W(lsxScratchReg3, reg, 0); + VSLLI_H(fpScratchReg3, fpScratchReg3, 4); + VOR_V(reg, reg,lsxScratchReg3); + VSRLI_W(reg, reg, 4); + + VILVL_B(reg, lsxScratchReg4, reg); + VILVL_H(reg, lsxScratchReg4, reg); + + VFFINT_S_W(reg, reg); + VFMUL_S(reg, reg, V9); + + // And now the weight. + VLDREPL_W(lsxScratchReg3, tempReg1, n * sizeof(float)); + VFMUL_S(reg, reg, lsxScratchReg3); + + if (!first) { + VFADD_S(lsxScratchReg, lsxScratchReg,lsxScratchReg2); + } else { + first = false; + } + } + + Jit_WriteMorphColor(dec_->decFmt.c0off); +} + +// The mask is intentionally in reverse order (but skips A.) +alignas(16) static const u32 color565Mask[4] = { 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000, }; +alignas(16) static const float byColor565[4] = { 255.0f / 31.0f, 255.0f / 63.0f, 255.0f / 31.0f, 255.0f / 1.0f, }; + +void VertexDecoderJitCache::Jit_Color565Morph() { + LI(tempReg1, &gstate_c.morphWeights[0]); + LI(tempReg2, &color565Mask[0]); + VLD(V8, tempReg2, 0); + LI(tempReg2, &byColor565[0]); + VLD(V9, tempReg2, 0); + + bool first = true; + for (int n = 0; n < dec_->morphcount; ++n) { + const LoongArch64Reg reg = first ? lsxScratchReg : lsxScratchReg3; + // Spread it out into each lane. We end up with it reversed (R high, A low.) + // Below, we shift out each lane from low to high and reverse them. + VLDREPL_W(lsxScratchReg2, srcReg, dec_->onesize_ * n + dec_->coloff); + VAND_V(lsxScratchReg2, lsxScratchReg2, V8); + + // Alpha handled in Jit_WriteMorphColor. + + // Blue first. + VEXTRINS_W(reg, lsxScratchReg2, 0); + VSRLI_W(reg, reg, 6); + VSHUF4I_W(reg, reg, 3 << 6); + + // Green, let's shift it into the right lane first. + VEXTRINS_W(reg, lsxScratchReg2, 1); + VSRLI_W(reg, reg, 5); + VSHUF4I_W(reg, reg, (3 << 6 | 2 << 4)); + + // Last one, red. + VEXTRINS_W(reg, lsxScratchReg2, 2); + VFFINT_S_W(reg, reg); + VFMUL_S(reg, reg, V9); + + // And now the weight. + VLDREPL_W(lsxScratchReg2, tempReg1, n * sizeof(float)); + VFMUL_S(reg, reg, lsxScratchReg2); + + if (!first) { + VFADD_S(fpScratchReg, fpScratchReg, fpScratchReg3); + } else { + first = false; + } + } + + Jit_WriteMorphColor(dec_->decFmt.c0off, false); +} + +// The mask is intentionally in reverse order. +alignas(16) static const u32 color5551Mask[4] = { 0x00008000, 0x00007c00, 0x000003e0, 0x0000001f, }; +alignas(16) static const float byColor5551[4] = { 255.0f / 31.0f, 255.0f / 31.0f, 255.0f / 31.0f, 255.0f / 1.0f, }; + +void VertexDecoderJitCache::Jit_Color5551Morph() { + LI(tempReg1, &gstate_c.morphWeights[0]); + LI(tempReg2, &color5551Mask[0]); + VLD(V8, tempReg2, 0); + LI(tempReg2, &byColor5551[0]); + VLD(V9, tempReg2, 0); + + bool first = true; + for (int n = 0; n < dec_->morphcount; ++n) { + const LoongArch64Reg reg = first ? lsxScratchReg : lsxScratchReg3; + // Spread it out into each lane. + VLDREPL_W(lsxScratchReg2, srcReg, dec_->onesize_ * n + dec_->coloff); + VAND_V(lsxScratchReg2, lsxScratchReg2, V8); + + // Alpha first. + VEXTRINS_W(reg, lsxScratchReg2, 0); + VSRLI_W(reg, reg, 5); + VSHUF4I_W(reg, reg, 0); + + // Blue, let's shift it into the right lane first. + VEXTRINS_W(reg, lsxScratchReg2, 1); + VSRLI_W(reg, reg, 5); + VSHUF4I_W(reg, reg, 3 << 6); + + // Green. + VEXTRINS_W(reg, lsxScratchReg2, 2); + VSRLI_W(reg, reg, 5); + VSHUF4I_W(reg, reg, (3 << 6 | 2 << 4)); + + // Last one, red. + VEXTRINS_W(reg, lsxScratchReg2, 3); + VFFINT_S_W(reg, reg); + VFMUL_S(reg, reg, V9); + + // And now the weight. + VLDREPL_W(lsxScratchReg2, tempReg1, n * sizeof(float)); + VFMUL_S(reg, reg, lsxScratchReg2); + + if (!first) { + VFADD_S(fpScratchReg, fpScratchReg, fpScratchReg3); + } else { + first = false; + } + } + + Jit_WriteMorphColor(dec_->decFmt.c0off); +} + +void VertexDecoderJitCache::Jit_WriteMorphColor(int outOff, bool checkAlpha) { + // Pack back into a u32, with saturation. + VFTINT_W_S(lsxScratchReg, lsxScratchReg); + VSSRLNI_H_W(lsxScratchReg, lsxScratchReg, 0); + VSSRLNI_BU_H(lsxScratchReg, lsxScratchReg, 0); + VPICKVE2GR_W(tempReg1, lsxScratchReg, 0); + + // TODO: Could be optimize with a SLLI on fullAlphaReg + SLLI_D(tempReg2, fullAlphaReg, 24); + if (checkAlpha) { + SLTU(tempReg3, tempReg1, tempReg2); + FixupBranch skip = BEQZ(tempReg3); + XOR(fullAlphaReg, fullAlphaReg, fullAlphaReg); + SetJumpTarget(skip); + } else { + // Force alpha to full if we're not checking it. + OR(tempReg1, tempReg1, tempReg2); + } + + ST_W(tempReg1, dstReg, outOff); +} + void VertexDecoderJitCache::Jit_TcU16ThroughToFloat() { LD_HU(tempReg1, srcReg, dec_->tcoff + 0); LD_HU(tempReg2, srcReg, dec_->tcoff + 2); @@ -739,6 +943,82 @@ void VertexDecoderJitCache::Jit_TcFloatPrescale() { FST_D(fpSrc[0], dstReg, dec_->decFmt.uvoff); // save the lower 64-bit of lsxScratchReg } +void VertexDecoderJitCache::Jit_TcAnyMorph(int bits) { + LI(tempReg1, &gstate_c.morphWeights[0]); + VXOR_V(lsxScratchReg4, lsxScratchReg4, lsxScratchReg4); + + bool first = true; + for (int n = 0; n < dec_->morphcount; ++n) { + const LoongArch64Reg reg = first ? lsxScratchReg : lsxScratchReg2; + + // Load the actual values and convert to float. + if (bits == 32) { + // Two floats + FLD_D((LoongArch64Reg)(DecodeReg(reg) + F0), srcReg, dec_->onesize_ * n + dec_->tcoff); + } else { + if (bits == 8) { + LD_HU(tempReg2, srcReg, dec_->onesize_ * n + dec_->tcoff); + VINSGR2VR_W(reg, tempReg2, 0); + VILVL_B(reg, lsxScratchReg4, reg); + } else { + FLD_S((LoongArch64Reg)(DecodeReg(reg) + F0), srcReg, dec_->onesize_ * n + dec_->tcoff); + } + + VILVL_H(reg, lsxScratchReg4, reg); + VFFINT_S_W(reg, reg); + } + + // And now scale by the weight. + VLDREPL_W(lsxScratchReg3, tempReg1, n * sizeof(float)); + VFMUL_S(reg, reg, lsxScratchReg3); + + if (!first) { + VFADD_S(lsxScratchReg, lsxScratchReg,lsxScratchReg2); + } else { + first = false; + } + } +} + +void VertexDecoderJitCache::Jit_TcU8MorphToFloat() { + Jit_TcAnyMorph(8); + // They were all added (weighted) pre-normalize, we normalize once here. + VFMUL_S(lsxScratchReg, lsxScratchReg, by128LSX); + FST_D(fpSrc[0], dstReg, dec_->decFmt.uvoff); +} + +void VertexDecoderJitCache::Jit_TcU16MorphToFloat() { + Jit_TcAnyMorph(16); + // They were all added (weighted) pre-normalize, we normalize once here. + VFMUL_S(lsxScratchReg, lsxScratchReg, by32768LSX); + FST_D(fpSrc[0], dstReg, dec_->decFmt.uvoff); +} + +void VertexDecoderJitCache::Jit_TcFloatMorph() { + Jit_TcAnyMorph(32); + FST_D(fpSrc[0], dstReg, dec_->decFmt.uvoff); +} + +void VertexDecoderJitCache::Jit_TcU8PrescaleMorph() { + Jit_TcAnyMorph(8); + // The scale takes into account the u8 normalization. + VFMADD_S(lsxScratchReg, lsxScratchReg, lsxScaleOffsetReg, lsxOffsetScaleReg); + FST_D(fpSrc[0], dstReg, dec_->decFmt.uvoff); +} + +void VertexDecoderJitCache::Jit_TcU16PrescaleMorph() { + Jit_TcAnyMorph(16); + // The scale takes into account the u16 normalization. + VFMADD_S(lsxScratchReg, lsxScratchReg, lsxScaleOffsetReg, lsxOffsetScaleReg); + FST_D(fpSrc[0], dstReg, dec_->decFmt.uvoff); +} + +void VertexDecoderJitCache::Jit_TcFloatPrescaleMorph() { + Jit_TcAnyMorph(32); + VFMADD_S(lsxScratchReg, lsxScratchReg, lsxScaleOffsetReg, lsxOffsetScaleReg); + FST_D(fpSrc[0], dstReg, dec_->decFmt.uvoff); +} + void VertexDecoderJitCache::Jit_PosS8() { Jit_AnyS8ToFloat(dec_->posoff); VST(lsxScratchReg, dstReg, dec_->decFmt.posoff); @@ -881,4 +1161,136 @@ void VertexDecoderJitCache::Jit_WriteMatrixMul(int outOff, bool pos) { VST(accLSX, dstReg, outOff); } +void VertexDecoderJitCache::Jit_AnyS8Morph(int srcoff, int dstoff) { + LI(tempReg1, &gstate_c.morphWeights[0]); + + // Sum into lsxScratchReg. + bool first = true; + for (int n = 0; n < dec_->morphcount; ++n) { + const LoongArch64Reg reg = first ? lsxScratchReg : lsxScratchReg2; + // Okay, first convert to floats. + FLD_S((LoongArch64Reg)(DecodeReg(reg) + F0), srcReg, dec_->onesize_ * n + srcoff); + VSLLWIL_H_B(reg, reg, 0); + VSLLWIL_W_H(reg, reg, 0); + + VFFINT_S_W(reg, reg); + + // Now, It's time to multiply by the weight and 1.0f/128.0f. + VLDREPL_W(lsxScratchReg3, tempReg1, sizeof(float) * n); + VFMUL_S(lsxScratchReg3, lsxScratchReg3, by128LSX); + VFMUL_S(reg, reg, lsxScratchReg3); + + if (!first) { + VFADD_S(lsxScratchReg, lsxScratchReg, lsxScratchReg2); + } else { + first = false; + } + } + + if (dstoff >= 0) + VST(lsxScratchReg, dstReg, dstoff); +} + +void VertexDecoderJitCache::Jit_AnyS16Morph(int srcoff, int dstoff) { + LI(tempReg1, &gstate_c.morphWeights[0]); + + // Sum into lsxScratchReg. + bool first = true; + for (int n = 0; n < dec_->morphcount; ++n) { + const LoongArch64Reg reg = first ? lsxScratchReg : lsxScratchReg2; + // Okay, first convert to floats. + FLD_D((LoongArch64Reg)(DecodeReg(reg) + F0), srcReg, dec_->onesize_ * n + srcoff); + VSLLWIL_W_H(reg, reg, 0); + VFFINT_S_W(reg, reg); + + // Now, It's time to multiply by the weight and 1.0f/32768.0f. + VLDREPL_W(lsxScratchReg3, tempReg1, sizeof(float) * n); + VFMUL_S(lsxScratchReg3, lsxScratchReg3, by32768LSX); + VFMUL_S(reg, reg, lsxScratchReg3); + + if (!first) { + VFADD_S(lsxScratchReg, lsxScratchReg, lsxScratchReg2); + } else { + first = false; + } + } + + if (dstoff >= 0) + VST(lsxScratchReg, dstReg, dstoff); +} + +void VertexDecoderJitCache::Jit_AnyFloatMorph(int srcoff, int dstoff) { + LI(tempReg1, &gstate_c.morphWeights[0]); + + // Sum into lsxScratchReg. + bool first = true; + for (int n = 0; n < dec_->morphcount; ++n) { + const LoongArch64Reg reg = first ? lsxScratchReg : lsxScratchReg2; + VLD(reg, srcReg, dec_->onesize_ * n + srcoff); + VLDREPL_W(lsxScratchReg3, tempReg1, sizeof(float) * n); + VFMUL_S(reg, reg, lsxScratchReg3); + if (!first) { + VFADD_S(lsxScratchReg, lsxScratchReg, lsxScratchReg2); + } else { + first = false; + } + } + + if (dstoff >= 0) + VST(lsxScratchReg, dstReg, dstoff); +} + +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_PosS8MorphSkin() { + Jit_AnyS8Morph(dec_->posoff, -1); + Jit_WriteMatrixMul(dec_->decFmt.posoff, true); +} + +void VertexDecoderJitCache::Jit_PosS16MorphSkin() { + Jit_AnyS16Morph(dec_->posoff, -1); + Jit_WriteMatrixMul(dec_->decFmt.posoff, true); +} + +void VertexDecoderJitCache::Jit_PosFloatMorphSkin() { + Jit_AnyFloatMorph(dec_->posoff, -1); + Jit_WriteMatrixMul(dec_->decFmt.posoff, true); +} + +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); +} + +void VertexDecoderJitCache::Jit_NormalS8MorphSkin() { + Jit_AnyS8Morph(dec_->nrmoff, -1); + Jit_WriteMatrixMul(dec_->decFmt.nrmoff, false); +} + +void VertexDecoderJitCache::Jit_NormalS16MorphSkin() { + Jit_AnyS16Morph(dec_->nrmoff, -1); + Jit_WriteMatrixMul(dec_->decFmt.nrmoff, false); +} + +void VertexDecoderJitCache::Jit_NormalFloatMorphSkin() { + Jit_AnyFloatMorph(dec_->nrmoff, -1); + Jit_WriteMatrixMul(dec_->decFmt.nrmoff, false); +} #endif // PPSSPP_ARCH(LOONGARCH64) \ No newline at end of file From 50ff547777f9caea0e79c8c6f341e39738834b19 Mon Sep 17 00:00:00 2001 From: Lin Runze Date: Sat, 19 Jul 2025 00:43:24 +0800 Subject: [PATCH 2/2] loongarch: Implement QuickTexHashLSX --- GPU/Common/TextureDecoder.cpp | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/GPU/Common/TextureDecoder.cpp b/GPU/Common/TextureDecoder.cpp index 985c5d6805..274ef6dd60 100644 --- a/GPU/Common/TextureDecoder.cpp +++ b/GPU/Common/TextureDecoder.cpp @@ -183,6 +183,45 @@ static u32 QuickTexHashNEON(const void *checkp, u32 size) { #endif // PPSSPP_ARCH(ARM_NEON) +#if PPSSPP_ARCH(LOONGARCH64_LSX) + +alignas(16) static const u16 QuickTexHashInitial[8] = { 0xc00bU, 0x9bd9U, 0x4b73U, 0xb651U, 0x4d9bU, 0x4309U, 0x0083U, 0x0001U }; + +static u32 QuickTexHashLSX(const void *checkp, u32 size) { + u32 check = 0; + + if (((intptr_t)checkp & 0xf) == 0 && (size & 0x3f) == 0) { + __m128i cursor = __lsx_vrepli_d(0); + __m128i cursor2 = __lsx_vld(QuickTexHashInitial, 0); + __m128i update = __lsx_vreplgr2vr_h(0x2455U); + const __m128i *p = (const __m128i *)checkp; + for (u32 i = 0; i < size / 16; i += 4) { + __m128i chunk = __lsx_vmul_h(__lsx_vld(&p[i], 0), cursor2); + cursor = __lsx_vadd_h(cursor, chunk); + cursor = __lsx_vxor_v(cursor, __lsx_vld(&p[i + 1], 0)); + cursor = __lsx_vadd_w(cursor, __lsx_vld(&p[i + 2], 0)); + chunk = __lsx_vmul_h(__lsx_vld(&p[i + 3], 0), cursor2); + cursor = __lsx_vxor_v(cursor, chunk); + cursor2 = __lsx_vadd_h(cursor2, update); + } + cursor = __lsx_vadd_w(cursor, cursor2); + // Add the four parts into the low i32. + cursor = __lsx_vadd_w(cursor, __lsx_vbsrl_v(cursor, 8)); + cursor = __lsx_vadd_w(cursor, __lsx_vbsrl_v(cursor, 4)); + check = __lsx_vpickve2gr_w(cursor, 0); + } else { + const u32 *p = (const u32 *)checkp; + for (u32 i = 0; i < size / 8; ++i) { + check += *p++; + check ^= *p++; + } + } + + return check; +} + +#endif // PPSSPP_ARCH(LOONGARCH64_LSX) + // Masks to downalign bufw to 16 bytes, and wrap at 2048. static const u32 textureAlignMask16[16] = { 0x7FF & ~(((8 * 16) / 16) - 1), //GE_TFMT_5650, @@ -278,6 +317,8 @@ u32 StableQuickTexHash(const void *checkp, u32 size) { return QuickTexHashSSE2(checkp, size); #elif PPSSPP_ARCH(ARM_NEON) return QuickTexHashNEON(checkp, size); +#elif PPSSPP_ARCH(LOONGARCH64_LSX) + return QuickTexHashLSX(checkp, size); #else return QuickTexHashNonSSE(checkp, size); #endif