diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 3b9aeb99e4..517c799d65 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -76,6 +76,7 @@ bool TextureReplacer::LoadIni() { hash_ = ReplacedTextureHash::QUICK; aliases_.clear(); hashranges_.clear(); + filtering_.clear(); allowVideo_ = false; ignoreAddress_ = false; @@ -183,6 +184,14 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, bool isOverride) { } } + if (ini.HasSection("filtering")) { + auto filters = ini.GetOrCreateSection("filtering")->ToMap(); + // Format: hashname = nearest or linear + for (const auto &item : filters) { + ParseFiltering(item.first, item.second); + } + } + return true; } @@ -221,6 +230,23 @@ void TextureReplacer::ParseHashRange(const std::string &key, const std::string & hashranges_[rangeKey] = WidthHeightPair(toW, toH); } +void TextureReplacer::ParseFiltering(const std::string &key, const std::string &value) { + ReplacementCacheKey itemKey(0, 0); + if (sscanf(key.c_str(), "%16llx%8x", &itemKey.cachekey, &itemKey.hash) >= 1) { + if (!strcasecmp(value.c_str(), "nearest")) { + filtering_[itemKey] = TEX_FILTER_FORCE_NEAREST; + } else if (!strcasecmp(value.c_str(), "linear")) { + filtering_[itemKey] = TEX_FILTER_FORCE_LINEAR; + } else if (!strcasecmp(value.c_str(), "auto")) { + filtering_[itemKey] = TEX_FILTER_AUTO; + } else { + ERROR_LOG(G3D, "Unsupported syntax under [filtering]: %s", value.c_str()); + } + } else { + ERROR_LOG(G3D, "Unsupported syntax under [filtering]: %s", key.c_str()); + } +} + u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV) { _dbg_assert_msg_(enabled_, "Replacement not enabled"); @@ -503,45 +529,74 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl savedCache_[replacementKey] = saved; } -std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, int level) { - ReplacementAliasKey key(cachekey, hash, level); - auto alias = aliases_.find(key); - if (alias == aliases_.end()) { - // Also check for a few more aliases with zeroed portions: - // Only clut hash (very dangerous in theory, in practice not more than missing "just" data hash) - key.cachekey = cachekey & 0xFFFFFFFFULL; +template +static typename std::unordered_map::const_iterator LookupWildcard(const std::unordered_map &map, Key &key, u64 cachekey, u32 hash, bool ignoreAddress) { + auto alias = map.find(key); + if (alias != map.end()) + return alias; + + // Also check for a few more aliases with zeroed portions: + // Only clut hash (very dangerous in theory, in practice not more than missing "just" data hash) + key.cachekey = cachekey & 0xFFFFFFFFULL; + key.hash = 0; + alias = map.find(key); + if (alias != map.end()) + return alias; + + if (!ignoreAddress) { + // No data hash. + key.cachekey = cachekey; key.hash = 0; - alias = aliases_.find(key); - - if (!ignoreAddress_ && alias == aliases_.end()) { - // No data hash. - key.cachekey = cachekey; - key.hash = 0; - alias = aliases_.find(key); - } - - if (alias == aliases_.end()) { - // No address. - key.cachekey = cachekey & 0xFFFFFFFFULL; - key.hash = hash; - alias = aliases_.find(key); - } - - if (!ignoreAddress_ && alias == aliases_.end()) { - // Address, but not clut hash (in case of garbage clut data.) - key.cachekey = cachekey & ~0xFFFFFFFFULL; - key.hash = hash; - alias = aliases_.find(key); - } - - if (alias == aliases_.end()) { - // Anything with this data hash (a little dangerous.) - key.cachekey = 0; - key.hash = hash; - alias = aliases_.find(key); - } + alias = map.find(key); + if (alias != map.end()) + return alias; } + // No address. + key.cachekey = cachekey & 0xFFFFFFFFULL; + key.hash = hash; + alias = map.find(key); + if (alias != map.end()) + return alias; + + if (!ignoreAddress) { + // Address, but not clut hash (in case of garbage clut data.) + key.cachekey = cachekey & ~0xFFFFFFFFULL; + key.hash = hash; + alias = map.find(key); + if (alias != map.end()) + return alias; + } + + // Anything with this data hash (a little dangerous.) + key.cachekey = 0; + key.hash = hash; + return map.find(key); +} + +bool TextureReplacer::FindFiltering(u64 cachekey, u32 hash, TextureFiltering *forceFiltering) { + if (!Enabled() || !g_Config.bReplaceTextures) { + return false; + } + + ReplacementCacheKey replacementKey(cachekey, hash); + auto filter = LookupWildcard(filtering_, replacementKey, cachekey, hash, ignoreAddress_); + if (filter == filtering_.end()) { + // Allow a global wildcard. + replacementKey.cachekey = 0; + replacementKey.hash = 0; + filter = filtering_.find(replacementKey); + } + if (filter != filtering_.end()) { + *forceFiltering = filter->second; + return true; + } + return false; +} + +std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, int level) { + ReplacementAliasKey key(cachekey, hash, level); + auto alias = LookupWildcard(aliases_, key, cachekey, hash, ignoreAddress_); if (alias != aliases_.end()) { // Note: this will be blank if explicitly ignored. return alias->second; @@ -649,13 +704,15 @@ bool TextureReplacer::GenerateIni(const std::string &gameID, std::string *genera fs << "[games]\n"; fs << "# Used to make it easier to install, and override settings for other regions.\n"; fs << "# Files still have to be copied to each TEXTURES folder."; - fs << gameID << " = textures.ini\n"; + fs << gameID << " = " << INI_FILENAME << "\n"; fs << "\n"; + fs << "[hashes]\n"; fs << "# Use / for folders not \\, avoid special characters, and stick to lowercase.\n"; fs << "# See wiki for more info.\n"; - fs << "[hashes]\n"; fs << "\n"; fs << "[hashranges]\n"; + fs << "\n"; + fs << "[filtering]\n"; fs.close(); } return File::Exists(texturesDirectory + INI_FILENAME); diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index d90f373c4e..46d67037ed 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -23,6 +23,7 @@ #include #include "Common/Common.h" #include "Common/MemoryUtil.h" +#include "GPU/Common/TextureDecoder.h" #include "GPU/ge_constants.h" class IniFile; @@ -183,6 +184,7 @@ public: u32 ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV); ReplacedTexture &FindReplacement(u64 cachekey, u32 hash, int w, int h); + bool FindFiltering(u64 cachekey, u32 hash, TextureFiltering *forceFiltering); void NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h); @@ -192,6 +194,7 @@ protected: bool LoadIni(); bool LoadIniValues(IniFile &ini, bool isOverride = false); void ParseHashRange(const std::string &key, const std::string &value); + void ParseFiltering(const std::string &key, const std::string &value); bool LookupHashRange(u32 addr, int &w, int &h); std::string LookupHashFile(u64 cachekey, u32 hash, int level); std::string HashName(u64 cachekey, u32 hash, int level); @@ -209,6 +212,7 @@ protected: typedef std::pair WidthHeightPair; std::unordered_map hashranges_; std::unordered_map aliases_; + std::unordered_map filtering_; ReplacedTexture none_; std::unordered_map cache_; diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 809921d790..79eac7be9f 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -28,12 +28,6 @@ #include "GPU/Common/GPUDebugInterface.h" #include "GPU/Common/TextureDecoder.h" -enum TextureFiltering { - TEX_FILTER_AUTO = 1, - TEX_FILTER_FORCE_NEAREST = 2, - TEX_FILTER_FORCE_LINEAR = 3, -}; - enum FramebufferNotification { NOTIFY_FB_CREATED, NOTIFY_FB_UPDATED, @@ -53,6 +47,7 @@ enum FramebufferNotificationChannel { #define TEXCACHE_MAX_TEXELS_SCALED (256*256) // Per frame struct VirtualFramebuffer; +class TextureReplacer; namespace Draw { class DrawContext; diff --git a/GPU/Common/TextureDecoder.h b/GPU/Common/TextureDecoder.h index 02e7939cdc..b00958a296 100644 --- a/GPU/Common/TextureDecoder.h +++ b/GPU/Common/TextureDecoder.h @@ -31,6 +31,12 @@ enum CheckAlphaResult { #include "GPU/Common/TextureDecoderNEON.h" #include "GPU/GPUState.h" +enum TextureFiltering { + TEX_FILTER_AUTO = 1, + TEX_FILTER_FORCE_NEAREST = 2, + TEX_FILTER_FORCE_LINEAR = 3, +}; + void SetupTextureDecoder(); // Pitch must be aligned to 16 bits (as is the case on a PSP) diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 1f3996398c..dfc0eea420 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -27,7 +27,6 @@ #include "Core/Reporting.h" #include "GPU/GPUState.h" -#include "GPU/Common/TextureCacheCommon.h" #include "GPU/Common/TextureDecoder.h" #include "GPU/Software/SoftGpu.h" #include "GPU/Software/Rasterizer.h" diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 567635ffe5..a5711a94eb 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -28,7 +28,7 @@ #include "UI/OnScreenDisplay.h" #include "GPU/Common/PostShader.h" #include "GPU/Common/FramebufferManagerCommon.h" -#include "GPU/Common/TextureCacheCommon.h" +#include "GPU/Common/TextureDecoder.h" #include "GPU/Common/TextureScalerCommon.h" #include "Core/Config.h"