Replacement: Read in texture filtering overrides.

If you're replacing, you can know more information about linear safety for
tests.
This commit is contained in:
Unknown W. Brackets 2021-02-27 17:16:16 -08:00
parent 5d4d8ab418
commit fb3ad1df4b
6 changed files with 107 additions and 46 deletions

View File

@ -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 <typename Key, typename Value>
static typename std::unordered_map<Key, Value>::const_iterator LookupWildcard(const std::unordered_map<Key, Value> &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);

View File

@ -23,6 +23,7 @@
#include <vector>
#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<int, int> WidthHeightPair;
std::unordered_map<u64, WidthHeightPair> hashranges_;
std::unordered_map<ReplacementAliasKey, std::string> aliases_;
std::unordered_map<ReplacementCacheKey, TextureFiltering> filtering_;
ReplacedTexture none_;
std::unordered_map<ReplacementCacheKey, ReplacedTexture> cache_;

View File

@ -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;

View File

@ -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)

View File

@ -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"

View File

@ -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"