From 13eca5984724ba9dc6be6c37c30c86f0078512ea Mon Sep 17 00:00:00 2001 From: refractionpcsx2 Date: Thu, 5 Feb 2026 12:34:44 +0000 Subject: [PATCH] GS/HW: Allow filling of mip levels from targets in DX12/VK --- pcsx2/GS/Renderers/Common/GSDevice.cpp | 7 +++ pcsx2/GS/Renderers/Common/GSDevice.h | 1 + pcsx2/GS/Renderers/Common/GSTexture.cpp | 4 +- pcsx2/GS/Renderers/Common/GSTexture.h | 5 +- pcsx2/GS/Renderers/DX11/GSTexture11.cpp | 2 +- pcsx2/GS/Renderers/DX11/GSTexture11.h | 2 +- pcsx2/GS/Renderers/DX12/GSDevice12.cpp | 47 ++++++++++-------- pcsx2/GS/Renderers/DX12/GSDevice12.h | 4 +- pcsx2/GS/Renderers/DX12/GSTexture12.cpp | 12 +++-- pcsx2/GS/Renderers/DX12/GSTexture12.h | 2 +- pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 56 +++++++++++++++++++--- pcsx2/GS/Renderers/HW/GSTextureCache.cpp | 20 ++++++-- pcsx2/GS/Renderers/HW/GSTextureCache.h | 5 +- pcsx2/GS/Renderers/OpenGL/GSTextureOGL.cpp | 2 +- pcsx2/GS/Renderers/OpenGL/GSTextureOGL.h | 2 +- pcsx2/GS/Renderers/Vulkan/GSTextureVK.cpp | 12 +++-- pcsx2/GS/Renderers/Vulkan/GSTextureVK.h | 2 +- 17 files changed, 133 insertions(+), 52 deletions(-) diff --git a/pcsx2/GS/Renderers/Common/GSDevice.cpp b/pcsx2/GS/Renderers/Common/GSDevice.cpp index a894002432..0e9fed80a9 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.cpp +++ b/pcsx2/GS/Renderers/Common/GSDevice.cpp @@ -706,6 +706,13 @@ void GSDevice::PurgePool() m_pool_memory_usage = 0; } +GSTexture* GSDevice::CreateMipRenderTarget(int w, int h, int mipmap_levels, GSTexture::Format format, bool clear, bool prefer_reuse /* = false */) +{ + pxAssert(mipmap_levels != 0 && (mipmap_levels < 0 || mipmap_levels <= GetMipmapLevelsForSize(w, h))); + const int levels = mipmap_levels < 0 ? GetMipmapLevelsForSize(w, h) : mipmap_levels; + return FetchSurface(GSTexture::Type::RenderTarget, w, h, levels, format, clear, !prefer_reuse); +} + GSTexture* GSDevice::CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear, bool prefer_reuse) { return FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, format, clear, !prefer_reuse); diff --git a/pcsx2/GS/Renderers/Common/GSDevice.h b/pcsx2/GS/Renderers/Common/GSDevice.h index 6ca3e6aff3..a74ace71e6 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.h +++ b/pcsx2/GS/Renderers/Common/GSDevice.h @@ -1060,6 +1060,7 @@ public: virtual void PopDebugGroup() = 0; virtual void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) = 0; + GSTexture* CreateMipRenderTarget(int w, int h, int mipmap_levels, GSTexture::Format format, bool clear = true, bool prefer_reuse = false); GSTexture* CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear = true, bool prefer_reuse = true); GSTexture* CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear = true, bool prefer_reuse = true); GSTexture* CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false); diff --git a/pcsx2/GS/Renderers/Common/GSTexture.cpp b/pcsx2/GS/Renderers/Common/GSTexture.cpp index 09252398fa..764a0ef8c5 100644 --- a/pcsx2/GS/Renderers/Common/GSTexture.cpp +++ b/pcsx2/GS/Renderers/Common/GSTexture.cpp @@ -158,13 +158,13 @@ u32 GSTexture::CalcUploadSize(Format format, u32 height, u32 pitch) return pitch * ((static_cast(height) + (block_size - 1)) / block_size); } -void GSTexture::GenerateMipmapsIfNeeded() +void GSTexture::GenerateMipmapsIfNeeded(GSTexture* target_list[], GSVector2i offsets_list[]) { if (!m_needs_mipmaps_generated || m_mipmap_levels <= 1 || IsCompressedFormat()) return; m_needs_mipmaps_generated = false; - GenerateMipmap(); + GenerateMipmap(target_list, offsets_list); } GSDownloadTexture::GSDownloadTexture(u32 width, u32 height, GSTexture::Format format) diff --git a/pcsx2/GS/Renderers/Common/GSTexture.h b/pcsx2/GS/Renderers/Common/GSTexture.h index 5d2c119dc7..8392ae6634 100644 --- a/pcsx2/GS/Renderers/Common/GSTexture.h +++ b/pcsx2/GS/Renderers/Common/GSTexture.h @@ -59,6 +59,7 @@ public: }; protected: + GSVector2i m_size{}; int m_mipmap_levels = 0; Type m_type = Type::Invalid; @@ -82,7 +83,7 @@ public: virtual bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) = 0; virtual bool Map(GSMap& m, const GSVector4i* r = nullptr, int layer = 0) = 0; virtual void Unmap() = 0; - virtual void GenerateMipmap() = 0; + virtual void GenerateMipmap(GSTexture* target_list[], GSVector2i offset_list[]) = 0; #ifdef PCSX2_DEVBUILD virtual void SetDebugName(std::string_view name) = 0; @@ -153,7 +154,7 @@ public: m_clear_value.depth = depth; } - void GenerateMipmapsIfNeeded(); + void GenerateMipmapsIfNeeded(GSTexture* target_list[], GSVector2i offsets_list[]); void ClearMipmapGenerationFlag() { m_needs_mipmaps_generated = false; } // Typical size of a RGBA texture diff --git a/pcsx2/GS/Renderers/DX11/GSTexture11.cpp b/pcsx2/GS/Renderers/DX11/GSTexture11.cpp index 38855dbede..ce80880357 100644 --- a/pcsx2/GS/Renderers/DX11/GSTexture11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSTexture11.cpp @@ -84,7 +84,7 @@ void GSTexture11::Unmap() pxFailRel("Should not be called."); } -void GSTexture11::GenerateMipmap() +void GSTexture11::GenerateMipmap(GSTexture* target_list[], GSVector2i offsets_list[]) { GSDevice11::GetInstance()->GetD3DContext()->GenerateMips(operator ID3D11ShaderResourceView*()); } diff --git a/pcsx2/GS/Renderers/DX11/GSTexture11.h b/pcsx2/GS/Renderers/DX11/GSTexture11.h index 06c38cac8e..f7879cc482 100644 --- a/pcsx2/GS/Renderers/DX11/GSTexture11.h +++ b/pcsx2/GS/Renderers/DX11/GSTexture11.h @@ -31,7 +31,7 @@ public: bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) override; bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) override; void Unmap() override; - void GenerateMipmap() override; + void GenerateMipmap(GSTexture* target_list[], GSVector2i offsets_list[]) override; #ifdef PCSX2_DEVBUILD void SetDebugName(std::string_view name) override; diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index ab5ce80a34..ae6008dc0f 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -1595,7 +1595,8 @@ void GSDevice12::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, dstloc.SubresourceIndex = 0; // DX12 requires ProgrammableSamplePositions tier 1 to support partial depth copies, otherwise fallback to full depth copies. - const bool full_rt_copy = src_dst_rect_match && ((sTex12->IsDepthStencil() && !m_programmable_sample_positions) || (destX == 0 && destY == 0 && r.eq(src_rect))); + // We only want to copy mip level 0, so check resource mip levels + const bool full_rt_copy = src_dst_rect_match && ((sTex12->IsDepthStencil() && !m_programmable_sample_positions) || (destX == 0 && destY == 0 && r.eq(src_rect))) && (sTex12->GetMipmapLevels() == 1 && dTex12->GetMipmapLevels() == 1); if (full_rt_copy) { GetCommandList().list4->CopyResource(dTex12->GetResource(), sTex12->GetResource()); @@ -3525,7 +3526,7 @@ void GSDevice12::UnbindTexture(GSTexture12* tex) } void GSDevice12::RenderTextureMipmap( - GSTexture12* texture, u32 dst_level, u32 dst_width, u32 dst_height, u32 src_level, u32 src_width, u32 src_height) + GSTexture12* src_texture, GSTexture12* dst_texture, u32 dst_level, u32 dst_width, u32 dst_height, u32 src_level, u32 src_width, u32 src_height, GSVector2i offsets) { EndRenderPass(); @@ -3540,16 +3541,17 @@ void GSDevice12::RenderTextureMipmap( ExecuteCommandList(false); // Setup views. This will be a partial view for the SRV. - D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = {texture->GetDXGIFormat(), D3D12_RTV_DIMENSION_TEXTURE2D}; + D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = {dst_texture->GetDXGIFormat(), D3D12_RTV_DIMENSION_TEXTURE2D}; rtv_desc.Texture2D = {dst_level, 0u}; - m_device.get()->CreateRenderTargetView(texture->GetResource(), &rtv_desc, rtv_handle); + m_device.get()->CreateRenderTargetView(dst_texture->GetResource(), &rtv_desc, rtv_handle); D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = { - texture->GetDXGIFormat(), D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING}; + src_texture->GetDXGIFormat(), D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING}; srv_desc.Texture2D = {src_level, 1u, 0u, 0.0f}; - m_device.get()->CreateShaderResourceView(texture->GetResource(), &srv_desc, srv_handle); + m_device.get()->CreateShaderResourceView(src_texture->GetResource(), &srv_desc, srv_handle); // We need to set the descriptors up manually, because we're not going through GSTexture. + m_dirty_flags |= DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE; if (!GetTextureGroupDescriptors(&m_utility_texture_gpu, &srv_handle, 1)) ExecuteCommandList(false); if (m_utility_sampler_cpu != m_linear_sampler_cpu) @@ -3561,12 +3563,12 @@ void GSDevice12::RenderTextureMipmap( // *now* we don't have to worry about running out of anything. const D3D12CommandList& cmdlist = GetCommandList(); - if (texture->GetResourceState() != GSTexture12::ResourceState::PixelShaderResource) - texture->TransitionSubresourceToState( - cmdlist, src_level, texture->GetResourceState(), GSTexture12::ResourceState::PixelShaderResource); - if (texture->GetResourceState() != GSTexture12::ResourceState::RenderTarget) - texture->TransitionSubresourceToState( - cmdlist, dst_level, texture->GetResourceState(), GSTexture12::ResourceState::RenderTarget); + if (src_texture->GetResourceState() != GSTexture12::ResourceState::PixelShaderResource) + src_texture->TransitionSubresourceToState( + cmdlist, src_level, src_texture->GetResourceState(), GSTexture12::ResourceState::PixelShaderResource); + if (dst_texture->GetResourceState() != GSTexture12::ResourceState::RenderTarget) + dst_texture->TransitionSubresourceToState( + cmdlist, dst_level, dst_texture->GetResourceState(), GSTexture12::ResourceState::RenderTarget); // We set the state directly here. constexpr u32 MODIFIED_STATE = DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_RENDER_TARGET; @@ -3574,7 +3576,7 @@ void GSDevice12::RenderTextureMipmap( // Using a render pass is probably a bit overkill. const D3D12_DISCARD_REGION discard_region = {0u, nullptr, dst_level, 1u}; - cmdlist.list4->DiscardResource(texture->GetResource(), &discard_region); + cmdlist.list4->DiscardResource(dst_texture->GetResource(), &discard_region); cmdlist.list4->OMSetRenderTargets(1, &rtv_handle.cpu_handle, FALSE, nullptr); const D3D12_VIEWPORT vp = {0.0f, 0.0f, static_cast(dst_width), static_cast(dst_height), 0.0f, 1.0f}; @@ -3585,16 +3587,21 @@ void GSDevice12::RenderTextureMipmap( SetUtilityRootSignature(); SetPipeline(m_convert[static_cast(ShaderConvert::COPY)].get()); - DrawStretchRect(GSVector4(0.0f, 0.0f, 1.0f, 1.0f), + const int src_tex_width = std::max(src_texture->GetWidth() >> src_level, 1); + const int src_tex_height = std::max(src_texture->GetHeight() >> src_level, 1); + GSVector4 src_level_size = GSVector4(src_tex_width, src_tex_height, src_tex_width, src_tex_height); + GSVector4 src_bounds = GSVector4(static_cast(offsets.x), static_cast(offsets.y), static_cast(offsets.x + src_width), static_cast(offsets.y + src_height)) / + src_level_size; + DrawStretchRect(src_bounds, GSVector4(0.0f, 0.0f, static_cast(dst_width), static_cast(dst_height)), GSVector2i(dst_width, dst_height)); - if (texture->GetResourceState() != GSTexture12::ResourceState::PixelShaderResource) - texture->TransitionSubresourceToState( - cmdlist, src_level, GSTexture12::ResourceState::PixelShaderResource, texture->GetResourceState()); - if (texture->GetResourceState() != GSTexture12::ResourceState::RenderTarget) - texture->TransitionSubresourceToState( - cmdlist, dst_level, GSTexture12::ResourceState::RenderTarget, texture->GetResourceState()); + if (src_texture->GetResourceState() != GSTexture12::ResourceState::PixelShaderResource) + src_texture->TransitionSubresourceToState( + cmdlist, src_level, GSTexture12::ResourceState::PixelShaderResource, src_texture->GetResourceState()); + if (dst_texture->GetResourceState() != GSTexture12::ResourceState::RenderTarget) + dst_texture->TransitionSubresourceToState( + cmdlist, dst_level, GSTexture12::ResourceState::RenderTarget, dst_texture->GetResourceState()); // Must destroy after current cmdlist. DeferDescriptorDestruction(m_descriptor_heap_manager, &srv_handle); diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.h b/pcsx2/GS/Renderers/DX12/GSDevice12.h index 7b08fe9762..8c8c52517e 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.h +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.h @@ -524,8 +524,8 @@ public: // Assumes that the previous level has been transitioned to PS resource, // and the current level has been transitioned to RT. - void RenderTextureMipmap(GSTexture12* texture, u32 dst_level, u32 dst_width, u32 dst_height, u32 src_level, - u32 src_width, u32 src_height); + void RenderTextureMipmap(GSTexture12* src_texture, GSTexture12* dst_texture, u32 dst_level, u32 dst_width, u32 dst_height, u32 src_level, + u32 src_width, u32 src_height, GSVector2i offsets); // Ends a render pass if we're currently in one. // When Bind() is next called, the pass will be restarted. diff --git a/pcsx2/GS/Renderers/DX12/GSTexture12.cpp b/pcsx2/GS/Renderers/DX12/GSTexture12.cpp index bd96b85378..50bf6f60f2 100644 --- a/pcsx2/GS/Renderers/DX12/GSTexture12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSTexture12.cpp @@ -771,20 +771,26 @@ void GSTexture12::Unmap() m_needs_mipmaps_generated |= (m_map_level == 0); } -void GSTexture12::GenerateMipmap() +void GSTexture12::GenerateMipmap(GSTexture* target_list[], GSVector2i offsets_list[]) { pxAssert(!IsCompressedFormat(m_format)); for (int dst_level = 1; dst_level < m_mipmap_levels; dst_level++) { - const int src_level = dst_level - 1; + const bool has_target_layer = target_list && target_list[dst_level] != nullptr; + int src_level = has_target_layer ? dst_level : (dst_level - 1); const int src_width = std::max(m_size.x >> src_level, 1); const int src_height = std::max(m_size.y >> src_level, 1); const int dst_width = std::max(m_size.x >> dst_level, 1); const int dst_height = std::max(m_size.y >> dst_level, 1); + GSVector2i offset = has_target_layer ? offsets_list[dst_level] : GSVector2i(0, 0); + src_level = has_target_layer ? 0 : src_level; + + GSTexture12* src_image = has_target_layer ? static_cast(target_list[dst_level]) : this; + GSDevice12::GetInstance()->RenderTextureMipmap( - this, dst_level, dst_width, dst_height, src_level, src_width, src_height); + src_image, this, dst_level, dst_width, dst_height, src_level, src_width, src_height, offset); } SetUseFenceCounter(GSDevice12::GetInstance()->GetCurrentFenceValue()); diff --git a/pcsx2/GS/Renderers/DX12/GSTexture12.h b/pcsx2/GS/Renderers/DX12/GSTexture12.h index 7ed880f81d..493045eaca 100644 --- a/pcsx2/GS/Renderers/DX12/GSTexture12.h +++ b/pcsx2/GS/Renderers/DX12/GSTexture12.h @@ -61,7 +61,7 @@ public: bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) override; bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) override; void Unmap() override; - void GenerateMipmap() override; + void GenerateMipmap(GSTexture* target_list[], GSVector2i offsets_list[]) override; #ifdef PCSX2_DEVBUILD void SetDebugName(std::string_view name) override; diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 22b95bdb06..918bebf594 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -4170,10 +4170,23 @@ void GSRendererHW::Draw() // Backup original coverage. const GSVector4i coverage = tmm.coverage; + GSTexture* target_levels[6] = {nullptr}; + GSVector2i target_offsets[6] = {}; - for (int layer = m_lod.x + 1; layer <= m_lod.y; layer++) + GSVector2i layer_levels(m_lod.x + 1, m_lod.y); + if (src->m_target) { - const GIFRegTEX0 MIP_TEX0(GetTex0Layer(layer)); + target_levels[0] = src->m_texture; + layer_levels.x = 1; + layer_levels.y = m_context->TEX1.MXL; + + } + + bool has_target_mips = true; + + for (int layer = layer_levels.x; layer <= layer_levels.y; layer++) + { + GIFRegTEX0 MIP_TEX0(GetTex0Layer(layer)); MIP_CLAMP.MINU >>= 1; MIP_CLAMP.MINV >>= 1; @@ -4185,7 +4198,37 @@ void GSRendererHW::Draw() tmm = GetTextureMinMax(MIP_TEX0, MIP_CLAMP, m_vt.IsLinear(), true); - src->UpdateLayer(MIP_TEX0, tmm.coverage, layer - m_lod.x); + if (!src->m_target) + { + src->UpdateLayer(MIP_TEX0, tmm.coverage, layer - m_lod.x); + has_target_mips = false; + } + else + { + const GSVector4i tgt_rect = GSVector4i(0, 0, tmm.coverage.z, tmm.coverage.w); + GSTextureCache::Target* tgt = g_texture_cache->FindOverlappingTarget(MIP_TEX0.TBP0, GSLocalMemory::GetEndBlockAddress(MIP_TEX0.TBP0, MIP_TEX0.TBW, MIP_TEX0.PSM, tgt_rect)); + + if (tgt != nullptr) + { + tgt->Update(); + target_levels[layer] = tgt->m_texture; + + if (tgt->m_TEX0.TBP0 != MIP_TEX0.TBP0) + { + GSVector4i offset = GSVector4i::zero(); + g_texture_cache->TranslateAlignedRectByPage(tgt, MIP_TEX0.TBP0, MIP_TEX0.PSM, MIP_TEX0.TBW, tgt_rect, false); + target_offsets[layer] = GSVector2i(offset.x * tgt->GetScale(), offset.y * tgt->GetScale()); + + } + } + else + has_target_mips = false; + } + } + if (src->m_target && !src->m_target_direct && m_lod.y > m_lod.x && has_target_mips) + { + src->m_texture->GenerateMipmapsIfNeeded(target_levels, target_offsets); + src->m_target_mips = true; } // we don't need to generate mipmaps since they were provided @@ -6507,12 +6550,13 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt, const bool need_mipmap = IsMipMapDraw(); const bool shader_emulated_sampler = tex->m_palette || (tex->m_target && !m_conf.ps.shuffle && cpsm.fmt != 0) || complex_wms_wmt || psm.depth || target_region; - const bool can_trilinear = !tex->m_palette && !tex->m_target && !m_conf.ps.shuffle; + const bool mips_available = tex->m_target_mips || !tex->m_target; + const bool can_trilinear = !tex->m_palette && mips_available && !m_conf.ps.shuffle; const bool trilinear_manual = need_mipmap && GSConfig.HWMipmap; bool bilinear = m_vt.IsLinear(); int trilinear = 0; - bool trilinear_auto = false; // Generate mipmaps if needed (basic). + bool trilinear_auto = true; // Generate mipmaps if needed (basic). switch (GSConfig.TriFilter) { case TriFiltering::Forced: @@ -6806,7 +6850,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt, } else if (trilinear_auto) { - tex->m_texture->GenerateMipmapsIfNeeded(); + tex->m_texture->GenerateMipmapsIfNeeded(nullptr, nullptr); m_conf.ps.automatic_lod = 1; } diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index af3a5efe3e..77d4513310 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -6220,6 +6220,8 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con // Create a cleared RT if we somehow end up with an empty source rect (because the RT isn't large enough). const bool source_rect_empty = sRect.rempty(); const bool use_texture = (shader == ShaderConvert::COPY && !source_rect_empty); + const bool needs_lod_levels = use_texture && (lod != nullptr) && lod->y > 0; + const int lod_level = (lod != nullptr) ? (lod->y + 1) : 1; GSVector4i region_rect = GSVector4i(0, 0, tw, th); // Assuming everything matches up, instead of copying the target, we can just sample it directly. @@ -6227,7 +6229,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con // TODO: We still need to copy if the TBW is mismatched. Except when TBW <= 1 (Jak 2). const GSVector2i dst_texture_size = dst->m_texture->GetSize(); if (use_texture && // not reinterpreting the RT - !force_target_copy) + !force_target_copy && !needs_lod_levels) { // sample the target directly src->m_texture = dst->m_texture; @@ -6262,7 +6264,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(new_size.x, new_size.y, 1, GSTexture::Format::Color, PreferReusedLabelledTexture()) : + g_gs_device->CreateTexture(new_size.x, new_size.y, lod_level, GSTexture::Format::Color, PreferReusedLabelledTexture()) : g_gs_device->CreateRenderTarget(new_size.x, new_size.y, GSTexture::Format::Color, source_rect_empty || destX != 0 || destY != 0, PreferReusedLabelledTexture()); if (!dTex) [[unlikely]] { @@ -6280,9 +6282,17 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con { if (dst->m_rt_alpha_scale) { - const GSVector4 sRectF = GSVector4(sRect) / GSVector4(1, 1, sTex->GetWidth(), sTex->GetHeight()); - g_gs_device->StretchRect( - sTex, sRectF, dTex, GSVector4(destX, destY, sRect.width(), sRect.height()), ShaderConvert::RTA_DECORRECTION, false); + if (!use_texture) + { + const GSVector4 sRectF = GSVector4(sRect) / GSVector4(1, 1, sTex->GetWidth(), sTex->GetHeight()); + g_gs_device->StretchRect( + sTex, sRectF, dTex, GSVector4(destX, destY, sRect.width(), sRect.height()), ShaderConvert::RTA_DECORRECTION, false); + } + else + { + dst->UnscaleRTAlpha(); + g_gs_device->CopyRect(sTex, dTex, sRect, destX, destY); + } } else g_gs_device->CopyRect(sTex, dTex, sRect, destX, destY); diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.h b/pcsx2/GS/Renderers/HW/GSTextureCache.h index 9cd0407362..bd9bd15141 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.h +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.h @@ -309,6 +309,7 @@ public: u8 m_complete_layers = 0; bool m_target = false; bool m_target_direct = false; + bool m_target_mips = false; bool m_repeating = false; bool m_valid_alpha_minmax = false; std::pair m_alpha_minmax = {0u, 255u}; @@ -455,8 +456,6 @@ protected: std::unique_ptr m_uint16_download_texture; std::unique_ptr m_uint32_download_texture; - Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, Target* t, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region, bool force_temporary = false); - bool PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, bool is_frame, bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst, GSTextureCache::Source* src = nullptr); @@ -508,6 +507,8 @@ public: GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, float* scale, const GSVector2i& size); std::shared_ptr LookupPaletteObject(const u32* clut, u16 pal, bool need_gs_texture); + Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, Target* t, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region, bool force_temporary = false); + Source* LookupSource(const bool is_color, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle, const bool linear, const GIFRegFRAME& frame, bool req_color = true, bool req_alpha = true); Source* LookupDepthSource(const bool is_depth, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, const bool linear, const GIFRegFRAME& frame, bool req_color = true, bool req_alpha = true, bool palette = false); diff --git a/pcsx2/GS/Renderers/OpenGL/GSTextureOGL.cpp b/pcsx2/GS/Renderers/OpenGL/GSTextureOGL.cpp index 7dfe4ccaab..6cb5cac4f9 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSTextureOGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GSTextureOGL.cpp @@ -311,7 +311,7 @@ void GSTextureOGL::Unmap() } } -void GSTextureOGL::GenerateMipmap() +void GSTextureOGL::GenerateMipmap(GSTexture* target_list[], GSVector2i offsets_list[]) { pxAssert(m_mipmap_levels > 1); GSDeviceOGL::GetInstance()->CommitClear(this, true); diff --git a/pcsx2/GS/Renderers/OpenGL/GSTextureOGL.h b/pcsx2/GS/Renderers/OpenGL/GSTextureOGL.h index 0fa79d0166..6e5a5992aa 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSTextureOGL.h +++ b/pcsx2/GS/Renderers/OpenGL/GSTextureOGL.h @@ -39,7 +39,7 @@ public: bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) override; bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) override; void Unmap() override; - void GenerateMipmap() override; + void GenerateMipmap(GSTexture* target_list[], GSVector2i offsets_list[]) override; #ifdef PCSX2_DEVBUILD void SetDebugName(std::string_view name) override; diff --git a/pcsx2/GS/Renderers/Vulkan/GSTextureVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSTextureVK.cpp index 133eaa70de..86732c208b 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSTextureVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSTextureVK.cpp @@ -461,7 +461,7 @@ void GSTextureVK::Unmap() m_needs_mipmaps_generated |= (m_map_level == 0); } -void GSTextureVK::GenerateMipmap() +void GSTextureVK::GenerateMipmap(GSTexture* target_list[], GSVector2i offsets_list[]) { const VkCommandBuffer cmdbuf = GetCommandBufferForUpdate(); @@ -470,7 +470,8 @@ void GSTextureVK::GenerateMipmap() for (int dst_level = 1; dst_level < m_mipmap_levels; dst_level++) { - const int src_level = dst_level - 1; + const bool has_target_layer = target_list && target_list[dst_level] != nullptr; + int src_level = has_target_layer ? dst_level : (dst_level - 1); const int src_width = std::max(m_size.x >> src_level, 1); const int src_height = std::max(m_size.y >> src_level, 1); const int dst_width = std::max(m_size.x >> dst_level, 1); @@ -479,14 +480,17 @@ void GSTextureVK::GenerateMipmap() TransitionSubresourcesToLayout(cmdbuf, src_level, 1, m_layout, Layout::TransferSrc); TransitionSubresourcesToLayout(cmdbuf, dst_level, 1, m_layout, Layout::TransferDst); + GSVector2i offset = has_target_layer ? offsets_list[dst_level] : GSVector2i(0, 0); + src_level = has_target_layer ? 0 : src_level; const VkImageBlit blit = { {VK_IMAGE_ASPECT_COLOR_BIT, static_cast(src_level), 0u, 1u}, // srcSubresource - {{0, 0, 0}, {src_width, src_height, 1}}, // srcOffsets + {{offset.x, offset.y, 0}, {offset.x + src_width, offset.y + src_height, 1}}, // srcOffsets {VK_IMAGE_ASPECT_COLOR_BIT, static_cast(dst_level), 0u, 1u}, // dstSubresource {{0, 0, 0}, {dst_width, dst_height, 1}} // dstOffsets }; - vkCmdBlitImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_image, + VkImage src_image = has_target_layer ? static_cast(target_list[dst_level])->m_image : m_image; + vkCmdBlitImage(cmdbuf, src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); TransitionSubresourcesToLayout(cmdbuf, src_level, 1, Layout::TransferSrc, m_layout); diff --git a/pcsx2/GS/Renderers/Vulkan/GSTextureVK.h b/pcsx2/GS/Renderers/Vulkan/GSTextureVK.h index fcc7f8f5c5..32bf4103f4 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSTextureVK.h +++ b/pcsx2/GS/Renderers/Vulkan/GSTextureVK.h @@ -51,7 +51,7 @@ public: bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) override; bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) override; void Unmap() override; - void GenerateMipmap() override; + void GenerateMipmap(GSTexture* target_list[], GSVector2i offsets_list[]) override; #ifdef PCSX2_DEVBUILD void SetDebugName(std::string_view name) override;