mirror of https://github.com/PCSX2/pcsx2.git
GS/HW: Reduce duplicates in hash cache
- Don't include TCC in the hashed TEX0 bits. - Hash the region size, not rectangle. Significantly reduces hash cache size in Ace Combat 5, over the course of 30 frames from 1,000+ textures down to 400. NOTE: This will change texture replacement hashes. Any "old" region textures will transparently be converted to the new internal name format upon loading.
This commit is contained in:
parent
b16bb14c58
commit
4fef86a635
|
@ -5035,7 +5035,7 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
|
|||
if (it != m_hash_cache.end())
|
||||
{
|
||||
// super easy, cache hit. remove paltex if it's a replacement texture.
|
||||
GL_CACHE("HC Hit: %" PRIx64 " %" PRIx64 " R-%" PRIx64, key.TEX0Hash, key.CLUTHash, key.region.bits);
|
||||
GL_CACHE("HC Hit: %" PRIx64 " %" PRIx64 " R-%ux%u", key.TEX0Hash, key.CLUTHash, key.region_width, key.region_height);
|
||||
HashCacheEntry* entry = &it->second;
|
||||
paltex &= (entry->texture->GetFormat() == GSTexture::Format::UNorm8);
|
||||
entry->refcount++;
|
||||
|
@ -5043,7 +5043,7 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
|
|||
}
|
||||
|
||||
// cache miss.
|
||||
GL_CACHE("HC Miss: %" PRIx64 " %" PRIx64 " R-%" PRIx64, key.TEX0Hash, key.CLUTHash, key.region.bits);
|
||||
GL_CACHE("HC Miss: %" PRIx64 " %" PRIx64 " R-%ux%u", key.TEX0Hash, key.CLUTHash, key.region_width, key.region_height);
|
||||
|
||||
// check for a replacement texture with the full clut key
|
||||
if (replace)
|
||||
|
@ -7005,6 +7005,8 @@ void GSTextureCache::PreloadTexture(const GIFRegTEX0& TEX0, const GIFRegTEXA& TE
|
|||
GSTextureCache::HashCacheKey::HashCacheKey()
|
||||
: TEX0Hash(0)
|
||||
, CLUTHash(0)
|
||||
, region_width(0)
|
||||
, region_height(0)
|
||||
{
|
||||
TEX0.U64 = 0;
|
||||
TEXA.U64 = 0;
|
||||
|
@ -7014,11 +7016,13 @@ GSTextureCache::HashCacheKey GSTextureCache::HashCacheKey::Create(const GIFRegTE
|
|||
{
|
||||
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
|
||||
// This should arguably include CPSM, but old replacement textures didn't have it...
|
||||
HashCacheKey ret;
|
||||
ret.TEX0.U64 = TEX0.U64 & 0x00000007FFF00000ULL;
|
||||
ret.TEX0.U64 = TEX0.U64 & 0x00000003FFFFC000ULL; // TBW, PSM, TW, TH
|
||||
ret.TEXA.U64 = (psm.pal == 0 && psm.fmt > 0) ? (TEXA.U64 & 0x000000FF000080FFULL) : 0;
|
||||
ret.CLUTHash = clut ? GSTextureCache::PaletteKeyHash{}({clut, psm.pal}) : 0;
|
||||
ret.region = region;
|
||||
ret.region_width = static_cast<u16>(region.GetWidth());
|
||||
ret.region_height = static_cast<u16>(region.GetHeight());
|
||||
|
||||
BlockHashState hash_st;
|
||||
BlockHashReset(hash_st);
|
||||
|
@ -7058,6 +7062,7 @@ void GSTextureCache::HashCacheKey::RemoveCLUTHash()
|
|||
u64 GSTextureCache::HashCacheKeyHash::operator()(const HashCacheKey& key) const
|
||||
{
|
||||
std::size_t h = 0;
|
||||
HashCombine(h, key.TEX0Hash, key.CLUTHash, key.TEX0.U64, key.TEXA.U64, key.region.bits);
|
||||
HashCombine(h, key.TEX0Hash, key.CLUTHash, key.TEX0.U64, key.TEXA.U64,
|
||||
static_cast<u64>(key.region_width) | (static_cast<u64>(key.region_height) << 16));
|
||||
return h;
|
||||
}
|
||||
|
|
|
@ -89,7 +89,8 @@ public:
|
|||
HashType TEX0Hash, CLUTHash;
|
||||
GIFRegTEX0 TEX0;
|
||||
GIFRegTEXA TEXA;
|
||||
SourceRegion region;
|
||||
u32 region_width;
|
||||
u32 region_height;
|
||||
|
||||
HashCacheKey();
|
||||
|
||||
|
@ -102,6 +103,7 @@ public:
|
|||
__fi bool operator!=(const HashCacheKey& e) const { return std::memcmp(this, &e, sizeof(*this)) != 0; }
|
||||
__fi bool operator<(const HashCacheKey& e) const { return std::memcmp(this, &e, sizeof(*this)) < 0; }
|
||||
};
|
||||
static_assert(sizeof(HashCacheKey) == 40, "HashCacheKey has no padding");
|
||||
|
||||
struct HashCacheKeyHash
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
#include "common/AlignedMalloc.h"
|
||||
|
@ -13,6 +13,7 @@
|
|||
#include "Config.h"
|
||||
#include "Host.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
#include "GS/GSExtra.h"
|
||||
#include "GS/GSLocalMemory.h"
|
||||
#include "GS/Renderers/HW/GSTextureReplacements.h"
|
||||
#include "VMManager.h"
|
||||
|
@ -31,18 +32,21 @@
|
|||
// this is a #define instead of a variable to avoid warnings from non-literal format strings
|
||||
#define TEXTURE_FILENAME_FORMAT_STRING "%" PRIx64 "-%08x"
|
||||
#define TEXTURE_FILENAME_CLUT_FORMAT_STRING "%" PRIx64 "-%" PRIx64 "-%08x"
|
||||
#define TEXTURE_FILENAME_REGION_FORMAT_STRING "%" PRIx64 "-r%" PRIx64 "-" "-%08x"
|
||||
#define TEXTURE_FILENAME_REGION_CLUT_FORMAT_STRING "%" PRIx64 "-%" PRIx64 "-r%" PRIx64 "-%08x"
|
||||
#define TEXTURE_FILENAME_REGION_FORMAT_STRING "%" PRIx64 "-r%ux%u-%08x"
|
||||
#define TEXTURE_FILENAME_REGION_CLUT_FORMAT_STRING "%" PRIx64 "-%" PRIx64 "-r%ux%u-%08x"
|
||||
#define TEXTURE_FILENAME_OLD_REGION_FORMAT_STRING "%" PRIx64 "-r%" PRIx64 "-%08x"
|
||||
#define TEXTURE_FILENAME_OLD_REGION_CLUT_FORMAT_STRING "%" PRIx64 "-%" PRIx64 "-r%" PRIx64 "-%08x"
|
||||
#define TEXTURE_REPLACEMENT_SUBDIRECTORY_NAME "replacements"
|
||||
#define TEXTURE_DUMP_SUBDIRECTORY_NAME "dumps"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct TextureName // 24 bytes
|
||||
struct TextureName // 32 bytes
|
||||
{
|
||||
u64 TEX0Hash;
|
||||
u64 CLUTHash;
|
||||
GSTextureCache::SourceRegion region;
|
||||
u32 region_width;
|
||||
u32 region_height;
|
||||
|
||||
union
|
||||
{
|
||||
|
@ -51,7 +55,7 @@ namespace
|
|||
u32 TEX0_PSM : 6;
|
||||
u32 TEX0_TW : 4;
|
||||
u32 TEX0_TH : 4;
|
||||
u32 TEX0_TCC : 1;
|
||||
u32 unused0 : 1; // was TCC
|
||||
u32 TEXA_TA0 : 8;
|
||||
u32 TEXA_AEM : 1;
|
||||
u32 TEXA_TA1 : 8;
|
||||
|
@ -60,25 +64,19 @@ namespace
|
|||
};
|
||||
u32 miplevel;
|
||||
|
||||
__fi u32 Width() const { return (region.HasX() ? region.GetWidth() : (1u << TEX0_TW)); }
|
||||
__fi u32 Height() const { return (region.HasY() ? region.GetWidth() : (1u << TEX0_TH)); }
|
||||
__fi u32 Width() const { return (region_width ? region_width : (1u << TEX0_TW)); }
|
||||
__fi u32 Height() const { return (region_height ? region_height : (1u << TEX0_TH)); }
|
||||
__fi bool HasPalette() const { return (GSLocalMemory::m_psm[TEX0_PSM].pal > 0); }
|
||||
__fi bool HasRegion() const { return region.HasEither(); }
|
||||
__fi bool HasRegion() const { return (region_width != 0 || region_height != 0); }
|
||||
|
||||
__fi bool operator==(const TextureName& rhs) const
|
||||
__fi bool operator==(const TextureName& rhs) const { return BitEqual(*this, rhs); }
|
||||
__fi bool operator!=(const TextureName& rhs) const { return !BitEqual(*this, rhs); }
|
||||
__fi bool operator<(const TextureName& rhs) const { return (std::memcmp(this, &rhs, sizeof(*this)) < 0); }
|
||||
|
||||
__fi void RemoveUnusedBits()
|
||||
{
|
||||
return std::tie(TEX0Hash, CLUTHash, region.bits, bits) ==
|
||||
std::tie(rhs.TEX0Hash, rhs.CLUTHash, region.bits, rhs.bits);
|
||||
}
|
||||
__fi bool operator!=(const TextureName& rhs) const
|
||||
{
|
||||
return std::tie(TEX0Hash, CLUTHash, region.bits, bits) !=
|
||||
std::tie(rhs.TEX0Hash, rhs.CLUTHash, region.bits, rhs.bits);
|
||||
}
|
||||
__fi bool operator<(const TextureName& rhs) const
|
||||
{
|
||||
return std::tie(TEX0Hash, CLUTHash, region.bits, bits) <
|
||||
std::tie(rhs.TEX0Hash, rhs.CLUTHash, region.bits, rhs.bits);
|
||||
// Remove bits which were previously present, but no longer used.
|
||||
unused0 = 0;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(TextureName) == 32, "ReplacementTextureName is expected size");
|
||||
|
@ -92,7 +90,9 @@ namespace std
|
|||
std::size_t operator()(const TextureName& val) const
|
||||
{
|
||||
std::size_t h = 0;
|
||||
HashCombine(h, val.TEX0Hash, val.CLUTHash, val.region.bits, val.bits, val.miplevel);
|
||||
HashCombine(h, val.TEX0Hash, val.CLUTHash,
|
||||
static_cast<u64>(val.region_width) | (static_cast<u64>(val.region_height) << 32),
|
||||
static_cast<u64>(val.bits) | (static_cast<u64>(val.miplevel) << 32));
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
@ -157,30 +157,34 @@ TextureName GSTextureReplacements::CreateTextureName(const GSTextureCache::HashC
|
|||
name.TEX0_PSM = hash.TEX0.PSM;
|
||||
name.TEX0_TW = hash.TEX0.TW;
|
||||
name.TEX0_TH = hash.TEX0.TH;
|
||||
name.TEX0_TCC = hash.TEX0.TCC;
|
||||
name.TEXA_TA0 = hash.TEXA.TA0;
|
||||
name.TEXA_AEM = hash.TEXA.AEM;
|
||||
name.TEXA_TA1 = hash.TEXA.TA1;
|
||||
name.TEX0Hash = hash.TEX0Hash;
|
||||
name.CLUTHash = name.HasPalette() ? hash.CLUTHash : 0;
|
||||
name.miplevel = miplevel;
|
||||
name.region = hash.region;
|
||||
name.region_width = hash.region_width;
|
||||
name.region_height = hash.region_height;
|
||||
return name;
|
||||
}
|
||||
|
||||
GSTextureCache::HashCacheKey GSTextureReplacements::HashCacheKeyFromTextureName(const TextureName& tn)
|
||||
{
|
||||
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[tn.TEX0_PSM];
|
||||
GSTextureCache::HashCacheKey key = {};
|
||||
key.TEX0.PSM = tn.TEX0_PSM;
|
||||
key.TEX0.TW = tn.TEX0_TW;
|
||||
key.TEX0.TH = tn.TEX0_TH;
|
||||
key.TEX0.TCC = tn.TEX0_TCC;
|
||||
key.TEXA.TA0 = tn.TEXA_TA0;
|
||||
key.TEXA.AEM = tn.TEXA_AEM;
|
||||
key.TEXA.TA1 = tn.TEXA_TA1;
|
||||
if (psm_s.pal == 0 && psm_s.fmt > 0)
|
||||
{
|
||||
key.TEXA.TA0 = tn.TEXA_TA0;
|
||||
key.TEXA.AEM = tn.TEXA_AEM;
|
||||
key.TEXA.TA1 = tn.TEXA_TA1;
|
||||
}
|
||||
key.TEX0Hash = tn.TEX0Hash;
|
||||
key.CLUTHash = tn.HasPalette() ? tn.CLUTHash : 0;
|
||||
key.region = tn.region;
|
||||
key.region_width = tn.region_width;
|
||||
key.region_height = tn.region_height;
|
||||
return key;
|
||||
}
|
||||
|
||||
|
@ -189,28 +193,56 @@ std::optional<TextureName> GSTextureReplacements::ParseReplacementName(const std
|
|||
TextureName ret;
|
||||
ret.miplevel = 0;
|
||||
|
||||
GSTextureCache::SourceRegion full_region;
|
||||
|
||||
char extension_dot;
|
||||
if (std::sscanf(filename.c_str(), TEXTURE_FILENAME_REGION_CLUT_FORMAT_STRING "%c", &ret.TEX0Hash, &ret.CLUTHash,
|
||||
&ret.region.bits, &ret.bits, &extension_dot) == 5 &&
|
||||
&ret.region_width, &ret.region_height, &ret.bits, &extension_dot) == 6 &&
|
||||
extension_dot == '.')
|
||||
{
|
||||
ret.RemoveUnusedBits();
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (std::sscanf(filename.c_str(), TEXTURE_FILENAME_REGION_FORMAT_STRING "%c", &ret.TEX0Hash, &ret.region.bits,
|
||||
&ret.bits, &extension_dot) == 4 &&
|
||||
if (std::sscanf(filename.c_str(), TEXTURE_FILENAME_REGION_FORMAT_STRING "%c", &ret.TEX0Hash,
|
||||
&ret.region_width, &ret.region_height, &ret.bits, &extension_dot) == 5 &&
|
||||
extension_dot == '.')
|
||||
{
|
||||
ret.RemoveUnusedBits();
|
||||
ret.CLUTHash = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.region.bits = 0;
|
||||
// Allow loading of dumped textures from older versions that included the full region bits.
|
||||
if (std::sscanf(filename.c_str(), TEXTURE_FILENAME_OLD_REGION_CLUT_FORMAT_STRING "%c", &ret.TEX0Hash, &ret.CLUTHash,
|
||||
&full_region.bits, &ret.bits, &extension_dot) == 5 &&
|
||||
extension_dot == '.')
|
||||
{
|
||||
ret.RemoveUnusedBits();
|
||||
ret.region_width = static_cast<u32>(full_region.GetWidth());
|
||||
ret.region_height = static_cast<u32>(full_region.GetHeight());
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (std::sscanf(filename.c_str(), TEXTURE_FILENAME_OLD_REGION_FORMAT_STRING "%c", &ret.TEX0Hash, &full_region.bits,
|
||||
&ret.bits, &extension_dot) == 4 &&
|
||||
extension_dot == '.')
|
||||
{
|
||||
ret.RemoveUnusedBits();
|
||||
ret.CLUTHash = 0;
|
||||
ret.region_width = static_cast<u32>(full_region.GetWidth());
|
||||
ret.region_height = static_cast<u32>(full_region.GetHeight());
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.region_width = 0;
|
||||
ret.region_height = 0;
|
||||
|
||||
if (std::sscanf(filename.c_str(), TEXTURE_FILENAME_CLUT_FORMAT_STRING "%c", &ret.TEX0Hash, &ret.CLUTHash, &ret.bits,
|
||||
&extension_dot) == 4 &&
|
||||
extension_dot == '.')
|
||||
{
|
||||
ret.RemoveUnusedBits();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -218,6 +250,7 @@ std::optional<TextureName> GSTextureReplacements::ParseReplacementName(const std
|
|||
3 &&
|
||||
extension_dot == '.')
|
||||
{
|
||||
ret.RemoveUnusedBits();
|
||||
ret.CLUTHash = 0;
|
||||
return ret;
|
||||
}
|
||||
|
@ -258,16 +291,18 @@ std::string GSTextureReplacements::GetDumpFilename(const TextureName& name, u32
|
|||
{
|
||||
filename = (level > 0) ?
|
||||
StringUtil::StdStringFromFormat(TEXTURE_FILENAME_REGION_CLUT_FORMAT_STRING "-mip%u.png",
|
||||
name.TEX0Hash, name.CLUTHash, name.region.bits, name.bits, level) :
|
||||
name.TEX0Hash, name.CLUTHash, name.region_width, name.region_height, name.bits, level) :
|
||||
StringUtil::StdStringFromFormat(TEXTURE_FILENAME_REGION_CLUT_FORMAT_STRING ".png",
|
||||
name.TEX0Hash, name.CLUTHash, name.region.bits, name.bits);
|
||||
name.TEX0Hash, name.CLUTHash, name.region_width, name.region_height, name.bits);
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = (level > 0) ? StringUtil::StdStringFromFormat(
|
||||
TEXTURE_FILENAME_FORMAT_STRING "-mip%u.png", name.TEX0Hash, name.bits, level) :
|
||||
TEXTURE_FILENAME_REGION_FORMAT_STRING "-mip%u.png", name.TEX0Hash,
|
||||
name.region_width, name.region_height, name.bits, level) :
|
||||
StringUtil::StdStringFromFormat(
|
||||
TEXTURE_FILENAME_FORMAT_STRING ".png", name.TEX0Hash, name.bits);
|
||||
TEXTURE_FILENAME_REGION_FORMAT_STRING ".png", name.TEX0Hash,
|
||||
name.region_width, name.region_height, name.bits);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
Loading…
Reference in New Issue