GS/TC: Break LookupTarget() into 3 functions.

Separate draw lookup from frame lookup. Put common code in another processing function.
This commit is contained in:
TJnotJT
2026-04-17 20:17:46 +02:00
committed by lightningterror
parent fa9c99a948
commit 09fcc4d60e
4 changed files with 800 additions and 725 deletions
+19 -19
View File
@@ -50,8 +50,8 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
GIFRegTEX0 RTLookup = GIFRegTEX0::Create(RTBP0, RFBW, RFPSM);
GSTextureCache::Source* src = g_texture_cache->LookupSource(true, RTLookup, r.m_cached_ctx.TEXA, r.m_cached_ctx.CLAMP, GSVector4i(0, 0, 1, 1), nullptr, true, false, r.m_cached_ctx.FRAME, true, true);
GSTextureCache::Target* rt = g_texture_cache->LookupTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i(0, 0, 1, 1), true, false, true, src);
GSTextureCache::Target* rt = g_texture_cache->LookupDrawTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, true, true, GSVector4i(0, 0, 1, 1), true, false, true, src);
if (!rt)
return false;
@@ -138,8 +138,8 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
GIFRegTEX0 RTLookup = GIFRegTEX0::Create(RTBP0, RFBW, RFPSM);
GSTextureCache::Source* src = g_texture_cache->LookupSource(true, RTLookup, r.m_cached_ctx.TEXA, r.m_cached_ctx.CLAMP, GSVector4i(0,0,1,1), nullptr, true, false, r.m_cached_ctx.FRAME, true, true);
GSTextureCache::Target* rt = g_texture_cache->LookupTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i(0,0,1,1), true, false, true, src);
GSTextureCache::Target* rt = g_texture_cache->LookupDrawTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, true, true, GSVector4i(0,0,1,1), true, false, true, src);
if (!rt)
return false;
@@ -283,7 +283,7 @@ bool GSHwHack::GSC_DTGames(GSRendererHW& r, int& skip)
// The further problem to this is the limitation of alpha we can save on an RT as they copy in 255, so I can cheese it here pretending it's RTA'd
if (RTME && RFPSM == PSMCT32 && RTBP0 == RFBP && RTPSM == PSMCT16 && RTEST.ATE && RTEST.ATST == ATST_NEVER && RTEST.AFAIL == AFAIL_FB_ONLY && RFBMSK == 0xFFFFFF)
{
GSTextureCache::Target* rt = g_texture_cache->LookupTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSTextureCache::Target* rt = g_texture_cache->LookupDrawTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget);
if (!rt)
@@ -373,13 +373,13 @@ bool GSHwHack::GSC_SandGrainGames(GSRendererHW& r, int& skip)
if (r.PRIM->PRIM == GS_SPRITE && RTME && RFPSM == PSMCT16S && RTPSM == PSMZ16S && next_ctx.TEX0.TBP0 == RFBP && next_ctx.TEX0.PSM == PSMT8H)
{
GSTextureCache::Target* texsrc = g_texture_cache->LookupTarget(GIFRegTEX0::Create(RTBP0, RTBW, RTPSM),
GSTextureCache::Target* texsrc = g_texture_cache->LookupDrawTarget(GIFRegTEX0::Create(RTBP0, RTBW, RTPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil);
if (!texsrc)
return false;
GSTextureCache::Target* rt = g_texture_cache->LookupTarget(GIFRegTEX0::Create(next_ctx.FRAME.Block(), next_ctx.FRAME.FBW, next_ctx.FRAME.PSM),
GSTextureCache::Target* rt = g_texture_cache->LookupDrawTarget(GIFRegTEX0::Create(next_ctx.FRAME.Block(), next_ctx.FRAME.FBW, next_ctx.FRAME.PSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget);
if (!rt)
@@ -468,7 +468,7 @@ bool GSHwHack::GSC_BurnoutGames(GSRendererHW& r, int& skip)
break;
// Next draw should contain our source.
GSTextureCache::Target* tgt = g_texture_cache->LookupTarget(r.m_env.CTXT[r.m_backed_up_ctx].TEX0,
GSTextureCache::Target* tgt = g_texture_cache->LookupDrawTarget(r.m_env.CTXT[r.m_backed_up_ctx].TEX0,
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget);
if (!tgt)
break;
@@ -718,8 +718,8 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip)
return false;
}
GSTextureCache::Target* src = g_texture_cache->LookupTarget(RTEX0, GSVector2i(1, 1), r.GetTextureScaleFactor(),
GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i::zero(), true);
GSTextureCache::Target* src = g_texture_cache->LookupDrawTarget(RTEX0, GSVector2i(1, 1), r.GetTextureScaleFactor(),
GSTextureCache::RenderTarget, true, 0, false, true, true, GSVector4i::zero(), true);
if (!src)
return false;
@@ -777,7 +777,7 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip)
for (u32 channel = 0; channel < 3; channel++)
{
const GIFRegTEX0 TEX0 = GIFRegTEX0::Create(base + channel * page_offset, 10, PSMCT32);
GSTextureCache::Target* dst = g_texture_cache->LookupTarget(TEX0, src->GetUnscaledSize(), src->GetScale(), GSTextureCache::RenderTarget, true, fbmsk);
GSTextureCache::Target* dst = g_texture_cache->LookupDrawTarget(TEX0, src->GetUnscaledSize(), src->GetScale(), GSTextureCache::RenderTarget, true, fbmsk);
if (!dst)
{
dst = g_texture_cache->CreateTarget(TEX0, size, size, src->GetScale(), GSTextureCache::RenderTarget, true, fbmsk);
@@ -822,7 +822,7 @@ bool GSHwHack::GSC_Battlefield2(GSRendererHW& r, int& skip)
GIFRegTEX0 TEX0 = {};
TEX0.TBP0 = RFBP;
TEX0.TBW = 8;
GSTextureCache::Target* dst = g_texture_cache->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil);
GSTextureCache::Target* dst = g_texture_cache->LookupDrawTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil);
if (!dst)
dst = g_texture_cache->CreateTarget(TEX0, r.GetTargetSize(), r.GetValidSize(nullptr), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil,
@@ -1067,7 +1067,7 @@ bool GSHwHack::OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTextu
TEX0.TBW = RFRAME.FBW;
TEX0.PSM = RFRAME.PSM;
if (GSTextureCache::Target* tmp_rt = g_texture_cache->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget))
if (GSTextureCache::Target* tmp_rt = g_texture_cache->LookupDrawTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget))
{
GL_INS("OI_RozenMaidenGebetGarden FB clear");
g_gs_device->ClearRenderTarget(tmp_rt->m_texture, 0);
@@ -1089,7 +1089,7 @@ bool GSHwHack::OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTextu
TEX0.TBW = RFRAME.FBW;
TEX0.PSM = RZBUF.PSM;
if (GSTextureCache::Target* tmp_ds = g_texture_cache->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil))
if (GSTextureCache::Target* tmp_ds = g_texture_cache->LookupDrawTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil))
{
GL_INS("OI_RozenMaidenGebetGarden ZB clear");
g_gs_device->ClearDepth(tmp_ds->m_texture, 0.0f);
@@ -1122,14 +1122,14 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
GL_INS("OI_SonicUnleashed replace draw by a copy draw %lld", r.s_n);
GSTextureCache::Target* src = g_texture_cache->LookupTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i::zero(), true);
GSTextureCache::Target* src = g_texture_cache->LookupDrawTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, true, true, GSVector4i::zero(), true);
if (!src)
return true;
const GSVector2i src_size(src->m_texture->GetSize());
GSTextureCache::Target* rt_again = g_texture_cache->LookupTarget(Frame, src_size, src->m_scale, GSTextureCache::RenderTarget);
GSTextureCache::Target* rt_again = g_texture_cache->LookupDrawTarget(Frame, src_size, src->m_scale, GSTextureCache::RenderTarget);
if ((rt_again->m_TEX0.PSM & 0x3) == PSMCT16)
{
GSVector4 dRect;
@@ -1284,15 +1284,15 @@ static bool GetMoveTargetPair(GSRendererHW& r, GSTextureCache::Target** src, GIF
const int src_type =
GSLocalMemory::m_psm[src_desc.PSM].depth ? GSTextureCache::DepthStencil : GSTextureCache::RenderTarget;
GSTextureCache::Target* tsrc =
g_texture_cache->LookupTarget(src_desc, GSVector2i(1, 1), r.GetTextureScaleFactor(), src_type);
g_texture_cache->LookupDrawTarget(src_desc, GSVector2i(1, 1), r.GetTextureScaleFactor(), src_type);
if (!tsrc)
return false;
// The target might not.
const int dst_type =
GSLocalMemory::m_psm[dst_desc.PSM].depth ? GSTextureCache::DepthStencil : GSTextureCache::RenderTarget;
GSTextureCache::Target* tdst = g_texture_cache->LookupTarget(dst_desc, tsrc->GetUnscaledSize(), tsrc->GetScale(),
dst_type, true, 0, false, false, preserve_target, preserve_target, tsrc->GetUnscaledRect());
GSTextureCache::Target* tdst = g_texture_cache->LookupDrawTarget(dst_desc, tsrc->GetUnscaledSize(), tsrc->GetScale(),
dst_type, true, 0, false, preserve_target, preserve_target, tsrc->GetUnscaledRect());
if (!tdst)
{
if (req_target)
+8 -8
View File
@@ -3000,8 +3000,8 @@ void GSRendererHW::Draw()
FRAME.TBW = m_conf.colclip_frame.FBW;
FRAME.PSM = m_conf.colclip_frame.PSM;
GSTextureCache::Target* old_rt = g_texture_cache->LookupTarget(FRAME, GSVector2i(1, 1), GetTextureScaleFactor(), GSTextureCache::RenderTarget, true,
fm, false, false, true, true, GSVector4i(0, 0, 1, 1), true, false, false);
GSTextureCache::Target* old_rt = g_texture_cache->LookupDrawTarget(FRAME, GSVector2i(1, 1), GetTextureScaleFactor(), GSTextureCache::RenderTarget, true,
fm, false, true, true, GSVector4i(0, 0, 1, 1), true, false, false);
if (old_rt)
{
@@ -3686,8 +3686,8 @@ void GSRendererHW::Draw()
ZBUF_TEX0.TBW = m_cached_ctx.FRAME.FBW;
ZBUF_TEX0.PSM = m_cached_ctx.ZBUF.PSM;
ds = g_texture_cache->LookupTarget(ZBUF_TEX0, t_size, target_scale, GSTextureCache::DepthStencil,
m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, preserve_depth, unclamped_draw_rect, IsPossibleChannelShuffle(), is_possible_mem_clear && ZBUF_TEX0.TBP0 != m_cached_ctx.FRAME.Block(), !no_rt,
ds = g_texture_cache->LookupDrawTarget(ZBUF_TEX0, t_size, target_scale, GSTextureCache::DepthStencil,
m_cached_ctx.DepthWrite(), 0, force_preload, preserve_depth, preserve_depth, unclamped_draw_rect, IsPossibleChannelShuffle(), is_possible_mem_clear && ZBUF_TEX0.TBP0 != m_cached_ctx.FRAME.Block(), !no_rt,
src, nullptr, -1);
ZBUF_TEX0.TBW = m_channel_shuffle ? src->m_from_target_TEX0.TBW : m_cached_ctx.FRAME.FBW;
@@ -3946,8 +3946,8 @@ void GSRendererHW::Draw()
// Of course if this size is different (in width) or this is a shuffle happening, this will be bypassed.
const bool preserve_downscale_draw = (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && ((std::abs(scale_draw) == 1 && !scaled_copy) || (scale_draw == 0 && src && src->m_from_target && src->m_from_target->m_downscaled))) || is_possible_mem_clear == ClearType::ClearWithDraw;
rt = g_texture_cache->LookupTarget(FRAME_TEX0, t_size, ((src && src->m_scale != 1) && (GSConfig.UserHacks_NativeScaling == GSNativeScaling::Normal || GSConfig.UserHacks_NativeScaling == GSNativeScaling::NormalUpscaled) && !possible_shuffle) ? GetTextureScaleFactor() : target_scale, GSTextureCache::RenderTarget, true,
fm, false, force_preload, preserve_rt_rgb, preserve_rt_alpha, lookup_rect, possible_shuffle, is_possible_mem_clear && FRAME_TEX0.TBP0 != m_cached_ctx.ZBUF.Block(),
rt = g_texture_cache->LookupDrawTarget(FRAME_TEX0, t_size, ((src && src->m_scale != 1) && (GSConfig.UserHacks_NativeScaling == GSNativeScaling::Normal || GSConfig.UserHacks_NativeScaling == GSNativeScaling::NormalUpscaled) && !possible_shuffle) ? GetTextureScaleFactor() : target_scale, GSTextureCache::RenderTarget, true,
fm, force_preload, preserve_rt_rgb, preserve_rt_alpha, lookup_rect, possible_shuffle, is_possible_mem_clear && FRAME_TEX0.TBP0 != m_cached_ctx.ZBUF.Block(),
GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && preserve_downscale_draw && is_possible_mem_clear != ClearType::NormalClear, src, ds, (no_ds || !ds) ? -1 : (m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0));
// Draw skipped because it was a clear and there was no target.
@@ -4328,8 +4328,8 @@ void GSRendererHW::Draw()
ZBUF_TEX0.TBW = m_cached_ctx.FRAME.FBW;
ZBUF_TEX0.PSM = m_cached_ctx.ZBUF.PSM;
ds = g_texture_cache->LookupTarget(ZBUF_TEX0, t_size, target_scale, GSTextureCache::DepthStencil,
m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, preserve_depth, unclamped_draw_rect, IsPossibleChannelShuffle(), is_possible_mem_clear && ZBUF_TEX0.TBP0 != m_cached_ctx.FRAME.Block(), false,
ds = g_texture_cache->LookupDrawTarget(ZBUF_TEX0, t_size, target_scale, GSTextureCache::DepthStencil,
m_cached_ctx.DepthWrite(), 0, force_preload, preserve_depth, preserve_depth, unclamped_draw_rect, IsPossibleChannelShuffle(), is_possible_mem_clear && ZBUF_TEX0.TBP0 != m_cached_ctx.FRAME.Block(), false,
src, nullptr, -1);
ZBUF_TEX0.TBW = m_channel_shuffle ? src->m_from_target_TEX0.TBW : m_cached_ctx.FRAME.FBW;
+432 -383
View File
@@ -2385,36 +2385,46 @@ void GSTextureCache::CombineAlignedInsideTargets(Target* target, GSTextureCache:
}
}
GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type,
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_rgb, bool preserve_alpha, const GSVector4i draw_rect,
GSTextureCache::RescaleHelper::RescaleHelper(const GSVector2i& size, float scale)
: m_size(size)
, m_scale(scale)
{
}
void GSTextureCache::RescaleHelper::CalcRescale(const Target* tgt)
{
// TODO Possible optimization: rescale only the validity rectangle of the old target texture into the new one.
m_clear = (m_size.x > tgt->m_unscaled_size.x || m_size.y > tgt->m_unscaled_size.y);
SetNewSize(m_size.max(tgt->m_unscaled_size), m_scale);
m_dRect = (GSVector4(GSVector4i::loadh(tgt->m_unscaled_size)) * GSVector4(m_scale)).ceil();
GL_INS("TC: Rescale: %dx%d: %dx%d @ %f -> %dx%d @ %f",
tgt->m_unscaled_size.x, tgt->m_unscaled_size.y,
tgt->m_texture->GetWidth(), tgt->m_texture->GetHeight(), tgt->m_scale,
m_new_scaled_size.x, m_new_scaled_size.y, m_scale);
}
void GSTextureCache::RescaleHelper::SetNewSize(const GSVector2i& new_size, float scale)
{
m_new_size = new_size;
m_new_scaled_size = ScaleRenderTargetSize(new_size, scale);
}
GSTextureCache::Target* GSTextureCache::LookupDrawTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type,
bool used, u32 fbmask, bool preload, bool preserve_rgb, bool preserve_alpha, const GSVector4i draw_rect,
bool is_shuffle, bool possible_clear, bool preserve_scale, GSTextureCache::Source* src, GSTextureCache::Target* ds, int offset)
{
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
const u32 bp = TEX0.TBP0;
GSVector2i new_size{0, 0};
GSVector2i new_scaled_size{0, 0};
const GSVector4 sRect(0, 0, 1, 1);
GSVector4 dRect{};
bool clear = true;
const auto& calcRescale = [&size, &scale, &new_size, &new_scaled_size, &clear, &dRect](const Target* tgt) {
// TODO Possible optimization: rescale only the validity rectangle of the old target texture into the new one.
clear = (size.x > tgt->m_unscaled_size.x || size.y > tgt->m_unscaled_size.y);
new_size = size.max(tgt->m_unscaled_size);
new_scaled_size = ScaleRenderTargetSize(new_size, scale);
dRect = (GSVector4(GSVector4i::loadh(tgt->m_unscaled_size)) * GSVector4(scale)).ceil();
GL_INS("TC: Rescale: %dx%d: %dx%d @ %f -> %dx%d @ %f", tgt->m_unscaled_size.x, tgt->m_unscaled_size.y,
tgt->m_texture->GetWidth(), tgt->m_texture->GetHeight(), tgt->m_scale, new_scaled_size.x, new_scaled_size.y,
scale);
};
RescaleHelper rescaler(size, scale);
Target* dst = nullptr;
Target* dst_match = nullptr;
auto* list = &m_dst[type];
const GSVector4i min_rect = draw_rect.max_u32(GSVector4i(0, 0, draw_rect.x, draw_rect.y));
// TODO: Move all frame stuff to its own routine too.
if (!is_frame)
{
// Two iterations: one for the same type and one for the opposite type.
for (int iteration = 0; iteration < 2; iteration++)
{
if (dst != nullptr)
@@ -2573,7 +2583,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
if (!is_shuffle && (ds && offset == 0 && (t->m_valid.w >= 896) && ((((t->m_end_block + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) <= bp))
{
const u32 local_offset = (((bp - t->m_TEX0.TBP0) >> 5) / std::max(t->m_TEX0.TBW, 1U)) * s_psm.pgs.y;
if ((new_dst = CreateTarget(TEX0, GSVector2i(t->m_valid.z, t->m_valid.w - local_offset), GSVector2i(t->m_valid.z, t->m_valid.w - local_offset), scale, type, true, fbmask, false, false, preserve_rgb || preserve_alpha, GSVector4i::zero(), src)))
if ((new_dst = CreateTarget(TEX0, GSVector2i(t->m_valid.z, t->m_valid.w - local_offset), GSVector2i(t->m_valid.z, t->m_valid.w - local_offset), rescaler.m_scale, type, true, fbmask, false, false, preserve_rgb || preserve_alpha, GSVector4i::zero(), src)))
new_dst->m_32_bits_fmt |= (psm_s.bpp != 16);
break;
@@ -2685,123 +2695,245 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
i++;
}
}
if (dst)
{
dst = ProcessTargetAfterLookup(rescaler, dst, TEX0, size, type, used, fbmask, false, preload, preserve_rgb,
preserve_alpha, draw_rect, is_shuffle, possible_clear, preserve_scale, src, ds, offset);
}
else if (!GSConfig.UserHacks_DisableDepthSupport)
{
if (!dst_match)
{
const int rev_type = (type == DepthStencil) ? RenderTarget : DepthStencil;
// Depth stencil/RT can be an older RT/DS but only check recent RT/DS to avoid to pick
// some bad data.
auto& rev_list = m_dst[rev_type];
for (auto i = rev_list.begin(); i != rev_list.end(); ++i)
{
Target* t = *i;
// Don't pull in targets without valid lower 24 bits unless the Z is 32bits and the alpha is valid, it makes no sense to convert them otherwise.
// FIXME: Technically the difference in size is fine, but if the target gets reinterpreted, the hw renderer doesn't rearrange the target.
// This does cause some extra uploads in some games (like Burnout), but without this, bad data gets displayed in games like Transformers.
if (bp != t->m_TEX0.TBP0 || (!t->m_valid_rgb && (!(GSUtil::GetChannelMask(TEX0.PSM) & 0x8) || !(t->m_valid_alpha_low || t->m_valid_alpha_high))) ||
(!is_shuffle && t->m_TEX0.TBW != TEX0.TBW && (possible_clear || ((~GSLocalMemory::m_psm[t->m_TEX0.PSM].fmsk | fbmask) == 0xffffffff))))
{
continue;
}
// If the format is completely different, but it's the same location, it's likely just overwriting it, so get rid.
// Make sure it's not currently in use, that could be bad.
if (!is_shuffle && (!ds || (ds != t)) &&
t->m_TEX0.TBW != TEX0.TBW && TEX0.TBW != 1 && !preserve_rgb && min_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y)
{
if (src && src->m_target && src->m_from_target == t && src->m_target_direct)
{
src->m_target_direct = false;
src->m_shared_texture = false;
t->m_texture = nullptr;
}
GL_CACHE("TC: Deleting Z draw %d", GSState::s_n);
InvalidateSourcesFromTarget(t);
i = rev_list.erase(i);
delete t;
continue;
}
const GSLocalMemory::psm_t& t_psm_s = GSLocalMemory::m_psm[t->m_TEX0.PSM];
if (t_psm_s.bpp != psm_s.bpp)
{
bool remove_target = possible_clear || (used && !is_shuffle);
// If we have a BW change, and it's not a multiple of 2 (for a shuffle), the game's going to get a jigsaw
// puzzle of pages and can't be expecting to have legitimate data. Tokimeki Memorial 3 reuses a BW 17
// buffer as BW 10, and if we don't discard the BW 17 buffer, the BW 10 buffer ends up twice the size.
const u32 shuffle_bw = (psm_s.bpp > t_psm_s.bpp) ? (TEX0.TBW / 2u) : (TEX0.TBW * 2u);
if (t->m_TEX0.TBW != TEX0.TBW && (t->m_TEX0.TBW != shuffle_bw && !is_shuffle))
{
// But we'll make sure the whole existing target's actually being drawn over to be safe.
const u32 end_block = GSLocalMemory::GetUnwrappedEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
if (end_block >= t->UnwrappedEndBlock())
{
GL_CACHE("TC: Not converting %s at %x TBW %u with end block of %x when we're drawing through %x",
to_string(rev_type), t->m_TEX0.TBP0, t->m_TEX0.TBW, t->UnwrappedEndBlock(), end_block);
remove_target = true;
}
}
// Probably an old target, get rid of it.
if (remove_target)
{
// DT Racer hits this path and causes a crash when RT in RT is disabled,
// so let's make sure source and target texture isn't linked/shared before deleting the target.
if (src && src->m_target && src->m_from_target == t && src->m_target_direct)
{
src->m_target_direct = false;
src->m_shared_texture = false;
t->m_texture = nullptr;
}
InvalidateSourcesFromTarget(t);
i = rev_list.erase(i);
delete t;
continue;
}
}
if (t->m_age == 0)
{
dst_match = t;
break;
}
else if (t->m_age == 1 && (preserve_rgb || (preserve_alpha && (t->m_valid_alpha_low || t->m_valid_alpha_high))))
{
dst_match = t;
}
}
}
if (dst_match)
{
// dst_match, we only want to use a matched target if it's actually being used.
rescaler.CalcRescale(dst_match);
// If we don't need A, and the existing target doesn't have valid alpha, don't bother converting it.
const bool has_alpha = dst_match->HasValidAlpha();
const bool preserve_target = (preserve_rgb || (preserve_alpha && has_alpha)) ||
!draw_rect.rintersect(dst_match->m_valid).eq(dst_match->m_valid);
bool half_width = false;
if (GSLocalMemory::m_psm[TEX0.PSM].bpp == 32 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp == 16)
if (dst_match->m_valid.z == ((TEX0.TBW * 64) * 2))
half_width = true;
// Clear instead of invalidating if there is anything which isn't touched.
rescaler.m_clear |= (!preserve_target && fbmask != 0);
GIFRegTEX0 new_TEX0;
new_TEX0.TBP0 = GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets ? dst_match->m_TEX0.TBP0 : TEX0.TBP0;
new_TEX0.TBW = (!half_width) ? dst_match->m_TEX0.TBW : TEX0.TBW;
new_TEX0.PSM = is_shuffle ? dst_match->m_TEX0.PSM : TEX0.PSM;
dst = Target::Create(new_TEX0, rescaler.m_new_size.x, rescaler.m_new_size.y, rescaler.m_scale, type, rescaler.m_clear);
if (!dst)
return nullptr;
dst->m_32_bits_fmt = dst_match->m_32_bits_fmt;
dst->OffsetHack_modxy = dst_match->OffsetHack_modxy;
dst->m_end_block = dst_match->m_end_block; // If we're copying the size, we need to keep the end block.
dst->m_valid = dst_match->m_valid;
dst->m_valid_alpha_low = dst_match->m_valid_alpha_low; //&& psm_s.trbpp != 24;
dst->m_valid_alpha_high = dst_match->m_valid_alpha_high; //&& psm_s.trbpp != 24;
dst->m_valid_rgb = dst_match->m_valid_rgb && (dst->m_TEX0.TBW == TEX0.TBW || min_rect.w <= GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].pgs.y);
dst->m_was_dst_matched = !dst_match->m_was_dst_matched;
dst_match->m_valid_rgb = preserve_rgb;
if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp > 16)
dst->m_TEX0.TBW = dst_match->m_TEX0.TBW; // Be careful of shuffles of the depth as C16, but using a buffer width of 16 (Mercenaries).
else if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 32 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp == 16)
{
// If we're coming from a 16bit target to 32bit, one of the dimensions is smaller.
if (half_width)
dst->m_valid.z /= 2;
else
dst->m_valid.w /= 2;
dst->UpdateValidity(dst->m_valid);
}
ShaderConvert shader;
// m_32_bits_fmt gets set on a shuffle or if the format isn't 16bit.
// In this case it needs to make sure it isn't part of a shuffle, where it needs to be interpreted as 32bits.
const bool fmt_16_bits = (psm_s.bpp == 16 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp == 16 && !dst->m_32_bits_fmt);
if (type == DepthStencil)
{
GL_CACHE("TC: Lookup Target(Depth) %dx%d, hit Color (0x%x, TBW %d, %s was %s)",
rescaler.m_new_size.x, rescaler.m_new_size.y,
bp, TEX0.TBW, GSUtil::GetPSMName(TEX0.PSM), GSUtil::GetPSMName(dst_match->m_TEX0.PSM));
shader = (fmt_16_bits) ? ShaderConvert::RGB5A1_TO_FLOAT16 :
(ShaderConvert)(static_cast<int>(ShaderConvert::RGBA8_TO_FLOAT32) + psm_s.fmt);
}
else
{
pxAssert(type == RenderTarget);
// Let's try to find a perfect frame that contains valid data
for (auto i = list->begin(); i != list->end(); ++i)
{
Target* t = *i;
GL_CACHE("TC: Lookup Target(Color) %dx%d, hit Depth (0x%x, TBW %d, FBMSK %0x, %s was %s)",
rescaler.m_new_size.x, rescaler.m_new_size.y, bp, TEX0.TBW, fbmask,
GSUtil::GetPSMName(TEX0.PSM), GSUtil::GetPSMName(dst_match->m_TEX0.PSM));
shader = (fmt_16_bits) ? ShaderConvert::FLOAT16_TO_RGB5A1 : ShaderConvert::FLOAT32_TO_RGBA8;
}
// Only checks that the texure starts at the requested bp, size isn't considered.
if (bp == t->m_TEX0.TBP0 && t->m_end_block >= bp)
if (!preserve_target)
{
if (TEX0.TBW != t->m_TEX0.TBW && t->m_TEX0.TBW > 1 && t->m_age > 0)
{
// If frame is old and dirty, probably modified by the EE, so kill the wrong dimension version.
if (!t->m_dirty.empty())
{
const GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
// It's dirty with the data we want at the right width, so just change it to that.
// Prince of Persia - Sands of Time
if (t->m_dirty.size() == 1 && t->m_dirty[0].bw == TEX0.TBW)
{
t->m_TEX0.TBW = TEX0.TBW;
t->m_valid = dirty_rect;
t->m_end_block = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, t->m_valid);
t->m_drawn_since_read = GSVector4i::zero();
GL_INS("TC: Not converting existing %s[%x] because it's fully overwritten.", to_string(!type), dst->m_TEX0.TBP0);
}
else
{
DevCon.Warning("Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
InvalidateSourcesFromTarget(t);
i = list->erase(i);
delete t;
continue;
}
}
}
dst = t;
GL_CACHE("TC: Lookup Frame %dx%d, perfect hit: (0x%x -> 0x%x %s)", size.x, size.y, bp, t->m_end_block, GSUtil::GetPSMName(TEX0.PSM));
if (size.x > 0 || size.y > 0)
ScaleTargetForDisplay(dst, TEX0, size.x, size.y);
// The old target's going to get invalidated (at least until we handle concurrent frame+depth at the same BP),
// so just move the dirty rects across, unless the format is diffent, in which case we need to update it.
if (dst->m_TEX0.PSM != dst_match->m_TEX0.PSM)
dst_match->Update();
break;
dst->m_dirty = std::move(dst_match->m_dirty);
dst_match->m_dirty = {};
dst->m_alpha_max = dst_match->m_alpha_max;
dst->m_alpha_min = dst_match->m_alpha_min;
// Don't bother copying the old target in if the whole thing is dirty.
if (dst->m_dirty.empty() || (~dst->m_dirty.GetDirtyChannels() & GSUtil::GetChannelMask(TEX0.PSM)) != 0 ||
!dst->m_dirty.GetDirtyRect(0, TEX0, dst->GetUnscaledRect(), false).eq(dst->GetUnscaledRect()))
{
// If the old target was cleared, simply propagate that through.
if (dst_match->m_texture->GetState() == GSTexture::State::Cleared)
{
if (type == DepthStencil)
{
const u32 cc = dst_match->m_texture->GetClearColor();
const float cd = ConvertColorToDepth(cc, shader);
GL_INS("TC: Convert clear color[%08X] to depth[%f]", cc, cd);
g_gs_device->ClearDepth(dst->m_texture, cd);
}
else
{
const float cd = dst_match->m_texture->GetClearDepth();
const u32 cc = ConvertDepthToColor(cd, shader);
GL_INS("TC: Convert clear depth[%f] to color[%08X]", cd, cc);
g_gs_device->ClearRenderTarget(dst->m_texture, cc);
}
}
else if (dst_match->m_texture->GetState() == GSTexture::State::Dirty)
{
dst_match->UnscaleRTAlpha();
g_gs_device->StretchRect(dst_match->m_texture, FullSrcRect, dst->m_texture, rescaler.m_dRect, shader, false);
}
}
// 2nd try ! Try to find a frame at the requested bp -> bp + size is inside of (or equal to)
if (!dst)
{
for (auto i = list->begin(); i != list->end(); ++i)
{
Target* t = *i;
const u32 end_block = GSLocalMemory::GetEndBlockAddress(bp, TEX0.TBW, TEX0.PSM, GSVector4i(0, size.y, size.x, size.y + 1));
const u32 bp_adj = (end_block < t->m_TEX0.TBP0 && t->UnwrappedEndBlock() > GS_MAX_BLOCKS) ? (bp + GS_MAX_BLOCKS) : bp;
const bool half_buffer_match = GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets && TEX0.TBW == t->m_TEX0.TBW && TEX0.PSM == t->m_TEX0.PSM &&
bp == GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, GSVector4i(0, size.y, size.x, size.y + 1));
// Make sure the target is inside the texture
if (t->m_TEX0.TBP0 <= bp_adj && bp_adj <= t->UnwrappedEndBlock() && (half_buffer_match || t->Inside(bp_adj, TEX0.TBW, TEX0.PSM, GSVector4i::loadh(size))))
{
if (dst && (GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw))
continue;
if (TEX0.TBW != t->m_TEX0.TBW && t->m_TEX0.TBW > 1 && t->m_age > 0)
{
// If frame is old and dirty, probably modified by the EE, so kill the wrong dimension version.
if (!t->m_dirty.empty())
{
DevCon.Warning("2 Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
InvalidateSourcesFromTarget(t);
i = list->erase(i);
delete t;
continue;
}
}
dst = t;
GL_CACHE("TC: Lookup Frame %dx%d, inclusive hit: (0x%x, took 0x%x -> 0x%x %s)", size.x, size.y, bp, t->m_TEX0.TBP0, t->m_end_block, GSUtil::GetPSMName(TEX0.PSM));
if (size.x > 0 || size.y > 0)
ScaleTargetForDisplay(dst, TEX0, size.x, size.y);
continue;
}
}
}
// 3rd try ! Try to find a frame that doesn't contain valid data (honestly I'm not sure we need to do it)
if (!dst)
{
for (auto i = list->begin(); i != list->end(); ++i)
{
Target* t = *i;
if (bp == t->m_TEX0.TBP0 && TEX0.TBW == t->m_TEX0.TBW)
{
if (TEX0.TBW != t->m_TEX0.TBW && t->m_TEX0.TBW > 1 && t->m_age > 0)
{
// If frame is old and dirty, probably modified by the EE, so kill the wrong dimension version.
if (!t->m_dirty.empty())
{
DevCon.Warning("3 Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
InvalidateSourcesFromTarget(t);
i = list->erase(i);
delete t;
continue;
}
}
dst = t;
GL_CACHE("TC: Lookup Frame %dx%d, empty hit: (0x%x -> 0x%x %s)", size.x, size.y, bp, t->m_end_block, GSUtil::GetPSMName(TEX0.PSM));
break;
}
// Now pull in any dirty areas in the new format.
dst->Update();
}
}
}
if (dst)
{
dst->m_used |= used;
dst->readbacks_since_draw = 0;
pxAssert(dst->m_texture && dst->m_scale == rescaler.m_scale);
}
return dst;
}
GSTextureCache::Target* GSTextureCache::ProcessTargetAfterLookup(RescaleHelper& rescaler, Target* dst, GIFRegTEX0 TEX0, const GSVector2i& size,
int type, bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_rgb, bool preserve_alpha,
const GSVector4i draw_rect, bool is_shuffle, bool possible_clear, bool preserve_scale,
GSTextureCache::Source* src, GSTextureCache::Target* ds, int offset)
{
pxAssert(dst);
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
const u32 bp = TEX0.TBP0;
if (type == DepthStencil)
{
GL_CACHE("TC: Lookup Target(Depth) %dx%d (0x%x, BW:%u, %s) hit (0x%x, BW:%d, %s)", size.x, size.y, bp,
@@ -2814,15 +2946,16 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
dst->m_TEX0.TBP0, dst->m_TEX0.TBW, GSUtil::GetPSMName(dst->m_TEX0.PSM));
}
if (dst->m_scale != scale && (!preserve_scale || (type == RenderTarget && (is_shuffle || !dst->m_downscaled || TEX0.TBW != dst->m_TEX0.TBW))))
if (dst->m_scale != rescaler.m_scale && (!preserve_scale || (type == RenderTarget && (is_shuffle || !dst->m_downscaled || TEX0.TBW != dst->m_TEX0.TBW))))
{
calcRescale(dst);
GSTexture* tex = type == RenderTarget ? g_gs_device->CreateRenderTarget(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::Color, clear) :
g_gs_device->CreateDepthStencil(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::DepthStencil, clear);
rescaler.CalcRescale(dst);
GSTexture* tex = type == RenderTarget ?
g_gs_device->CreateRenderTarget(rescaler.m_new_scaled_size.x, rescaler.m_new_scaled_size.y, GSTexture::Format::Color, rescaler.m_clear) :
g_gs_device->CreateDepthStencil(rescaler.m_new_scaled_size.x, rescaler.m_new_scaled_size.y, GSTexture::Format::DepthStencil, rescaler.m_clear);
if (!tex)
return nullptr;
if (scale == 1.0f && type == RenderTarget)
if (rescaler.m_scale == 1.0f && type == RenderTarget)
{
// When using native HPO, the top-left column/row of pixels are often not drawn. Clamp these away to avoid sampling black,
// causing bleeding into the edges of the downsampled texture.
@@ -2836,20 +2969,22 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
g_gs_device->FilteredDownsampleTexture(dst->m_texture, tex, downsample_factor, clamp_min, dRect);
}
else
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, type == RenderTarget && !preserve_scale);
g_gs_device->StretchRect(dst->m_texture, FullSrcRect, tex, rescaler.m_dRect,
(type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, type == RenderTarget && !preserve_scale);
m_target_memory_usage = (m_target_memory_usage - dst->m_texture->GetMemUsage()) + tex->GetMemUsage();
// If we're changing resolution scale, just toss the texture, it's not going to get reused.
if ((!GSConfig.UserHacks_NativePaletteDraw && !dst->m_downscaled && scale != 1.0f) || (dst->m_scale != 1.0f && scale != 1.0f))
if ((!GSConfig.UserHacks_NativePaletteDraw && !dst->m_downscaled && rescaler.m_scale != 1.0f) ||
(dst->m_scale != 1.0f && rescaler.m_scale != 1.0f))
delete dst->m_texture;
else
g_gs_device->Recycle(dst->m_texture);
dst->m_texture = tex;
dst->m_scale = scale;
dst->m_unscaled_size = new_size;
dst->m_downscaled = scale == 1.0f && g_gs_renderer->GetUpscaleMultiplier() > 1.0f;
dst->m_scale = rescaler.m_scale;
dst->m_unscaled_size = rescaler.m_new_size;
dst->m_downscaled = rescaler.m_scale == 1.0f && g_gs_renderer->GetUpscaleMultiplier() > 1.0f;
if (src && src->m_target && src->m_from_target == dst && src->m_shared_texture)
{
@@ -2857,17 +2992,17 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
src->m_scale = dst->m_scale;
}
}
else if (dst->m_scale != scale)
scale = dst->m_scale;
else if (dst->m_scale != rescaler.m_scale)
rescaler.m_scale = dst->m_scale;
// Game is changing from 32bit deptth to 24bit, meaning any top values in the depth will no longer be valid, I hope no games rely on these values being maintained, else we're screwed.
// Game is changing from 32bit depth to 24bit, meaning any top values in the depth will no longer be valid, I hope no games rely on these values being maintained, else we're screwed.
if (type == DepthStencil && dst->m_type == DepthStencil && GSLocalMemory::m_psm[dst->m_TEX0.PSM].trbpp == 32 && GSLocalMemory::m_psm[TEX0.PSM].trbpp == 24 && dst->m_alpha_max > 0)
{
calcRescale(dst);
GSTexture* tex = g_gs_device->CreateDepthStencil(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::DepthStencil, false);
rescaler.CalcRescale(dst);
GSTexture* tex = g_gs_device->CreateDepthStencil(rescaler.m_new_scaled_size.x, rescaler.m_new_scaled_size.y, GSTexture::Format::DepthStencil, false);
if (!tex)
return nullptr;
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, ShaderConvert::FLOAT32_TO_FLOAT24, false);
g_gs_device->StretchRect(dst->m_texture, FullSrcRect, tex, rescaler.m_dRect, ShaderConvert::FLOAT32_TO_FLOAT24, false);
g_gs_device->Recycle(dst->m_texture);
dst->m_texture = tex;
@@ -2880,10 +3015,8 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
const bool scale_down = GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp > GSLocalMemory::m_psm[TEX0.PSM].bpp;
bool req_copy = true;
new_size = dst->m_unscaled_size;
new_scaled_size = ScaleRenderTargetSize(dst->m_unscaled_size, dst->m_scale);
dRect = (GSVector4(dst->m_valid) * GSVector4(dst->m_scale)).ceil();
rescaler.SetNewSize(dst->m_unscaled_size, dst->m_scale);
rescaler.m_dRect = (GSVector4(dst->m_valid) * GSVector4(dst->m_scale)).ceil();
GSVector4 source_rect = GSVector4(
static_cast<float>(dst->m_valid.x) / static_cast<float>(dst->m_unscaled_size.x),
static_cast<float>(dst->m_valid.y) / static_cast<float>(dst->m_unscaled_size.y),
@@ -2895,21 +3028,21 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
{
dst->m_valid.y *= 2;
dst->m_valid.w *= 2;
dRect.y *= 2;
dRect.w *= 2;
rescaler.m_dRect.y *= 2;
rescaler.m_dRect.w *= 2;
if (new_size.y < dst->m_valid.w)
if (rescaler.m_new_size.y < dst->m_valid.w)
{
new_size.y = dst->m_valid.w;
new_scaled_size = ScaleRenderTargetSize(new_size, dst->m_scale);
rescaler.m_new_size.y = dst->m_valid.w;
rescaler.SetNewSize(rescaler.m_new_size, dst->m_scale);
// Using our resize texture only really works if we're scaling exactly.
req_copy = source_rect.w != 1.0f;
}
}
else
{
dRect.y /= 2;
dRect.w /= 2;
rescaler.m_dRect.y /= 2;
rescaler.m_dRect.w /= 2;
dst->m_valid.y /= 2;
dst->m_valid.w /= 2;
req_copy = true;
@@ -2919,9 +3052,10 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
}
if (!is_shuffle)
{
GL_INS("TC: Convert to 16bit: %dx%d: %dx%d @ %f -> %dx%d @ %f", dst->m_unscaled_size.x, dst->m_unscaled_size.y,
dst->m_texture->GetWidth(), dst->m_texture->GetHeight(), dst->m_scale, new_scaled_size.x, new_scaled_size.y,
scale);
GL_INS("TC: Convert to 16bit: %dx%d: %dx%d @ %f -> %dx%d @ %f",
dst->m_unscaled_size.x, dst->m_unscaled_size.y,
dst->m_texture->GetWidth(), dst->m_texture->GetHeight(), dst->m_scale,
rescaler.m_new_scaled_size.x, rescaler.m_new_scaled_size.y, rescaler.m_scale);
if (src && src->m_from_target && src->m_from_target == dst)
{
@@ -2930,45 +3064,47 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
src->m_shared_texture = false;
if (!req_copy)
dst->ResizeTexture(new_size.x, new_size.y, true, true, GSVector4i(dRect), true);
dst->ResizeTexture(rescaler.m_new_size.x, rescaler.m_new_size.y, true, true, GSVector4i(rescaler.m_dRect), true);
else
{
GSTexture* tex = type == RenderTarget ? g_gs_device->CreateRenderTarget(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::Color, clear) :
g_gs_device->CreateDepthStencil(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::DepthStencil, clear);
GSTexture* tex = type == RenderTarget ?
g_gs_device->CreateRenderTarget(rescaler.m_new_scaled_size.x, rescaler.m_new_scaled_size.y, GSTexture::Format::Color, rescaler.m_clear) :
g_gs_device->CreateDepthStencil(rescaler.m_new_scaled_size.x, rescaler.m_new_scaled_size.y, GSTexture::Format::DepthStencil, rescaler.m_clear);
if (!tex)
return nullptr;
g_gs_device->StretchRect(dst->m_texture, source_rect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
g_gs_device->StretchRect(dst->m_texture, source_rect, tex, rescaler.m_dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
m_target_memory_usage = m_target_memory_usage + tex->GetMemUsage();
// Don't kill the target here as it's being used for the source.
dst->m_texture = tex;
dst->m_unscaled_size = new_size;
dst->m_unscaled_size = rescaler.m_new_size;
}
}
else
{
if (!req_copy)
dst->ResizeTexture(new_size.x, new_size.y, true, true, GSVector4i(dRect));
dst->ResizeTexture(rescaler.m_new_size.x, rescaler.m_new_size.y, true, true, GSVector4i(rescaler.m_dRect));
else
{
GSTexture* tex = type == RenderTarget ? g_gs_device->CreateRenderTarget(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::Color, clear) :
g_gs_device->CreateDepthStencil(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::DepthStencil, clear);
GSTexture* tex = type == RenderTarget ?
g_gs_device->CreateRenderTarget(rescaler.m_new_scaled_size.x, rescaler.m_new_scaled_size.y, GSTexture::Format::Color, rescaler.m_clear) :
g_gs_device->CreateDepthStencil(rescaler.m_new_scaled_size.x, rescaler.m_new_scaled_size.y, GSTexture::Format::DepthStencil, rescaler.m_clear);
if (!tex)
return nullptr;
if (scale_down)
g_gs_device->StretchRect(dst->m_texture, source_rect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
g_gs_device->StretchRect(dst->m_texture, source_rect, tex, rescaler.m_dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
else
g_gs_device->StretchRect(dst->m_texture, source_rect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
g_gs_device->StretchRect(dst->m_texture, source_rect, tex, rescaler.m_dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
m_target_memory_usage = (m_target_memory_usage - dst->m_texture->GetMemUsage()) + tex->GetMemUsage();
g_gs_device->Recycle(dst->m_texture);
dst->m_texture = tex;
dst->m_unscaled_size = new_size;
dst->m_unscaled_size = rescaler.m_new_size;
}
}
}
@@ -3109,8 +3245,8 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
// And invalidate the target, we're drawing over it so we don't care what's there.
// We can't do this when upscaling, because of the vertex offset, the top/left rows often aren't drawn.
GL_INS("TC: Invalidating%s target %s[%x] because it's completely overwritten.", to_string(type),
(scale > 1.0f && GSConfig.UserHacks_HalfPixelOffset >= GSHalfPixelOffset::Native) ? "[clearing] " : "", dst->m_TEX0.TBP0);
if (scale > 1.0f && GSConfig.UserHacks_HalfPixelOffset < GSHalfPixelOffset::Native)
(rescaler.m_scale > 1.0f && GSConfig.UserHacks_HalfPixelOffset >= GSHalfPixelOffset::Native) ? "[clearing] " : "", dst->m_TEX0.TBP0);
if (rescaler.m_scale > 1.0f && GSConfig.UserHacks_HalfPixelOffset < GSHalfPixelOffset::Native)
{
if (dst->m_type == RenderTarget)
g_gs_device->ClearRenderTarget(dst->m_texture, 0);
@@ -3123,224 +3259,6 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
}
}
}
}
else if (!is_frame && !GSConfig.UserHacks_DisableDepthSupport)
{
if (!dst_match)
{
const int rev_type = (type == DepthStencil) ? RenderTarget : DepthStencil;
// Depth stencil/RT can be an older RT/DS but only check recent RT/DS to avoid to pick
// some bad data.
auto& rev_list = m_dst[rev_type];
for (auto i = rev_list.begin(); i != rev_list.end(); ++i)
{
Target* t = *i;
// Don't pull in targets without valid lower 24 bits unless the Z is 32bits and the alpha is valid, it makes no sense to convert them otherwise.
// FIXME: Technically the difference in size is fine, but if the target gets reinterpreted, the hw renderer doesn't rearrange the target.
// This does cause some extra uploads in some games (like Burnout), but without this, bad data gets displayed in games like Transformers.
if (bp != t->m_TEX0.TBP0 || (!t->m_valid_rgb && (!(GSUtil::GetChannelMask(TEX0.PSM) & 0x8) || !(t->m_valid_alpha_low || t->m_valid_alpha_high))) ||
(!is_shuffle && t->m_TEX0.TBW != TEX0.TBW && (possible_clear || ((~GSLocalMemory::m_psm[t->m_TEX0.PSM].fmsk | fbmask) == 0xffffffff))))
{
continue;
}
// If the format is completely different, but it's the same location, it's likely just overwriting it, so get rid.
// Make sure it's not currently in use, that could be bad.
if (!is_shuffle && (!ds || (ds != t)) &&
t->m_TEX0.TBW != TEX0.TBW && TEX0.TBW != 1 && !preserve_rgb && min_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y)
{
if (src && src->m_target && src->m_from_target == t && src->m_target_direct)
{
src->m_target_direct = false;
src->m_shared_texture = false;
t->m_texture = nullptr;
}
GL_CACHE("TC: Deleting Z draw %d", GSState::s_n);
InvalidateSourcesFromTarget(t);
i = rev_list.erase(i);
delete t;
continue;
}
const GSLocalMemory::psm_t& t_psm_s = GSLocalMemory::m_psm[t->m_TEX0.PSM];
if (t_psm_s.bpp != psm_s.bpp)
{
bool remove_target = possible_clear || (used && !is_shuffle);
// If we have a BW change, and it's not a multiple of 2 (for a shuffle), the game's going to get a jigsaw
// puzzle of pages and can't be expecting to have legitimate data. Tokimeki Memorial 3 reuses a BW 17
// buffer as BW 10, and if we don't discard the BW 17 buffer, the BW 10 buffer ends up twice the size.
const u32 shuffle_bw = (psm_s.bpp > t_psm_s.bpp) ? (TEX0.TBW / 2u) : (TEX0.TBW * 2u);
if (t->m_TEX0.TBW != TEX0.TBW && (t->m_TEX0.TBW != shuffle_bw && !is_shuffle))
{
// But we'll make sure the whole existing target's actually being drawn over to be safe.
const u32 end_block = GSLocalMemory::GetUnwrappedEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
if (end_block >= t->UnwrappedEndBlock())
{
GL_CACHE("TC: Not converting %s at %x TBW %u with end block of %x when we're drawing through %x",
to_string(rev_type), t->m_TEX0.TBP0, t->m_TEX0.TBW, t->UnwrappedEndBlock(), end_block);
remove_target = true;
}
}
// Probably an old target, get rid of it.
if (remove_target)
{
// DT Racer hits this path and causes a crash when RT in RT is disabled,
// so let's make sure source and target texture isn't linked/shared before deleting the target.
if (src && src->m_target && src->m_from_target == t && src->m_target_direct)
{
src->m_target_direct = false;
src->m_shared_texture = false;
t->m_texture = nullptr;
}
InvalidateSourcesFromTarget(t);
i = rev_list.erase(i);
delete t;
continue;
}
}
if (t->m_age == 0)
{
dst_match = t;
break;
}
else if (t->m_age == 1 && (preserve_rgb || (preserve_alpha && (t->m_valid_alpha_low || t->m_valid_alpha_high))))
{
dst_match = t;
}
}
}
if (dst_match)
{
// dst_match, we only want to use a matched target if it's actually being used.
calcRescale(dst_match);
// If we don't need A, and the existing target doesn't have valid alpha, don't bother converting it.
const bool has_alpha = dst_match->HasValidAlpha();
const bool preserve_target = (preserve_rgb || (preserve_alpha && has_alpha)) ||
!draw_rect.rintersect(dst_match->m_valid).eq(dst_match->m_valid);
bool half_width = false;
if (GSLocalMemory::m_psm[TEX0.PSM].bpp == 32 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp == 16)
if (dst_match->m_valid.z == ((TEX0.TBW * 64) * 2))
half_width = true;
// Clear instead of invalidating if there is anything which isn't touched.
clear |= (!preserve_target && fbmask != 0);
GIFRegTEX0 new_TEX0;
new_TEX0.TBP0 = GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets ? dst_match->m_TEX0.TBP0 : TEX0.TBP0;
new_TEX0.TBW = (!half_width) ? dst_match->m_TEX0.TBW : TEX0.TBW;
new_TEX0.PSM = is_shuffle ? dst_match->m_TEX0.PSM : TEX0.PSM;
dst = Target::Create(new_TEX0, new_size.x, new_size.y, scale, type, clear);
if (!dst)
return nullptr;
dst->m_32_bits_fmt = dst_match->m_32_bits_fmt;
dst->OffsetHack_modxy = dst_match->OffsetHack_modxy;
dst->m_end_block = dst_match->m_end_block; // If we're copying the size, we need to keep the end block.
dst->m_valid = dst_match->m_valid;
dst->m_valid_alpha_low = dst_match->m_valid_alpha_low; //&& psm_s.trbpp != 24;
dst->m_valid_alpha_high = dst_match->m_valid_alpha_high; //&& psm_s.trbpp != 24;
dst->m_valid_rgb = dst_match->m_valid_rgb && (dst->m_TEX0.TBW == TEX0.TBW || min_rect.w <= GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].pgs.y);
dst->m_was_dst_matched = !dst_match->m_was_dst_matched;
dst_match->m_valid_rgb = preserve_rgb;
if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp > 16)
dst->m_TEX0.TBW = dst_match->m_TEX0.TBW; // Be careful of shuffles of the depth as C16, but using a buffer width of 16 (Mercenaries).
else if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 32 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp == 16)
{
// If we're coming from a 16bit target to 32bit, one of the dimensions is smaller.
if (half_width)
dst->m_valid.z /= 2;
else
dst->m_valid.w /= 2;
dst->UpdateValidity(dst->m_valid);
}
ShaderConvert shader;
// m_32_bits_fmt gets set on a shuffle or if the format isn't 16bit.
// In this case it needs to make sure it isn't part of a shuffle, where it needs to be interpreted as 32bits.
const bool fmt_16_bits = (psm_s.bpp == 16 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp == 16 && !dst->m_32_bits_fmt);
if (type == DepthStencil)
{
GL_CACHE("TC: Lookup Target(Depth) %dx%d, hit Color (0x%x, TBW %d, %s was %s)", new_size.x, new_size.y,
bp, TEX0.TBW, GSUtil::GetPSMName(TEX0.PSM), GSUtil::GetPSMName(dst_match->m_TEX0.PSM));
shader = (fmt_16_bits) ? ShaderConvert::RGB5A1_TO_FLOAT16 :
(ShaderConvert)(static_cast<int>(ShaderConvert::RGBA8_TO_FLOAT32) + psm_s.fmt);
}
else
{
GL_CACHE("TC: Lookup Target(Color) %dx%d, hit Depth (0x%x, TBW %d, FBMSK %0x, %s was %s)", new_size.x,
new_size.y, bp, TEX0.TBW, fbmask, GSUtil::GetPSMName(TEX0.PSM), GSUtil::GetPSMName(dst_match->m_TEX0.PSM));
shader = (fmt_16_bits) ? ShaderConvert::FLOAT16_TO_RGB5A1 : ShaderConvert::FLOAT32_TO_RGBA8;
}
if (!preserve_target)
{
GL_INS("TC: Not converting existing %s[%x] because it's fully overwritten.", to_string(!type), dst->m_TEX0.TBP0);
}
else
{
// The old target's going to get invalidated (at least until we handle concurrent frame+depth at the same BP),
// so just move the dirty rects across, unless the format is diffent, in which case we need to update it.
if (dst->m_TEX0.PSM != dst_match->m_TEX0.PSM)
dst_match->Update();
dst->m_dirty = std::move(dst_match->m_dirty);
dst_match->m_dirty = {};
dst->m_alpha_max = dst_match->m_alpha_max;
dst->m_alpha_min = dst_match->m_alpha_min;
// Don't bother copying the old target in if the whole thing is dirty.
if (dst->m_dirty.empty() || (~dst->m_dirty.GetDirtyChannels() & GSUtil::GetChannelMask(TEX0.PSM)) != 0 ||
!dst->m_dirty.GetDirtyRect(0, TEX0, dst->GetUnscaledRect(), false).eq(dst->GetUnscaledRect()))
{
// If the old target was cleared, simply propagate that through.
if (dst_match->m_texture->GetState() == GSTexture::State::Cleared)
{
if (type == DepthStencil)
{
const u32 cc = dst_match->m_texture->GetClearColor();
const float cd = ConvertColorToDepth(cc, shader);
GL_INS("TC: Convert clear color[%08X] to depth[%f]", cc, cd);
g_gs_device->ClearDepth(dst->m_texture, cd);
}
else
{
const float cd = dst_match->m_texture->GetClearDepth();
const u32 cc = ConvertDepthToColor(cd, shader);
GL_INS("TC: Convert clear depth[%f] to color[%08X]", cd, cc);
g_gs_device->ClearRenderTarget(dst->m_texture, cc);
}
}
else if (dst_match->m_texture->GetState() == GSTexture::State::Dirty)
{
dst_match->UnscaleRTAlpha();
g_gs_device->StretchRect(dst_match->m_texture, sRect, dst->m_texture, dRect, shader, false);
}
}
// Now pull in any dirty areas in the new format.
dst->Update();
}
}
}
if (dst)
{
dst->m_used |= used;
dst->readbacks_since_draw = 0;
pxAssert(dst && dst->m_texture && dst->m_scale == scale);
}
return dst;
}
@@ -3926,7 +3844,128 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
GSTextureCache::Target* GSTextureCache::LookupDisplayTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, bool is_feedback)
{
Target* dst = LookupTarget(TEX0, size, scale, RenderTarget, true, 0, true);
const u32 bp = TEX0.TBP0;
Target* dst = nullptr;
Target* dst_match = nullptr;
auto* list = &m_dst[RenderTarget];
// Let's try to find a perfect frame that contains valid data
for (auto i = list->begin(); i != list->end(); ++i)
{
Target* t = *i;
// Only checks that the texure starts at the requested bp, size isn't considered.
if (bp == t->m_TEX0.TBP0 && t->m_end_block >= bp)
{
if (TEX0.TBW != t->m_TEX0.TBW && t->m_TEX0.TBW > 1 && t->m_age > 0)
{
// If frame is old and dirty, probably modified by the EE, so kill the wrong dimension version.
if (!t->m_dirty.empty())
{
const GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
// It's dirty with the data we want at the right width, so just change it to that.
// Prince of Persia - Sands of Time
if (t->m_dirty.size() == 1 && t->m_dirty[0].bw == TEX0.TBW)
{
t->m_TEX0.TBW = TEX0.TBW;
t->m_valid = dirty_rect;
t->m_end_block = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, t->m_valid);
t->m_drawn_since_read = GSVector4i::zero();
}
else
{
DevCon.Warning("Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
InvalidateSourcesFromTarget(t);
i = list->erase(i);
delete t;
continue;
}
}
}
dst = t;
GL_CACHE("TC: Lookup Frame %dx%d, perfect hit: (0x%x -> 0x%x %s)", size.x, size.y, bp, t->m_end_block, GSUtil::GetPSMName(TEX0.PSM));
if (size.x > 0 || size.y > 0)
ScaleTargetForDisplay(dst, TEX0, size.x, size.y);
break;
}
}
// 2nd try ! Try to find a frame at the requested bp -> bp + size is inside of (or equal to)
if (!dst)
{
for (auto i = list->begin(); i != list->end(); ++i)
{
Target* t = *i;
const u32 end_block = GSLocalMemory::GetEndBlockAddress(bp, TEX0.TBW, TEX0.PSM, GSVector4i(0, size.y, size.x, size.y + 1));
const u32 bp_adj = (end_block < t->m_TEX0.TBP0 && t->UnwrappedEndBlock() > GS_MAX_BLOCKS) ? (bp + GS_MAX_BLOCKS) : bp;
const bool half_buffer_match = GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets && TEX0.TBW == t->m_TEX0.TBW && TEX0.PSM == t->m_TEX0.PSM &&
bp == GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, GSVector4i(0, size.y, size.x, size.y + 1));
// Make sure the target is inside the texture
if (t->m_TEX0.TBP0 <= bp_adj && bp_adj <= t->UnwrappedEndBlock() && (half_buffer_match || t->Inside(bp_adj, TEX0.TBW, TEX0.PSM, GSVector4i::loadh(size))))
{
if (dst && (GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw))
continue;
if (TEX0.TBW != t->m_TEX0.TBW && t->m_TEX0.TBW > 1 && t->m_age > 0)
{
// If frame is old and dirty, probably modified by the EE, so kill the wrong dimension version.
if (!t->m_dirty.empty())
{
DevCon.Warning("2 Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
InvalidateSourcesFromTarget(t);
i = list->erase(i);
delete t;
continue;
}
}
dst = t;
GL_CACHE("TC: Lookup Frame %dx%d, inclusive hit: (0x%x, took 0x%x -> 0x%x %s)", size.x, size.y, bp, t->m_TEX0.TBP0, t->m_end_block, GSUtil::GetPSMName(TEX0.PSM));
if (size.x > 0 || size.y > 0)
ScaleTargetForDisplay(dst, TEX0, size.x, size.y);
continue;
}
}
}
// 3rd try ! Try to find a frame that doesn't contain valid data (honestly I'm not sure we need to do it)
if (!dst)
{
for (auto i = list->begin(); i != list->end(); ++i)
{
Target* t = *i;
if (bp == t->m_TEX0.TBP0 && TEX0.TBW == t->m_TEX0.TBW)
{
if (TEX0.TBW != t->m_TEX0.TBW && t->m_TEX0.TBW > 1 && t->m_age > 0)
{
// If frame is old and dirty, probably modified by the EE, so kill the wrong dimension version.
if (!t->m_dirty.empty())
{
DevCon.Warning("3 Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
InvalidateSourcesFromTarget(t);
i = list->erase(i);
delete t;
continue;
}
}
dst = t;
GL_CACHE("TC: Lookup Frame %dx%d, empty hit: (0x%x -> 0x%x %s)", size.x, size.y, bp, t->m_end_block, GSUtil::GetPSMName(TEX0.PSM));
break;
}
}
}
if (dst)
{
RescaleHelper rescaler(size, scale);
dst = ProcessTargetAfterLookup(rescaler, dst, TEX0, size, RenderTarget, true, 0, true, GSConfig.PreloadFrameWithGSData);
}
if (dst)
return dst;
@@ -4031,6 +4070,16 @@ GSTextureCache::Target* GSTextureCache::LookupDisplayTarget(GIFRegTEX0 TEX0, con
{
return nullptr;
}
if (dst)
{
dst->m_used = true;
dst->readbacks_since_draw = 0;
pxAssert(dst->m_texture && dst->m_scale == scale);
}
return dst;
}
void GSTextureCache::Target::ScaleRTAlpha()
@@ -5178,7 +5227,7 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
new_TEX0.PSM = DPSM;
const GSVector2i target_size = (dx == 0 && dy == 0) ? GetTargetSize(DBP, DBW, DPSM, Common::AlignUpPow2(w, 64), h) : GSVector2i(src->m_valid.z, src->m_valid.w);
dst = LookupTarget(new_TEX0, target_size, src->m_scale, src->m_type);
dst = LookupDrawTarget(new_TEX0, target_size, src->m_scale, src->m_type);
if (!dst)
{
dst = Target::Create(new_TEX0, target_size.x, target_size.y, src->m_scale, GSLocalMemory::m_psm[DPSM].depth, true);
+31 -5
View File
@@ -30,6 +30,8 @@ public:
constexpr static u32 MAX_BP = 0x3fff;
constexpr static GSVector4 FullSrcRect = GSVector4::cxpr(0.0f, 0.0f, 1.0f, 1.0f);
constexpr static bool CheckOverlap(const u32 a_bp, const u32 a_bp_end, const u32 b_bp, const u32 b_bp_end) noexcept
{
u32 b_bp_start_synced = b_bp;
@@ -485,6 +487,20 @@ protected:
Source* CreateMergedSource(GIFRegTEX0 TEX0, GIFRegTEXA TEXA, SourceRegion region, float scale);
// Used in target lookup functions to determine new sizes of targets.
struct RescaleHelper
{
RescaleHelper(const GSVector2i& size, float scale);
void CalcRescale(const Target* tgt);
void SetNewSize(const GSVector2i& new_size, float scale);
const GSVector2i m_size;
float m_scale;
GSVector2i m_new_size{};
GSVector2i m_new_scaled_size{};
GSVector4 m_dRect{};
bool m_clear = true;
};
public:
GSTextureCache();
~GSTextureCache();
@@ -516,14 +532,24 @@ public:
Target* FindTargetOverlap(Target* target, int type, int psm);
void CombineAlignedInsideTargets(Target* target, GSTextureCache::Source* src = nullptr);
Target* LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, bool used = true, u32 fbmask = 0,
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_rgb = true, bool preserve_alpha = true,
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false, bool preserve_scale = false, GSTextureCache::Source* src = nullptr, GSTextureCache::Target* ds = nullptr, int offset = -1);
Target* CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, float scale, int type, bool used = true, u32 fbmask = 0,
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_target = true,
private:
Target* ProcessTargetAfterLookup(RescaleHelper& rescaler, Target* dst, GIFRegTEX0 TEX0, const GSVector2i& size, int type,
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_rgb = true, bool preserve_alpha = true,
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false,
bool preserve_scale = false, GSTextureCache::Source* src = nullptr, GSTextureCache::Target* ds = nullptr, int offset = -1);
public:
Target* CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, float scale, int type, bool used = true,
u32 fbmask = 0, bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_target = true,
const GSVector4i draw_rc = GSVector4i::zero(), GSTextureCache::Source* src = nullptr);
Target* LookupDisplayTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, bool is_feedback);
Target* LookupDrawTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, bool used = true, u32 fbmask = 0,
bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_rgb = true, bool preserve_alpha = true,
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false, bool preserve_scale = false, GSTextureCache::Source* src = nullptr, GSTextureCache::Target* ds = nullptr, int offset = -1);
/// Looks up a target in the cache, and only returns it if the BP/BW match exactly.
Target* GetExactTarget(u32 BP, u32 BW, int type, u32 end_bp);
Target* GetTargetWithSharedBits(u32 BP, u32 PSM) const;