mirror of https://github.com/PCSX2/pcsx2.git
GS/HW: Fix more regressions with RT in RT
This commit is contained in:
parent
73595d93f3
commit
3f0250957b
|
@ -232,7 +232,7 @@ bool GSHwHack::GSC_Tekken5(GSRendererHW& r, int& skip)
|
|||
else
|
||||
{
|
||||
// Fixes the alignment of the two halves for the heat haze on the temple stage.
|
||||
for (int i = 0; i < r.m_index.tail; i+=2)
|
||||
for (u32 i = 0; i < r.m_index.tail; i+=2)
|
||||
{
|
||||
v[i].XYZ.Y -= 0x8;
|
||||
}
|
||||
|
@ -1060,12 +1060,27 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
|
|||
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);
|
||||
if ((rt_again->m_TEX0.PSM & 0x3) == PSMCT16)
|
||||
{
|
||||
GSVector4i dRect = rt_again->m_valid;
|
||||
|
||||
dRect = GSVector4i(GSVector4(GSVector4i::loadh(rt_again->m_unscaled_size)) * rt_again->m_scale);
|
||||
dRect.y /= 2;
|
||||
dRect.w /= 2;
|
||||
rt_again->m_valid.y /= 2;
|
||||
rt_again->m_valid.w /= 2;
|
||||
rt_again->m_TEX0.PSM = PSMCT32;
|
||||
rt_again->ResizeTexture(rt_again->m_unscaled_size.x, rt_again->m_unscaled_size.y / 2, true, true, dRect, false);
|
||||
rt = rt_again->m_texture;
|
||||
}
|
||||
|
||||
GSVector2i rt_size(rt->GetSize());
|
||||
|
||||
// This is awful, but so is the CRC hack... it's a texture shuffle split horizontally instead of vertically.
|
||||
if (rt_size.x < src_size.x || rt_size.y < src_size.y)
|
||||
{
|
||||
GSTextureCache::Target* rt_again = g_texture_cache->LookupTarget(Frame, src_size, src->m_scale, GSTextureCache::RenderTarget);
|
||||
if (rt_again->m_unscaled_size.x < src->m_unscaled_size.x || rt_again->m_unscaled_size.y < src->m_unscaled_size.y)
|
||||
{
|
||||
GSVector2i new_size = GSVector2i(std::max(rt_again->m_unscaled_size.x, src->m_unscaled_size.x),
|
||||
|
@ -1076,6 +1091,7 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
|
|||
rt_again->UpdateDrawn(GSVector4i::loadh(new_size));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const GSVector2i copy_size(std::min(rt_size.x, src_size.x), std::min(rt_size.y, src_size.y));
|
||||
|
||||
|
|
|
@ -762,7 +762,7 @@ void GSRendererHW::ConvertSpriteTextureShuffle(u32& process_rg, u32& process_ba,
|
|||
// Special case used in Call of Duty - World at War where it doubles the height and halves the width, but the height is double doubled.
|
||||
// Check the height of the original texture, if it's half of the draw height, then make it wide instead.
|
||||
if (half_bottom_uv && tex->m_from_target && m_cached_ctx.TEX0.TBW == m_cached_ctx.FRAME.FBW &&
|
||||
tex->m_from_target->m_TEX0.TBW == (m_cached_ctx.TEX0.TBW * 2) && (m_cached_ctx.TEX0.TBW * 64) == floor(m_vt.m_max.t.x))
|
||||
tex->m_from_target->m_TEX0.TBW == (m_cached_ctx.TEX0.TBW * 2) && (m_cached_ctx.TEX0.TBW * 64) == floor(m_vt.m_max.t.x) && m_vt.m_max.t.y > tex->m_from_target->m_valid.w)
|
||||
{
|
||||
m_r.z *= 2;
|
||||
m_r.w /= 2;
|
||||
|
@ -1029,14 +1029,15 @@ GSVector2i GSRendererHW::GetValidSize(const GSTextureCache::Source* tex)
|
|||
// e.g. Burnout 3, God of War II, etc.
|
||||
int height = std::min<int>(m_context->scissor.in.w, m_r.w);
|
||||
|
||||
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM];
|
||||
// We can check if the next draw is doing the same from the next page, and assume it's a per line clear.
|
||||
// Battlefield 2 does this.
|
||||
const int pages = ((GSLocalMemory::GetEndBlockAddress(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r) + 1) - m_cached_ctx.FRAME.Block()) >> 5;
|
||||
if (m_cached_ctx.FRAME.FBW > 1 && m_r.height() <= 64 && (pages % m_cached_ctx.FRAME.FBW) == 0 && m_env.CTXT[m_backed_up_ctx].FRAME.FBP == (m_cached_ctx.FRAME.FBP + pages) && NextDrawMatchesShuffle())
|
||||
if (m_cached_ctx.FRAME.FBW > 1 && m_r.height() == frame_psm.pgs.y && (pages % m_cached_ctx.FRAME.FBW) == 0 && m_env.CTXT[m_backed_up_ctx].FRAME.FBP == (m_cached_ctx.FRAME.FBP + pages) &&
|
||||
!IsPossibleChannelShuffle() && NextDrawMatchesShuffle())
|
||||
height = std::max<int>(m_context->scissor.in.w, height);
|
||||
|
||||
// If the draw is less than a page high, FBW=0 is the same as FBW=1.
|
||||
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM];
|
||||
int width = std::min(std::max<int>(m_cached_ctx.FRAME.FBW, 1) * 64, m_context->scissor.in.z);
|
||||
if (m_cached_ctx.FRAME.FBW == 0 && m_r.w > frame_psm.pgs.y)
|
||||
{
|
||||
|
@ -2250,13 +2251,17 @@ void GSRendererHW::Draw()
|
|||
|
||||
if (!m_full_screen_shuffle)
|
||||
{
|
||||
const u32 width_pages = ((num_skipped_channel_shuffle_draws + 1) % std::max(1U, m_channel_shuffle_width) % std::max(1U, m_channel_shuffle_width)) * 64;;
|
||||
const u32 width_pages = ((num_skipped_channel_shuffle_draws + 1) % std::max(1U, m_channel_shuffle_width) % std::max(1U, m_channel_shuffle_width)) * 64;
|
||||
;
|
||||
m_conf.scissor.w = m_conf.scissor.y + (((num_skipped_channel_shuffle_draws + 1 + (m_channel_shuffle_width - 1)) / std::max(1U, m_channel_shuffle_width)) * 32) * m_conf.cb_ps.ScaleFactor.z;
|
||||
if (width_pages)
|
||||
m_conf.scissor.z = m_conf.scissor.x + (((num_skipped_channel_shuffle_draws + 1) % std::max(1U, m_channel_shuffle_width) % std::max(1U, m_channel_shuffle_width)) * 64) * m_conf.cb_ps.ScaleFactor.z;
|
||||
|
||||
m_last_rt->UpdateValidity(GSVector4i::loadh(m_last_rt->m_unscaled_size).rintersect(GSVector4i(GSVector4(m_conf.scissor) / m_conf.cb_ps.ScaleFactor.z)), true);
|
||||
}
|
||||
else
|
||||
m_last_rt->UpdateValidity(m_channel_shuffle_src_valid);
|
||||
|
||||
g_gs_device->RenderHW(m_conf);
|
||||
|
||||
if (GSConfig.DumpGSData)
|
||||
|
@ -2692,6 +2697,7 @@ void GSRendererHW::Draw()
|
|||
GSTextureCache::Source* src = nullptr;
|
||||
TextureMinMaxResult tmm;
|
||||
bool possible_shuffle = false;
|
||||
bool draw_uses_target = false;
|
||||
// Disable texture mapping if the blend is black and using alpha from vertex.
|
||||
if (m_process_texture)
|
||||
{
|
||||
|
@ -2811,7 +2817,7 @@ void GSRendererHW::Draw()
|
|||
if (!no_rt && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp >= 16 &&
|
||||
(m_vt.m_primclass == GS_SPRITE_CLASS || (m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_index.tail % 6) == 0 && TrianglesAreQuads(true) && m_index.tail > 6)))
|
||||
{
|
||||
if (!shuffle_target && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16)
|
||||
if (GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16)
|
||||
{
|
||||
const GSVertex* v = &m_vertex.buff[0];
|
||||
|
||||
|
@ -2834,7 +2840,8 @@ void GSRendererHW::Draw()
|
|||
shuffle_target = shuffle_coords && (((draw_width & 7) == 0 && std::abs(draw_width - read_width) <= 1) || m_skip > 50);
|
||||
}
|
||||
|
||||
if (!shuffle_target)
|
||||
// It's possible it's writing to an old 32bit target, but is actually just a 16bit copy, so let's make sure it's actually using a mask.
|
||||
if (!shuffle_target && m_cached_ctx.FRAME.FBMSK)
|
||||
{
|
||||
// FBW is going to be wrong for channel shuffling into a new target, so take it from the source.
|
||||
FRAME_TEX0.U64 = 0;
|
||||
|
@ -2878,6 +2885,17 @@ void GSRendererHW::Draw()
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const u32 draw_end = GSLocalMemory::GetEndBlockAddress(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r) + 1;
|
||||
const u32 draw_start = GSLocalMemory::GetStartBlockAddress(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r);
|
||||
draw_uses_target = src->m_from_target && ((src->m_from_target_TEX0.TBP0 <= draw_start &&
|
||||
src->m_from_target->UnwrappedEndBlock() > m_cached_ctx.FRAME.Block()) ||
|
||||
(m_cached_ctx.FRAME.Block() < src->m_from_target_TEX0.TBP0 && draw_end > src->m_from_target_TEX0.TBP0));
|
||||
|
||||
if (possible_shuffle && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp != 16)
|
||||
possible_shuffle &= draw_uses_target;
|
||||
|
||||
possible_shuffle &= src && (src->m_from_target != nullptr || (m_skip && possible_shuffle));
|
||||
// We don't know the alpha range of direct sources when we first tried to optimize the alpha test.
|
||||
// Moving the texture lookup before the ATST optimization complicates things a lot, so instead,
|
||||
|
@ -3061,7 +3079,8 @@ void GSRendererHW::Draw()
|
|||
|
||||
if (!no_rt)
|
||||
{
|
||||
possible_shuffle |= draw_sprite_tex && m_primitive_covers_without_gaps != NoGapsType::FullCover && (((src && src->m_target && src->m_from_target && src->m_from_target->m_32_bits_fmt) && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) ||
|
||||
possible_shuffle |= draw_sprite_tex && m_primitive_covers_without_gaps != NoGapsType::FullCover && (((src && src->m_target && src->m_from_target && src->m_from_target->m_32_bits_fmt) &&
|
||||
(GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16 || draw_uses_target) && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) ||
|
||||
IsPossibleChannelShuffle());
|
||||
|
||||
const bool possible_horizontal_texture_shuffle = possible_shuffle && src && src->m_from_target && m_r.w <= src->m_from_target->m_valid.w && m_r.z > src->m_from_target->m_valid.z && m_cached_ctx.FRAME.FBW > src->m_from_target_TEX0.TBW;
|
||||
|
@ -3277,7 +3296,7 @@ void GSRendererHW::Draw()
|
|||
rt->ResizeTexture(new_width, new_height);
|
||||
|
||||
const bool frame_masked = ((m_cached_ctx.FRAME.FBMSK & frame_psm.fmsk) == frame_psm.fmsk) || (m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST == ATST_NEVER && !(m_cached_ctx.TEST.AFAIL & AFAIL_FB_ONLY));
|
||||
|
||||
|
||||
rt->UpdateValidity(m_r, !frame_masked);
|
||||
rt->UpdateDrawn(m_r, !frame_masked);
|
||||
}
|
||||
|
@ -3294,6 +3313,14 @@ void GSRendererHW::Draw()
|
|||
|
||||
target_scale = rt->GetScale();
|
||||
|
||||
if (ds && ds->m_scale != target_scale)
|
||||
{
|
||||
const GSVector2i unscaled_size(ds->m_unscaled_size.x, ds->m_unscaled_size.y);
|
||||
ds->ResizeTexture(ds->m_unscaled_size.x * target_scale, ds->m_unscaled_size.y * target_scale, true);
|
||||
// Slightly abusing the texture resize.
|
||||
ds->m_scale = target_scale;
|
||||
ds->m_unscaled_size = unscaled_size;
|
||||
}
|
||||
// The target might have previously been a C32 format with valid alpha. If we're switching to C24, we need to preserve it.
|
||||
preserve_rt_alpha |= (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].trbpp == 24 && rt->HasValidAlpha());
|
||||
preserve_rt_color = preserve_rt_rgb || preserve_rt_alpha;
|
||||
|
@ -3392,11 +3419,6 @@ void GSRendererHW::Draw()
|
|||
const int first_x = (((v[0].XYZ.X - m_context->XYOFFSET.OFX) + 8) >> 4) - horizontal_offset;
|
||||
const int first_u = PRIM->FST ? ((v[0].U + 8) >> 4) : static_cast<int>(((1 << m_cached_ctx.TEX0.TW) * (v[0].ST.S / v[1].RGBAQ.Q)) + 0.5f);
|
||||
const bool shuffle_coords = (first_x ^ first_u) & 8;
|
||||
const u32 draw_end = GSLocalMemory::GetEndBlockAddress(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r) + 1;
|
||||
const u32 draw_start = GSLocalMemory::GetStartBlockAddress(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r);
|
||||
const bool draw_uses_target = src->m_from_target && ((src->m_from_target_TEX0.TBP0 <= draw_start &&
|
||||
src->m_from_target->UnwrappedEndBlock() > m_cached_ctx.FRAME.Block()) ||
|
||||
(m_cached_ctx.FRAME.Block() < src->m_from_target_TEX0.TBP0 && draw_end > src->m_from_target_TEX0.TBP0));
|
||||
|
||||
// copy of a 16bit source in to this target, make sure it's opaque and not bilinear to reduce false positives.
|
||||
m_copy_16bit_to_target_shuffle = m_cached_ctx.TEX0.TBP0 != m_cached_ctx.FRAME.Block() && rt->m_32_bits_fmt == true && IsOpaque()
|
||||
|
@ -3866,6 +3888,34 @@ void GSRendererHW::Draw()
|
|||
ds->ResizeTexture(new_w, new_h);
|
||||
}
|
||||
|
||||
// Hitman Contracts double duties the framebuffer it's messing with and swaps between 16bit and 32bit data, so if it's grabbed the 32bit target, we need to resize it.
|
||||
if (!m_texture_shuffle && !m_channel_shuffle && rt && src && src->m_from_target == rt && src->m_target_direct && rt->m_texture == src->m_texture)
|
||||
{
|
||||
if (GSLocalMemory::m_psm[src->m_from_target_TEX0.PSM].bpp != (GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp))
|
||||
{
|
||||
GSVector2i new_size = src->m_from_target->m_unscaled_size;
|
||||
|
||||
if (GSLocalMemory::m_psm[src->m_from_target->m_TEX0.PSM].bpp == 32)
|
||||
new_size.y *= 2;
|
||||
else
|
||||
new_size.y /= 2;
|
||||
|
||||
const GSVector4i dRect = GSVector4i(GSVector4(GSVector4i(0, 0, new_size.x, new_size.y)) * rt->m_scale);
|
||||
const GSVector2i old_unscaled = rt->m_unscaled_size;
|
||||
rt->ResizeTexture(new_size.x, new_size.y, false, true, dRect, true);
|
||||
|
||||
if (GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp >= 16)
|
||||
{
|
||||
GSTexture* new_tex = rt->m_texture;
|
||||
rt->m_texture = src->m_texture;
|
||||
rt->m_unscaled_size = old_unscaled;
|
||||
src->m_target_direct = false;
|
||||
src->m_shared_texture = false;
|
||||
src->m_texture = new_tex;
|
||||
src->m_unscaled_size = new_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool skip_draw = false;
|
||||
if (!GSConfig.UserHacks_DisableSafeFeatures && is_possible_mem_clear)
|
||||
skip_draw = TryTargetClear(rt, ds, preserve_rt_color, preserve_depth);
|
||||
|
@ -3877,7 +3927,7 @@ void GSRendererHW::Draw()
|
|||
{
|
||||
const u32 alpha = m_cached_ctx.FRAME.FBMSK >> 24;
|
||||
const u32 alpha_mask = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk >> 24;
|
||||
rt->Update(m_texture_shuffle || (alpha != 0 && (alpha & alpha_mask) != alpha_mask) || (!alpha && GetAlphaMinMax().max > 128));
|
||||
rt->Update(m_texture_shuffle || (alpha != 0 && (alpha & alpha_mask) != alpha_mask) || (!alpha && (GetAlphaMinMax().max | (m_context->FBA.FBA << 7)) > 128));
|
||||
}
|
||||
else
|
||||
rt->m_age = 0;
|
||||
|
@ -4735,6 +4785,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
|||
|
||||
const GSLocalMemory::psm_t frame_psm = GSLocalMemory::m_psm[m_context->FRAME.PSM];
|
||||
m_full_screen_shuffle = (m_r.height() > frame_psm.pgs.y) || (m_r.width() > frame_psm.pgs.x) || GSConfig.UserHacks_TextureInsideRt == GSTextureInRtMode::Disabled;
|
||||
m_channel_shuffle_src_valid = src->m_valid;
|
||||
if (GSConfig.UserHacks_TextureInsideRt == GSTextureInRtMode::Disabled || (!m_in_target_draw && IsPageCopy()) || m_conf.ps.urban_chaos_hle || m_conf.ps.tales_of_abyss_hle)
|
||||
{
|
||||
GSVertex* s = &m_vertex.buff[0];
|
||||
|
@ -4752,7 +4803,13 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
|||
|
||||
// We need to count the pages that get shuffled to, some games (like Hitman Blood Money dialogue blur effects) only do half the screen.
|
||||
if (!m_full_screen_shuffle && !m_conf.ps.urban_chaos_hle && !m_conf.ps.tales_of_abyss_hle && src)
|
||||
{
|
||||
// We've probably gotten a fake number, so just reset it, it'll be updated again later.
|
||||
if (rt->m_last_draw >= s_n)
|
||||
rt->ResizeValidity(GSVector4i::zero());
|
||||
|
||||
m_channel_shuffle_width = src->m_TEX0.TBW;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4787,14 +4844,23 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
|||
s[0].XYZ.Y = static_cast<u16>(m_context->XYOFFSET.OFY + (m_r.y << 4));
|
||||
s[1].XYZ.Y = static_cast<u16>(m_context->XYOFFSET.OFY + (m_r.w << 4));
|
||||
|
||||
const GSLocalMemory::psm_t tex_psm = GSLocalMemory::m_psm[m_context->TEX0.PSM];
|
||||
const u32 tex_page_offset = (m_vt.m_min.t.x / tex_psm.pgs.x) + (m_vt.m_min.t.y / tex_psm.pgs.y);
|
||||
|
||||
s[0].U = m_r.x << 4;
|
||||
s[1].U = m_r.z << 4;
|
||||
s[0].V = m_r.y << 4;
|
||||
s[1].V = m_r.w << 4;
|
||||
m_last_channel_shuffle_fbmsk = 0xFFFFFFFF;
|
||||
|
||||
// If we're doing per page copying, then set the valid 1 frame ahead if we're continuing, as this will save the target lookup making a new target for the new row.
|
||||
u32 frame_offset = m_cached_ctx.FRAME.Block() + (IsPageCopy() ? 0x20 : 0);
|
||||
GSVector4i new_valid = rt->m_valid;
|
||||
int offset_height = static_cast<int>((((frame_offset - rt->m_TEX0.TBP0) >> 5) / rt->m_TEX0.TBW) * frame_psm.pgs.y) + frame_psm.pgs.y;
|
||||
|
||||
// This is an annoying case where the draw is offset to draw on the right hand side of a texture (Hitman Blood Money pause screen).
|
||||
if (!IsPageCopy() && NextDrawMatchesShuffle() && ((frame_offset >> 5) + 1) % rt->m_TEX0.TBW == 0)
|
||||
offset_height += frame_psm.pgs.y;
|
||||
|
||||
new_valid.w = std::max(new_valid.w, offset_height);
|
||||
rt->UpdateValidity(new_valid, true);
|
||||
}
|
||||
|
||||
m_vertex.head = m_vertex.tail = m_vertex.next = 2;
|
||||
|
@ -6464,6 +6530,8 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
|||
m_conf.ps.scanmsk = env.SCANMSK.MSK;
|
||||
m_conf.rt = rt ? rt->m_texture : nullptr;
|
||||
m_conf.ds = ds ? (g_texture_cache->GetTemporaryZ() ? g_texture_cache->GetTemporaryZ() : ds->m_texture) : nullptr;
|
||||
|
||||
pxAssert(!ds || !rt || (ds->m_texture->GetSize().x == rt->m_texture->GetSize().x && ds->m_texture->GetSize().y == rt->m_texture->GetSize().y));
|
||||
|
||||
// Z setup has to come before channel shuffle
|
||||
EmulateZbuffer(ds);
|
||||
|
@ -7482,7 +7550,16 @@ bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_t
|
|||
return false;
|
||||
}
|
||||
|
||||
if (((GSUtil::GetChannelMask(m_cached_ctx.TEX0.PSM) & 0x7) && !src_target->m_valid_rgb) || ((GSUtil::GetChannelMask(m_cached_ctx.TEX0.PSM) & 0x8) && (!src_target->m_valid_alpha_low || !src_target->m_valid_alpha_high)))
|
||||
const bool need_aem_color = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 24 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal == 0 && ((PRIM->ABE && m_context->ALPHA.C == 0) || IsDiscardingDstAlpha()) && m_draw_env->TEXA.AEM;
|
||||
const u32 color_mask = (m_vt.m_max.c > GSVector4i::zero()).mask();
|
||||
const bool texture_function_color = m_cached_ctx.TEX0.TFX == TFX_DECAL || (color_mask & 0xFFF) || (m_cached_ctx.TEX0.TFX > TFX_DECAL && (color_mask & 0xF000));
|
||||
const bool texture_function_alpha = m_cached_ctx.TEX0.TFX != TFX_MODULATE || (color_mask & 0xF000);
|
||||
const u32 fm_mask = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk;
|
||||
const bool req_color = (texture_function_color && (!PRIM->ABE || (PRIM->ABE && IsUsingCsInBlend())) && (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0x00FFFFFF)) != (fm_mask & 0x00FFFFFF)) || need_aem_color;
|
||||
const bool alpha_used = (GSUtil::GetChannelMask(m_context->TEX0.PSM) == 0x8 || (m_context->TEX0.TCC && texture_function_alpha)) && ((PRIM->ABE && IsUsingAsInBlend()) || (m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST > ATST_ALWAYS) || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0xFF000000)) != (fm_mask & 0xFF000000));
|
||||
const bool req_alpha = (GSUtil::GetChannelMask(m_context->TEX0.PSM) & 0x8) && alpha_used;
|
||||
|
||||
if ((req_color && !src_target->m_valid_rgb) || (req_alpha && (!src_target->m_valid_alpha_low || !src_target->m_valid_alpha_high)))
|
||||
return true;
|
||||
|
||||
// If the EE has written over our sample area, we're fine to do this on the CPU, despite the target.
|
||||
|
|
|
@ -177,6 +177,7 @@ private:
|
|||
u32 m_last_channel_shuffle_tbp = 0;
|
||||
u32 m_last_channel_shuffle_end_block = 0;
|
||||
u32 m_channel_shuffle_width = 0;
|
||||
GSVector4i m_channel_shuffle_src_valid = GSVector4i::zero();
|
||||
bool m_full_screen_shuffle = false;
|
||||
|
||||
GSTextureCache::Target* m_last_rt;
|
||||
|
@ -206,6 +207,7 @@ public:
|
|||
|
||||
__fi static GSRendererHW* GetInstance() { return static_cast<GSRendererHW*>(g_gs_renderer.get()); }
|
||||
__fi HWCachedCtx* GetCachedCtx() { return &m_cached_ctx; }
|
||||
__fi u32 GetLastChannelShuffleFBP() { return m_last_channel_shuffle_fbp; }
|
||||
void Destroy() override;
|
||||
|
||||
void UpdateRenderFixes() override;
|
||||
|
|
|
@ -276,8 +276,6 @@ GSVector4i GSTextureCache::TranslateAlignedRectByPage(u32 tbp, u32 tebp, u32 tbw
|
|||
|
||||
if (!(s_psm.bpp == t_psm.bpp))
|
||||
{
|
||||
const int src_bpp = s_psm.bpp;
|
||||
|
||||
if (block_offset)
|
||||
in_rect = in_rect.ralign<Align_Outside>(s_psm.bs);
|
||||
else
|
||||
|
@ -406,6 +404,21 @@ GSVector4i GSTextureCache::TranslateAlignedRectByPage(u32 tbp, u32 tebp, u32 tbw
|
|||
new_rect = in_rect + offset_rect.xyxy();
|
||||
}
|
||||
|
||||
if (new_rect.z > dst_bw)
|
||||
{
|
||||
if (new_rect.x >= dst_bw)
|
||||
{
|
||||
new_rect.x -= dst_bw;
|
||||
new_rect.z -= dst_bw;
|
||||
new_rect.y += t_psm.pgs.y;
|
||||
new_rect.w += t_psm.pgs.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_rect.z = (dst_pgw * dst_page_size.x);
|
||||
new_rect.w += dst_page_size.y;
|
||||
}
|
||||
}
|
||||
return new_rect;
|
||||
}
|
||||
|
||||
|
@ -1048,7 +1061,6 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const bool is_depth, c
|
|||
}
|
||||
else if (!dst && bp >= t->m_TEX0.TBP0 && bp < t->m_end_block)
|
||||
{
|
||||
const GSVector2i page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
|
||||
const bool can_translate = CanTranslate(bp, TEX0.TBW, psm, r, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
const bool swizzle_match = psm_s.depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
|
||||
GSVector4i new_rect = block_boundary_rect;
|
||||
|
@ -1399,7 +1411,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
|||
{
|
||||
for (auto& dirty : t->m_dirty)
|
||||
{
|
||||
GSVector4i dirty_rect = dirty.GetDirtyRect(t->m_TEX0, true);
|
||||
GSVector4i dirty_rect = dirty.GetDirtyRect(t->m_TEX0, t->m_TEX0.PSM != dirty.psm);
|
||||
if (!dirty_rect.rintersect(new_rect).rempty())
|
||||
{
|
||||
rect_clean = false;
|
||||
|
@ -1626,8 +1638,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
|||
continue;
|
||||
}
|
||||
// Keep note that 2 bw is basically 1 normal page, as bw is in 64 pixels, and 8bit pages are 128 pixels wide, aka 2 bw.
|
||||
else if (!possible_shuffle && (GSLocalMemory::m_psm[color_psm].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32 &&
|
||||
!((t->m_TEX0.TBW == (bw / 2)) || (((bw + 1) / 2) <= t->m_TEX0.TBW && (block_boundary_rect.w <= GSLocalMemory::m_psm[psm].pgs.y)))))
|
||||
// Also check for 4HH/HL and 8H which use the alpha channel, if the page order is wrong this can cause problems as well (Jak X font).
|
||||
else if (!possible_shuffle && GSLocalMemory::m_psm[psm].trbpp <= 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32 &&
|
||||
(!(block_boundary_rect.w <= GSLocalMemory::m_psm[psm].pgs.y && ((GSLocalMemory::m_psm[psm].bpp == 32) ? bw : ((bw + 1) / 2)) <= t->m_TEX0.TBW) &&
|
||||
!(((GSLocalMemory::m_psm[psm].bpp == 32) ? bw : ((bw + 1) / 2)) == t->m_TEX0.TBW)))
|
||||
{
|
||||
DevCon.Warning("BP %x - 8bit bad match for target bp %x bw %d src %d format %d", bp, t->m_TEX0.TBP0, t->m_TEX0.TBW, bw, t->m_TEX0.PSM);
|
||||
continue;
|
||||
|
@ -2080,7 +2094,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
// Dragon Quest 8 reuses a render-target sized buffer as a single-page buffer, without clearing it. But,
|
||||
// it does dirty it by writing over the 64x64 region. So while we can't use this heuristic for tossing
|
||||
// targets at BW=1 because it breaks other games, we can when the *new* buffer area is completely dirty.
|
||||
if (!preserve_rgb && !preserve_alpha && TEX0.TBW != t->m_TEX0.TBW)
|
||||
if (((!preserve_rgb && !preserve_alpha) || (t->m_was_dst_matched && fbmask == 0xffffff)) && TEX0.TBW != t->m_TEX0.TBW)
|
||||
{
|
||||
// Old targets or shrunk targets where Y draw height goes outside the page.
|
||||
if (TEX0.TBW > 1 && (t->m_age >= 1 || (type == RenderTarget && draw_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y && TEX0.TBW < t->m_TEX0.TBW)))
|
||||
|
@ -2160,8 +2174,19 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
{
|
||||
const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
|
||||
// I know what you're thinking, and I hate the guy who wrote it too (me). Project Snowblind, Tomb Raider etc decide to offset where they're drawing using a channel shuffle, and this gets messy, so best just to kill the old target.
|
||||
if (is_shuffle && src->m_TEX0.PSM == PSMT8 && GSRendererHW::GetInstance()->m_context->FRAME.FBW == 1 && t->m_last_draw != (GSState::s_n - 1) && src && src->m_from_target && src->m_from_target->m_TEX0.TBP0 == src->m_TEX0.TBP0 && widthpage_offset && src->m_from_target != t)
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s offset overwrite shuffle", t->m_TEX0.TBP0, t->m_TEX0.TBW, psm_str(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
delete t;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_shuffle && (!GSUtil::HasSameSwizzleBits(t->m_TEX0.PSM, TEX0.PSM) ||
|
||||
(widthpage_offset % std::max(t->m_TEX0.TBW, 1U)) != 0 && ((widthpage_offset + (min_rect.width() + (s_psm.pgs.x - 1)) / s_psm.pgs.x)) > t->m_TEX0.TBW))
|
||||
((widthpage_offset % std::max(t->m_TEX0.TBW, 1U)) != 0 && ((widthpage_offset + (min_rect.width() + (s_psm.pgs.x - 1)) / s_psm.pgs.x)) > t->m_TEX0.TBW)))
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, psm_str(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
|
@ -2172,7 +2197,6 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
}
|
||||
else if (t->m_dirty.empty())
|
||||
{
|
||||
|
||||
if (TEX0.TBW == t->m_TEX0.TBW && !is_shuffle && widthpage_offset == 0 && ((min_rect.w + 63)/ 64) > 1)
|
||||
{
|
||||
// Beyond Good and Evil does this awful thing where it puts one framebuffer at 0xf00, with the first row of pages blanked out, and the whole thing goes down to 0x2080
|
||||
|
@ -2337,6 +2361,12 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
dst->m_scale = scale;
|
||||
dst->m_unscaled_size = new_size;
|
||||
dst->m_downscaled = scale == 1.0f && g_gs_renderer->GetUpscaleMultiplier() > 1.0f;
|
||||
|
||||
if (src && src->m_target && src->m_from_target == dst)
|
||||
{
|
||||
src->m_texture = dst->m_texture;
|
||||
src->m_scale = dst->m_scale;
|
||||
}
|
||||
}
|
||||
else if (dst->m_scale != scale)
|
||||
scale = dst->m_scale;
|
||||
|
@ -2356,15 +2386,15 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
dst->m_alpha_min = 0;
|
||||
dst->m_alpha_max = 0;
|
||||
}
|
||||
else if (std::abs(static_cast<s16>(GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp - GSLocalMemory::m_psm[TEX0.PSM].bpp)) == 16)
|
||||
else if ((used || type == GSTextureCache::DepthStencil) && (std::abs(static_cast<s16>(GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp - GSLocalMemory::m_psm[TEX0.PSM].bpp)) == 16))
|
||||
{
|
||||
dst->Update(false);
|
||||
dst->Update(dst->m_alpha_max <= 128);
|
||||
|
||||
const bool scale_down = GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp > GSLocalMemory::m_psm[TEX0.PSM].bpp;
|
||||
new_size = dst->m_unscaled_size;
|
||||
new_scaled_size = ScaleRenderTargetSize(dst->m_unscaled_size, scale);
|
||||
new_scaled_size = ScaleRenderTargetSize(dst->m_unscaled_size, dst->m_scale);
|
||||
|
||||
dRect = (GSVector4(GSVector4i::loadh(dst->m_unscaled_size)) * GSVector4(scale)).ceil();
|
||||
dRect = (GSVector4(GSVector4i::loadh(dst->m_unscaled_size)) * GSVector4(dst->m_scale)).ceil();
|
||||
if (!is_shuffle || GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16)
|
||||
{
|
||||
if (scale_down)
|
||||
|
@ -2408,10 +2438,20 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
dst->ResizeTexture(new_size.x, new_size.y, true, true, GSVector4i(dRect));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// New format or doing a shuffle to a 32bit target that used to be 16bit
|
||||
if (!is_shuffle || GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp < GSLocalMemory::m_psm[TEX0.PSM].bpp)
|
||||
dst->m_TEX0.PSM = TEX0.PSM;
|
||||
if ((!is_shuffle && (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp != GSLocalMemory::m_psm[TEX0.PSM].bpp || GSLocalMemory::m_psm[dst->m_TEX0.PSM].depth != GSLocalMemory::m_psm[TEX0.PSM].depth)) ||
|
||||
(is_shuffle && GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16))
|
||||
{
|
||||
if(GSLocalMemory::m_psm[dst->m_TEX0.PSM].depth != GSLocalMemory::m_psm[TEX0.PSM].depth || dst->m_TEX0.TBW != TEX0.TBW)
|
||||
dst->m_32_bits_fmt = GSLocalMemory::m_psm[TEX0.PSM].bpp != 16;
|
||||
|
||||
if (!is_shuffle || (is_shuffle && GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16))
|
||||
{
|
||||
dst->m_TEX0.PSM = TEX0.PSM;
|
||||
dst->m_TEX0.TBW = TEX0.TBW;
|
||||
}
|
||||
}
|
||||
// LEGO Dome Racers does a copy to a target as 8bit in alpha only, this doesn't really work great for us, so let's make it 32bit with invalid RGB.
|
||||
else if (dst->m_TEX0.PSM == PSMT8H)
|
||||
{
|
||||
|
@ -2538,7 +2578,8 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
// Don't pull in targets without valid lower 24 bits, it makes no sense to convert them.
|
||||
// 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 || (!is_shuffle && t->m_TEX0.TBW < TEX0.TBW && possible_clear))
|
||||
if (bp != t->m_TEX0.TBP0 || !t->m_valid_rgb || (!is_shuffle && t->m_TEX0.TBW != TEX0.TBW &&
|
||||
(possible_clear || ((~GSLocalMemory::m_psm[t->m_TEX0.PSM].fmsk | fbmask) == 0xffffffff))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -2715,7 +2756,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (dst)
|
||||
{
|
||||
dst->m_used |= used;
|
||||
|
@ -2960,6 +3001,7 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
|
|||
const GSVector4i save_rect = preserve_target ? newrect : eerect;
|
||||
|
||||
GL_INS("Preloading the RT DATA from updated GS Memory");
|
||||
|
||||
AddDirtyRectTarget(dst, save_rect, TEX0.PSM, TEX0.TBW, rgba, GSLocalMemory::m_psm[TEX0.PSM].trbpp >= 16);
|
||||
}
|
||||
}
|
||||
|
@ -3010,7 +3052,7 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
|
|||
}
|
||||
|
||||
// could be overwriting a double buffer, so if it's the second half of it, just reduce the size down to half.
|
||||
if (((((t->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) == dst->m_TEX0.TBP0)
|
||||
if (((((t->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) == dst->m_TEX0.TBP0 && dst->m_TEX0.TBW == t->m_TEX0.TBW)
|
||||
{
|
||||
GSVector4i new_valid = t->m_valid;
|
||||
new_valid.w /= 2;
|
||||
|
@ -3205,7 +3247,7 @@ void GSTextureCache::Target::UnscaleRTAlpha()
|
|||
{
|
||||
const GSVector2i rtsize(m_texture->GetSize());
|
||||
const GSVector4i valid_rect = GSVector4i(GSVector4(m_valid) * GSVector4(m_scale));
|
||||
GL_PUSH("UnscaleRTAlpha(valid=(%dx%d %d,%d=>%d,%d))", m_valid.width(), m_valid.height(), m_valid.x, m_valid.y, m_valid.z, m_valid.w);
|
||||
GL_PUSH("UnscaleRTAlpha(valid=(%dx%d %d,%d=>%d,%d))", valid_rect.width(), valid_rect.height(), valid_rect.x, valid_rect.y, valid_rect.z, valid_rect.w);
|
||||
|
||||
if (GSTexture* temp_rt = g_gs_device->CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::Color, !GSVector4i::loadh(rtsize).eq(valid_rect)))
|
||||
{
|
||||
|
@ -4997,14 +5039,17 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
GL_CACHE("TC: Sample offset (%d,%d) reduced region directly from target: %dx%d -> %dx%d @ %d,%d",
|
||||
dst->m_texture->GetWidth(), x_offset, y_offset, dst->m_texture->GetHeight(), w, h, x_offset, y_offset);
|
||||
|
||||
if (x_offset < 0)
|
||||
src->m_region.SetX(x_offset, region.GetMaxX() + x_offset);
|
||||
else
|
||||
src->m_region.SetX(x_offset, x_offset + tw);
|
||||
if (y_offset < 0)
|
||||
src->m_region.SetY(y_offset, region.GetMaxY() + y_offset);
|
||||
else
|
||||
src->m_region.SetY(y_offset, y_offset + th);
|
||||
if (!GSRendererHW::GetInstance()->IsTBPFrameOrZ(TEX0.TBP0) || !channel_shuffle)
|
||||
{
|
||||
if (x_offset < 0)
|
||||
src->m_region.SetX(x_offset, region.GetMaxX() + x_offset);
|
||||
else
|
||||
src->m_region.SetX(x_offset, x_offset + tw);
|
||||
if (y_offset < 0)
|
||||
src->m_region.SetY(y_offset, region.GetMaxY() + y_offset);
|
||||
else
|
||||
src->m_region.SetY(y_offset, y_offset + th);
|
||||
}
|
||||
|
||||
src->m_target_direct = true;
|
||||
src->m_texture = dst->m_texture;
|
||||
|
@ -5139,11 +5184,24 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
|
||||
if (is_8bits)
|
||||
{
|
||||
// Unscale 8 bits textures, quality won't be nice but format is really awful
|
||||
src->m_unscaled_size.x = tw;
|
||||
src->m_unscaled_size.y = th;
|
||||
new_size.x = tw;
|
||||
new_size.y = th;
|
||||
if (dst->m_TEX0.TBP0 == TEX0.TBP0)
|
||||
{
|
||||
// Unscale 8 bits textures, quality won't be nice but format is really awful
|
||||
src->m_unscaled_size.x = tw;
|
||||
src->m_unscaled_size.y = th;
|
||||
new_size.x = tw;
|
||||
new_size.y = th;
|
||||
}
|
||||
else
|
||||
{
|
||||
const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
const GSLocalMemory::psm_t& t_psm = GSLocalMemory::m_psm[dst->m_TEX0.PSM];
|
||||
u32 dst_pages = (dst->m_unscaled_size.x / t_psm.pgs.x) * (dst->m_unscaled_size.y / t_psm.pgs.y);
|
||||
src->m_unscaled_size.x = std::max(static_cast<int>(TEX0.TBW) * (s_psm.pgs.x / 2), (s_psm.pgs.x / 2));
|
||||
src->m_unscaled_size.y = std::max(static_cast<int>(dst_pages / std::max((TEX0.TBW / 2U), 1U)) * s_psm.pgs.y, s_psm.pgs.y);
|
||||
new_size.x = src->m_unscaled_size.x;
|
||||
new_size.y = src->m_unscaled_size.y;
|
||||
}
|
||||
}
|
||||
|
||||
// pitch conversion
|
||||
|
@ -5347,6 +5405,9 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
}
|
||||
src->m_region.SetX(x_offset, x_offset + tw);
|
||||
src->m_region.SetY(y_offset, y_offset + th);
|
||||
|
||||
if(!GSConfig.UserHacks_NativePaletteDraw)
|
||||
m_temporary_source = src;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -5358,8 +5419,6 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
sTex, sRectF, dTex, GSVector4(destX, destY, new_size.x, new_size.y), shader, false);
|
||||
}
|
||||
|
||||
m_temporary_source = src;
|
||||
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
|
|
Loading…
Reference in New Issue