From f159d1de55b62d3bfb836587dda830c8830d0bba Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Thu, 24 Nov 2022 19:35:43 +1000 Subject: [PATCH] GS/TextureCache: Better handle batched tiny moves e.g. Dark Cloud's menu. Also prevents an out-of-VRAM situation from crashing. --- pcsx2/GS/Renderers/HW/GSTextureCache.cpp | 60 +++++++++++++++++------- pcsx2/GS/Renderers/HW/GSTextureCache.h | 4 +- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index 38987aae15..10ae601e09 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -745,10 +745,12 @@ void GSTextureCache::ExpandTarget(const GIFRegBITBLTBUF& BITBLTBUF, const GSVect const int upsc_height = std::max(rect_scaled.y, dst->m_texture->GetHeight()); if (dst->m_texture->GetWidth() < upsc_width || dst->m_texture->GetHeight() < upsc_height) { - dst->ResizeTexture(upsc_width, upsc_height); - dst->m_dirty.push_back(GSDirtyRect(r, TEX0.PSM, TEX0.TBW)); - GetTargetHeight(TEX0.TBP0, TEX0.TBW, TEX0.PSM, r.w); - dst->Update(true); + if (dst->ResizeTexture(upsc_width, upsc_height)) + { + dst->m_dirty.push_back(GSDirtyRect(r, TEX0.PSM, TEX0.TBW)); + GetTargetHeight(TEX0.TBP0, TEX0.TBW, TEX0.PSM, r.w); + dst->Update(true); + } } } } @@ -1320,14 +1322,25 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u const int required_dh = scaled_dy + scaled_h; if ((scaled_dx + scaled_w) <= dst->m_texture->GetWidth() && required_dh > dst->m_texture->GetHeight()) { - const int new_height = dy + h; + int new_height = dy + h; if (new_height > GSRendererHW::MAX_FRAMEBUFFER_HEIGHT) return false; + // Align height to page size, that way we don't do too many small resizes (Dark Cloud). + new_height = Common::AlignUpPow2(new_height, static_cast(GSLocalMemory::m_psm[DPSM].bs.y)); + + // We don't recycle the old texture here, because the height cache will track the new size, + // so the old size won't get created again. const int scaled_new_height = static_cast(static_cast(new_height) * scale.y); GL_INS("Resize %dx%d target to %dx%d for move", dst->m_texture->GetWidth(), dst->m_texture->GetHeight(), dst->m_texture->GetHeight(), scaled_new_height); - dst->ResizeTexture(dst->m_texture->GetWidth(), scaled_new_height); GetTargetHeight(DBP, DBW, DPSM, new_height); + + if (!dst->ResizeTexture(dst->m_texture->GetWidth(), scaled_new_height, false)) + { + // Resize failed, probably ran out of VRAM, better luck next time. Fall back to CPU. + // We injected the new height into the cache, so hopefully won't happen again. + return false; + } } // Make sure the copy doesn't go out of bounds (it shouldn't). @@ -2344,26 +2357,36 @@ bool GSTextureCache::Surface::Overlaps(u32 bp, u32 bw, u32 psm, const GSVector4i return overlap; } -void GSTextureCache::Surface::ResizeTexture(int new_width, int new_height) +bool GSTextureCache::Surface::ResizeTexture(int new_width, int new_height, bool recycle_old) { - ResizeTexture(new_width, new_height, m_texture->GetScale()); + return ResizeTexture(new_width, new_height, m_texture->GetScale(), recycle_old); } -void GSTextureCache::Surface::ResizeTexture(int new_width, int new_height, GSVector2 new_scale) +bool GSTextureCache::Surface::ResizeTexture(int new_width, int new_height, GSVector2 new_scale, bool recycle_old) { const int width = m_texture->GetWidth(); const int height = m_texture->GetHeight(); if (width == new_width && height == new_height) - return; + return true; + // 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 = m_texture->IsDepthStencil() ? - g_gs_device->CreateDepthStencil(new_width, new_height, m_texture->GetFormat(), clear) : - g_gs_device->CreateRenderTarget(new_width, new_height, m_texture->GetFormat(), clear); + GSTexture* tex = nullptr; + try + { + tex = m_texture->IsDepthStencil() ? + g_gs_device->CreateDepthStencil(new_width, new_height, m_texture->GetFormat(), clear) : + g_gs_device->CreateRenderTarget(new_width, new_height, m_texture->GetFormat(), clear); + } + catch (const std::bad_alloc&) + { + } + if (!tex) { - Console.Error("(GSTextureCache::Surface::ResizeTexture) Failed to allocate %dx%d texture", new_width, new_height); - return; + Console.Error("(ResizeTexture) Failed to allocate %dx%d texture from %dx%d texture", new_width, new_height, width, height); + return false; } tex->SetScale(new_scale); @@ -2381,8 +2404,13 @@ void GSTextureCache::Surface::ResizeTexture(int new_width, int new_height, GSVec g_gs_device->CopyRect(m_texture, tex, rc, 0, 0); } - g_gs_device->Recycle(m_texture); + if (recycle_old) + g_gs_device->Recycle(m_texture); + else + delete m_texture; + m_texture = tex; + return true; } // GSTextureCache::Source diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.h b/pcsx2/GS/Renderers/HW/GSTextureCache.h index 60fa082b50..049c645435 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.h +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.h @@ -93,8 +93,8 @@ public: bool Inside(u32 bp, u32 bw, u32 psm, const GSVector4i& rect); bool Overlaps(u32 bp, u32 bw, u32 psm, const GSVector4i& rect); - void ResizeTexture(int new_width, int new_height); - void ResizeTexture(int new_width, int new_height, GSVector2 new_scale); + bool ResizeTexture(int new_width, int new_height, bool recycle_old = true); + bool ResizeTexture(int new_width, int new_height, GSVector2 new_scale, bool recycle_old = true); }; struct PaletteKey