mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Fix saving of textures
This commit is contained in:
parent
03df5b7831
commit
092bbf5eaa
4 changed files with 34 additions and 26 deletions
|
@ -244,7 +244,7 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, bool isOverride) {
|
||||||
if (ini.HasSection("hashes")) {
|
if (ini.HasSection("hashes")) {
|
||||||
auto hashes = ini.GetOrCreateSection("hashes")->ToMap();
|
auto hashes = ini.GetOrCreateSection("hashes")->ToMap();
|
||||||
// Format: hashname = filename.png
|
// Format: hashname = filename.png
|
||||||
bool checkFilenames = g_Config.bSaveNewTextures && !g_Config.bIgnoreTextureFilenames;
|
bool checkFilenames = g_Config.bSaveNewTextures && !g_Config.bIgnoreTextureFilenames && !vfsIsZip_;
|
||||||
|
|
||||||
std::map<ReplacementCacheKey, std::map<int, std::string>> filenameMap;
|
std::map<ReplacementCacheKey, std::map<int, std::string>> filenameMap;
|
||||||
|
|
||||||
|
@ -257,7 +257,7 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, bool isOverride) {
|
||||||
#if PPSSPP_PLATFORM(WINDOWS)
|
#if PPSSPP_PLATFORM(WINDOWS)
|
||||||
// Uppercase probably means the filenames don't match.
|
// Uppercase probably means the filenames don't match.
|
||||||
// Avoiding an actual check of the filenames to avoid performance impact.
|
// Avoiding an actual check of the filenames to avoid performance impact.
|
||||||
filenameWarning = filenameWarning || item.second.find_first_of("\\ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos;
|
filenameWarning = filenameWarning || item.second.find_first_of("\\ABCDEFGHIJKLMNOPQRSTUVWXYZ:<>|?*") != std::string::npos;
|
||||||
#else
|
#else
|
||||||
filenameWarning = filenameWarning || item.second.find_first_of("\\:<>|?*") != std::string::npos;
|
filenameWarning = filenameWarning || item.second.find_first_of("\\:<>|?*") != std::string::npos;
|
||||||
#endif
|
#endif
|
||||||
|
@ -510,9 +510,10 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *texture, u64 cachekey
|
||||||
}
|
}
|
||||||
|
|
||||||
bool foundReplacement = false;
|
bool foundReplacement = false;
|
||||||
const std::string hashfiles = LookupHashFile(cachekey, hash, &foundReplacement);
|
bool ignored = false;
|
||||||
|
const std::string hashfiles = LookupHashFile(cachekey, hash, &foundReplacement, &ignored);
|
||||||
|
|
||||||
if (!foundReplacement) {
|
if (!foundReplacement || ignored) {
|
||||||
// nothing to do?
|
// nothing to do?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -641,9 +642,10 @@ static bool WriteTextureToPNG(png_imagep image, const Path &filename, int conver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextureSaveTask : public Task {
|
// We save textures on threadpool tasks since it's a fire-and-forget task, and both I/O and png compression
|
||||||
|
// can be pretty slow.
|
||||||
|
class SaveTextureTask : public Task {
|
||||||
public:
|
public:
|
||||||
// Could probably just use a vector.
|
|
||||||
std::vector<u8> rgbaData;
|
std::vector<u8> rgbaData;
|
||||||
|
|
||||||
int w = 0;
|
int w = 0;
|
||||||
|
@ -652,13 +654,13 @@ public:
|
||||||
|
|
||||||
Path basePath;
|
Path basePath;
|
||||||
std::string hashfile;
|
std::string hashfile;
|
||||||
u32 replacedInfoHash;
|
u32 replacedInfoHash = 0;
|
||||||
|
|
||||||
bool skipIfExists = false;
|
bool skipIfExists = false;
|
||||||
|
|
||||||
TextureSaveTask(std::vector<u8> _rgbaData) : rgbaData(std::move(_rgbaData)) {}
|
SaveTextureTask(std::vector<u8> &&_rgbaData) : rgbaData(std::move(_rgbaData)) {}
|
||||||
|
|
||||||
// This must be IO blocking because of Android storage, despite being CPU heavy.
|
// This must be set to I/O blocking because of Android storage (so we attach the thread to JNI), while being CPU heavy too.
|
||||||
TaskType Type() const override { return TaskType::IO_BLOCKING; }
|
TaskType Type() const override { return TaskType::IO_BLOCKING; }
|
||||||
|
|
||||||
TaskPriority Priority() const override {
|
TaskPriority Priority() const override {
|
||||||
|
@ -670,9 +672,9 @@ public:
|
||||||
const Path saveFilename = basePath / NEW_TEXTURE_DIR / hashfile;
|
const Path saveFilename = basePath / NEW_TEXTURE_DIR / hashfile;
|
||||||
|
|
||||||
// Should we skip writing if the newly saved data already exists?
|
// 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)) {
|
||||||
if (skipIfExists && File::Exists(saveFilename))
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// And we always skip if the replace file already exists.
|
// And we always skip if the replace file already exists.
|
||||||
if (File::Exists(filename))
|
if (File::Exists(filename))
|
||||||
|
@ -701,9 +703,11 @@ public:
|
||||||
bool success = WriteTextureToPNG(&png, saveFilename, 0, rgbaData.data(), pitch, nullptr);
|
bool success = WriteTextureToPNG(&png, saveFilename, 0, rgbaData.data(), pitch, nullptr);
|
||||||
png_image_free(&png);
|
png_image_free(&png);
|
||||||
if (png.warning_or_error >= 2) {
|
if (png.warning_or_error >= 2) {
|
||||||
ERROR_LOG(COMMON, "Saving screenshot to PNG produced errors.");
|
ERROR_LOG(G3D, "Saving screenshot to PNG produced errors.");
|
||||||
} else if (success) {
|
} else if (success) {
|
||||||
NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d in '%s'", replacedInfoHash, w, h, saveFilename.ToVisualString().c_str());
|
NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d in '%s'", replacedInfoHash, w, h, saveFilename.ToVisualString().c_str());
|
||||||
|
} else {
|
||||||
|
ERROR_LOG(G3D, "Failed to write '%s'", saveFilename.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -736,29 +740,32 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
|
||||||
cachekey = cachekey & 0xFFFFFFFFULL;
|
cachekey = cachekey & 0xFFFFFFFFULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool found = false;
|
bool found = false, ignored = false;
|
||||||
std::string hashfile = LookupHashFile(cachekey, replacedInfo.hash, &found);
|
std::string hashfile = LookupHashFile(cachekey, replacedInfo.hash, &found, &ignored);
|
||||||
const Path filename = basePath_ / hashfile;
|
|
||||||
|
|
||||||
// If it's empty, it's an ignored hash, we intentionally don't save.
|
// If it's empty, it's an ignored hash, we intentionally don't save.
|
||||||
if (hashfile.empty()) {
|
if (found) {
|
||||||
// If it exists, must've been decoded and saved as a new texture already.
|
// If it exists, must've been decoded and saved as a new texture already.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate a new filename.
|
||||||
|
hashfile = HashName(cachekey, replacedInfo.hash, level) + ".png";
|
||||||
|
|
||||||
|
const Path filename = basePath_ / hashfile;
|
||||||
|
|
||||||
ReplacementCacheKey replacementKey(cachekey, replacedInfo.hash);
|
ReplacementCacheKey replacementKey(cachekey, replacedInfo.hash);
|
||||||
auto it = savedCache_.find(replacementKey);
|
auto it = savedCache_.find(replacementKey);
|
||||||
bool skipIfExists = false;
|
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. Let's only save if it's bigger (e.g. scaled now.)
|
// We've already saved this texture. Let's only save if it's bigger (e.g. scaled now.)
|
||||||
// TODO: Isn't this check backwards?
|
// This check isn't backwards, it's just to check if we should *skip* saving, a bit confusing.
|
||||||
if (it->second.levelW[level] >= w && it->second.levelH[level] >= h) {
|
if (it->second.levelW[level] >= w && it->second.levelH[level] >= h) {
|
||||||
// If it's been more than 5 seconds, we'll check again. Maybe they deleted.
|
// If it's been more than 5 seconds, we'll check again. Maybe they deleted.
|
||||||
double age = now - it->second.lastTimeSaved;
|
double age = now - it->second.lastTimeSaved;
|
||||||
if (age < 5.0)
|
if (age < 5.0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
skipIfExists = true;
|
skipIfExists = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -781,8 +788,7 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
|
||||||
}
|
}
|
||||||
pitch = w * 4;
|
pitch = w * 4;
|
||||||
|
|
||||||
TextureSaveTask *task = new TextureSaveTask(std::move(saveBuf));
|
SaveTextureTask *task = new SaveTextureTask(std::move(saveBuf));
|
||||||
// Should probably do a proper move constructor but this'll work.
|
|
||||||
task->w = w;
|
task->w = w;
|
||||||
task->h = h;
|
task->h = h;
|
||||||
task->pitch = pitch;
|
task->pitch = pitch;
|
||||||
|
@ -898,15 +904,17 @@ bool TextureReplacer::FindFiltering(u64 cachekey, u32 hash, TextureFiltering *fo
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, bool *foundReplacement) {
|
std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, bool *foundReplacement, bool *ignored) {
|
||||||
ReplacementCacheKey key(cachekey, hash);
|
ReplacementCacheKey key(cachekey, hash);
|
||||||
auto alias = LookupWildcard(aliases_, key, cachekey, hash, ignoreAddress_);
|
auto alias = LookupWildcard(aliases_, key, cachekey, hash, ignoreAddress_);
|
||||||
if (alias != aliases_.end()) {
|
if (alias != aliases_.end()) {
|
||||||
// Note: this will be blank if explicitly ignored.
|
// Note: this will be blank if explicitly ignored.
|
||||||
*foundReplacement = alias->second.size() > 0;
|
*foundReplacement = true;
|
||||||
|
*ignored = alias->second.empty();
|
||||||
return alias->second;
|
return alias->second;
|
||||||
}
|
}
|
||||||
*foundReplacement = false;
|
*foundReplacement = false;
|
||||||
|
*ignored = false;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ struct SavedTextureCacheData {
|
||||||
int levelW[8]{};
|
int levelW[8]{};
|
||||||
int levelH[8]{};
|
int levelH[8]{};
|
||||||
bool levelSaved[8]{};
|
bool levelSaved[8]{};
|
||||||
double lastTimeSaved;
|
double lastTimeSaved = 0.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ReplacedLevelsCache {
|
struct ReplacedLevelsCache {
|
||||||
|
@ -230,7 +230,7 @@ protected:
|
||||||
void ParseReduceHashRange(const std::string& key, const std::string& value);
|
void ParseReduceHashRange(const std::string& key, const std::string& value);
|
||||||
bool LookupHashRange(u32 addr, int &w, int &h);
|
bool LookupHashRange(u32 addr, int &w, int &h);
|
||||||
float LookupReduceHashRange(int& w, int& h);
|
float LookupReduceHashRange(int& w, int& h);
|
||||||
std::string LookupHashFile(u64 cachekey, u32 hash, bool *foundReplacement);
|
std::string LookupHashFile(u64 cachekey, u32 hash, bool *foundReplacement, bool *ignored);
|
||||||
std::string HashName(u64 cachekey, u32 hash, int level);
|
std::string HashName(u64 cachekey, u32 hash, int level);
|
||||||
void PopulateReplacement(ReplacedTexture *result, u64 cachekey, u32 hash, int w, int h);
|
void PopulateReplacement(ReplacedTexture *result, u64 cachekey, u32 hash, int w, int h);
|
||||||
bool PopulateLevel(ReplacedTextureLevel &level, bool ignoreError);
|
bool PopulateLevel(ReplacedTextureLevel &level, bool ignoreError);
|
||||||
|
|
|
@ -2901,7 +2901,7 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (replacer_.Enabled() && plan.replaced->IsInvalid()) {
|
if (plan.saveTexture && !lowMemoryMode_) {
|
||||||
ReplacedTextureDecodeInfo replacedInfo;
|
ReplacedTextureDecodeInfo replacedInfo;
|
||||||
replacedInfo.cachekey = entry.CacheKey();
|
replacedInfo.cachekey = entry.CacheKey();
|
||||||
replacedInfo.hash = entry.fullhash;
|
replacedInfo.hash = entry.fullhash;
|
||||||
|
|
|
@ -614,7 +614,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
||||||
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||||
}
|
}
|
||||||
// Format might be wrong in lowMemoryMode_, so don't save.
|
// Format might be wrong in lowMemoryMode_, so don't save.
|
||||||
if (replacer_.Enabled() && plan.replaced->IsInvalid() && !lowMemoryMode_) {
|
if (plan.saveTexture && !lowMemoryMode_) {
|
||||||
// When hardware texture scaling is enabled, this saves the original.
|
// When hardware texture scaling is enabled, this saves the original.
|
||||||
int w = dataScaled ? mipWidth : mipUnscaledWidth;
|
int w = dataScaled ? mipWidth : mipUnscaledWidth;
|
||||||
int h = dataScaled ? mipHeight : mipUnscaledHeight;
|
int h = dataScaled ? mipHeight : mipUnscaledHeight;
|
||||||
|
|
Loading…
Add table
Reference in a new issue