diff --git a/pcsx2/GS/Renderers/HW/GSHwHack.cpp b/pcsx2/GS/Renderers/HW/GSHwHack.cpp index 9ad4452723..7d86ddb815 100644 --- a/pcsx2/GS/Renderers/HW/GSHwHack.cpp +++ b/pcsx2/GS/Renderers/HW/GSHwHack.cpp @@ -1264,7 +1264,7 @@ static bool GetMoveTargetPair(GSRendererHW& r, GSTextureCache::Target** src, GIF 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, tsrc->GetUnscaledRect()); + dst_type, true, 0, false, false, preserve_target, preserve_target, tsrc->GetUnscaledRect()); if (!tdst) { if (req_target) diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index a57acede81..6b9f7c067e 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -1832,11 +1832,14 @@ void GSRendererHW::Draw() const bool process_texture = PRIM->TME && !(PRIM->ABE && m_context->ALPHA.IsBlack() && !m_cached_ctx.TEX0.TCC); const u32 frame_end_bp = GSLocalMemory::GetUnwrappedEndBlockAddress(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r); - bool preserve_rt_color = - ((!no_rt && (!IsDiscardingDstColor() || !PrimitiveCoversWithoutGaps() || !all_depth_tests_pass)) || // Using Dst Color or draw has gaps - (process_texture && m_cached_ctx.TEX0.TBP0 >= m_cached_ctx.FRAME.Block() && - m_cached_ctx.TEX0.TBP0 < frame_end_bp)); // Tex is RT - bool preserve_depth = preserve_rt_color || (!no_ds && (!all_depth_tests_pass || !m_cached_ctx.DepthWrite())); + const bool tex_is_rt = (process_texture && m_cached_ctx.TEX0.TBP0 >= m_cached_ctx.FRAME.Block() && + m_cached_ctx.TEX0.TBP0 < frame_end_bp); + const bool not_writing_to_all = (!PrimitiveCoversWithoutGaps() || !all_depth_tests_pass); + const bool preserve_rt_rgb = (!no_rt && (!IsDiscardingDstRGB() || not_writing_to_all)); + const bool preserve_rt_alpha = (!no_rt && (!IsDiscardingDstAlpha() || not_writing_to_all)); + bool preserve_rt_color = preserve_rt_rgb || preserve_rt_alpha; + bool preserve_depth = + preserve_rt_color || (!no_ds && (!all_depth_tests_pass || !m_cached_ctx.DepthWrite() || m_cached_ctx.TEST.ATE)); // SW CLUT Render enable. bool force_preload = GSConfig.PreloadFrameWithGSData; @@ -2133,6 +2136,7 @@ void GSRendererHW::Draw() const GSVector4i t_size_rect = GSVector4i::loadh(t_size); // Ensure draw rect is clamped to framebuffer size. Necessary for updating valid area. + const GSVector4i unclamped_draw_rect = m_r; m_r = m_r.rintersect(t_size_rect); float target_scale = GetTextureScaleFactor(); @@ -2163,7 +2167,7 @@ void GSRendererHW::Draw() const bool is_square = (t_size.y == t_size.x) && m_r.w >= 1023 && PrimitiveCoversWithoutGaps(); const bool is_clear = is_possible_mem_clear && is_square; rt = g_texture_cache->LookupTarget(FRAME_TEX0, t_size, target_scale, GSTextureCache::RenderTarget, true, - fm, false, force_preload, preserve_rt_color, m_r); + fm, false, force_preload, preserve_rt_rgb, preserve_rt_alpha, unclamped_draw_rect); // Draw skipped because it was a clear and there was no target. if (!rt) @@ -2197,7 +2201,7 @@ void GSRendererHW::Draw() 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, m_r); + m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, preserve_depth, unclamped_draw_rect); if (!ds) { ds = g_texture_cache->CreateTarget(ZBUF_TEX0, t_size, GetValidSize(src), target_scale, GSTextureCache::DepthStencil, @@ -5814,7 +5818,25 @@ bool GSRendererHW::IsDiscardingDstColor() (!PRIM->ABE || IsOpaque() || m_context->ALPHA.IsBlack()) && // no blending or writing black !m_cached_ctx.TEST.ATE && // not testing alpha (might discard some pixels) !m_cached_ctx.TEST.DATE && // not reading alpha - (m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) == 0); // no channels masked + (m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) == 0); // no channels masked +} + +bool GSRendererHW::IsDiscardingDstRGB() +{ + return ((!PRIM->ABE || IsOpaque() || m_context->ALPHA.IsBlack()) && // no blending or writing black + !m_cached_ctx.TEST.ATE && // not testing alpha (might discard some pixels) + !m_cached_ctx.TEST.DATE && // not reading alpha + ((m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) & 0xFFFFFFu) == + 0); // RGB isn't masked +} + +bool GSRendererHW::IsDiscardingDstAlpha() +{ + return ((!PRIM->ABE || m_context->ALPHA.C != 1) && // not using Ad + !m_cached_ctx.TEST.ATE && // not testing alpha (might discard some pixels) + !m_cached_ctx.TEST.DATE && // not reading alpha + ((m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) & 0xFF000000u) == + 0); // alpha isn't masked } bool GSRendererHW::PrimitiveCoversWithoutGaps() diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.h b/pcsx2/GS/Renderers/HW/GSRendererHW.h index 672f443f28..14819d1f61 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.h +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.h @@ -62,6 +62,8 @@ private: u32 GetConstantDirectWriteMemClearColor() const; bool IsReallyDithered() const; bool IsDiscardingDstColor(); + bool IsDiscardingDstRGB(); + bool IsDiscardingDstAlpha(); bool PrimitiveCoversWithoutGaps(); enum class CLUTDrawTestResult diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index 1e71d0a29d..d5c8d27c11 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -1241,7 +1241,7 @@ GSVector2i GSTextureCache::ScaleRenderTargetSize(const GSVector2i& sz, float sca } GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, - bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_target, const GSVector4i draw_rect) + bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_rgb, bool preserve_alpha, const GSVector4i draw_rect) { const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM]; const u32 bp = TEX0.TBP0; @@ -1393,6 +1393,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe } // If our RGB was invalidated, we need to pull it from depth. + const bool preserve_target = preserve_rgb || preserve_alpha; if (type == RenderTarget && (preserve_target || !dst->m_valid.rintersect(draw_rect).eq(dst->m_valid)) && !dst->m_valid_rgb && !FullRectDirty(dst, 0x7) && (GSLocalMemory::m_psm[TEX0.PSM].trbpp < 24 || fbmask != 0x00FFFFFFu)) @@ -1479,6 +1480,15 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe if (dst_match) { 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->m_valid_alpha_low || dst_match->m_valid_alpha_high; + const bool preserve_target = (preserve_rgb || (preserve_alpha && has_alpha)) || + !draw_rect.rintersect(dst_match->m_valid).eq(dst_match->m_valid); + + // Clear instead of invalidating if there is anything which isn't touched. + clear |= (!preserve_target && fbmask != 0); + dst = Target::Create(TEX0, new_size.x, new_size.y, scale, type, clear); if (!dst) return nullptr; @@ -1509,7 +1519,8 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe shader = (fmt_16_bits) ? ShaderConvert::FLOAT16_TO_RGB5A1 : ShaderConvert::FLOAT32_TO_RGBA8; } - if (!preserve_target && draw_rect.rintersect(dst_match->m_valid).eq(dst_match->m_valid)) + + if (!preserve_target) { GL_INS("TC: Not converting existing %s[%x] because it's fully overwritten.", to_string(!type), dst->m_TEX0.TBP0); } diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.h b/pcsx2/GS/Renderers/HW/GSTextureCache.h index be099228d9..db7458acd4 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.h +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.h @@ -478,7 +478,7 @@ public: Target* FindTargetOverlap(Target* target, int type, int psm); 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_target = true, + bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_rgb = true, bool preserve_alpha = true, const GSVector4i draw_rc = GSVector4i::zero()); 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,