Skip to content

Commit

Permalink
GS/HW: Further fixes and rewrite of AlignedRectTranslate
Browse files Browse the repository at this point in the history
  • Loading branch information
refractionpcsx2 committed Jan 24, 2025
1 parent 27b4504 commit 52586f0
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 71 deletions.
92 changes: 75 additions & 17 deletions pcsx2/GS/Renderers/HW/GSRendererHW.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3251,7 +3251,27 @@ void GSRendererHW::Draw()
}
}
}
else if (ds && !m_in_target_draw && ds->m_TEX0.TBP0 != m_cached_ctx.ZBUF.Block())
{
const GSLocalMemory::psm_t& z_psm = GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM];
int vertical_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) / std::max(static_cast<int>(ds->m_TEX0.TBW), 1)) * z_psm.pgs.y; // I know I could just not shift it..
int texture_offset = 0;
int horizontal_offset = ((static_cast<int>((m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0)) / 32) % static_cast<int>(std::max(ds->m_TEX0.TBW, 1U))) * z_psm.pgs.x;

// Z isn't offset but RT is, so we need a temp Z to align it, hopefully nothing will ever write to the Z too, right??
if (ds && vertical_offset && (m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) != (m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0))
{
int z_vertical_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) / std::max(rt->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
int z_offset = -vertical_offset;
GL_CACHE("RT in RT Z copy on draw %d z_vert_offset %d z_offset %d", s_n, z_vertical_offset, z_offset);
GSVector4i dRect = GSVector4i(0, std::max(static_cast<int>(z_offset * ds->m_scale), 0), ds->m_unscaled_size.x * ds->m_scale, (z_offset + ds->m_unscaled_size.y) * ds->m_scale);
int new_height = std::max(static_cast<int>(ds->m_unscaled_size.y * ds->m_scale), dRect.w);
GSTexture* tex = g_gs_device->CreateDepthStencil(ds->m_unscaled_size.x * ds->m_scale, new_height, GSTexture::Format::DepthStencil, true);
g_gs_device->StretchRect(ds->m_texture, GSVector4(0.0f, z_vertical_offset / static_cast<float>(ds->m_unscaled_size.y), 1.0f, std::min(z_vertical_offset + m_r.w + 1, ds->m_unscaled_size.y) / static_cast<float>(ds->m_unscaled_size.y)), tex, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);

g_texture_cache->SetTemporaryZ(tex);
}
}
if (m_process_texture)
{
GIFRegCLAMP MIP_CLAMP = m_cached_ctx.CLAMP;
Expand All @@ -3261,7 +3281,8 @@ void GSRendererHW::Draw()
{
// Hypothesis: texture shuffle is used as a postprocessing effect so texture will be an old target.
// Initially code also tested the RT but it gives too much false-positive
const int first_x = ((v[0].XYZ.X - m_context->XYOFFSET.OFX) + 8) >> 4;
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;
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;
Expand Down Expand Up @@ -3440,7 +3461,9 @@ void GSRendererHW::Draw()

// Deferred update of TEX0. We don't want to change it when we're doing a shuffle/clear, because it
// may increase the buffer width, or change PSM, which breaks P8 conversion amongst other things.
// Some texture shuffles can be to new targets (or reused ones) so they may need their valid rects adjusting.
const bool can_update_size = !is_possible_mem_clear && !m_texture_shuffle && !m_channel_shuffle;

if (!m_texture_shuffle && !m_channel_shuffle)
{
// Try to turn blits in to single sprites, saves upscaling problems when striped clears/blits.
Expand Down Expand Up @@ -3612,12 +3635,14 @@ void GSRendererHW::Draw()
rt->ResizeDrawn(rt->GetUnscaledRect());
}

const GSVector4i update_rect = m_r.rintersect(GSVector4i::loadh(GSVector2i(new_w, new_h)));
const GSVector4i update_rect = m_r.rintersect(GSVector4i::loadh(GSVector2i(new_size.x, new_size.y)));
const bool rt_update = can_update_size || (m_texture_shuffle && (src && rt && src->m_from_target != rt));

// if frame is masked or afailing always to never write frame, wanna make sure we don't touch it. This might happen if DATE or Alpha Test is being used to write to Z.
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));
// Limit to 2x the vertical height of the resolution (for double buffering)
rt->UpdateValidity(update_rect, !frame_masked && (can_update_size || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle)));
rt->UpdateDrawn(update_rect, !frame_masked && (can_update_size || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle)));
rt->UpdateValidity(update_rect, !frame_masked && (rt_update || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle)));
rt->UpdateDrawn(update_rect, !frame_masked && (rt_update || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle)));
// Probably changing to double buffering, so invalidate any old target that was next to it.
// This resolves an issue where the PCRTC will find the old target in FMV's causing flashing.
// Grandia Xtreme, Onimusha Warlord.
Expand Down Expand Up @@ -3882,16 +3907,28 @@ void GSRendererHW::Draw()
GSTextureCache::RenderTarget, m_cached_ctx.ZBUF.Block(), m_cached_ctx.ZBUF.PSM, zm);


if (g_texture_cache->GetTemporaryZ())
if (ds && g_texture_cache->GetTemporaryZ())
{
if (m_cached_ctx.DepthWrite())
{
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;
int z_vertical_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) / std::max(rt->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
int z_offset = vertical_offset;
GL_CACHE("RT in RT Z copy back draw %d z_vert_offset %d z_offset %d", s_n, z_vertical_offset, z_offset);
GSVector4i dRect = GSVector4i(0, z_vertical_offset * ds->m_scale, ds->m_unscaled_size.x * ds->m_scale, std::min(z_vertical_offset + m_r.w + 1 - vertical_offset, ds->m_unscaled_size.y) * ds->m_scale);
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), GSVector4(0.0f, z_offset / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight()), 1.0f, std::min(real_rect.w + 1, ds->m_unscaled_size.y + z_offset) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight())), ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
if (m_in_target_draw)
{
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;
int z_vertical_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) / std::max(rt->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
int z_offset = vertical_offset;
GL_CACHE("RT in RT Z copy back draw %d z_vert_offset %d z_offset %d", s_n, z_vertical_offset, z_offset);
GSVector4i dRect = GSVector4i(0, z_vertical_offset * ds->m_scale, ds->m_unscaled_size.x * ds->m_scale, std::min(z_vertical_offset + m_r.w + 1 - vertical_offset, ds->m_unscaled_size.y) * ds->m_scale);
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), GSVector4(0.0f, z_offset / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight()), 1.0f, std::min(real_rect.w + 1, ds->m_unscaled_size.y + z_offset) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight())), ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
}
else
{
int z_vertical_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) / std::max(rt->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
int z_offset = z_vertical_offset;
GL_CACHE("RT in RT Z copy back draw %d z_vert_offset %d z_offset %d", s_n, z_vertical_offset, z_offset);
GSVector4i dRect = GSVector4i(0, z_vertical_offset * ds->m_scale, ds->m_unscaled_size.x * ds->m_scale, ds->m_unscaled_size.y * ds->m_scale);
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), GSVector4(0.0f, 0 / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight()), 1.0f, (ds->m_unscaled_size.y - z_offset) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight())), ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);

}
}
}
}
Expand Down Expand Up @@ -4382,7 +4419,7 @@ bool GSRendererHW::TestChannelShuffle(GSTextureCache::Target* src)
return m_channel_shuffle;
}

__ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only)
__ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt)
{
if ((src->m_texture->GetType() == GSTexture::Type::DepthStencil) && !src->m_32_bits_fmt)
{
Expand Down Expand Up @@ -4587,9 +4624,30 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
}
else
{
const u32 frame_page_offset = std::max(static_cast<int>(((m_r.x / frame_psm.pgs.x) + (m_r.y / frame_psm.pgs.y) * src->m_TEX0.TBW) - m_target_offset), 0);
const u32 frame_page_offset = std::max(static_cast<int>(((m_r.x / frame_psm.pgs.x) + (m_r.y / frame_psm.pgs.y) * rt->m_TEX0.TBW)), 0);
m_r = GSVector4i(m_r.x & ~(frame_psm.pgs.x - 1), m_r.y & ~(frame_psm.pgs.y - 1), (m_r.z + (frame_psm.pgs.x - 1)) & ~(frame_psm.pgs.x - 1), (m_r.w + (frame_psm.pgs.y - 1)) & ~(frame_psm.pgs.y - 1));
//m_cached_ctx.FRAME.FBP += frame_page_offset;

// Hitman suffers from this, not sure on the exact scenario at the moment, but we need the barrier.
if (PRIM->ABE && m_context->ALPHA.IsCdInBlend())
{
if (m_prim_overlap == PRIM_OVERLAP_NO || !g_gs_device->Features().texture_barrier)
m_conf.require_one_barrier = true;
else
m_conf.require_full_barrier = true;
}

// This is for offsetting the texture, however if the texture has a region clamp, we don't want to move it.
// A good two test games for this is Ghost in the Shell (no region clamp) and Tekken 5 (offset clamp on shadows)
if (rt && rt->m_TEX0.TBP0 == m_cached_ctx.FRAME.Block())
{
const bool req_offset = (m_cached_ctx.CLAMP.WMS != 3 || (m_cached_ctx.CLAMP.MAXU & ~0xF) == 0) &&
(m_cached_ctx.CLAMP.WMT != 3 || (m_cached_ctx.CLAMP.MAXV & ~0x3) == 0);
//DevCon.Warning("Draw %d offset %d", s_n, frame_page_offset);
// Offset the frame but clear the draw offset
if (req_offset)
m_cached_ctx.FRAME.FBP += frame_page_offset;
}

m_in_target_draw |= frame_page_offset > 0;
GSVertex* s = &m_vertex.buff[0];
s[0].XYZ.X = static_cast<u16>(m_context->XYOFFSET.OFX + (m_r.x << 4));
Expand All @@ -4599,7 +4657,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool

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);
//m_cached_ctx.TEX0.TBP0 += tex_page_offset << 5;

s[0].U = m_r.x << 4;
s[1].U = m_r.z << 4;
s[0].V = m_r.y << 4;
Expand Down Expand Up @@ -5429,7 +5487,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,

// Hazard handling (i.e. reading from the current RT/DS).
GSTextureCache::SourceRegion source_region = tex->GetRegion();
bool target_region = (tex->IsFromTarget() && source_region.HasEither());
bool target_region = tex->IsFromTarget() && source_region.HasEither();
GSVector2i unscaled_size = target_region ? tex->GetRegionSize() : tex->GetUnscaledSize();
float scale = tex->GetScale();
HandleTextureHazards(rt, ds, tex, tmm, source_region, target_region, unscaled_size, scale, src_copy);
Expand Down Expand Up @@ -6203,7 +6261,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
// vertex list (it will interact with PrimitiveOverlap and accurate
// blending)
if (m_channel_shuffle && tex && tex->m_from_target)
EmulateChannelShuffle(tex->m_from_target, false);
EmulateChannelShuffle(tex->m_from_target, false, rt);

// Upscaling hack to avoid various line/grid issues
MergeSprite(tex);
Expand Down
2 changes: 1 addition & 1 deletion pcsx2/GS/Renderers/HW/GSRendererHW.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class GSRendererHW : public GSRenderer
void ResetStates();
void SetupIA(float target_scale, float sx, float sy, bool req_vert_backup);
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only);
bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt = nullptr);
void EmulateBlending(int rt_alpha_min, int rt_alpha_max, const bool DATE, bool& DATE_PRIMID, bool& DATE_BARRIER, GSTextureCache::Target* rt,
bool can_scale_rt_alpha, bool& new_rt_alpha_scale);
void CleanupDraw(bool invalidate_temp_src);
Expand Down
Loading

0 comments on commit 52586f0

Please sign in to comment.