// Copyright (c) 2016- PPSSPP Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0 or later versions. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #pragma once #include #include #include #include #include "Common/Common.h" #include "Common/MemoryUtil.h" #include "Common/File/Path.h" #include "GPU/Common/TextureDecoder.h" #include "GPU/ge_constants.h" class IniFile; class TextureCacheCommon; class TextureReplacer; class ReplacedTextureTask; class LimitedWaitable; enum class ReplacedTextureFormat { F_5650, F_5551, F_4444, F_8888, F_0565_ABGR, F_1555_ABGR, F_4444_ABGR, F_8888_BGRA, }; // These must match the constants in TextureCacheCommon. enum class ReplacedTextureAlpha { UNKNOWN = 0x04, FULL = 0x00, }; // For forward comatibility, we specify the hash. enum class ReplacedTextureHash { QUICK, XXH32, XXH64, }; struct ReplacedTextureLevel { int w; int h; ReplacedTextureFormat fmt; Path file; }; struct ReplacementCacheKey { u64 cachekey; u32 hash; ReplacementCacheKey(u64 c, u32 h) : cachekey(c), hash(h) { } bool operator ==(const ReplacementCacheKey &k) const { return k.cachekey == cachekey && k.hash == hash; } bool operator <(const ReplacementCacheKey &k) const { if (k.cachekey == cachekey) { return k.hash < hash; } return k.cachekey < cachekey; } }; struct ReplacementAliasKey { u64 cachekey; union { u64 hashAndLevel; struct { u32 level; u32 hash; }; }; ReplacementAliasKey(u64 c, u32 h, u32 l) : cachekey(c), level(l), hash(h) { } bool operator ==(const ReplacementAliasKey &k) const { return k.cachekey == cachekey && k.hashAndLevel == hashAndLevel; } bool operator <(const ReplacementAliasKey &k) const { if (k.cachekey == cachekey) { return k.hashAndLevel < hashAndLevel; } return k.cachekey < cachekey; } }; namespace std { template <> struct hash { size_t operator()(const ReplacementCacheKey &k) const { return std::hash()(k.cachekey ^ ((u64)k.hash << 32)); } }; template <> struct hash { size_t operator()(const ReplacementAliasKey &k) const { return std::hash()(k.cachekey ^ k.hashAndLevel); } }; } struct ReplacedTexture { ~ReplacedTexture(); inline bool Valid() { return !levels_.empty(); } bool GetSize(int level, int &w, int &h) { if ((size_t)level < levels_.size()) { w = levels_[level].w; h = levels_[level].h; return true; } return false; } int MaxLevel() { return (int)levels_.size() - 1; } ReplacedTextureFormat Format(int level) { if ((size_t)level < levels_.size()) { return levels_[level].fmt; } return ReplacedTextureFormat::F_8888; } u8 AlphaStatus() { return (u8)alphaStatus_; } bool IsReady(double budget); bool Load(int level, void *out, int rowPitch); protected: void Prepare(); void PrepareData(int level); void PurgeIfOlder(double t); std::vector levels_; std::vector> levelData_; ReplacedTextureAlpha alphaStatus_; double lastUsed_ = 0.0; LimitedWaitable *threadWaitable_ = nullptr; bool cancelPrepare_ = false; friend TextureReplacer; friend ReplacedTextureTask; }; struct ReplacedTextureDecodeInfo { u64 cachekey; u32 hash; u32 addr; bool isVideo; bool isFinal; int scaleFactor; ReplacedTextureFormat fmt; }; class TextureReplacer { public: TextureReplacer(); ~TextureReplacer(); void Init(); void NotifyConfigChanged(); inline bool Enabled() { return enabled_; } 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); ReplacedTexture &FindNone() { return none_; } void NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h); void Decimate(bool forcePressure); static bool GenerateIni(const std::string &gameID, Path &generatedFilename); 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); void ParseReduceHashRange(const std::string& key, const std::string& value); bool LookupHashRange(u32 addr, int &w, int &h); float LookupReduceHashRange(int& w, int& h); std::string LookupHashFile(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); bool PopulateLevel(ReplacedTextureLevel &level); SimpleBuf saveBuf; bool enabled_ = false; bool allowVideo_ = false; bool ignoreAddress_ = false; bool reduceHash_ = false; float reduceHashSize = 1.0; // default value with reduceHash to false float reduceHashGlobalValue = 0.5; // Global value for textures dump pngs of all sizes, 0.5 by default but can be set in textures.ini bool ignoreMipmap_ = false; std::string gameID_; Path basePath_; ReplacedTextureHash hash_ = ReplacedTextureHash::QUICK; typedef std::pair WidthHeightPair; std::unordered_map hashranges_; std::unordered_map reducehashranges_; std::unordered_map aliases_; std::unordered_map filtering_; ReplacedTexture none_; std::unordered_map cache_; std::unordered_map savedCache_; };