diff --git a/GPU/Common/ReplacedTexture.cpp b/GPU/Common/ReplacedTexture.cpp index 87b8c30fe6..b0823b1a2a 100644 --- a/GPU/Common/ReplacedTexture.cpp +++ b/GPU/Common/ReplacedTexture.cpp @@ -220,6 +220,12 @@ void ReplacedTexture::Prepare(VFSBackend *vfs) { VFSFileReference *fileRef = vfs_->GetFile(desc_.filenames[i].c_str()); if (!fileRef) { + if (i == 0) { + WARN_LOG(G3D, "Texture replacement file '%s' not found", desc_.filenames[i].c_str()); + // No file at all. Mark as NOT_FOUND. + SetState(ReplacementState::NOT_FOUND); + return; + } // If the file doesn't exist, let's just bail immediately here. // Mark as DONE, not error. result = LoadLevelResult::DONE; @@ -276,8 +282,7 @@ void ReplacedTexture::Prepare(VFSBackend *vfs) { SetState(ReplacementState::ACTIVE); - if (threadWaitable_) - threadWaitable_->Notify(); + // the caller calls threadWaitable->notify(). } // Returns true if Prepare should keep calling this to load more levels. diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 16fa289e9b..5ad9f98299 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2967,7 +2967,7 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, s replacedInfo.fmt = dstFmt; // NOTE: Reading the decoded texture here may be very slow, if we just wrote it to write-combined memory. - replacer_.NotifyTextureDecoded(replacedInfo, pixelData, decPitch, srcLevel, w, h, scaledW, scaledH); + replacer_.NotifyTextureDecoded(plan.replaced, replacedInfo, pixelData, decPitch, srcLevel, w, h, scaledW, scaledH); } } } diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index c8b73a2e43..41f12b966d 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -582,6 +582,7 @@ public: Path filename; Path saveFilename; + Path alreadyReplacedFilename; // if the texture was already replaced bool createSaveDirectory = false; Path saveDirectory; @@ -604,9 +605,15 @@ public: return; } - // And we always skip if the replace file already exists. - if (File::Exists(filename)) + // And we always skip if the auto-hash-named replace file already exists. + if (File::Exists(filename)) { return; + } + + // Also, if a named replacement already exists, don't save. + if (File::Exists(alreadyReplacedFilename)) { + return; + } if (createSaveDirectory && !File::Exists(saveDirectory)) { File::CreateFullPath(saveDirectory); @@ -643,8 +650,9 @@ bool TextureReplacer::WillSave(const ReplacedTextureDecodeInfo &replacedInfo) { return true; } -void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int origW, int origH, int scaledW, int scaledH) { +void TextureReplacer::NotifyTextureDecoded(ReplacedTexture *texture, const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int origW, int origH, int scaledW, int scaledH) { _assert_msg_(enabled_, "Replacement not enabled"); + if (!WillSave(replacedInfo)) { // Ignore. return; @@ -659,20 +667,24 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl } bool foundAlias = false, ignored = false; - std::string hashfile = LookupHashFile(cachekey, replacedInfo.hash, &foundAlias, &ignored); + std::string replacedLevelNames = LookupHashFile(cachekey, replacedInfo.hash, &foundAlias, &ignored); + std::vector names; + SplitString(replacedLevelNames, '|', names); + std::string replacedLevelName = names[std::min(level, (int)(names.size() - 1))]; - // If it's empty, it's an ignored hash, we intentionally don't save. - if (foundAlias || ignored) { - // If it exists, must've been decoded and saved as a new texture already. + if (ignored) { + // If it's empty, it's an ignored hash, we intentionally don't save. return; } + // We do push other textures on a thread for saving. + // Generate a new PNG filename, complete with level. - hashfile = HashName(cachekey, replacedInfo.hash, level) + ".png"; + // TODO: If we already have a filename but it's missing, should we use that filename or a generated one? + std::string hashfile = HashName(cachekey, replacedInfo.hash, level) + ".png"; ReplacementCacheKey replacementKey(cachekey, replacedInfo.hash); auto it = savedCache_.find(replacementKey); - bool skipIfExists = false; double now = time_now_d(); if (it != savedCache_.end()) { // We've already saved this texture. Ignore it. @@ -706,6 +718,7 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl task->filename = basePath_ / hashfile; task->saveFilename = newTextureDir_ / hashfile; + task->alreadyReplacedFilename = basePath_ / replacedLevelName; task->createSaveDirectory = false; // Create subfolder as needed. @@ -728,7 +741,7 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl task->h = h; task->pitch = pitch; task->replacedInfoHash = replacedInfo.hash; - task->skipIfExists = skipIfExists; + task->skipIfExists = true; // if already in the "new" folder, don't bother writing. g_threadManager.EnqueueTask(task); // We don't care about waiting for the task. It'll be fine. // Remember that we've saved this for next time. diff --git a/GPU/Common/TextureReplacer.h b/GPU/Common/TextureReplacer.h index fdcbfde1f4..0f22a5b3a5 100644 --- a/GPU/Common/TextureReplacer.h +++ b/GPU/Common/TextureReplacer.h @@ -111,7 +111,8 @@ public: bool WillSave(const ReplacedTextureDecodeInfo &replacedInfo); // Notify that a new texture was decoded. May already be upscaled, saves the data passed. - void NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int origW, int origH, int scaledW, int scaledH); + // If the replacer knows about this one already, texture will be passed in, otherwise nullptr. + void NotifyTextureDecoded(ReplacedTexture *texture, const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int origW, int origH, int scaledW, int scaledH); void Decimate(ReplacerDecimateMode mode); diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index f1ddb8889c..51cd770fe6 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -1636,7 +1636,7 @@ size_t GPUCommonHW::FormatGPUStatsCommon(char *buffer, size_t size) { "FBOs active: %d (evaluations: %d)\n" "Textures: %d, dec: %d, invalidated: %d, hashed: %d kB\n" "readbacks %d (%d non-block), uploads %d, depal %d\n" - "replacer: tracks %d textures, %d unique loaded\n" + "replacer: tracks %d references, %d unique textures\n" "Copies: depth %d, color %d, reint %d, blend %d, selftex %d\n" "GPU cycles executed: %d (%f per vertex)\n", gpuStats.msProcessingDisplayLists * 1000.0f, diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 9ead809799..be00c8efa6 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -648,7 +648,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { replacedInfo.isVideo = IsVideo(entry->addr); replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; replacedInfo.fmt = FromVulkanFormat(actualFmt); - replacer_.NotifyTextureDecoded(replacedInfo, data, byteStride, plan.baseLevelSrc + i, mipUnscaledWidth, mipUnscaledHeight, w, h); + replacer_.NotifyTextureDecoded(plan.replaced, replacedInfo, data, byteStride, plan.baseLevelSrc + i, mipUnscaledWidth, mipUnscaledHeight, w, h); } } }