Replacement: Save textures even if already replaced, if the png is missing.

Fixes #17182

Not exactly sure what behavior we really want, but I think this one is
OK, and at least more similar to the old one. Now we save
already-replaced textures if the named replacement texture is missing, and there
isn't already a hash-named one in new or the "root".
This commit is contained in:
Henrik Rydgård 2023-03-27 15:43:18 +02:00
parent 668a6d63cb
commit 4e41233bb7
6 changed files with 35 additions and 16 deletions

View File

@ -220,6 +220,12 @@ void ReplacedTexture::Prepare(VFSBackend *vfs) {
VFSFileReference *fileRef = vfs_->GetFile(desc_.filenames[i].c_str()); VFSFileReference *fileRef = vfs_->GetFile(desc_.filenames[i].c_str());
if (!fileRef) { 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. // If the file doesn't exist, let's just bail immediately here.
// Mark as DONE, not error. // Mark as DONE, not error.
result = LoadLevelResult::DONE; result = LoadLevelResult::DONE;
@ -276,8 +282,7 @@ void ReplacedTexture::Prepare(VFSBackend *vfs) {
SetState(ReplacementState::ACTIVE); SetState(ReplacementState::ACTIVE);
if (threadWaitable_) // the caller calls threadWaitable->notify().
threadWaitable_->Notify();
} }
// Returns true if Prepare should keep calling this to load more levels. // Returns true if Prepare should keep calling this to load more levels.

View File

@ -2967,7 +2967,7 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, s
replacedInfo.fmt = dstFmt; replacedInfo.fmt = dstFmt;
// NOTE: Reading the decoded texture here may be very slow, if we just wrote it to write-combined memory. // 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);
} }
} }
} }

View File

@ -582,6 +582,7 @@ public:
Path filename; Path filename;
Path saveFilename; Path saveFilename;
Path alreadyReplacedFilename; // if the texture was already replaced
bool createSaveDirectory = false; bool createSaveDirectory = false;
Path saveDirectory; Path saveDirectory;
@ -604,9 +605,15 @@ public:
return; return;
} }
// And we always skip if the replace file already exists. // And we always skip if the auto-hash-named replace file already exists.
if (File::Exists(filename)) if (File::Exists(filename)) {
return; return;
}
// Also, if a named replacement already exists, don't save.
if (File::Exists(alreadyReplacedFilename)) {
return;
}
if (createSaveDirectory && !File::Exists(saveDirectory)) { if (createSaveDirectory && !File::Exists(saveDirectory)) {
File::CreateFullPath(saveDirectory); File::CreateFullPath(saveDirectory);
@ -643,8 +650,9 @@ bool TextureReplacer::WillSave(const ReplacedTextureDecodeInfo &replacedInfo) {
return true; 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"); _assert_msg_(enabled_, "Replacement not enabled");
if (!WillSave(replacedInfo)) { if (!WillSave(replacedInfo)) {
// Ignore. // Ignore.
return; return;
@ -659,20 +667,24 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
} }
bool foundAlias = false, ignored = false; 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<std::string> 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 (ignored) {
if (foundAlias || ignored) { // If it's empty, it's an ignored hash, we intentionally don't save.
// If it exists, must've been decoded and saved as a new texture already.
return; return;
} }
// We do push other textures on a thread for saving.
// Generate a new PNG filename, complete with level. // 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); ReplacementCacheKey replacementKey(cachekey, replacedInfo.hash);
auto it = savedCache_.find(replacementKey); auto it = savedCache_.find(replacementKey);
bool skipIfExists = false;
double now = time_now_d(); double now = time_now_d();
if (it != savedCache_.end()) { if (it != savedCache_.end()) {
// We've already saved this texture. Ignore it. // We've already saved this texture. Ignore it.
@ -706,6 +718,7 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
task->filename = basePath_ / hashfile; task->filename = basePath_ / hashfile;
task->saveFilename = newTextureDir_ / hashfile; task->saveFilename = newTextureDir_ / hashfile;
task->alreadyReplacedFilename = basePath_ / replacedLevelName;
task->createSaveDirectory = false; task->createSaveDirectory = false;
// Create subfolder as needed. // Create subfolder as needed.
@ -728,7 +741,7 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
task->h = h; task->h = h;
task->pitch = pitch; task->pitch = pitch;
task->replacedInfoHash = replacedInfo.hash; 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. 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. // Remember that we've saved this for next time.

View File

@ -111,7 +111,8 @@ public:
bool WillSave(const ReplacedTextureDecodeInfo &replacedInfo); bool WillSave(const ReplacedTextureDecodeInfo &replacedInfo);
// Notify that a new texture was decoded. May already be upscaled, saves the data passed. // 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); void Decimate(ReplacerDecimateMode mode);

View File

@ -1636,7 +1636,7 @@ size_t GPUCommonHW::FormatGPUStatsCommon(char *buffer, size_t size) {
"FBOs active: %d (evaluations: %d)\n" "FBOs active: %d (evaluations: %d)\n"
"Textures: %d, dec: %d, invalidated: %d, hashed: %d kB\n" "Textures: %d, dec: %d, invalidated: %d, hashed: %d kB\n"
"readbacks %d (%d non-block), uploads %d, depal %d\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" "Copies: depth %d, color %d, reint %d, blend %d, selftex %d\n"
"GPU cycles executed: %d (%f per vertex)\n", "GPU cycles executed: %d (%f per vertex)\n",
gpuStats.msProcessingDisplayLists * 1000.0f, gpuStats.msProcessingDisplayLists * 1000.0f,

View File

@ -648,7 +648,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
replacedInfo.isVideo = IsVideo(entry->addr); replacedInfo.isVideo = IsVideo(entry->addr);
replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0;
replacedInfo.fmt = FromVulkanFormat(actualFmt); 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);
} }
} }
} }