From 36a2174ac0831caf3e7db4902dc7428924132c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 7 Dec 2023 10:35:04 +0100 Subject: [PATCH 1/3] Vulkan: Add indicator of swizzle mode to texture debug names --- Common/UI/Screen.cpp | 2 +- GPU/Common/TextureCacheCommon.cpp | 1 + GPU/Vulkan/TextureCacheVulkan.cpp | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index cf5e754de436..03337d081c98 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -14,7 +14,7 @@ #include "Core/KeyMap.h" void Screen::focusChanged(ScreenFocusChange focusChange) { - char *eventName = ""; + const char *eventName = ""; switch (focusChange) { case ScreenFocusChange::FOCUS_LOST_TOP: eventName = "FOCUS_LOST_TOP"; break; case ScreenFocusChange::FOCUS_BECAME_TOP: eventName = "FOCUS_BECAME_TOP"; break; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index c3ad81ddd78d..f086e3013ef1 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -494,6 +494,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { // Exponential backoff up to 512 frames. Textures are often reused. if (entry->numFrames > 32) { // Also, try to add some "randomness" to avoid rehashing several textures the same frame. + // textureName is unioned with texturePtr and vkTex so will work for the other backends. entry->framesUntilNextFullHash = std::min(512, entry->numFrames) + (((intptr_t)(entry->textureName) >> 12) & 15); } else { entry->framesUntilNextFullHash = entry->numFrames; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 70999904279a..7525f80fcffd 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -503,11 +503,11 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { case VULKAN_4444_FORMAT: mapping = &VULKAN_4444_SWIZZLE; break; case VULKAN_1555_FORMAT: mapping = &VULKAN_1555_SWIZZLE; break; case VULKAN_565_FORMAT: mapping = &VULKAN_565_SWIZZLE; break; - default: mapping = &VULKAN_8888_SWIZZLE; break; // no swizzle + default: mapping = &VULKAN_8888_SWIZZLE; break; // no channel swizzle } - char texName[64]{}; - snprintf(texName, sizeof(texName), "tex_%08x_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format, gstate.getClutPaletteFormat())); + char texName[64]; + snprintf(texName, sizeof(texName), "tex_%08x_%s_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format, gstate.getClutPaletteFormat()), gstate.isTextureSwizzled() ? "swz" : "lin"); entry->vkTex = new VulkanTexture(vulkan, texName); VulkanTexture *image = entry->vkTex; bool allocSuccess = image->CreateDirect(cmdInit, plan.createW, plan.createH, plan.depth, plan.levelsToCreate, actualFmt, imageLayout, usage, mapping); From 443a882041bc56af0be11701d9e03e89da12ff47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 7 Dec 2023 10:45:31 +0100 Subject: [PATCH 2/3] Fix the size calculation when hashing small swizzled textures --- GPU/Common/TextureCacheCommon.cpp | 6 ++++-- GPU/Common/TextureCacheCommon.h | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index f086e3013ef1..c5fb8428d88f 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2115,7 +2115,8 @@ void TextureCacheCommon::ApplyTexture() { // Update the hash on the texture. int w = gstate.getTextureWidth(0); int h = gstate.getTextureHeight(0); - entry->fullhash = QuickTexHash(replacer_, entry->addr, entry->bufw, w, h, GETextureFormat(entry->format), entry); + bool swizzled = gstate.isTextureSwizzled(); + entry->fullhash = QuickTexHash(replacer_, entry->addr, entry->bufw, w, h, swizzled, GETextureFormat(entry->format), entry); // TODO: Here we could check the secondary cache; maybe the texture is in there? // We would need to abort the build if so. @@ -2536,6 +2537,7 @@ bool TextureCacheCommon::CheckFullHash(TexCacheEntry *entry, bool &doDelete) { int w = gstate.getTextureWidth(0); int h = gstate.getTextureHeight(0); bool isVideo = IsVideo(entry->addr); + bool swizzled = gstate.isTextureSwizzled(); // Don't even check the texture, just assume it has changed. if (isVideo && g_Config.bTextureBackoffCache) { @@ -2547,7 +2549,7 @@ bool TextureCacheCommon::CheckFullHash(TexCacheEntry *entry, bool &doDelete) { u32 fullhash; { PROFILE_THIS_SCOPE("texhash"); - fullhash = QuickTexHash(replacer_, entry->addr, entry->bufw, w, h, GETextureFormat(entry->format), entry); + fullhash = QuickTexHash(replacer_, entry->addr, entry->bufw, w, h, swizzled, GETextureFormat(entry->format), entry); } if (fullhash == entry->fullhash) { diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 372043870e91..4666e3d92b5b 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -438,7 +438,7 @@ class TextureCacheCommon { static CheckAlphaResult CheckCLUTAlpha(const uint8_t *pixelData, GEPaletteFormat clutFmt, int w); - inline u32 QuickTexHash(TextureReplacer &replacer, u32 addr, int bufw, int w, int h, GETextureFormat format, const TexCacheEntry *entry) const { + static inline u32 QuickTexHash(TextureReplacer &replacer, u32 addr, int bufw, int w, int h, bool swizzled, GETextureFormat format, const TexCacheEntry *entry) { if (replacer.Enabled()) { return replacer.ComputeHash(addr, bufw, w, h, format, entry->maxSeenV); } @@ -447,7 +447,17 @@ class TextureCacheCommon { h = (int)entry->maxSeenV; } - const u32 sizeInRAM = (textureBitsPerPixel[format] * bufw * h) / 8; + u32 sizeInRAM; + if (swizzled) { + // In swizzle mode, textures are stored in rectangular blocks with the height 8. + // That means that for a 64x4 texture, like in issue #9308, we would only hash half of the texture! + // In theory, we should make sure to only hash half of each block, but in reality it's not likely that + // games are using that memory for anything else. So we'll just make sure to compute the full size to hash. + // To do that, we just use the same calculation but round the height upwards to the nearest multiple of 8. + sizeInRAM = (textureBitsPerPixel[format] * bufw * ((h + 7) & ~7)) >> 3; + } else { + sizeInRAM = (textureBitsPerPixel[format] * bufw * h) >> 3; + } const u32 *checkp = (const u32 *)Memory::GetPointer(addr); gpuStats.numTextureDataBytesHashed += sizeInRAM; From 877324c9781ea5181a3f6060b3c96c116218fd9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 7 Dec 2023 11:01:51 +0100 Subject: [PATCH 3/3] Add comment about swizzling to the texture replacer hash --- GPU/Common/TextureCacheCommon.h | 2 +- GPU/Common/TextureReplacer.cpp | 5 ++++- GPU/Common/TextureReplacer.h | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 4666e3d92b5b..4e15abab660c 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -440,7 +440,7 @@ class TextureCacheCommon { static inline u32 QuickTexHash(TextureReplacer &replacer, u32 addr, int bufw, int w, int h, bool swizzled, GETextureFormat format, const TexCacheEntry *entry) { if (replacer.Enabled()) { - return replacer.ComputeHash(addr, bufw, w, h, format, entry->maxSeenV); + return replacer.ComputeHash(addr, bufw, w, h, swizzled, format, entry->maxSeenV); } if (h == 512 && entry->maxSeenV < 512 && entry->maxSeenV != 0) { diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index cc60129d4081..a9f84e5e2d6d 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -457,9 +457,12 @@ void TextureReplacer::ParseReduceHashRange(const std::string& key, const std::st reducehashranges_[reducerangeKey] = rhashvalue; } -u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV) { +u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, bool swizzled, GETextureFormat fmt, u16 maxSeenV) { _dbg_assert_msg_(enabled_, "Replacement not enabled"); + // TODO: Take swizzled into account, like in QuickTexHash(). + // Note: Currently, only the MLB games are known to need this. + if (!LookupHashRange(addr, w, h, &w, &h)) { // There wasn't any hash range, let's fall back to maxSeenV logic. if (h == 512 && maxSeenV < 512 && maxSeenV != 0) { diff --git a/GPU/Common/TextureReplacer.h b/GPU/Common/TextureReplacer.h index 132ba050e13c..a0f76a3bf977 100644 --- a/GPU/Common/TextureReplacer.h +++ b/GPU/Common/TextureReplacer.h @@ -101,7 +101,7 @@ class TextureReplacer { bool Enabled() const { return enabled_; } bool AllowVideo() const { return allowVideo_; } - u32 ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV); + u32 ComputeHash(u32 addr, int bufw, int w, int h, bool swizzled, GETextureFormat fmt, u16 maxSeenV); // Returns nullptr if not found. ReplacedTexture *FindReplacement(u64 cachekey, u32 hash, int w, int h);