mirror of https://github.com/PCSX2/pcsx2.git
GS: Support mipmaps in hash cache
This commit is contained in:
parent
ee82c7834d
commit
9d51c64d92
|
@ -217,7 +217,6 @@ protected:
|
|||
bool IsOpaque();
|
||||
bool IsMipMapDraw();
|
||||
bool IsMipMapActive();
|
||||
GIFRegTEX0 GetTex0Layer(u32 lod);
|
||||
|
||||
public:
|
||||
GIFPath m_path[4];
|
||||
|
@ -303,4 +302,5 @@ public:
|
|||
void SetFrameSkip(int skip);
|
||||
|
||||
PRIM_OVERLAP PrimitiveOverlap();
|
||||
GIFRegTEX0 GetTex0Layer(u32 lod);
|
||||
};
|
||||
|
|
|
@ -1331,6 +1331,7 @@ void GSRendererHW::Draw()
|
|||
if (PRIM->TME)
|
||||
{
|
||||
GIFRegCLAMP MIP_CLAMP = context->CLAMP;
|
||||
GSVector2i hash_lod_range(0, 0);
|
||||
m_lod = GSVector2i(0, 0);
|
||||
|
||||
// Code from the SW renderer
|
||||
|
@ -1393,6 +1394,10 @@ void GSRendererHW::Draw()
|
|||
|
||||
TEX0 = GetTex0Layer(m_lod.x);
|
||||
|
||||
// upload the full chain (with offset) for the hash cache, in case some other texture uses more levels
|
||||
// for basic mipmapping, we can get away with just doing the base image, since all the mips get generated anyway.
|
||||
hash_lod_range = GSVector2i(m_lod.x, (m_hw_mipmap == HWMipmapLevel::Full) ? mxl : m_lod.x);
|
||||
|
||||
MIP_CLAMP.MINU >>= m_lod.x;
|
||||
MIP_CLAMP.MINV >>= m_lod.x;
|
||||
MIP_CLAMP.MAXU >>= m_lod.x;
|
||||
|
@ -1416,8 +1421,8 @@ void GSRendererHW::Draw()
|
|||
TextureMinMaxResult tmm = GetTextureMinMax(TEX0, MIP_CLAMP, m_vt.IsLinear());
|
||||
|
||||
m_src = tex_psm.depth ? m_tc->LookupDepthSource(TEX0, env.TEXA, tmm.coverage) :
|
||||
m_tc->LookupSource(TEX0, env.TEXA, tmm.coverage, m_hw_mipmap >= HWMipmapLevel::Basic ||
|
||||
GSConfig.UserHacks_TriFilter == TriFiltering::Forced);
|
||||
m_tc->LookupSource(TEX0, env.TEXA, tmm.coverage, (m_hw_mipmap >= HWMipmapLevel::Basic ||
|
||||
GSConfig.UserHacks_TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr);
|
||||
|
||||
int tw = 1 << TEX0.TW;
|
||||
int th = 1 << TEX0.TH;
|
||||
|
@ -1471,7 +1476,7 @@ void GSRendererHW::Draw()
|
|||
}
|
||||
|
||||
// Round 2
|
||||
if (IsMipMapActive() && m_hw_mipmap == HWMipmapLevel::Full && !tex_psm.depth)
|
||||
if (IsMipMapActive() && m_hw_mipmap == HWMipmapLevel::Full && !tex_psm.depth && !m_src->m_from_hash_cache)
|
||||
{
|
||||
// Upload remaining texture layers
|
||||
const GSVector4 tmin = m_vt.m_min.t;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "GSTextureCache.h"
|
||||
#include "GS/Renderers/Common/GSFunctionMap.h"
|
||||
#include "GS/Renderers/Common/GSRenderer.h"
|
||||
#include "GS/GSState.h"
|
||||
|
||||
class GSRendererHW : public GSRenderer
|
||||
|
|
|
@ -187,7 +187,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
|||
else if (m_renderer->m_game.title == CRC::SVCChaos)
|
||||
{
|
||||
// SVCChaos black screen on main menu, regardless of depth enabled or disabled.
|
||||
return LookupSource(TEX0, TEXA, r, false);
|
||||
return LookupSource(TEX0, TEXA, r, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -210,7 +210,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
|||
return src;
|
||||
}
|
||||
|
||||
GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r, bool mipmap)
|
||||
GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r, const GSVector2i* lod)
|
||||
{
|
||||
GL_CACHE("TC: Lookup Source <%d,%d => %d,%d> (0x%x, %s, BW: %u)", r.x, r.y, r.z, r.w, TEX0.TBP0, psm_str(TEX0.PSM), TEX0.TBW);
|
||||
|
||||
|
@ -413,7 +413,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
|||
GL_CACHE("TC: src miss (0x%x, 0x%x, %s)", TEX0.TBP0, psm_s.pal > 0 ? TEX0.CBP : 0, psm_str(TEX0.PSM));
|
||||
}
|
||||
#endif
|
||||
src = CreateSource(TEX0, TEXA, dst, half_right, x_offset, y_offset, mipmap);
|
||||
src = CreateSource(TEX0, TEXA, dst, half_right, x_offset, y_offset, lod);
|
||||
new_source = true;
|
||||
}
|
||||
else
|
||||
|
@ -1186,7 +1186,7 @@ void GSTextureCache::IncAge()
|
|||
}
|
||||
|
||||
//Fixme: Several issues in here. Not handling depth stencil, pitch conversion doesnt work.
|
||||
GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* dst, bool half_right, int x_offset, int y_offset, bool mipmap)
|
||||
GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* dst, bool half_right, int x_offset, int y_offset, const GSVector2i* lod)
|
||||
{
|
||||
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
Source* src = new Source(m_renderer, TEX0, TEXA, false);
|
||||
|
@ -1451,19 +1451,31 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
else
|
||||
{
|
||||
// try the hash cache
|
||||
if (!mipmap && CanCacheTextureSize(TEX0.TW, TEX0.TH))
|
||||
if (CanCacheTextureSize(TEX0.TW, TEX0.TH))
|
||||
{
|
||||
const bool paltex = (GSConfig.GPUPaletteConversion && psm.pal > 0);
|
||||
const u32* clut = (!paltex && psm.pal > 0) ? static_cast<const u32*>(m_renderer->m_mem.m_clut) : nullptr;
|
||||
const HashCacheKey key{ HashCacheKey::Create(TEX0, TEXA, m_renderer, clut) };
|
||||
const HashCacheKey key{ HashCacheKey::Create(TEX0, TEXA, m_renderer, clut, lod) };
|
||||
|
||||
auto it = m_hash_cache.find(key);
|
||||
if (it == m_hash_cache.end())
|
||||
{
|
||||
// hash and upload texture
|
||||
src->m_texture = g_gs_device->CreateTexture(tw, th, paltex ? false : mipmap, paltex ? GSTexture::Format::UNorm8 : GSTexture::Format::Color);
|
||||
src->m_texture = g_gs_device->CreateTexture(tw, th, paltex ? false : (lod != nullptr), paltex ? GSTexture::Format::UNorm8 : GSTexture::Format::Color);
|
||||
PreloadTexture(TEX0, TEXA, m_renderer->m_mem, paltex, src->m_texture, 0);
|
||||
|
||||
// upload mips if present
|
||||
if (lod)
|
||||
{
|
||||
const int basemip = lod->x;
|
||||
const int nmips = lod->y - lod->x + 1;
|
||||
for (int mip = 1; mip < nmips; mip++)
|
||||
{
|
||||
const GIFRegTEX0 MIP_TEX0{m_renderer->GetTex0Layer(basemip + mip)};
|
||||
PreloadTexture(MIP_TEX0, TEXA, m_renderer->m_mem, paltex, src->m_texture, mip);
|
||||
}
|
||||
}
|
||||
|
||||
// insert it into the hash cache
|
||||
HashCacheEntry entry{ src->m_texture, 1, 0 };
|
||||
it = m_hash_cache.emplace(key, entry).first;
|
||||
|
@ -1488,7 +1500,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
}
|
||||
else
|
||||
{
|
||||
src->m_texture = g_gs_device->CreateTexture(tw, th, mipmap, GSTexture::Format::Color);
|
||||
src->m_texture = g_gs_device->CreateTexture(tw, th, (lod != nullptr), GSTexture::Format::Color);
|
||||
if (psm.pal > 0)
|
||||
{
|
||||
AttachPaletteToSource(src, psm.pal, false);
|
||||
|
@ -1965,13 +1977,9 @@ void GSTextureCache::Source::Flush(u32 count, int layer)
|
|||
void GSTextureCache::Source::PreloadLevel(int level)
|
||||
{
|
||||
// m_TEX0 is adjusted for mips (messy, should be changed).
|
||||
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[m_TEX0.PSM];
|
||||
const GSVector2i& bs = psm.bs;
|
||||
const int tw = 1 << m_TEX0.TW;
|
||||
const int th = 1 << m_TEX0.TH;
|
||||
const HashType hash = HashTexture(m_renderer, m_TEX0, m_TEXA);
|
||||
|
||||
// Layer is complete again, regardless of whether the hash matches or not (and we reupload).
|
||||
const HashType hash = HashTexture(m_renderer, m_TEX0, m_TEXA);
|
||||
const u8 layer_bit = static_cast<u8>(1) << level;
|
||||
m_complete_layers |= layer_bit;
|
||||
|
||||
|
@ -2596,7 +2604,7 @@ __fi static GSTextureCache::HashType FinishBlockHash(BlockHashState& st)
|
|||
return XXH3_64bits_digest(&st);
|
||||
}
|
||||
|
||||
GSTextureCache::HashType GSTextureCache::HashTexture(GSRenderer* renderer, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA)
|
||||
static void HashTextureLevel(GSRenderer* renderer, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, BlockHashState& hash_st, u8* temp)
|
||||
{
|
||||
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
const GSVector2i& bs = psm.bs;
|
||||
|
@ -2612,8 +2620,6 @@ GSTextureCache::HashType GSTextureCache::HashTexture(GSRenderer* renderer, const
|
|||
|
||||
// For textures which are smaller than the block size, we expand and then hash.
|
||||
// This is because otherwise we get the padding bytes, which can be random junk.
|
||||
GSTextureCache::HashType hash;
|
||||
BlockHashState hash_st;
|
||||
if (tw < bs.x || th < bs.y)
|
||||
{
|
||||
// Expand texture indices. Align to 32 bytes for AVX2.
|
||||
|
@ -2622,11 +2628,10 @@ GSTextureCache::HashType GSTextureCache::HashTexture(GSRenderer* renderer, const
|
|||
const GSLocalMemory::readTexture rtx = psm.rtxP;
|
||||
|
||||
// Use temp buffer for expanding, since we may not need to update.
|
||||
(renderer->m_mem.*rtx)(off, block_rect, m_temp, pitch, TEXA);
|
||||
(renderer->m_mem.*rtx)(off, block_rect, temp, pitch, TEXA);
|
||||
|
||||
// Hash the expanded texture.
|
||||
u8* ptr = m_temp;
|
||||
BlockHashReset(hash_st);
|
||||
u8* ptr = temp;
|
||||
if (pitch == row_size)
|
||||
{
|
||||
BlockHashAccumulate(hash_st, ptr, pitch * static_cast<u32>(th));
|
||||
|
@ -2636,7 +2641,6 @@ GSTextureCache::HashType GSTextureCache::HashTexture(GSRenderer* renderer, const
|
|||
for (int y = 0; y < th; y++, ptr += pitch)
|
||||
BlockHashAccumulate(hash_st, ptr, row_size);
|
||||
}
|
||||
hash = FinishBlockHash(hash_st);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2654,11 +2658,15 @@ GSTextureCache::HashType GSTextureCache::HashTexture(GSRenderer* renderer, const
|
|||
BlockHashAccumulate(hash_st, mem.BlockPtr(bn.value()));
|
||||
}
|
||||
}
|
||||
|
||||
hash = FinishBlockHash(hash_st);
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
GSTextureCache::HashType GSTextureCache::HashTexture(GSRenderer* renderer, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA)
|
||||
{
|
||||
BlockHashState hash_st;
|
||||
BlockHashReset(hash_st);
|
||||
HashTextureLevel(renderer, TEX0, TEXA, hash_st, m_temp);
|
||||
return FinishBlockHash(hash_st);
|
||||
}
|
||||
|
||||
void GSTextureCache::PreloadTexture(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GSLocalMemory& mem, bool paltex, GSTexture* tex, u32 level)
|
||||
|
@ -2710,7 +2718,7 @@ GSTextureCache::HashCacheKey::HashCacheKey()
|
|||
TEXA.U64 = 0;
|
||||
}
|
||||
|
||||
GSTextureCache::HashCacheKey GSTextureCache::HashCacheKey::Create(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GSRenderer* renderer, const u32* clut)
|
||||
GSTextureCache::HashCacheKey GSTextureCache::HashCacheKey::Create(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GSRenderer* renderer, const u32* clut, const GSVector2i* lod)
|
||||
{
|
||||
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
|
||||
|
@ -2718,7 +2726,26 @@ GSTextureCache::HashCacheKey GSTextureCache::HashCacheKey::Create(const GIFRegTE
|
|||
ret.TEX0.U64 = TEX0.U64 & 0x00000007FFF00000ULL;
|
||||
ret.TEXA.U64 = (psm.pal == 0 && psm.fmt > 0) ? (TEXA.U64 & 0x000000FF000080FFULL) : 0;
|
||||
ret.CLUTHash = clut ? GSTextureCache::PaletteKeyHash{}({clut, psm.pal}) : 0;
|
||||
ret.TEX0Hash = HashTexture(renderer, TEX0, TEXA);
|
||||
|
||||
BlockHashState hash_st;
|
||||
BlockHashReset(hash_st);
|
||||
|
||||
// base level is always hashed
|
||||
HashTextureLevel(renderer, TEX0, TEXA, hash_st, m_temp);
|
||||
|
||||
if (lod)
|
||||
{
|
||||
// hash and combine full mipmaps when enabled
|
||||
const int basemip = lod->x;
|
||||
const int nmips = lod->y - lod->x + 1;
|
||||
for (int i = 1; i < nmips; i++)
|
||||
{
|
||||
const GIFRegTEX0 MIP_TEX0{renderer->GetTex0Layer(basemip + i)};
|
||||
HashTextureLevel(renderer, MIP_TEX0, TEXA, hash_st, m_temp);
|
||||
}
|
||||
}
|
||||
|
||||
ret.TEX0Hash = FinishBlockHash(hash_st);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -50,7 +50,8 @@ public:
|
|||
|
||||
HashCacheKey();
|
||||
|
||||
static HashCacheKey Create(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GSRenderer* renderer, const u32* clut);
|
||||
static HashCacheKey Create(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GSRenderer* renderer, const u32* clut,
|
||||
const GSVector2i* lod);
|
||||
|
||||
__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; }
|
||||
|
@ -287,7 +288,7 @@ protected:
|
|||
constexpr static size_t S_SURFACE_OFFSET_CACHE_MAX_SIZE = std::numeric_limits<u16>::max();
|
||||
std::unordered_map<SurfaceOffsetKey, SurfaceOffset, SurfaceOffsetKeyHash, SurfaceOffsetKeyEqual> m_surface_offset_cache;
|
||||
|
||||
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t = NULL, bool half_right = false, int x_offset = 0, int y_offset = 0, bool mipmap = false);
|
||||
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t = NULL, bool half_right = false, int x_offset = 0, int y_offset = 0, const GSVector2i* lod = nullptr);
|
||||
Target* CreateTarget(const GIFRegTEX0& TEX0, int w, int h, int type, const bool clear);
|
||||
|
||||
static void PreloadTexture(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GSLocalMemory& mem, bool paltex, GSTexture* tex, u32 level);
|
||||
|
@ -307,7 +308,7 @@ public:
|
|||
void RemoveAll();
|
||||
void RemovePartial();
|
||||
|
||||
Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r, bool mipmap);
|
||||
Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r, const GSVector2i* lod);
|
||||
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r, bool palette = false);
|
||||
|
||||
Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask = 0, const bool is_frame = false, const int real_h = 0);
|
||||
|
|
Loading…
Reference in New Issue