GS/HW: Texture cache improvements

GS/HW: Only use temporary source for recursive draw

.. and don't insert it into the page map.

GS/HW: Lookup page list for depth sources

GS/HW: Avoid target copies by using shader sampling

GS/HW: Make texture cache a global pointer

GS/HW: Remove GetID() from GSTexture

It only made sense for OpenGL, was always zero in Vulkan.

GS/HW: Rewrite texture sampling hazard detection

Also avoid redundant channel shuffle setup.

GS/HW: Turn Haunting Ground CRC into an OI fix
This commit is contained in:
Stenzek 2023-03-17 23:20:06 +10:00 committed by refractionpcsx2
parent ed90c8868f
commit faecc6913b
28 changed files with 1256 additions and 893 deletions

View File

@ -16193,6 +16193,7 @@ SLES-52877:
compat: 5
gsHWFixes:
halfPixelOffset: 1 # Fixes blurriness.
beforeDraw: "OI_HauntingGround" # Fix bloom.
SLES-52882:
name: "Stolen"
region: "PAL-M5"
@ -31346,6 +31347,7 @@ SLPM-65913:
compat: 5
gsHWFixes:
halfPixelOffset: 1 # Fixes blurriness.
beforeDraw: "OI_HauntingGround" # Fix bloom.
SLPM-65914:
name: "Nana"
region: "NTSC-J"
@ -34169,6 +34171,7 @@ SLPM-66638:
region: "NTSC-J"
gsHWFixes:
halfPixelOffset: 1 # Fixes blurriness.
beforeDraw: "OI_HauntingGround" # Fix bloom.
SLPM-66639:
name: "Street Fighter III - 3rd Strike [Capcom the Best]"
region: "NTSC-J"
@ -46709,6 +46712,7 @@ SLUS-21075:
compat: 5
gsHWFixes:
halfPixelOffset: 1 # Fixes blurriness.
beforeDraw: "OI_HauntingGround" # Fix bloom.
SLUS-21076:
name: "Atari Anthology"
region: "NTSC-U"

View File

@ -37,6 +37,7 @@
#define PS_LTF 1
#define PS_TCOFFSETHACK 0
#define PS_POINT_SAMPLER 0
#define PS_REGION_RECT 0
#define PS_SHUFFLE 0
#define PS_READ_BA 0
#define PS_READ16_SRC 0
@ -178,6 +179,8 @@ float4 sample_c(float2 uv, float uv_w)
{
#if PS_TEX_IS_FB == 1
return RtTexture.Load(int3(int2(uv * WH.zw), 0));
#elif PS_REGION_RECT == 1
return Texture.Load(int3(int2(uv), 0));
#else
if (PS_POINT_SAMPLER)
{
@ -241,7 +244,15 @@ float4 clamp_wrap_uv(float4 uv)
if(PS_WMS == PS_WMT)
{
if(PS_WMS == 2)
if(PS_REGION_RECT != 0 && PS_WMS == 0)
{
uv = frac(uv);
}
else if(PS_REGION_RECT != 0 && PS_WMS == 1)
{
uv = saturate(uv);
}
else if(PS_WMS == 2)
{
uv = clamp(uv, MinMax.xyxy, MinMax.zwzw);
}
@ -257,7 +268,15 @@ float4 clamp_wrap_uv(float4 uv)
}
else
{
if(PS_WMS == 2)
if(PS_REGION_RECT != 0 && PS_WMS == 0)
{
uv.xz = frac(uv.xz);
}
else if(PS_REGION_RECT != 0 && PS_WMS == 1)
{
uv.xz = saturate(uv.xz);
}
else if(PS_WMS == 2)
{
uv.xz = clamp(uv.xz, MinMax.xx, MinMax.zz);
}
@ -268,7 +287,15 @@ float4 clamp_wrap_uv(float4 uv)
#endif
uv.xz = (float2)(((uint2)(uv.xz * tex_size.xx) & asuint(MinMax.xx)) | asuint(MinMax.zz)) / tex_size.xx;
}
if(PS_WMT == 2)
if(PS_REGION_RECT != 0 && PS_WMT == 0)
{
uv.yw = frac(uv.yw);
}
else if(PS_REGION_RECT != 0 && PS_WMT == 1)
{
uv.yw = saturate(uv.yw);
}
else if(PS_WMT == 2)
{
uv.yw = clamp(uv.yw, MinMax.yy, MinMax.ww);
}
@ -281,6 +308,12 @@ float4 clamp_wrap_uv(float4 uv)
}
}
if(PS_REGION_RECT != 0)
{
// Normalized -> Integer Coordinates.
uv = clamp(uv * WH.zwzw + STRange.xyxy, STRange.xyxy, STRange.zwzw);
}
return uv;
}
@ -564,7 +597,7 @@ float4 sample_color(float2 st, float uv_w)
float4x4 c;
float2 dd;
if (PS_LTF == 0 && PS_AEM_FMT == FMT_32 && PS_PAL_FMT == 0 && PS_WMS < 2 && PS_WMT < 2)
if (PS_LTF == 0 && PS_AEM_FMT == FMT_32 && PS_PAL_FMT == 0 && PS_REGION_RECT == 0 && PS_WMS < 2 && PS_WMT < 2)
{
c[0] = sample_c(st, uv_w);
}

View File

@ -98,6 +98,8 @@ vec4 sample_c(vec2 uv)
{
#if PS_TEX_IS_FB == 1
return fetch_rt();
#elif PS_REGION_RECT
return texelFetch(TextureSampler, ivec2(uv), 0);
#else
#if PS_POINT_SAMPLER
@ -163,7 +165,11 @@ vec4 clamp_wrap_uv(vec4 uv)
#if PS_WMS == PS_WMT
#if PS_WMS == 2
#if PS_REGION_RECT == 1 && PS_WMS == 0
uv_out = fract(uv);
#elif PS_REGION_RECT == 1 && PS_WMS == 1
uv_out = clamp(uv, vec4(0.0f), vec4(1.0f));
#elif PS_WMS == 2
uv_out = clamp(uv, MinMax.xyxy, MinMax.zwzw);
#elif PS_WMS == 3
#if PS_FST == 0
@ -176,7 +182,13 @@ vec4 clamp_wrap_uv(vec4 uv)
#else // PS_WMS != PS_WMT
#if PS_WMS == 2
#if PS_REGION_RECT == 1 && PS_WMS == 0
uv.xz = fract(uv.xz);
#elif PS_REGION_RECT == 1 && PS_WMS == 1
uv.xz = clamp(uv.xz, vec2(0.0f), vec2(1.0f));
#elif PS_WMS == 2
uv_out.xz = clamp(uv.xz, MinMax.xx, MinMax.zz);
#elif PS_WMS == 3
@ -187,7 +199,13 @@ vec4 clamp_wrap_uv(vec4 uv)
#endif
#if PS_WMT == 2
#if PS_REGION_RECT == 1 && PS_WMT == 0
uv_out.yw = fract(uv.yw);
#elif PS_REGION_RECT == 1 && PS_WMT == 1
uv_out.yw = clamp(uv.yw, vec2(0.0f), vec2(1.0f));
#elif PS_WMT == 2
uv_out.yw = clamp(uv.yw, MinMax.yy, MinMax.ww);
#elif PS_WMT == 3
@ -197,6 +215,11 @@ vec4 clamp_wrap_uv(vec4 uv)
uv_out.yw = vec2((uvec2(uv.yw * tex_size.yy) & floatBitsToUint(MinMax.yy)) | floatBitsToUint(MinMax.ww)) / tex_size.yy;
#endif
#endif
#if PS_REGION_RECT == 1
// Normalized -> Integer Coordinates.
uv_out = clamp(uv_out * WH.zwzw + STRange.xyxy, STRange.xyxy, STRange.zwzw);
#endif
return uv_out;
@ -473,7 +496,7 @@ vec4 sample_color(vec2 st)
vec2 dd;
// FIXME I'm not sure this condition is useful (I think code will be optimized)
#if (PS_LTF == 0 && PS_AEM_FMT == FMT_32 && PS_PAL_FMT == 0 && PS_WMS < 2 && PS_WMT < 2)
#if (PS_LTF == 0 && PS_AEM_FMT == FMT_32 && PS_PAL_FMT == 0 && PS_REGION_RECT == 0 && PS_WMS < 2 && PS_WMT < 2)
// No software LTF and pure 32 bits RGBA texure without special texture wrapping
c[0] = sample_c(st);
#ifdef TEX_COORD_DEBUG

View File

@ -415,6 +415,8 @@ vec4 sample_c(vec2 uv)
{
#if PS_TEX_IS_FB
return sample_from_rt();
#elif PS_REGION_RECT
return texelFetch(Texture, ivec2(uv), 0);
#else
#if PS_POINT_SAMPLER
// Weird issue with ATI/AMD cards,
@ -477,7 +479,15 @@ vec4 clamp_wrap_uv(vec4 uv)
#if PS_WMS == PS_WMT
{
#if PS_WMS == 2
#if PS_REGION_RECT == 1 && PS_WMS == 0
{
uv = fract(uv);
}
#elif PS_REGION_RECT == 1 && PS_WMS == 1
{
uv = clamp(uv, vec4(0.0f), vec4(1.0f));
}
#elif PS_WMS == 2
{
uv = clamp(uv, MinMax.xyxy, MinMax.zwzw);
}
@ -494,7 +504,15 @@ vec4 clamp_wrap_uv(vec4 uv)
}
#else
{
#if PS_WMS == 2
#if PS_REGION_RECT == 1 && PS_WMS == 0
{
uv.xz = fract(uv.xz);
}
#elif PS_REGION_RECT == 1 && PS_WMS == 1
{
uv.xz = clamp(uv.xz, vec2(0.0f), vec2(1.0f));
}
#elif PS_WMS == 2
{
uv.xz = clamp(uv.xz, MinMax.xx, MinMax.zz);
}
@ -506,7 +524,15 @@ vec4 clamp_wrap_uv(vec4 uv)
uv.xz = vec2((uvec2(uv.xz * tex_size.xx) & floatBitsToUint(MinMax.xx)) | floatBitsToUint(MinMax.zz)) / tex_size.xx;
}
#endif
#if PS_WMT == 2
#if PS_REGION_RECT == 1 && PS_WMT == 0
{
uv.yw = fract(uv.yw);
}
#elif PS_REGION_RECT == 1 && PS_WMT == 1
{
uv.yw = clamp(uv.yw, vec2(0.0f), vec2(1.0f));
}
#elif PS_WMT == 2
{
uv.yw = clamp(uv.yw, MinMax.yy, MinMax.ww);
}
@ -521,6 +547,11 @@ vec4 clamp_wrap_uv(vec4 uv)
}
#endif
#if PS_REGION_RECT == 1
// Normalized -> Integer Coordinates.
uv = clamp(uv * WH.zwzw + STRange.xyxy, STRange.xyxy, STRange.zwzw);
#endif
return uv;
}
@ -797,7 +828,7 @@ vec4 sample_color(vec2 st)
mat4 c;
vec2 dd;
#if PS_LTF == 0 && PS_AEM_FMT == FMT_32 && PS_PAL_FMT == 0 && PS_WMS < 2 && PS_WMT < 2
#if PS_LTF == 0 && PS_AEM_FMT == FMT_32 && PS_PAL_FMT == 0 && PS_REGION_RECT == 0 && PS_WMS < 2 && PS_WMT < 2
{
c[0] = sample_c(st);
}

View File

@ -282,7 +282,7 @@ bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::
if (recreate_display)
{
g_gs_device->ResetAPIState();
if (Host::BeginPresentFrame(false) == HostDisplay::PresentResult::OK)
if (Host::BeginPresentFrame(true) == HostDisplay::PresentResult::OK)
Host::EndPresentFrame();
}
@ -643,12 +643,12 @@ void GSgetStats(std::string& info)
void GSgetMemoryStats(std::string& info)
{
if (GSConfig.Renderer == GSRendererType::SW || GSConfig.Renderer == GSRendererType::Null)
if (!g_texture_cache)
return;
const u64 targets = GSRendererHW::GetInstance()->GetTextureCache()->GetTargetMemoryUsage();
const u64 sources = GSRendererHW::GetInstance()->GetTextureCache()->GetSourceMemoryUsage();
const u64 hashcache = GSRendererHW::GetInstance()->GetTextureCache()->GetHashCacheMemoryUsage();
const u64 targets = g_texture_cache->GetTargetMemoryUsage();
const u64 sources = g_texture_cache->GetSourceMemoryUsage();
const u64 hashcache = g_texture_cache->GetHashCacheMemoryUsage();
const u64 pool = g_gs_device->GetPoolMemoryUsage();
const u64 total = targets + sources + hashcache + pool;

View File

@ -24,11 +24,6 @@ const CRC::Game CRC::m_games[] =
{
// Note: IDs 0x7ACF7E03, 0x7D4EA48F, 0x37C53760 - shouldn't be added as it's from the multiloaders when packing games.
{0x00000000, NoTitle /* NoRegion */},
{0x08C1ED4D, HauntingGround /* EU */},
{0x2CD5794C, HauntingGround /* EU */},
{0x867BB945, HauntingGround /* JP */},
{0xE263BC4B, HauntingGround /* JP */},
{0x901AAC09, HauntingGround /* US */},
{0x6F8545DB, ICO /* US */},
{0x48CDF317, ICO /* US */}, // Demo
{0xB01A4C95, ICO /* JP */},

View File

@ -24,7 +24,6 @@ public:
{
NoTitle,
GetawayGames,
HauntingGround,
ICO,
KOF2002,
PolyphonyDigitalGames,

View File

@ -3357,7 +3357,7 @@ static bool UsesRegionRepeat(int fix, int msk, int min, int max, int* min_out, i
return sets_bits || clears_bits;
}
GSState::TextureMinMaxResult GSState::GetTextureMinMax(const GIFRegTEX0& TEX0, const GIFRegCLAMP& CLAMP, bool linear)
GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCLAMP CLAMP, bool linear, bool clamp_to_tsize)
{
// TODO: some of the +1s can be removed if linear == false
@ -3366,21 +3366,18 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(const GIFRegTEX0& TEX0, c
const int w = 1 << tw;
const int h = 1 << th;
const int tw_mask = w - 1;
const int th_mask = h - 1;
const int tw_mask = (1 << tw) - 1;
const int th_mask = (1 << th) - 1;
const GSVector4i tr(0, 0, w, h);
GSVector4i tr(0, 0, w, h);
const int wms = CLAMP.WMS;
const int wmt = CLAMP.WMT;
const int minu = (int)CLAMP.MINU;
const int minv = (int)CLAMP.MINV;
// For the FixedTEX0 case, in hardware, we handle this in the texture cache. Don't OR the bits in here, otherwise
// we'll end up with an invalid rectangle, we want the passed-in rectangle to be relative to the normalized size.
const int maxu = (wms != CLAMP_REGION_REPEAT || (int)CLAMP.MAXU < w) ? (int)CLAMP.MAXU : 0;
const int maxv = (wmt != CLAMP_REGION_REPEAT || (int)CLAMP.MAXV < h) ? (int)CLAMP.MAXV : 0;
const int maxu = (int)CLAMP.MAXU;
const int maxv = (int)CLAMP.MAXV;
GSVector4i vr = tr;
@ -3391,10 +3388,8 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(const GIFRegTEX0& TEX0, c
case CLAMP_CLAMP:
break;
case CLAMP_REGION_CLAMP:
if (vr.x < minu)
vr.x = minu;
if (vr.z > maxu + 1)
vr.z = maxu + 1;
vr.x = minu;
vr.z = maxu + 1;
break;
case CLAMP_REGION_REPEAT:
vr.x = maxu;
@ -3411,10 +3406,8 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(const GIFRegTEX0& TEX0, c
case CLAMP_CLAMP:
break;
case CLAMP_REGION_CLAMP:
if (vr.y < minv)
vr.y = minv;
if (vr.w > maxv + 1)
vr.w = maxv + 1;
vr.y = minv;
vr.w = maxv + 1;
break;
case CLAMP_REGION_REPEAT:
vr.y = maxv;
@ -3424,6 +3417,13 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(const GIFRegTEX0& TEX0, c
__assume(0);
}
// Software renderer fixes TEX0 so that TW/TH contain MAXU/MAXV.
// Hardware renderer doesn't, and handles it in the texture cache, so don't clamp here.
if (clamp_to_tsize)
vr = vr.rintersect(tr);
else
tr = tr.runion(vr);
u8 uses_border = 0;
if (m_vt.m_max.t.x >= FLT_MAX || m_vt.m_min.t.x <= -FLT_MAX ||
@ -3886,6 +3886,33 @@ GIFRegTEX0 GSState::GetTex0Layer(u32 lod)
return TEX0;
}
bool GSState::IsTBPFrameOrZ(u32 tbp) const
{
GSDrawingContext* context = m_context;
const bool is_frame = (context->FRAME.Block() == tbp);
const bool is_z = (context->ZBUF.Block() == tbp);
if (!is_frame && !is_z)
return false;
const u32 fm = context->FRAME.FBMSK;
const u32 zm = context->ZBUF.ZMSK || context->TEST.ZTE == 0 ? 0xffffffff : 0;
const u32 fm_mask = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk;
const u32 max_z = (0xFFFFFFFF >> (GSLocalMemory::m_psm[context->ZBUF.PSM].fmt * 8));
const bool no_rt = (context->ALPHA.IsCd() && PRIM->ABE && (context->FRAME.PSM == 1))
|| (!context->TEST.DATE && (context->FRAME.FBMSK & GSLocalMemory::m_psm[context->FRAME.PSM].fmsk) == GSLocalMemory::m_psm[context->FRAME.PSM].fmsk);
const bool no_ds = (
// Depth is always pass/fail (no read) and write are discarded.
(zm != 0 && context->TEST.ZTST <= ZTST_ALWAYS) ||
// Depth test will always pass
(zm != 0 && context->TEST.ZTST == ZTST_GEQUAL && m_vt.m_eq.z && std::min(m_vertex.buff[0].XYZ.Z, max_z) == max_z) ||
// Depth will be written through the RT
(!no_rt && context->FRAME.FBP == context->ZBUF.ZBP && !PRIM->TME && zm == 0 && (fm & fm_mask) == 0 && context->TEST.ZTE));
// Relying a lot on the optimizer here... I don't like it.
return (is_frame && !no_rt) || (is_z && !no_ds);
}
// GSTransferBuffer
GSState::GSTransferBuffer::GSTransferBuffer()

View File

@ -199,7 +199,7 @@ protected:
GSVector4i coverage; ///< Part of the texture used
u8 uses_boundary; ///< Whether or not the usage touches the left, top, right, or bottom edge (and therefore needs wrap modes preserved)
};
TextureMinMaxResult GetTextureMinMax(const GIFRegTEX0& TEX0, const GIFRegCLAMP& CLAMP, bool linear);
TextureMinMaxResult GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCLAMP CLAMP, bool linear, bool clamp_to_tsize);
bool TryAlphaTest(u32& fm, const u32 fm_mask, u32& zm);
bool IsOpaque();
bool IsMipMapDraw();
@ -918,4 +918,7 @@ public:
PRIM_OVERLAP PrimitiveOverlap();
GIFRegTEX0 GetTex0Layer(u32 lod);
/// Returns true if the specified texture address matches the frame or Z buffer.
bool IsTBPFrameOrZ(u32 tbp) const;
};

View File

@ -449,11 +449,22 @@ bool GSDevice::ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h,
{
const GSTexture::Format fmt = t2 ? t2->GetFormat() : GetDefaultTextureFormat(type);
const int levels = t2 ? (t2->IsMipmap() ? MipmapLevelsForSize(w, h) : 1) : 1;
delete t2;
t2 = FetchSurface(type, w, h, levels, fmt, clear, prefer_reuse);
GSTexture* new_t = FetchSurface(type, w, h, levels, fmt, clear, prefer_reuse);
if (new_t)
{
if (t2)
{
// TODO: We probably want to make this optional if we're overwriting it...
const GSVector4 sRect(0, 0, 1, 1);
const GSVector4 dRect(0, 0, t2->GetWidth(), t2->GetHeight());
StretchRect(m_current, sRect, new_t, dRect, ShaderConvert::COPY, true);
Recycle(t2);
}
*t = t2;
t2 = new_t;
*t = t2;
}
}
return t2 != NULL;

View File

@ -296,7 +296,6 @@ struct alignas(16) GSHWDrawConfig
{
struct
{
// *** Word 1
// Format
u32 aem_fmt : 2;
u32 pal_fmt : 2;
@ -328,9 +327,6 @@ struct alignas(16) GSHWDrawConfig
u32 write_rg : 1;
u32 fbmask : 1;
//u32 _free1:0;
// *** Word 2
// Blend and Colclip
u32 blend_a : 2;
u32 blend_b : 2;
@ -366,6 +362,7 @@ struct alignas(16) GSHWDrawConfig
u32 automatic_lod : 1;
u32 manual_lod : 1;
u32 point_sampler : 1;
u32 region_rect : 1;
// Scan mask
u32 scanmsk : 2;
@ -862,6 +859,9 @@ public:
/// Converts a colour format to an indexed format texture.
virtual void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) {}
/// Converts a colour format to an indexed format texture.
virtual void ConvertToIndexedTexture(GSTexture* sTex, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) {}
virtual void RenderHW(GSHWDrawConfig& config) {}
__fi FeatureSupport Features() const { return m_features; }

View File

@ -91,7 +91,6 @@ public:
virtual void GenerateMipmap() {}
virtual bool Save(const std::string& fn);
virtual void Swap(GSTexture* tex);
virtual u32 GetID() { return 0; }
__fi int GetWidth() const { return m_size.x; }
__fi int GetHeight() const { return m_size.y; }

View File

@ -1458,7 +1458,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
PSSetShaderResources(config.tex, config.pal);
GSTexture* rt_copy = nullptr;
GSTexture* ds_copy = nullptr;
if (config.require_one_barrier || (config.tex && config.tex == config.rt)) // Used as "bind rt" flag when texture barrier is unsupported
{
// Bind the RT.This way special effect can use it.
@ -1475,15 +1474,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
}
if (config.tex && config.tex == config.ds)
{
// mainly for ico (depth buffer used as texture)
// binding to 0 here is safe, because config.tex can't equal both tex and rt
CloneTexture(config.ds, &ds_copy, config.drawarea);
if (ds_copy)
PSSetShaderResource(0, ds_copy);
}
SetupVS(config.vs, &config.cb_vs);
SetupGS(config.gs);
SetupPS(config.ps, &config.cb_ps, config.sampler);
@ -1556,8 +1546,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
if (rt_copy)
Recycle(rt_copy);
if (ds_copy)
Recycle(ds_copy);
if (primid_tex)
Recycle(primid_tex);

View File

@ -158,6 +158,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
sm.AddMacro("PS_LTF", sel.ltf);
sm.AddMacro("PS_TCOFFSETHACK", sel.tcoffsethack);
sm.AddMacro("PS_POINT_SAMPLER", sel.point_sampler);
sm.AddMacro("PS_REGION_RECT", sel.region_rect);
sm.AddMacro("PS_SHUFFLE", sel.shuffle);
sm.AddMacro("PS_READ_BA", sel.read_ba);
sm.AddMacro("PS_READ16_SRC", sel.real16src);

View File

@ -1622,6 +1622,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
sm.AddMacro("PS_LTF", sel.ltf);
sm.AddMacro("PS_TCOFFSETHACK", sel.tcoffsethack);
sm.AddMacro("PS_POINT_SAMPLER", sel.point_sampler);
sm.AddMacro("PS_REGION_RECT", sel.region_rect);
sm.AddMacro("PS_SHUFFLE", sel.shuffle);
sm.AddMacro("PS_READ_BA", sel.read_ba);
sm.AddMacro("PS_READ16_SRC", sel.real16src);
@ -2582,7 +2583,6 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
GSTexture12* draw_ds = static_cast<GSTexture12*>(config.ds);
GSTexture12* draw_rt_clone = nullptr;
GSTexture12* hdr_rt = nullptr;
GSTexture12* copy_ds = nullptr;
// Switch to hdr target for colclip rendering
if (pipe.ps.hdr)
@ -2634,30 +2634,9 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
}
if (config.tex)
{
if (config.tex == config.ds)
{
// requires a copy of the depth buffer. this is mainly for ico.
copy_ds = static_cast<GSTexture12*>(CreateDepthStencil(rtsize.x, rtsize.y, GSTexture::Format::DepthStencil, false));
if (copy_ds)
{
EndRenderPass();
GL_PUSH("Copy depth to temp texture for shuffle {%d,%d %dx%d}",
config.drawarea.left, config.drawarea.top,
config.drawarea.width(), config.drawarea.height());
pxAssert(copy_ds->GetState() == GSTexture::State::Invalidated);
CopyRect(config.ds, copy_ds, GSVector4i(config.ds->GetSize()).zwxy(), 0, 0);
PSSetShaderResource(0, copy_ds, true);
}
}
}
// clear texture binding when it's bound to RT or DS
else if (m_tfx_textures[0] &&
((config.rt && static_cast<GSTexture12*>(config.rt)->GetSRVDescriptor() == m_tfx_textures[0]) ||
(config.ds && static_cast<GSTexture12*>(config.ds)->GetSRVDescriptor() == m_tfx_textures[0])))
if (((config.rt && static_cast<GSTexture12*>(config.rt)->GetSRVDescriptor() == m_tfx_textures[0]) ||
(config.ds && static_cast<GSTexture12*>(config.ds)->GetSRVDescriptor() == m_tfx_textures[0])))
{
PSSetShaderResource(0, nullptr, false);
}
@ -2748,9 +2727,6 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
}
if (copy_ds)
Recycle(copy_ds);
if (draw_rt_clone)
Recycle(draw_rt_clone);

View File

@ -110,6 +110,9 @@ bool GSHwHack::GSC_Manhunt2(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
bool GSHwHack::GSC_CrashBandicootWoC(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
{
if (s_nativeres)
return false;
// Channel effect not properly supported - Removes fog to fix the fog wall issue on Direct3D at any resolution, and while upscaling on every Hardware renderer.
if (skip == 0)
{
@ -548,6 +551,18 @@ bool GSHwHack::GSC_UrbanReign(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
{
skip = 1; // Black shadow
}
// Urban Reign downsamples the framebuffer with page-wide columns at a time, and offsets the TBP0 forward as such,
// which would be fine, except their texture coordinates appear to be off by one. Which prevents the page translation
// from matching the last column, because it's trying to fit the last 65 columns of a 640x448 (effectively 641x448)
// texture into a 640x448 render target.
if (fi.TME && fi.TBP0 != fi.FBP && fi.FPSM == PSM_PSMCT32 && fi.TPSM == PSM_PSMCT32 &&
RCONTEXT->FRAME.FBW == (RCONTEXT->TEX0.TBW / 2) && RCONTEXT->CLAMP.WMS == CLAMP_REGION_CLAMP &&
RCONTEXT->CLAMP.WMT == CLAMP_REGION_CLAMP && ((r.m_vt.m_max.t == GSVector4(64.0f, 448.0f)).mask() == 0x3))
{
GL_CACHE("GSC_UrbanReign: Fix region clamp to 64 wide");
r.m_context->CLAMP.MAXU = 63;
}
}
return true;
@ -810,7 +825,7 @@ bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds
const u32 c = vi.RGBAQ.U32[0];
r.m_mem.WritePixel32(x, y, c, FBP, FBW);
}
r.m_tc->InvalidateVideoMem(r.m_context->offset.fb, r.m_r);
g_texture_cache->InvalidateVideoMem(r.m_context->offset.fb, r.m_r);
return false;
}
return true;
@ -981,7 +996,7 @@ bool GSHwHack::OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTextu
TEX0.TBW = RCONTEXT->FRAME.FBW;
TEX0.PSM = RCONTEXT->FRAME.PSM;
if (GSTextureCache::Target* tmp_rt = r.m_tc->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true))
if (GSTextureCache::Target* tmp_rt = g_texture_cache->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true))
{
GL_INS("OI_RozenMaidenGebetGarden FB clear");
g_gs_device->ClearRenderTarget(tmp_rt->m_texture, 0);
@ -999,7 +1014,7 @@ bool GSHwHack::OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTextu
TEX0.TBW = RCONTEXT->FRAME.FBW;
TEX0.PSM = RCONTEXT->ZBUF.PSM;
if (GSTextureCache::Target* tmp_ds = r.m_tc->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil, true))
if (GSTextureCache::Target* tmp_ds = g_texture_cache->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil, true))
{
GL_INS("OI_RozenMaidenGebetGarden ZB clear");
g_gs_device->ClearDepth(tmp_ds->m_texture);
@ -1032,7 +1047,7 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
GL_INS("OI_SonicUnleashed replace draw by a copy");
GSTextureCache::Target* src = r.m_tc->LookupTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true);
GSTextureCache::Target* src = g_texture_cache->LookupTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true);
const GSVector2i src_size(src->m_texture->GetSize());
GSVector2i rt_size(rt->GetSize());
@ -1040,7 +1055,7 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
// This is awful, but so is the CRC hack... it's a texture shuffle split horizontally instead of vertically.
if (rt_size.x < src_size.x || rt_size.y < src_size.y)
{
GSTextureCache::Target* rt_again = r.m_tc->LookupTarget(Frame, src_size, src->m_scale, GSTextureCache::RenderTarget, true);
GSTextureCache::Target* rt_again = g_texture_cache->LookupTarget(Frame, src_size, src->m_scale, GSTextureCache::RenderTarget, true);
if (rt_again->m_unscaled_size.x < src->m_unscaled_size.x || rt_again->m_unscaled_size.y < src->m_unscaled_size.y)
{
rt_again->ResizeTexture(std::max(rt_again->m_unscaled_size.x, src->m_unscaled_size.x),
@ -1122,7 +1137,7 @@ bool GSHwHack::GSC_Battlefield2(GSRendererHW& r, const GSFrameInfo& fi, int& ski
GIFRegTEX0 TEX0 = {};
TEX0.TBP0 = fi.FBP;
TEX0.TBW = 8;
GSTextureCache::Target* dst = r.m_tc->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil, true);
GSTextureCache::Target* dst = g_texture_cache->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil, true);
if (dst)
{
g_gs_device->ClearDepth(dst->m_texture);
@ -1144,10 +1159,25 @@ bool GSHwHack::OI_Battlefield2(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GS
g_gs_device->CopyRect(t->m_texture, rt, rc, 0, 0);
}
r.m_tc->InvalidateTemporarySource();
g_texture_cache->InvalidateTemporarySource();
return false;
}
bool GSHwHack::OI_HauntingGround(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
// Haunting Ground clears two targets by doing a 256x448 direct colour write at 0x3000, covering a target at 0x3380.
// This currently isn't handled in our HLE clears, so we need to manually remove the other target.
if (rt && !ds && !t && r.IsConstantDirectWriteMemClear(true))
{
GL_CACHE("GSHwHack::OI_HauntingGround()");
g_texture_cache->InvalidateVideoMemTargets(GSTextureCache::RenderTarget, RCONTEXT->FRAME.Block(),
RCONTEXT->FRAME.FBW, RCONTEXT->FRAME.PSM, r.m_r);
}
// Not skipping anything. This is just an invalidation hack.
return true;
}
#undef RCONTEXT
#undef RPRIM
@ -1224,6 +1254,7 @@ const GSHwHack::Entry<GSRendererHW::OI_Ptr> GSHwHack::s_before_draw_functions[]
CRC_F(OI_ArTonelico2, CRCHackLevel::Minimum),
CRC_F(OI_BurnoutGames, CRCHackLevel::Minimum),
CRC_F(OI_Battlefield2, CRCHackLevel::Minimum),
CRC_F(OI_HauntingGround, CRCHackLevel::Minimum)
};
#undef CRC_F

View File

@ -62,8 +62,8 @@ public:
static bool OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_ArTonelico2(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_BurnoutGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_Battlefield2(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_HauntingGround(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
template <typename F>
struct Entry

File diff suppressed because it is too large Load Diff

View File

@ -84,14 +84,21 @@ private:
template <bool linear>
void RoundSpriteOffset();
void DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Target* ds, GSTextureCache::Source* tex);
void DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Target* ds, GSTextureCache::Source* tex, const TextureMinMaxResult& tmm);
void ResetStates();
void SetupIA(float target_scale, float sx, float sy);
void EmulateTextureShuffleAndFbmask();
void EmulateChannelShuffle(const GSTextureCache::Source* tex);
bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only);
void EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass);
void EmulateTextureSampler(const GSTextureCache::Source* tex);
void EmulateTextureSampler(const GSTextureCache::Target* rt, const GSTextureCache::Target* ds,
GSTextureCache::Source* tex, const TextureMinMaxResult& tmm, GSTexture*& src_copy);
void HandleTextureHazards(const GSTextureCache::Target* rt, const GSTextureCache::Target* ds,
const GSTextureCache::Source* tex, const TextureMinMaxResult& tmm, GSTextureCache::SourceRegion& source_region,
bool& target_region, GSVector2i& unscaled_size, float& scale, GSTexture*& src_copy);
bool CanUseTexIsFB(const GSTextureCache::Target* rt, const GSTextureCache::Source* tex) const;
void EmulateZbuffer();
void EmulateATST(float& AREF, GSHWDrawConfig::PSSelector& ps, bool pass_2);
@ -101,9 +108,7 @@ private:
bool IsSplitTextureShuffle();
GSVector4i GetSplitTextureShuffleDrawRect() const;
GSTextureCache* m_tc;
GSVector4i m_r = {};
GSTextureCache::Source* m_src = nullptr;
// CRC Hacks
bool IsBadFrame();
@ -120,7 +125,6 @@ private:
u32 m_last_channel_shuffle_fbmsk = 0;
bool m_channel_shuffle = false;
bool m_tex_is_fb = false;
bool m_userhacks_tcoffset = false;
float m_userhacks_tcoffset_x = 0.0f;
float m_userhacks_tcoffset_y = 0.0f;
@ -143,7 +147,6 @@ public:
virtual ~GSRendererHW() override;
__fi static GSRendererHW* GetInstance() { return static_cast<GSRendererHW*>(g_gs_renderer.get()); }
__fi GSTextureCache* GetTextureCache() const { return m_tc; }
void Destroy() override;
@ -177,9 +180,6 @@ public:
void ReadbackTextureCache() override;
GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, float* scale, const GSVector2i& size) override;
// Called by the texture cache to know if current texture is useful
bool UpdateTexIsFB(GSTextureCache::Target* src, const GIFRegTEX0& TEX0);
// Called by the texture cache when optimizing the copy range for sources
bool IsPossibleTextureShuffle(GSTextureCache::Target* dst, const GIFRegTEX0& TEX0) const;
/// Called by the texture cache to know for certain whether there is a channel shuffle.
bool TestChannelShuffle(GSTextureCache::Target* src);
};

View File

@ -199,7 +199,7 @@ bool GSRendererHWFunctions::SwPrimRender(GSRendererHW& hw, bool invalidate_tc)
GIFRegTEX0 TEX0 = context->GetSizeFixedTEX0(vt.m_min.t.xyxy(vt.m_max.t), vt.IsLinear(), mipmap);
const GSVector4i r = hw.GetTextureMinMax(TEX0, context->CLAMP, gd.sel.ltf).coverage;
const GSVector4i r = hw.GetTextureMinMax(TEX0, context->CLAMP, gd.sel.ltf, true).coverage;
if (!hw.m_sw_texture[0])
hw.m_sw_texture[0] = std::make_unique<GSTextureCacheSW::Texture>(0, TEX0, env.TEXA);
@ -300,7 +300,7 @@ bool GSRendererHWFunctions::SwPrimRender(GSRendererHW& hw, bool invalidate_tc)
else
hw.m_sw_texture[i]->Reset(gd.sel.tw + 3, MIP_TEX0, env.TEXA);
GSVector4i r = hw.GetTextureMinMax(MIP_TEX0, MIP_CLAMP, gd.sel.ltf).coverage;
GSVector4i r = hw.GetTextureMinMax(MIP_TEX0, MIP_CLAMP, gd.sel.ltf, true).coverage;
hw.m_sw_texture[i]->Update(r);
gd.tex[i] = hw.m_sw_texture[i]->m_buff;
}
@ -554,7 +554,7 @@ bool GSRendererHWFunctions::SwPrimRender(GSRendererHW& hw, bool invalidate_tc)
static_cast<GSSingleRasterizer*>(hw.m_sw_rasterizer.get())->Draw(data);
if (invalidate_tc)
hw.m_tc->InvalidateVideoMem(context->offset.fb, bbox);
g_texture_cache->InvalidateVideoMem(context->offset.fb, bbox);
return true;
}

File diff suppressed because it is too large Load Diff

View File

@ -64,10 +64,14 @@ public:
u32 GetHeight() const { return (GetMaxY() - GetMinY()); }
/// Returns true if the area of the region exceeds the TW/TH size (i.e. "fixed tex0").
bool IsFixedTEX0(GIFRegTEX0 TEX0) const;
bool IsFixedTEX0(int tw, int th) const;
bool IsFixedTEX0W(int tw) const;
bool IsFixedTEX0H(int th) const;
/// Returns the size that the region occupies.
GSVector2i GetSize(int tw, int th) const;
/// Returns the rectangle relative to the texture base pointer that the region occupies.
GSVector4i GetRect(int tw, int th) const;
@ -80,6 +84,9 @@ public:
/// Adjusts the texture base pointer and block width relative to the region.
void AdjustTEX0(GIFRegTEX0* TEX0) const;
/// Creates a new source region based on the CLAMP register.
static SourceRegion Create(GIFRegTEX0 TEX0, GIFRegCLAMP CLAMP);
};
using HashType = u64;
@ -146,8 +153,6 @@ public:
/// Can be used for overlap tests.
u32 UnwrappedEndBlock() const { return (m_end_block + (Wraps() ? MAX_BLOCKS : 0)); }
void UpdateAge();
bool Inside(u32 bp, u32 bw, u32 psm, const GSVector4i& rect);
bool Overlaps(u32 bp, u32 bw, u32 psm, const GSVector4i& rect);
};
@ -196,58 +201,6 @@ public:
bool operator()(const PaletteKey& lhs, const PaletteKey& rhs) const;
};
class Source : public Surface
{
struct
{
GSVector4i* rect;
u32 count;
} m_write = {};
void PreloadLevel(int level);
void Write(const GSVector4i& r, int layer);
void Flush(u32 count, int layer);
public:
HashCacheEntry* m_from_hash_cache = nullptr;
std::shared_ptr<Palette> m_palette_obj;
std::unique_ptr<u32[]> m_valid;// each u32 bits map to the 32 blocks of that page
GSTexture* m_palette = nullptr;
GSVector4i m_valid_rect = {};
GSVector2i m_lod = {};
SourceRegion m_region = {};
u8 m_valid_hashes = 0;
u8 m_complete_layers = 0;
bool m_target = false;
bool m_repeating = false;
std::vector<GSVector2i>* m_p2t = nullptr;
// Keep a trace of the target origin. There is no guarantee that pointer will
// still be valid on future. However it ought to be good when the source is created
// so it can be used to access un-converted data for the current draw call.
GSTexture** m_from_target = nullptr;
GIFRegTEX0 m_from_target_TEX0 = {}; // TEX0 of the target texture, if any, else equal to texture TEX0
GIFRegTEX0 m_layer_TEX0[7] = {}; // Detect already loaded value
HashType m_layer_hash[7] = {};
// Keep a GSTextureCache::SourceMap::m_map iterator to allow fast erase
// Deliberately not initialized to save cycles.
std::array<u16, MAX_PAGES> m_erase_it;
GSOffset::PageLooper m_pages;
public:
Source(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA);
virtual ~Source();
__fi bool CanPreload() const { return CanPreloadTextureSize(m_TEX0.TW, m_TEX0.TH); }
void SetPages();
void Update(const GSVector4i& rect, int layer = 0);
void UpdateLayer(const GIFRegTEX0& TEX0, const GSVector4i& rect, int layer = 0);
bool ClutMatch(const PaletteKey& palette_key);
};
class Target : public Surface
{
public:
@ -282,6 +235,63 @@ public:
bool ResizeTexture(int new_unscaled_width, int new_unscaled_height, bool recycle_old = true);
};
class Source : public Surface
{
struct
{
GSVector4i* rect;
u32 count;
} m_write = {};
void PreloadLevel(int level);
void Write(const GSVector4i& r, int layer);
void Flush(u32 count, int layer);
public:
HashCacheEntry* m_from_hash_cache = nullptr;
std::shared_ptr<Palette> m_palette_obj;
std::unique_ptr<u32[]> m_valid;// each u32 bits map to the 32 blocks of that page
GSTexture* m_palette = nullptr;
GSVector4i m_valid_rect = {};
GSVector2i m_lod = {};
SourceRegion m_region = {};
u8 m_valid_hashes = 0;
u8 m_complete_layers = 0;
bool m_target = false;
bool m_repeating = false;
std::vector<GSVector2i>* m_p2t = nullptr;
// Keep a trace of the target origin. There is no guarantee that pointer will
// still be valid on future. However it ought to be good when the source is created
// so it can be used to access un-converted data for the current draw call.
Target* m_from_target = nullptr;
GIFRegTEX0 m_from_target_TEX0 = {}; // TEX0 of the target texture, if any, else equal to texture TEX0
GIFRegTEX0 m_layer_TEX0[7] = {}; // Detect already loaded value
HashType m_layer_hash[7] = {};
// Keep a GSTextureCache::SourceMap::m_map iterator to allow fast erase
// Deliberately not initialized to save cycles.
std::array<u16, MAX_PAGES> m_erase_it;
GSOffset::PageLooper m_pages;
public:
Source(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA);
virtual ~Source();
__fi bool CanPreload() const { return CanPreloadTextureSize(m_TEX0.TW, m_TEX0.TH); }
__fi bool IsFromTarget() const { return m_target; }
__fi const SourceRegion& GetRegion() const { return m_region; }
__fi GSVector2i GetRegionSize() const { return m_region.GetSize(m_unscaled_size.x, m_unscaled_size.y); }
__fi GSVector4i GetRegionRect() const { return m_region.GetRect(m_unscaled_size.x, m_unscaled_size.y); }
void SetPages();
void Update(const GSVector4i& rect, int layer = 0);
void UpdateLayer(const GIFRegTEX0& TEX0, const GSVector4i& rect, int layer = 0);
bool ClutMatch(const PaletteKey& palette_key);
};
class PaletteMap
{
private:
@ -328,7 +338,7 @@ public:
struct
{
u32 fbp : 14;
u32 bp : 14;
u32 fbw : 6;
u32 psm : 6;
u32 pad : 6;
@ -434,20 +444,27 @@ public:
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, bool palette = false);
Target* FindTargetOverlap(u32 bp, u32 end_block, int type, int psm);
Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, float scale, int type, bool used, u32 fbmask = 0, const bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool is_clear = false);
Target* LookupDisplayTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, float scale);
Target* LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, bool used, u32 fbmask = 0, const bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool is_clear = false);
Target* LookupDisplayTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale);
/// Looks up a target in the cache, and only returns it if the BP/BW/PSM match exactly.
Target* GetExactTarget(u32 BP, u32 BW, u32 PSM) const;
Target* GetTargetWithSharedBits(u32 BP, u32 PSM) const;
u32 GetTargetHeight(u32 fbp, u32 fbw, u32 psm, u32 min_height);
u32 GetTargetHeight(u32 bp, u32 fbw, u32 psm, u32 min_height);
bool Has32BitTarget(u32 bp);
void InvalidateVideoMemType(int type, u32 bp);
void InvalidateVideoMemSubTarget(GSTextureCache::Target* rt);
void InvalidateVideoMem(const GSOffset& off, const GSVector4i& r, bool eewrite = false, bool target = true);
void InvalidateLocalMem(const GSOffset& off, const GSVector4i& r);
/// Removes any targets overlapping the specified BP and rectangle.
void InvalidateVideoMemTargets(int type, u32 bp, u32 bw, u32 psm, const GSVector4i& r);
/// Removes any sources which point to the specified target.
void InvalidateSourcesFromTarget(const Target* t);
bool Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u32 DBW, u32 DPSM, int dx, int dy, int w, int h);
bool ShuffleMove(u32 BP, u32 BW, u32 PSM, int sx, int sy, int dx, int dy, int w, int h);
@ -470,3 +487,5 @@ public:
/// Injects a texture into the hash cache, by using GSTexture::Swap(), transitively applying to all sources. Ownership of tex is transferred.
void InjectHashCacheTexture(const HashCacheKey& key, GSTexture* tex);
};
extern std::unique_ptr<GSTextureCache> g_texture_cache;

View File

@ -130,9 +130,6 @@ namespace GSTextureReplacements
static std::string s_current_serial;
/// Backreference to the texture cache so we can inject replacements.
static GSTextureCache* s_tc;
/// Textures that have been dumped, to save stat() calls.
static std::unordered_set<TextureName> s_dumped_textures;
@ -302,9 +299,8 @@ std::string GSTextureReplacements::GetDumpFilename(const TextureName& name, u32
return ret;
}
void GSTextureReplacements::Initialize(GSTextureCache* tc)
void GSTextureReplacements::Initialize()
{
s_tc = tc;
s_current_serial = VMManager::GetGameSerial();
if (GSConfig.DumpReplaceableTextures || GSConfig.LoadTextureReplacements)
@ -315,9 +311,6 @@ void GSTextureReplacements::Initialize(GSTextureCache* tc)
void GSTextureReplacements::GameChanged()
{
if (!s_tc)
return;
std::string new_serial(VMManager::GetGameSerial());
if (s_current_serial == new_serial)
return;
@ -420,7 +413,6 @@ void GSTextureReplacements::Shutdown()
std::string().swap(s_current_serial);
ClearReplacementTextures();
ClearDumpedTextureList();
s_tc = nullptr;
}
u32 GSTextureReplacements::CalcMipmapLevelsForReplacement(u32 width, u32 height)
@ -637,7 +629,7 @@ void GSTextureReplacements::ProcessAsyncLoadedTextures()
// upload and inject into TC
GSTexture* tex = CreateReplacementTexture(it->second, mipmap);
if (tex)
s_tc->InjectHashCacheTexture(HashCacheKeyFromTextureName(name), tex);
g_texture_cache->InjectHashCacheTexture(HashCacheKeyFromTextureName(name), tex);
}
s_async_loaded_textures.clear();
}

View File

@ -38,7 +38,7 @@ namespace GSTextureReplacements
std::vector<MipData> mips;
};
void Initialize(GSTextureCache* tc);
void Initialize();
void GameChanged();
void ReloadReplacementMap();
void UpdateConfig(Pcsx2Config::GSOptions& old_config);

View File

@ -1027,6 +1027,7 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
+ fmt::format("#define PS_DATE {}\n", sel.date)
+ fmt::format("#define PS_TCOFFSETHACK {}\n", sel.tcoffsethack)
+ fmt::format("#define PS_POINT_SAMPLER {}\n", sel.point_sampler)
+ fmt::format("#define PS_REGION_RECT {}\n", sel.region_rect)
+ fmt::format("#define PS_BLEND_A {}\n", sel.blend_a)
+ fmt::format("#define PS_BLEND_B {}\n", sel.blend_b)
+ fmt::format("#define PS_BLEND_C {}\n", sel.blend_c)
@ -1164,7 +1165,7 @@ void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
// Init
// ************************************
GL_PUSH("StretchRect from %d to %d", sTex->GetID(), dTex->GetID());
GL_PUSH("StretchRect from %d to %d", static_cast<GSTextureOGL*>(sTex)->GetID(), static_cast<GSTextureOGL*>(dTex)->GetID());
if (draw_in_depth)
OMSetRenderTargets(NULL, dTex);
else

View File

@ -66,7 +66,7 @@ public:
return (m_int_type == GL_UNSIGNED_BYTE || m_int_type == GL_UNSIGNED_SHORT || m_int_type == GL_UNSIGNED_INT);
}
u32 GetID() final { return m_texture_id; }
u32 GetID() { return m_texture_id; }
bool HasBeenCleaned() { return m_clean; }
void WasAttached() { m_clean = false; }
void WasCleaned() { m_clean = true; }

View File

@ -1065,7 +1065,7 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data)
GIFRegTEX0 TEX0 = m_context->GetSizeFixedTEX0(m_vt.m_min.t.xyxy(m_vt.m_max.t), m_vt.IsLinear(), mipmap);
GSVector4i r = GetTextureMinMax(TEX0, context->CLAMP, gd.sel.ltf).coverage;
GSVector4i r = GetTextureMinMax(TEX0, context->CLAMP, gd.sel.ltf, true).coverage;
GSTextureCacheSW::Texture* t = m_tc->Lookup(TEX0, env.TEXA);
@ -1171,7 +1171,7 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data)
return false;
}
GSVector4i r = GetTextureMinMax(MIP_TEX0, MIP_CLAMP, gd.sel.ltf).coverage;
GSVector4i r = GetTextureMinMax(MIP_TEX0, MIP_CLAMP, gd.sel.ltf, true).coverage;
data->SetSource(t, r, i);
}

View File

@ -2085,6 +2085,7 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector
AddMacro(ss, "PS_DATE", sel.date);
AddMacro(ss, "PS_TCOFFSETHACK", sel.tcoffsethack);
AddMacro(ss, "PS_POINT_SAMPLER", sel.point_sampler);
AddMacro(ss, "PS_REGION_RECT", sel.region_rect);
AddMacro(ss, "PS_BLEND_A", sel.blend_a);
AddMacro(ss, "PS_BLEND_B", sel.blend_b);
AddMacro(ss, "PS_BLEND_C", sel.blend_c);
@ -3058,7 +3059,6 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
GSTextureVK* draw_ds = static_cast<GSTextureVK*>(config.ds);
GSTextureVK* draw_rt_clone = nullptr;
GSTextureVK* hdr_rt = nullptr;
GSTextureVK* copy_ds = nullptr;
// Switch to hdr target for colclip rendering
if (pipe.ps.hdr)
@ -3109,26 +3109,6 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
}
}
if (config.tex)
{
if (config.tex == config.ds)
{
// requires a copy of the depth buffer. this is mainly for ico.
copy_ds = static_cast<GSTextureVK*>(CreateDepthStencil(rtsize.x, rtsize.y, GSTexture::Format::DepthStencil, false));
if (copy_ds)
{
EndRenderPass();
GL_PUSH("Copy depth to temp texture for shuffle {%d,%d %dx%d}",
config.drawarea.left, config.drawarea.top,
config.drawarea.width(), config.drawarea.height());
pxAssert(copy_ds->GetState() == GSTexture::State::Invalidated);
CopyRect(config.ds, copy_ds, GSVector4i(config.ds->GetSize()).zwxy(), 0, 0);
PSSetShaderResource(0, copy_ds, true);
}
}
}
// clear texture binding when it's bound to RT or DS
if (!config.tex && ((config.rt && static_cast<GSTextureVK*>(config.rt)->GetView() == m_tfx_textures[0]) ||
(config.ds && static_cast<GSTextureVK*>(config.ds)->GetView() == m_tfx_textures[0])))
@ -3258,9 +3238,6 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
}
}
if (copy_ds)
Recycle(copy_ds);
if (draw_rt_clone)
Recycle(draw_rt_clone);