diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 660b211948..079ae7202e 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -1309,13 +1309,13 @@ void GSRendererHW::Draw() // 2/ SuperMan really draws (0,0,0,0) color and a (0) 32-bits depth // 3/ 50cents really draws (0,0,0,128) color and a (0) 24 bits depth // Note: FF DoC has both buffer at same location but disable the depth test (write?) with ZTE = 0 - const bool no_rt = (context->ALPHA.IsCd() && PRIM->ABE && (context->FRAME.PSM == 1)); - const bool no_ds = !no_rt && ( + const bool no_rt = (context->ALPHA.IsCd() && PRIM->ABE && (context->FRAME.PSM == 1)) + || (!context->TEST.DATE && (context->FRAME.FBMSK & GSLocalMemory::m_psm[context->FRAME.PSM].fmsk) == GSLocalMemory::m_psm[context->FRAME.PSM].fmsk); + const bool no_ds = ( // Depth is always pass/fail (no read) and write are discarded (tekken 5). (Note: DATE is currently implemented with a stencil buffer => a depth/stencil buffer) (zm != 0 && m_context->TEST.ZTST <= ZTST_ALWAYS && !m_context->TEST.DATE) || // Depth will be written through the RT - (context->FRAME.FBP == context->ZBUF.ZBP && !PRIM->TME && zm == 0 && (fm & fm_mask) == 0 && context->TEST.ZTE) - ); + (!no_rt && context->FRAME.FBP == context->ZBUF.ZBP && !PRIM->TME && zm == 0 && (fm & fm_mask) == 0 && context->TEST.ZTE)); if (no_rt && no_ds) { @@ -1740,15 +1740,72 @@ void GSRendererHW::Draw() if (rt) { + const u32 old_end_block = rt->m_end_block; + const bool new_height = new_h > rt->m_texture->GetHeight(); + const int old_height = rt->m_texture->GetHeight(); + pxAssert(rt->m_texture->GetScale() == up_s); rt->ResizeTexture(new_w, new_h, up_s); + + if (!m_texture_shuffle && !m_channel_shuffle) + { + const GSVector4i new_valid = GSVector4i(0, 0, new_w / up_s.x, new_h / up_s.y); + rt->ResizeValidity(new_valid); + } rt->UpdateValidity(m_r); + + // 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. + if (new_height && old_end_block != rt->m_end_block) + { + GSTextureCache::Target* old_rt = nullptr; + old_rt = m_tc->FindTargetOverlap(old_end_block, rt->m_end_block, GSTextureCache::RenderTarget, context->FRAME.PSM); + + if (old_rt && old_rt != rt && GSUtil::HasSharedBits(old_rt->m_TEX0.PSM, rt->m_TEX0.PSM)) + { + const int copy_width = (old_rt->m_texture->GetWidth()) > (rt->m_texture->GetWidth()) ? (rt->m_texture->GetWidth()) : old_rt->m_texture->GetWidth(); + const int copy_height = (old_rt->m_texture->GetHeight()) > (rt->m_texture->GetHeight() - old_height) ? (rt->m_texture->GetHeight() - old_height) : old_rt->m_texture->GetHeight(); + + g_gs_device->CopyRect(old_rt->m_texture, rt->m_texture, GSVector4i(0, 0, copy_width, copy_height), 0, old_height); + + m_tc->InvalidateVideoMemType(GSTextureCache::RenderTarget, old_rt->m_TEX0.TBP0); + old_rt = nullptr; + } + } } if (ds) { + const u32 old_end_block = ds->m_end_block; + const bool new_height = new_h > ds->m_texture->GetHeight(); + const int old_height = ds->m_texture->GetHeight(); + pxAssert(ds->m_texture->GetScale() == up_s); ds->ResizeTexture(new_w, new_h, up_s); + + if (!m_texture_shuffle && !m_channel_shuffle) + { + const GSVector4i new_valid = GSVector4i(0, 0, new_w / up_s.x, new_h / up_s.y); + ds->ResizeValidity(new_valid); + } ds->UpdateValidity(m_r); + + if (new_height && old_end_block != ds->m_end_block) + { + GSTextureCache::Target* old_ds = nullptr; + old_ds = m_tc->FindTargetOverlap(old_end_block, ds->m_end_block, GSTextureCache::DepthStencil, context->ZBUF.PSM); + + if (old_ds && old_ds != ds && GSUtil::HasSharedBits(old_ds->m_TEX0.PSM, ds->m_TEX0.PSM)) + { + const int copy_width = (old_ds->m_texture->GetWidth()) > (ds->m_texture->GetWidth()) ? (ds->m_texture->GetWidth()) : old_ds->m_texture->GetWidth(); + const int copy_height = (old_ds->m_texture->GetHeight()) > (ds->m_texture->GetHeight() - old_height) ? (ds->m_texture->GetHeight() - old_height) : old_ds->m_texture->GetHeight(); + + g_gs_device->CopyRect(old_ds->m_texture, ds->m_texture, GSVector4i(0, 0, copy_width, copy_height), 0, old_height); + + m_tc->InvalidateVideoMemType(GSTextureCache::DepthStencil, old_ds->m_TEX0.TBP0); + old_ds = nullptr; + } + } } } @@ -1924,7 +1981,7 @@ void GSRendererHW::Draw() s = GetDrawDumpPath("%05d_f%lld_rz1_%05x_%s.bmp", s_n, frame, context->ZBUF.Block(), psm_str(context->ZBUF.PSM)); if (ds) - rt->m_texture->Save(s); + ds->m_texture->Save(s); } if (GSConfig.SaveL > 0 && (s_n - GSConfig.SaveN) > GSConfig.SaveL) diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index 10c0b68113..297c0a2619 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -581,6 +581,19 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con return src; } +GSTextureCache::Target* GSTextureCache::FindTargetOverlap(u32 bp, u32 end_block, int type, int psm) +{ + u32 end_block_bp = end_block < bp ? (MAX_BP + 1) : end_block; + + for (auto t : m_dst[type]) + { + // Only checks that the texure starts at the requested bp, which shares data. Size isn't considered. + if (t->m_TEX0.TBP0 >= bp && t->m_TEX0.TBP0 < end_block_bp && GSUtil::HasSharedBits(t->m_TEX0.PSM, psm)) + return t; + } + return nullptr; +} + GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask, const bool is_frame, const int real_w, const int real_h, bool preload) { const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM]; @@ -3251,14 +3264,44 @@ void GSTextureCache::Target::UpdateIfDirtyIntersects(const GSVector4i& rc) } } +void GSTextureCache::Target::ResizeValidity(const GSVector4i& rect) +{ + if (!m_valid.eq(GSVector4i::zero())) + { + m_valid = m_valid.rintersect(rect); + m_drawn_since_read = m_drawn_since_read.rintersect(rect); + } + else + { + m_valid = rect; + m_drawn_since_read = rect; + } + // Block of the bottom right texel of the validity rectangle, last valid block of the texture + // TODO: This is not correct when the PSM changes. e.g. a 512x448 target being shuffled will become 512x896 temporarily, and + // at the moment, we blow the valid rect out to twice the size. The only thing stopping everything breaking is the fact + // that we clamp the draw rect to the target size in GSRendererHW::Draw(). + m_end_block = GSLocalMemory::m_psm[m_TEX0.PSM].info.bn(m_valid.z - 1, m_valid.w - 1, m_TEX0.TBP0, m_TEX0.TBW); // Valid only for color formats + + // GL_CACHE("UpdateValidity (0x%x->0x%x) from R:%d,%d Valid: %d,%d", m_TEX0.TBP0, m_end_block, rect.z, rect.w, m_valid.z, m_valid.w); +} + void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect) { + if (m_valid.runion(rect).eq(m_valid)) + { + m_drawn_since_read = m_drawn_since_read.runion(rect); + return; + } + if (m_valid.eq(GSVector4i::zero())) m_valid = rect; else m_valid = m_valid.runion(rect); - m_drawn_since_read = m_drawn_since_read.runion(rect); + if (m_drawn_since_read.eq(GSVector4i::zero())) + m_drawn_since_read = rect; + else + m_drawn_since_read = m_drawn_since_read.runion(rect); // Block of the bottom right texel of the validity rectangle, last valid block of the texture // TODO: This is not correct when the PSM changes. e.g. a 512x448 target being shuffled will become 512x896 temporarily, and // at the moment, we blow the valid rect out to twice the size. The only thing stopping everything breaking is the fact @@ -3284,6 +3327,7 @@ bool GSTextureCache::Target::ResizeTexture(int new_width, int new_height, GSVect // These exceptions *really* need to get lost. This gets called outside of draws, which just crashes // when it tries to propogate the exception back. const bool clear = (new_width > width || new_height > height); + GSTexture* tex = nullptr; try { @@ -3325,6 +3369,7 @@ bool GSTextureCache::Target::ResizeTexture(int new_width, int new_height, GSVect delete m_texture; m_texture = tex; + return true; } diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.h b/pcsx2/GS/Renderers/HW/GSTextureCache.h index 8be30fedc1..7cb72f7a11 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.h +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.h @@ -248,6 +248,7 @@ public: Target(const GIFRegTEX0& TEX0, const bool depth_supported, const int type); ~Target(); + void ResizeValidity(const GSVector4i& rect); void UpdateValidity(const GSVector4i& rect); void Update(bool reset_age); @@ -404,6 +405,7 @@ public: Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod); Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, bool palette = false); + Target* FindTargetOverlap(u32 bp, u32 end_block, int type, int psm); Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask = 0, const bool is_frame = false, const int real_w = 0, const int real_h = 0, bool preload = GSConfig.PreloadFrameWithGSData); Target* LookupDisplayTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_w, const int real_h);