mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-10-11 22:49:56 +00:00
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:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user