From 75bcb9b10c8d84c1721003452050f3c3a307e619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 18 Mar 2023 11:45:26 +0100 Subject: [PATCH 1/4] Some renaming, flag updates --- GPU/Common/TextureCacheCommon.cpp | 21 ++++++++++++--------- GPU/Common/TextureCacheCommon.h | 6 +++--- GPU/D3D11/TextureCacheD3D11.cpp | 6 +++--- GPU/Directx9/TextureCacheDX9.cpp | 4 ++-- GPU/GLES/TextureCacheGLES.cpp | 6 +++--- GPU/Vulkan/TextureCacheVulkan.cpp | 12 ++++++------ 6 files changed, 29 insertions(+), 26 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 7c3b0d3b92..e7a4b87012 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -328,7 +328,7 @@ SamplerCacheKey TextureCacheCommon::GetFramebufferSamplingParams(u16 bufferWidth // Kill any mipmapping settings. key.mipEnable = false; key.mipFilt = false; - key.aniso = 0.0; + key.aniso = 0.0f; key.maxLevel = 0.0f; key.lodBias = 0.0f; @@ -858,7 +858,7 @@ void TextureCacheCommon::HandleTextureChange(TexCacheEntry *const entry, const c if (doDelete) { ForgetLastTexture(); ReleaseTexture(entry, true); - entry->status &= ~TexCacheEntry::STATUS_IS_SCALED; + entry->status &= ~(TexCacheEntry::STATUS_IS_SCALED_OR_REPLACED | TexCacheEntry::STATUS_TO_REPLACE); } // Mark as hashing, if marked as reliable. @@ -1537,7 +1537,7 @@ ReplacedTexture *TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int & if (replaced->State() == ReplacementState::ACTIVE) { replaced->GetSize(0, &w, &h); // Consider it already "scaled.". - entry->status |= TexCacheEntry::STATUS_IS_SCALED; + entry->status |= TexCacheEntry::STATUS_IS_SCALED_OR_REPLACED; } // Remove the flag, even if it was invalid. @@ -2073,6 +2073,8 @@ void TextureCacheCommon::ApplyTexture() { // This prevents temporary scaling perf hits on the first second of video. if (IsVideo(entry->addr)) { entry->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT | TexCacheEntry::STATUS_VIDEO; + } else { + entry->status &= ~TexCacheEntry::STATUS_VIDEO; } if (nextNeedsRehash_) { @@ -2782,7 +2784,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt plan.scaleFactor = 1; } else { entry->status &= ~TexCacheEntry::STATUS_TO_SCALE; - entry->status |= TexCacheEntry::STATUS_IS_SCALED; + entry->status |= TexCacheEntry::STATUS_IS_SCALED_OR_REPLACED; texelsScaledThisFrame_ += plan.w * plan.h; } } @@ -2819,17 +2821,18 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt } if (canReplace) { + // This is the "trigger point" for replacement. plan.replaced = FindReplacement(entry, plan.w, plan.h, plan.depth); - plan.replaceValid = plan.replaced ? plan.replaced->State() == ReplacementState::ACTIVE : false; + plan.doReplace = plan.replaced ? plan.replaced->State() == ReplacementState::ACTIVE : false; } else { plan.replaced = nullptr; - plan.replaceValid = false; + plan.doReplace = false; } // NOTE! Last chance to change scale factor here! plan.saveTexture = false; - if (plan.replaceValid) { + if (plan.doReplace) { // We're replacing, so we won't scale. plan.scaleFactor = 1; // We're ignoring how many levels were specified - instead we just load all available from the replacer. @@ -2839,7 +2842,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt // But, we still need to create the texture at a larger size. plan.replaced->GetSize(0, &plan.createW, &plan.createH); } else { - if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1 && canReplace) { + if (replacer_.Enabled() && !plan.doReplace && plan.depth == 1 && canReplace) { ReplacedTextureDecodeInfo replacedInfo; // TODO: Do we handle the race where a replacement becomes valid AFTER this but before we save? replacedInfo.cachekey = entry->CacheKey(); @@ -2889,7 +2892,7 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, s PROFILE_THIS_SCOPE("decodetex"); - if (plan.replaceValid) { + if (plan.doReplace) { plan.replaced->GetSize(srcLevel, &w, &h); double replaceStart = time_now_d(); plan.replaced->CopyLevelTo(srcLevel, data, dataSize, stride); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index b70e1ea1a4..67e78c6f47 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -133,7 +133,7 @@ struct TexCacheEntry { STATUS_CHANGE_FREQUENT = 0x10, // Changes often (less than 6 frames in between.) STATUS_CLUT_RECHECK = 0x20, // Another texture with same addr had a hashfail. STATUS_TO_SCALE = 0x80, // Pending texture scaling in a later frame. - STATUS_IS_SCALED = 0x100, // Has been scaled (can't be replaceImages'd.) + STATUS_IS_SCALED_OR_REPLACED = 0x100, // Has been scaled already (ignored for replacement checks). STATUS_TO_REPLACE = 0x0200, // Pending texture replacement. // When hashing large textures, we optimize 512x512 down to 512x272 by default, since this // is commonly the only part accessed. If access is made above 272, we hash the entire @@ -287,14 +287,14 @@ struct BuildTexturePlan { // The replacement for the texture. ReplacedTexture *replaced; // Need to only check once since it can change during the load! - bool replaceValid; + bool doReplace; bool saveTexture; // TODO: Expand32 should probably also be decided in PrepareBuildTexture. bool decodeToClut8; void GetMipSize(int level, int *w, int *h) const { - if (replaceValid) { + if (doReplace) { replaced->GetSize(level, w, h); return; } diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index d26d48a5fa..aed4f29b07 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -261,7 +261,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { } DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); - if (plan.replaceValid) { + if (plan.doReplace) { dstFmt = ToDXGIFormat(plan.replaced->Format()); } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = DXGI_FORMAT_B8G8R8A8_UNORM; @@ -298,7 +298,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { int stride = 0; int dataSize; - if (plan.replaceValid) { + if (plan.doReplace) { int blockSize = 0; if (Draw::DataFormatIsBlockCompressed(plan.replaced->Format(), &blockSize)) { stride = ((mipWidth + 3) & ~3) * blockSize / 4; // Number of blocks * 4 * Size of a block / 4 @@ -404,7 +404,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { entry->status &= ~TexCacheEntry::STATUS_NO_MIPS; } - if (plan.replaceValid) { + if (plan.doReplace) { entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus())); if (!Draw::DataFormatIsBlockCompressed(plan.replaced->Format(), nullptr)) { diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 3de9c870c1..ef823b3673 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -230,7 +230,7 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { } D3DFORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); - if (plan.replaceValid) { + if (plan.doReplace) { dstFmt = ToD3D9Format(plan.replaced->Format()); } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = D3DFMT_A8R8G8B8; @@ -316,7 +316,7 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { entry->status |= TexCacheEntry::STATUS_3D; } - if (plan.replaceValid) { + if (plan.doReplace) { entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus())); if (!Draw::DataFormatIsBlockCompressed(plan.replaced->Format(), nullptr)) { diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 9445d67421..437144e296 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -246,7 +246,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { int th = plan.createH; Draw::DataFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); - if (plan.replaceValid) { + if (plan.doReplace) { plan.replaced->GetSize(plan.baseLevelSrc, &tw, &th); dstFmt = plan.replaced->Format(); } else if (plan.scaleFactor > 1 || plan.saveTexture) { @@ -296,7 +296,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { bool bc = false; - if (plan.replaceValid) { + if (plan.doReplace) { int blockSize = 0; if (Draw::DataFormatIsBlockCompressed(plan.replaced->Format(), &blockSize)) { stride = mipWidth * 4; @@ -357,7 +357,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { render_->FinalizeTexture(entry->textureName, 1, false); } - if (plan.replaceValid) { + if (plan.doReplace) { entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus())); } } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index dcbb764a38..5fccb0cf37 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -435,7 +435,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { VkFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); if (plan.scaleFactor > 1) { - _dbg_assert_(!plan.replaceValid); + _dbg_assert_(!plan.doReplace); // Whether hardware or software scaling, this is the dest format. dstFmt = VULKAN_8888_FORMAT; } else if (plan.decodeToClut8) { @@ -445,7 +445,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { // We don't generate mipmaps for 512x512 textures because they're almost exclusively used for menu backgrounds // and similar, which don't really need it. // Also, if using replacements, check that we really can generate mips for this format - that's not possible for compressed ones. - if (g_Config.iTexFiltering == TEX_FILTER_AUTO_MAX_QUALITY && plan.w <= 256 && plan.h <= 256 && (!plan.replaceValid || plan.replaced->Format() == Draw::DataFormat::R8G8B8A8_UNORM)) { + if (g_Config.iTexFiltering == TEX_FILTER_AUTO_MAX_QUALITY && plan.w <= 256 && plan.h <= 256 && (!plan.doReplace || plan.replaced->Format() == Draw::DataFormat::R8G8B8A8_UNORM)) { // Boost the number of mipmaps. if (plan.maxPossibleLevels > plan.levelsToCreate) { // TODO: Should check against levelsToLoad, no? // We have to generate mips with a shader. This requires decoding to R8G8B8A8_UNORM format to avoid extra complications. @@ -458,7 +458,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { VkFormat actualFmt = plan.scaleFactor > 1 ? VULKAN_8888_FORMAT : dstFmt; bool bcFormat = false; int bcAlign = 0; - if (plan.replaceValid) { + if (plan.doReplace) { Draw::DataFormat fmt = plan.replaced->Format(); bcFormat = Draw::DataFormatIsBlockCompressed(fmt, &bcAlign); actualFmt = ToVulkanFormat(fmt); @@ -591,7 +591,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { }; bool dataScaled = true; - if (plan.replaceValid) { + if (plan.doReplace) { int rowLength = pixelStride; if (bcFormat) { // For block compressed formats, we just set the upload size to the data size.. @@ -601,7 +601,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { // Directly load the replaced image. data = pushBuffer->Allocate(uploadSize, pushAlignment, &texBuf, &bufferOffset); double replaceStart = time_now_d(); - if (!plan.replaced->CopyLevelTo(plan.baseLevelSrc + i, (uint8_t *)data, uploadSize, byteStride)) { // If plan.replaceValid, this shouldn't fail. + if (!plan.replaced->CopyLevelTo(plan.baseLevelSrc + i, (uint8_t *)data, uploadSize, byteStride)) { // If plan.doReplace, this shouldn't fail. WARN_LOG(G3D, "Failed to copy replaced texture level"); // TODO: Fill with some pattern? } @@ -681,7 +681,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { entry->status |= TexCacheEntry::STATUS_3D; } - if (plan.replaceValid) { + if (plan.doReplace) { entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus())); } } From 97c9b9e4f715eceea0734bdcc740b2c7d65867dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 18 Mar 2023 12:01:24 +0100 Subject: [PATCH 2/4] Break out PollReplacement from TextureCacheCommon::FindReplacement --- GPU/Common/ReplacedTexture.cpp | 2 +- GPU/Common/ReplacedTexture.h | 2 +- GPU/Common/TextureCacheCommon.cpp | 36 ++++++++++++++++++------------- GPU/Common/TextureCacheCommon.h | 8 ++++++- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/GPU/Common/ReplacedTexture.cpp b/GPU/Common/ReplacedTexture.cpp index 293d7c1a76..9461a08141 100644 --- a/GPU/Common/ReplacedTexture.cpp +++ b/GPU/Common/ReplacedTexture.cpp @@ -145,7 +145,7 @@ void ReplacedTexture::PurgeIfNotUsedSinceTime(double t) { } // This can only return true if ACTIVE or NOT_FOUND. -bool ReplacedTexture::IsReady(double budget) { +bool ReplacedTexture::Poll(double budget) { _assert_(vfs_ != nullptr); double now = time_now_d(); diff --git a/GPU/Common/ReplacedTexture.h b/GPU/Common/ReplacedTexture.h index eaeb5ed230..b4716c8b64 100644 --- a/GPU/Common/ReplacedTexture.h +++ b/GPU/Common/ReplacedTexture.h @@ -161,7 +161,7 @@ public: return (u8)alphaStatus_; } - bool IsReady(double budget); + bool Poll(double budget); bool CopyLevelTo(int level, uint8_t *out, size_t outDataSize, int rowPitch); std::string logId_; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index e7a4b87012..c2359efa67 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -531,7 +531,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { int w0 = gstate.getTextureWidth(0); int h0 = gstate.getTextureHeight(0); int d0 = 1; - ReplacedTexture *replaced = FindReplacement(entry, w0, h0, d0); + ReplacedTexture *replaced = FindReplacement(entry, &w0, &h0, &d0); if (replaced) { // This texture is pending a replacement load. // So check the replacer if it's reached a conclusion. @@ -1502,8 +1502,8 @@ u32 TextureCacheCommon::EstimateTexMemoryUsage(const TexCacheEntry *entry) { return pixelSize << (dimW + dimH); } -ReplacedTexture *TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int &w, int &h, int &d) { - if (d != 1) { +ReplacedTexture *TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int *w, int *h, int *d) { + if (*d != 1) { // We don't yet support replacing 3D textures. return nullptr; } @@ -1519,23 +1519,30 @@ ReplacedTexture *TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int & return nullptr; } - // Allow some delay to reduce pop-in. - constexpr double MAX_BUDGET_PER_TEX = 0.25 / 60.0; - double replaceStart = time_now_d(); u64 cachekey = replacer_.Enabled() ? entry->CacheKey() : 0; - ReplacedTexture *replaced = replacer_.FindReplacement(cachekey, entry->fullhash, w, h); + ReplacedTexture *replaced = replacer_.FindReplacement(cachekey, entry->fullhash, *w, *h); + replacementTimeThisFrame_ += time_now_d() - replaceStart; if (!replaced) { // TODO: Remove the flag here? // entry->status &= ~TexCacheEntry::STATUS_TO_REPLACE; - replacementTimeThisFrame_ += time_now_d() - replaceStart; return nullptr; } + entry->replacedTexture = replaced; // we know it's non-null here. + PollReplacement(entry, w, h, d); + return replaced; +} + +void TextureCacheCommon::PollReplacement(TexCacheEntry *entry, int *w, int *h, int *d) { + // Allow some delay to reduce pop-in. + constexpr double MAX_BUDGET_PER_TEX = 0.25 / 60.0; double budget = std::min(MAX_BUDGET_PER_TEX, replacementFrameBudget_ - replacementTimeThisFrame_); - if (replaced->IsReady(budget)) { - if (replaced->State() == ReplacementState::ACTIVE) { - replaced->GetSize(0, &w, &h); + + double replaceStart = time_now_d(); + if (entry->replacedTexture->Poll(budget)) { + if (entry->replacedTexture->State() == ReplacementState::ACTIVE) { + entry->replacedTexture->GetSize(0, w, h); // Consider it already "scaled.". entry->status |= TexCacheEntry::STATUS_IS_SCALED_OR_REPLACED; } @@ -1543,8 +1550,9 @@ ReplacedTexture *TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int & // Remove the flag, even if it was invalid. entry->status &= ~TexCacheEntry::STATUS_TO_REPLACE; } + replacementTimeThisFrame_ += time_now_d() - replaceStart; - switch (replaced->State()) { + switch (entry->replacedTexture->State()) { case ReplacementState::UNLOADED: case ReplacementState::PENDING: // Make sure we keep polling. @@ -1553,8 +1561,6 @@ ReplacedTexture *TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int & default: break; } - replacementTimeThisFrame_ += time_now_d() - replaceStart; - return replaced; } // This is only used in the GLES backend, where we don't point these to video memory. @@ -2822,7 +2828,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt if (canReplace) { // This is the "trigger point" for replacement. - plan.replaced = FindReplacement(entry, plan.w, plan.h, plan.depth); + plan.replaced = FindReplacement(entry, &plan.w, &plan.h, &plan.depth); plan.doReplace = plan.replaced ? plan.replaced->State() == ReplacementState::ACTIVE : false; } else { plan.replaced = nullptr; diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 67e78c6f47..ba97bb135b 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -105,12 +105,18 @@ struct TextureDefinition { GETextureFormat format; }; -// TODO: Shrink this struct. There is some fluff. +// Texture replacement state machine: +// Call FindReplacement during PrepareBuild. +// If replacedTexture gets set: If not found, -> STATUS_TO_REPLACE, otherwise directly -> STATUS_IS_SCALED. +// If replacedTexture is null, leave it at null. +// If replacedTexture is set in SetTexture and STATUS_IS_SCALED is not set, query status. If ready rebuild texture, which will set STATUS_IS_SCALED. // NOTE: These only handle textures loaded directly from PSP memory contents. // Framebuffer textures do not have entries, we bind the framebuffers directly. // At one point we might merge the concepts of framebuffers and textures, but that // moment is far away. + +// TODO: Shrink this struct. There is some fluff. struct TexCacheEntry { ~TexCacheEntry() { if (texturePtr || textureName || vkTex) From 9a228b1f23a76bfe865cc32acd54fb5661bae231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 18 Mar 2023 12:03:32 +0100 Subject: [PATCH 3/4] Avoid calling FindReplacement unnecessarily. --- GPU/Common/TextureCacheCommon.cpp | 6 +++--- GPU/Common/TextureCacheCommon.h | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index c2359efa67..c1572f0b93 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -531,11 +531,11 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { int w0 = gstate.getTextureWidth(0); int h0 = gstate.getTextureHeight(0); int d0 = 1; - ReplacedTexture *replaced = FindReplacement(entry, &w0, &h0, &d0); - if (replaced) { + if (entry->replacedTexture) { + PollReplacement(entry, &w0, &h0, &d0); // This texture is pending a replacement load. // So check the replacer if it's reached a conclusion. - switch (replaced->State()) { + switch (entry->replacedTexture->State()) { case ReplacementState::NOT_FOUND: // Didn't find a replacement, so stop looking. // DEBUG_LOG(G3D, "No replacement for texture %dx%d", w0, h0); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index ba97bb135b..6b431184ee 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -389,7 +389,8 @@ protected: CheckAlphaResult DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, TexDecodeFlags flags); void UnswizzleFromMem(u32 *dest, u32 destPitch, const u8 *texptr, u32 bufw, u32 height, u32 bytesPerPixel); CheckAlphaResult ReadIndexedTex(u8 *out, int outPitch, int level, const u8 *texptr, int bytesPerIndex, int bufw, bool reverseColors, bool expandTo32Bit); - ReplacedTexture *FindReplacement(TexCacheEntry *entry, int &w, int &h, int &d); + ReplacedTexture *FindReplacement(TexCacheEntry *entry, int *w, int *h, int *d); + void PollReplacement(TexCacheEntry *entry, int *w, int *h, int *d); // Return value is mapData normally, but could be another buffer allocated with AllocateAlignedMemory. void LoadTextureLevel(TexCacheEntry &entry, uint8_t *mapData, size_t dataSize, int mapRowPitch, BuildTexturePlan &plan, int srcLevel, Draw::DataFormat dstFmt, TexDecodeFlags texDecFlags); From 3302e6a76bc5b9326f544cac292a5d8b462aa2a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 18 Mar 2023 13:03:05 +0100 Subject: [PATCH 4/4] Lookup filtering overrides at texture load time instead of at runtime. --- GPU/Common/ReplacedTexture.h | 11 +++++++++++ GPU/Common/TextureCacheCommon.cpp | 4 ++-- GPU/Common/TextureReplacer.cpp | 3 +++ GPU/Common/TextureReplacer.h | 3 ++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/GPU/Common/ReplacedTexture.h b/GPU/Common/ReplacedTexture.h index b4716c8b64..4273ffead5 100644 --- a/GPU/Common/ReplacedTexture.h +++ b/GPU/Common/ReplacedTexture.h @@ -23,6 +23,7 @@ #include "Common/File/VFS/VFS.h" #include "Common/GPU/thin3d.h" #include "Common/Log.h" +#include "Core/ConfigValues.h" class TextureReplacer; class LimitedWaitable; @@ -75,6 +76,7 @@ struct ReplacementDesc { uint32_t hash; int w; int h; + TextureFiltering forceFiltering; std::string hashfiles; Path basePath; std::vector filenames; @@ -147,6 +149,15 @@ public: return sz; } + bool ForceFiltering(TextureFiltering *forceFiltering) const { + if (desc_.forceFiltering != (TextureFiltering)0) { + *forceFiltering = desc_.forceFiltering; + return true; + } else { + return false; + } + } + int NumLevels() const { _dbg_assert_(State() == ReplacementState::ACTIVE); return (int)levels_.size(); diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index c1572f0b93..2b5624f47a 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -252,8 +252,8 @@ SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, const TexCac // Filtering overrides from replacements or settings. TextureFiltering forceFiltering = TEX_FILTER_AUTO; - u64 cachekey = replacer_.Enabled() ? (entry ? entry->CacheKey() : 0) : 0; - if (!replacer_.Enabled() || entry == nullptr || !replacer_.FindFiltering(cachekey, entry->fullhash, &forceFiltering)) { + bool useReplacerFiltering = entry && replacer_.Enabled() && entry->replacedTexture && entry->replacedTexture->ForceFiltering(&forceFiltering); + if (!useReplacerFiltering) { switch (g_Config.iTexFiltering) { case TEX_FILTER_AUTO: // Follow what the game wants. We just do a single heuristic change to avoid bleeding of wacky color test colors diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index 1dbc82e26a..1628d5fd54 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -501,6 +501,9 @@ ReplacedTexture *TextureReplacer::FindReplacement(u64 cachekey, u32 hash, int w, return nullptr; } + desc.forceFiltering = (TextureFiltering)0; // invalid value + FindFiltering(cachekey, hash, &desc.forceFiltering); + if (!foundAlias) { // We'll just need to generate the names for each level. // By default, we look for png since that's also what's dumped. diff --git a/GPU/Common/TextureReplacer.h b/GPU/Common/TextureReplacer.h index bd6dd29807..645f26b47c 100644 --- a/GPU/Common/TextureReplacer.h +++ b/GPU/Common/TextureReplacer.h @@ -106,7 +106,6 @@ public: // Returns nullptr if not found. ReplacedTexture *FindReplacement(u64 cachekey, u32 hash, int w, int h); - bool FindFiltering(u64 cachekey, u32 hash, TextureFiltering *forceFiltering); // Check if a NotifyTextureDecoded for this texture is desired (used to avoid reads from write-combined memory.) bool WillSave(const ReplacedTextureDecodeInfo &replacedInfo); @@ -125,6 +124,8 @@ public: static std::string HashName(u64 cachekey, u32 hash, int level); protected: + bool FindFiltering(u64 cachekey, u32 hash, TextureFiltering *forceFiltering); + bool LoadIni(); bool LoadIniValues(IniFile &ini, bool isOverride = false); void ParseHashRange(const std::string &key, const std::string &value);