From 1910c8fc0f531a93631b50f8c425b528410f0dac Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 9 Oct 2022 00:45:40 +1000 Subject: [PATCH] GS/HW: Only allocate as many mip levels as present This prevents any possibility of sampling undefined or garbage/reused texture data from levels which aren't used by the current draw. And a tiny bit of VRAM. But nothing really measurable. Also fixes a bug in Vulkan where a copied texture can get mip-generated in the wrong command buffer when using basic mipmapping. --- pcsx2/GS/Renderers/Common/GSDevice.cpp | 4 +-- pcsx2/GS/Renderers/Common/GSDevice.h | 2 +- pcsx2/GS/Renderers/DX12/GSDevice12.cpp | 7 ++++-- pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 2 +- pcsx2/GS/Renderers/HW/GSTextureCache.cpp | 25 ++++++++++++------- .../GS/Renderers/HW/GSTextureReplacements.cpp | 2 +- pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp | 2 +- pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp | 3 ++- 8 files changed, 29 insertions(+), 18 deletions(-) diff --git a/pcsx2/GS/Renderers/Common/GSDevice.cpp b/pcsx2/GS/Renderers/Common/GSDevice.cpp index f9c192ce25..8a955e0e0d 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.cpp +++ b/pcsx2/GS/Renderers/Common/GSDevice.cpp @@ -264,9 +264,9 @@ GSTexture* GSDevice::CreateDepthStencil(int w, int h, GSTexture::Format format, return FetchSurface(GSTexture::Type::DepthStencil, w, h, 1, format, clear, true); } -GSTexture* GSDevice::CreateTexture(int w, int h, bool mipmap, GSTexture::Format format, bool prefer_reuse /* = false */) +GSTexture* GSDevice::CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse /* = false */) { - const int levels = mipmap ? MipmapLevelsForSize(w, h) : 1; + const int levels = mipmap_levels < 0 ? MipmapLevelsForSize(w, h) : mipmap_levels; return FetchSurface(GSTexture::Type::Texture, w, h, levels, format, false, prefer_reuse); } diff --git a/pcsx2/GS/Renderers/Common/GSDevice.h b/pcsx2/GS/Renderers/Common/GSDevice.h index 3ce44371d6..4dd17f6cfa 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.h +++ b/pcsx2/GS/Renderers/Common/GSDevice.h @@ -812,7 +812,7 @@ public: GSTexture* CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear = true); GSTexture* CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear = true); - GSTexture* CreateTexture(int w, int h, bool mipmap, GSTexture::Format format, bool prefer_reuse = false); + GSTexture* CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false); GSTexture* CreateOffscreen(int w, int h, GSTexture::Format format); GSTexture::Format GetDefaultTextureFormat(GSTexture::Type type); diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index 284cd6a124..2de568630e 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -440,9 +440,12 @@ void GSDevice12::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, dTexVK->CommitClear(); EndRenderPass(); + sTexVK->TransitionToState(D3D12_RESOURCE_STATE_COPY_SOURCE); - dTexVK->SetState(GSTexture::State::Dirty); + sTexVK->SetUsedThisCommandBuffer(); + dTexVK->TransitionToState(D3D12_RESOURCE_STATE_COPY_DEST); + dTexVK->SetUsedThisCommandBuffer(); D3D12_TEXTURE_COPY_LOCATION srcloc; srcloc.pResource = sTexVK->GetResource(); @@ -2514,7 +2517,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config) else if (config.require_one_barrier) { // requires a copy of the RT - draw_rt_clone = static_cast(CreateTexture(rtsize.x, rtsize.y, false, GSTexture::Format::Color, false)); + draw_rt_clone = static_cast(CreateTexture(rtsize.x, rtsize.y, 1, GSTexture::Format::Color, false)); if (draw_rt_clone) { EndRenderPass(); diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index f79553139e..3816b03810 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -4661,7 +4661,7 @@ bool GSRendererHW::OI_FFXII(GSTexture* rt, GSTexture* ds, GSTextureCache::Source g_gs_device->Recycle(t->m_texture); - t->m_texture = g_gs_device->CreateTexture(512, 512, false, GSTexture::Format::Color); + t->m_texture = g_gs_device->CreateTexture(512, 512, 1, GSTexture::Format::Color); t->m_texture->Update(GSVector4i(0, 0, 448, lines), video, 448 * 4); diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index fd9e8c7e83..c78fbac0a4 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -1562,12 +1562,18 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con { const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM]; Source* src = new Source(TEX0, TEXA, false); - if (lod) - src->m_lod = *lod; int tw = 1 << TEX0.TW; int th = 1 << TEX0.TH; //int tp = TEX0.TBW << 6; + int tlevels = 1; + if (lod) + { + // lod won't contain the full range when using basic mipmapping, only that + // which is hashed, so we just allocate the full thing. + tlevels = (GSConfig.HWMipmap != HWMipmapLevel::Full) ? -1 : (lod->y - lod->x + 1); + src->m_lod = *lod; + } bool hack = false; @@ -1584,7 +1590,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con GSTexture* sTex = dst->m_texture; GSTexture* dTex = outside_target ? g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Color, true) : - g_gs_device->CreateTexture(w, h, false, GSTexture::Format::Color, true); + g_gs_device->CreateTexture(w, h, tlevels, GSTexture::Format::Color, true); // copy the rt in const GSVector4i area(GSVector4i(x, y, x + w, y + h).rintersect(GSVector4i(sTex->GetSize()).zwxy())); @@ -1816,7 +1822,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con // 'src' is the new texture cache entry (hence the output) GSTexture* sTex = dst->m_texture; GSTexture* dTex = use_texture ? - g_gs_device->CreateTexture(w, h, false, GSTexture::Format::Color, true) : + g_gs_device->CreateTexture(w, h, 1, GSTexture::Format::Color, true) : g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Color, false); src->m_texture = dTex; @@ -1880,12 +1886,12 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con } else if (paltex) { - src->m_texture = g_gs_device->CreateTexture(tw, th, false, GSTexture::Format::UNorm8); + src->m_texture = g_gs_device->CreateTexture(tw, th, tlevels, GSTexture::Format::UNorm8); AttachPaletteToSource(src, psm.pal, true); } else { - src->m_texture = g_gs_device->CreateTexture(tw, th, (lod != nullptr), GSTexture::Format::Color); + src->m_texture = g_gs_device->CreateTexture(tw, th, tlevels, GSTexture::Format::Color); if (psm.pal > 0) { AttachPaletteToSource(src, psm.pal, false); @@ -2009,7 +2015,8 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0 // expand/upload texture const int tw = 1 << TEX0.TW; const int th = 1 << TEX0.TH; - GSTexture* tex = g_gs_device->CreateTexture(tw, th, paltex ? false : (lod != nullptr), paltex ? GSTexture::Format::UNorm8 : GSTexture::Format::Color); + const int tlevels = lod ? ((GSConfig.HWMipmap != HWMipmapLevel::Full) ? -1 : (lod->y - lod->x + 1)) : 1; + GSTexture* tex = g_gs_device->CreateTexture(tw, th, tlevels, paltex ? GSTexture::Format::UNorm8 : GSTexture::Format::Color); if (!tex) { // out of video memory if we hit here @@ -2627,7 +2634,7 @@ void GSTextureCache::Target::Update() TEXA.TA0 = 0; TEXA.TA1 = 0x80; - GSTexture* t = g_gs_device->CreateTexture(w, h, false, GSTexture::Format::Color); + GSTexture* t = g_gs_device->CreateTexture(w, h, 1, GSTexture::Format::Color); GSOffset off = g_gs_renderer->m_mem.GetOffset(m_TEX0.TBP0, m_TEX0.TBW, m_TEX0.PSM); @@ -3047,7 +3054,7 @@ void GSTextureCache::Palette::InitializeTexture() // sampling such texture are always normalized by 255. // This is because indexes are stored as normalized values of an RGBA texture (e.g. index 15 will be read as (15/255), // and therefore will read texel 15/255 * texture size). - m_tex_palette = g_gs_device->CreateTexture(256, 1, false, GSTexture::Format::Color); + m_tex_palette = g_gs_device->CreateTexture(256, 1, 1, GSTexture::Format::Color); m_tex_palette->Update(GSVector4i(0, 0, m_pal, 1), m_clut, m_pal * sizeof(m_clut[0])); } } diff --git a/pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp b/pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp index ac47e43eef..fbc75d173a 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp @@ -536,7 +536,7 @@ GSTexture* GSTextureReplacements::CreateReplacementTexture(const ReplacementText mipmap = false; } - GSTexture* tex = g_gs_device->CreateTexture(rtex.width, rtex.height, mipmap, rtex.format); + GSTexture* tex = g_gs_device->CreateTexture(rtex.width, rtex.height, static_cast(rtex.mips.size()) + 1, rtex.format); if (!tex) return nullptr; diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp index d3a296796c..1bb643258b 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp @@ -1873,7 +1873,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config) else if (config.require_one_barrier && !m_features.texture_barrier) { // Requires a copy of the RT - draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, false, GSTexture::Format::Color, false); + draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, GSTexture::Format::Color, false); GL_PUSH("Copy RT to temp texture for fbmask {%d,%d %dx%d}", config.drawarea.left, config.drawarea.top, config.drawarea.width(), config.drawarea.height()); diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index 8536880f2a..9e8621d03e 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -552,6 +552,7 @@ void GSDeviceVK::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, EndRenderPass(); dTexVK->TransitionToLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + dTexVK->SetUsedThisCommandBuffer(); sTexVK->TransitionToLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); sTexVK->SetUsedThisCommandBuffer(); @@ -2954,7 +2955,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config) else if (config.require_one_barrier && !m_features.texture_barrier) { // requires a copy of the RT - draw_rt_clone = static_cast(CreateTexture(rtsize.x, rtsize.y, false, GSTexture::Format::Color, false)); + draw_rt_clone = static_cast(CreateTexture(rtsize.x, rtsize.y, 1, GSTexture::Format::Color, false)); if (draw_rt_clone) { EndRenderPass();