From 54ffbed26a5ec0a10c37c5da0b6ec62bb199af77 Mon Sep 17 00:00:00 2001 From: Lain <46204173+Cereal-Lain@users.noreply.github.com> Date: Sat, 19 Sep 2026 05:39:54 +0200 Subject: [PATCH] Latte: Handle edge cases in texture hashing for 16-byte compressed texture formats (#2051) --- src/Cafe/HW/Latte/Core/LatteTextureCache.cpp | 35 +++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteTextureCache.cpp b/src/Cafe/HW/Latte/Core/LatteTextureCache.cpp index 754d6715..b1ed968b 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureCache.cpp @@ -22,7 +22,7 @@ void LatteTC_UnregisterTexture(LatteTexture* tex) } // sample few uint64s uniformly over memory range -uint32 _quickStochasticHash(void* texData, uint32 memRange) +uint32 _quickStochasticHash(void* texData, uint32 memRange, bool is16ByteFormat) { uint64* texDataU64 = (uint64*)texData; @@ -30,11 +30,25 @@ uint32 _quickStochasticHash(void* texData, uint32 memRange) memRange /= sizeof(uint64); uint32 memStep = memRange / 37; // use prime here to avoid memStep aligning nicely with pitch of texture, leading to sampling only along the border of a texture - for (sint32 i = 0; i < 37; i++) + + if (!is16ByteFormat) { - hashVal += *texDataU64; - hashVal = (hashVal << 3) | (hashVal >> 61); - texDataU64 += memStep; + for (sint32 i = 0; i < 37; i++) + { + hashVal += *texDataU64; + hashVal = (hashVal << 3) | (hashVal >> 61); + texDataU64 += memStep; + } + } + else + { + for (sint32 i = 0; i < 37; i++) + { + hashVal += *texDataU64 ^ *(texDataU64 + 1); + hashVal = (hashVal << 3) | (hashVal >> 61); + texDataU64 += memStep; + } + } return (uint32)hashVal ^ (uint32)(hashVal >> 32); } @@ -84,7 +98,12 @@ uint32 LatteTexture_CalculateTextureDataHash(LatteTexture* hostTexture) } else { - hashVal = _quickStochasticHash(texDataU32, memRange); + bool is16ByteFormat = (hostTexture->format == Latte::E_GX2SURFFMT::BC2_UNORM || + hostTexture->format == Latte::E_GX2SURFFMT::BC2_SRGB || + hostTexture->format == Latte::E_GX2SURFFMT::BC3_UNORM || + hostTexture->format == Latte::E_GX2SURFFMT::BC3_SRGB); + + hashVal = _quickStochasticHash(texDataU32, memRange, is16ByteFormat); } return hashVal; } @@ -212,7 +231,7 @@ bool LatteTC_HasTextureChanged(LatteTexture* hostTexture, bool force) return false; hostTexture->lastDataUpdateFrameCounter = LatteGPUState.frameCounter; // we assume that certain texture properties indicate that the texture will never be written by the CPU - if (hostTexture->width == 1280 && hostTexture->format != Latte::E_GX2SURFFMT::R8_UNORM && force == false) + if (hostTexture->width == 1280 && hostTexture->format != Latte::E_GX2SURFFMT::R8_UNORM && hostTexture->format != Latte::E_GX2SURFFMT::BC3_UNORM && force == false) { // todo - remove this or find a better way to handle excluded texture invalidation checks (maybe via game profile?) return false; @@ -439,4 +458,4 @@ void LatteTC_UnloadAllTextures() LatteTexture_Delete(itr); } LatteRenderTarget_unloadAll(); -} \ No newline at end of file +}