Skip to content

Commit

Permalink
GS/HW: Fixes to texture is target offsets
Browse files Browse the repository at this point in the history
  • Loading branch information
refractionpcsx2 committed Jan 6, 2025
1 parent b211ca2 commit 66de280
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 33 deletions.
2 changes: 1 addition & 1 deletion pcsx2/GS/GSState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3100,7 +3100,7 @@ __forceinline bool GSState::IsAutoFlushDraw(u32 prim)
{
// Pretty confident here...
GSVertex* buffer = &m_vertex.buff[0];
const bool const_spacing = (buffer[m_index.buff[0]].U - buffer[m_index.buff[0]].XYZ.X) == (m_v.U - m_v.XYZ.X);
const bool const_spacing = std::abs(buffer[m_index.buff[0]].U - buffer[m_index.buff[0]].XYZ.X) == std::abs(m_v.U - m_v.XYZ.X) && std::abs(buffer[m_index.buff[1]].XYZ.X - buffer[m_index.buff[0]].XYZ.X) < 64;

if (const_spacing)
return false;
Expand Down
6 changes: 3 additions & 3 deletions pcsx2/GS/Renderers/HW/GSHwHack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,7 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
// compute shadow in RG,
// save result in alpha with a TS,
// Restore RG channel that we previously copied to render shadows.

// Important note: The game downsizes the target to half height, then later expands it back up to full size, that's why PCSX2 doesn't like it, we don't support that behaviour.
const GIFRegTEX0& Texture = RTEX0;

GIFRegTEX0 Frame = {};
Expand All @@ -1058,9 +1058,9 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
if ((!rt) || (!RPRIM->TME) || (GSLocalMemory::m_psm[Texture.PSM].bpp != 16) || (GSLocalMemory::m_psm[Frame.PSM].bpp != 16) || (Texture.TBP0 == Frame.TBP0) || (Frame.TBW != 16 && Texture.TBW != 16))
return true;

GL_INS("OI_SonicUnleashed replace draw by a copy");
GL_INS("OI_SonicUnleashed replace draw by a copy draw %d", r.s_n);

GSTextureCache::Target* src = g_texture_cache->LookupTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget);
GSTextureCache::Target* src = g_texture_cache->LookupTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i::zero(), true);

if (!src)
return true;
Expand Down
89 changes: 61 additions & 28 deletions pcsx2/GS/Renderers/HW/GSRendererHW.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2570,22 +2570,6 @@ void GSRendererHW::Draw()
bool shuffle_target = false;
if (!no_rt && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp >= 16)
{
if (m_cached_ctx.FRAME.Block() != m_cached_ctx.TEX0.TBP0)
{
// FBW is going to be wrong for channel shuffling into a new target, so take it from the source.
FRAME_TEX0.U64 = 0;
FRAME_TEX0.TBP0 = m_cached_ctx.FRAME.Block();
FRAME_TEX0.TBW = m_cached_ctx.FRAME.FBW;
FRAME_TEX0.PSM = m_cached_ctx.FRAME.PSM;

GSTextureCache::Target* tgt = g_texture_cache->LookupTarget(FRAME_TEX0, GSVector2i(m_vt.m_max.p.x, m_vt.m_max.p.y), GetTextureScaleFactor(), GSTextureCache::RenderTarget, false,
fm, false, false, false, false, GSVector4i::zero(), true);

if (tgt)
shuffle_target = tgt->m_32_bits_fmt;

tgt = nullptr;
}
if (!shuffle_target && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16)
{
const GSVertex* v = &m_vertex.buff[0];
Expand All @@ -2604,6 +2588,23 @@ void GSRendererHW::Draw()

shuffle_target = shuffle_coords && (draw_width & 7) == 0 && std::abs(draw_width - read_width) <= 1;
}

if (m_cached_ctx.FRAME.Block() != m_cached_ctx.TEX0.TBP0 || !shuffle_target)
{
// FBW is going to be wrong for channel shuffling into a new target, so take it from the source.
FRAME_TEX0.U64 = 0;
FRAME_TEX0.TBP0 = m_cached_ctx.FRAME.Block();
FRAME_TEX0.TBW = m_cached_ctx.FRAME.FBW;
FRAME_TEX0.PSM = m_cached_ctx.FRAME.PSM;

GSTextureCache::Target* tgt = g_texture_cache->LookupTarget(FRAME_TEX0, GSVector2i(m_vt.m_max.p.x, m_vt.m_max.p.y), GetTextureScaleFactor(), GSTextureCache::RenderTarget, false,
fm, false, false, false, false, GSVector4i::zero(), true);

if (tgt)
shuffle_target = tgt->m_32_bits_fmt;

tgt = nullptr;
}
}
possible_shuffle = !no_rt && (((shuffle_target/*&& GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16*/) /*|| (m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 && ((m_cached_ctx.TEX0.PSM & 0x6) || m_cached_ctx.FRAME.PSM != m_cached_ctx.TEX0.PSM))*/) || IsPossibleChannelShuffle());
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 && m_context->ALPHA.C == 0 && m_env.TEXA.AEM;
Expand Down Expand Up @@ -2829,6 +2830,7 @@ void GSRendererHW::Draw()
if (!possible_shuffle && m_split_texture_shuffle_pages == 0)
m_r = m_r.rintersect(t_size_rect);

GSVector4i lookup_rect = unclamped_draw_rect;
// Do the lookup with the real format on a shuffle, if possible.
if (possible_shuffle && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16 && GSLocalMemory ::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16)
{
Expand All @@ -2842,6 +2844,22 @@ void GSRendererHW::Draw()
FRAME_TEX0.PSM = next_ctx.TEX0.PSM;
else
FRAME_TEX0.PSM = PSMCT32; // Guess full color if no upcoming hint, it'll fix itself later.

// This is just for overlap detection, it doesn't matter which direction we do this in
if (GSLocalMemory::m_psm[FRAME_TEX0.PSM].bpp == 32)
{
// Shuffling with a double width (Sonic Unleashed for example which does a wierd shuffle/not shuffle green backup/restore).
if (src && std::abs((lookup_rect.width() / 2) - src->m_from_target->m_unscaled_size.x) <= 8)
{
lookup_rect.x /= 2;
lookup_rect.z /= 2;
}
else
{
lookup_rect.y /= 2;
lookup_rect.w /= 2;
}
}
}

// Normally we would use 1024 here to match the clear above, but The Godfather does a 1023x1023 draw instead
Expand All @@ -2855,7 +2873,7 @@ void GSRendererHW::Draw()
const bool preserve_downscale_draw = scale_draw < 0 || (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 && !possible_shuffle) ? GetTextureScaleFactor() : target_scale, GSTextureCache::RenderTarget, true,
fm, false, force_preload, preserve_rt_rgb, preserve_rt_alpha, unclamped_draw_rect, possible_shuffle, is_possible_mem_clear && FRAME_TEX0.TBP0 != m_cached_ctx.ZBUF.Block(),
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(),
GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && preserve_downscale_draw && is_possible_mem_clear != ClearType::NormalClear, src, (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.
Expand Down Expand Up @@ -2894,7 +2912,7 @@ void GSRendererHW::Draw()
else if (rt->m_TEX0.TBP0 != m_cached_ctx.FRAME.Block())
{
int vertical_offset = ((static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32) / std::max(static_cast<int>(rt->m_TEX0.TBW), 1)) * frame_psm.pgs.y; // I know I could just not shift it..

int texture_offset = 0;
const int horizontal_offset = ((static_cast<int>((m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0)) / 32) % static_cast<int>(std::max(rt->m_TEX0.TBW, 1U))) * frame_psm.pgs.x;
// Used to reduce the offset made later in channel shuffles
m_target_offset = std::abs(static_cast<int>((m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0)) >> 5);
Expand All @@ -2905,6 +2923,7 @@ void GSRendererHW::Draw()
GSVector2i new_scaled_size = rt->m_unscaled_size * rt->m_scale;
// Make sure to use the original format for the offset.
int new_offset = std::abs((vertical_offset / frame_psm.pgs.y) * GSLocalMemory::m_psm[rt->m_TEX0.PSM].pgs.y);
texture_offset = new_offset;

new_scaled_size.y += new_offset * rt->m_scale;
GSTexture* tex = g_gs_device->CreateRenderTarget(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::Color, true);
Expand All @@ -2915,18 +2934,13 @@ void GSRendererHW::Draw()
g_gs_device->StretchRect(rt->m_texture, GSVector4(0,0,1,1), tex, GSVector4(dRect), ShaderConvert::COPY, false);


if (src && src->m_from_target && src->m_from_target == rt)
{
src->m_texture = rt->m_texture;
src->m_target_direct = false;
src->m_shared_texture = false;
}
else
if (src && src->m_from_target && src->m_from_target == rt && src->m_target_direct)
{
//m_target_memory_usage -= dst->m_texture->GetMemUsage();
g_gs_device->Recycle(rt->m_texture);
src->m_texture = tex;
}

g_gs_device->Recycle(rt->m_texture);

rt->m_valid.y += new_offset;
rt->m_valid.w += new_offset;
rt->m_drawn_since_read.y += new_offset;
Expand Down Expand Up @@ -2957,8 +2971,26 @@ void GSRendererHW::Draw()

for (u32 i = 0; i < m_vertex.tail; i++)
{
v[i].XYZ.Y += vertical_offset << 4;
v[i].XYZ.X += horizontal_offset << 4;
v[i].XYZ.Y += vertical_offset << 4;
}

if (texture_offset && src && src->m_from_target && src->m_target_direct && src->m_from_target == rt)
{
GSVector4i src_region = src->GetRegionRect();

if (src_region.rempty())
{
src_region = GSVector4i::loadh(rt->m_unscaled_size);
src_region.y += texture_offset;
}
else
{
src_region.y += texture_offset;
src_region.w += texture_offset;
}
src->m_region.SetX(src_region.x, src_region.z);
src->m_region.SetY(src_region.y, src_region.w);
}

m_context->scissor.in.x += horizontal_offset;
Expand Down Expand Up @@ -3003,6 +3035,7 @@ void GSRendererHW::Draw()
src->m_texture = rt->m_texture;
src->m_scale = rt->GetScale();
src->m_unscaled_size = rt->m_unscaled_size;

}

target_scale = rt->GetScale();
Expand Down
2 changes: 1 addition & 1 deletion pcsx2/GS/Renderers/HW/GSTextureCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1915,7 +1915,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
}
}
// Probably pointing to half way through the target
else if (!min_rect.rempty()&& GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets)
else if (!min_rect.rempty() && GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets)
{
// Problem: Project - Snowblind and Tomb Raider offset the RT but not the Z
/*if (offset != -1 && (bp - t->m_TEX0.TBP0) != offset)
Expand Down

0 comments on commit 66de280

Please sign in to comment.