From e0e3223c19c5ecb186b9ee50a19289642ad14dc7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Apr 2022 20:25:41 -0700 Subject: [PATCH] Replacement: Move IO checks to saving thread. This will spin up more threads that might not actually save, but it will remember this in savedCache. --- Core/TextureReplacer.cpp | 90 +++++++++++++++++++++++++--------------- Core/TextureReplacer.h | 3 ++ 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index aa709b12ee..b5c8f5c070 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -512,20 +512,50 @@ public: int h = 0; int pitch = 0; // bytes - Path path; + Path basePath; + std::string hashfile; u32 replacedInfoHash; + bool skipIfExists = false; + TextureSaveTask(SimpleBuf _data) : data(std::move(_data)) {} TaskType Type() const override { return TaskType::CPU_COMPUTE; } // Also I/O blocking but dominated by compute void Run() override { + const Path filename = basePath / hashfile; + const Path saveFilename = basePath / NEW_TEXTURE_DIR / hashfile; + + // Should we skip writing if the newly saved data already exists? + // We do this on the thread due to slow IO. + if (skipIfExists && File::Exists(saveFilename)) + return; + + // And we always skip if the replace file already exists. + if (hashfile.empty() || File::Exists(filename)) + return; + + // Create subfolder as needed. +#ifdef _WIN32 + size_t slash = hashfile.find_last_of("/\\"); +#else + size_t slash = hashfile.find_last_of("/"); +#endif + if (slash != hashfile.npos) { + // Create any directory structure as needed. + const Path saveDirectory = basePath / NEW_TEXTURE_DIR / hashfile.substr(0, slash); + if (!File::Exists(saveDirectory)) { + File::CreateFullPath(saveDirectory); + File::CreateEmptyFile(saveDirectory / ".nomedia"); + } + } + png_image png; memset(&png, 0, sizeof(png)); png.version = PNG_IMAGE_VERSION; png.format = PNG_FORMAT_RGBA; png.width = w; png.height = h; - bool success = WriteTextureToPNG(&png, path, 0, data.data(), pitch, nullptr); + bool success = WriteTextureToPNG(&png, saveFilename, 0, data.data(), pitch, nullptr); png_image_free(&png); if (png.warning_or_error >= 2) { ERROR_LOG(COMMON, "Saving screenshot to PNG produced errors."); @@ -535,58 +565,50 @@ public: } }; +bool TextureReplacer::WillSave(const ReplacedTextureDecodeInfo &replacedInfo) { + _assert_msg_(enabled_, "Replacement not enabled"); + if (!g_Config.bSaveNewTextures) + return false; + // Don't save the PPGe texture. + if (replacedInfo.addr > 0x05000000 && replacedInfo.addr < PSP_GetKernelMemoryEnd()) + return false; + if (replacedInfo.isVideo && !allowVideo_) + return false; + + return true; +} void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h) { _assert_msg_(enabled_, "Replacement not enabled"); - if (!g_Config.bSaveNewTextures) { + if (!WillSave(replacedInfo)) { // Ignore. return; } - if (replacedInfo.addr > 0x05000000 && replacedInfo.addr < PSP_GetKernelMemoryEnd()) { - // Don't save the PPGe texture. - return; - } - if (replacedInfo.isVideo && !allowVideo_) { - return; - } - u64 cachekey = replacedInfo.cachekey; - if (ignoreAddress_) { - cachekey = cachekey & 0xFFFFFFFFULL; - } if (ignoreMipmap_ && level > 0) { return; } + u64 cachekey = replacedInfo.cachekey; + if (ignoreAddress_) { + cachekey = cachekey & 0xFFFFFFFFULL; + } + std::string hashfile = LookupHashFile(cachekey, replacedInfo.hash, level); const Path filename = basePath_ / hashfile; - const Path saveFilename = basePath_ / NEW_TEXTURE_DIR / hashfile; // If it's empty, it's an ignored hash, we intentionally don't save. - if (hashfile.empty() || File::Exists(filename)) { + if (hashfile.empty()) { // If it exists, must've been decoded and saved as a new texture already. return; } ReplacementCacheKey replacementKey(cachekey, replacedInfo.hash); auto it = savedCache_.find(replacementKey); - if (it != savedCache_.end() && File::Exists(saveFilename)) { + bool skipIfExists = false; + if (it != savedCache_.end()) { // We've already saved this texture. Let's only save if it's bigger (e.g. scaled now.) if (it->second.w >= w && it->second.h >= h) { - return; - } - } - -#ifdef _WIN32 - size_t slash = hashfile.find_last_of("/\\"); -#else - size_t slash = hashfile.find_last_of("/"); -#endif - if (slash != hashfile.npos) { - // Create any directory structure as needed. - const Path saveDirectory = basePath_ / NEW_TEXTURE_DIR / hashfile.substr(0, slash); - if (!File::Exists(saveDirectory)) { - File::CreateFullPath(saveDirectory); - File::CreateEmptyFile(saveDirectory / ".nomedia"); + skipIfExists = true; } } @@ -651,8 +673,10 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl task->w = w; task->h = h; task->pitch = pitch; - task->path = saveFilename; + task->basePath = basePath_; + task->hashfile = hashfile; task->replacedInfoHash = replacedInfo.hash; + task->skipIfExists = skipIfExists; 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/Core/TextureReplacer.h b/Core/TextureReplacer.h index 1778c7f631..1ee8db6384 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -208,6 +208,9 @@ public: return none_; } + // Check if a NotifyTextureDecoded for this texture is desired (used to avoid reads from write-combined memory.) + 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 w, int h);