From b344ee094fc32fc72d1bf58b0514cd90f58440bc Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Thu, 21 Apr 2022 23:43:00 +1000 Subject: [PATCH] GS: Make CopyRect() take a destination offset --- pcsx2/GS/Renderers/Common/GSDevice.h | 2 +- pcsx2/GS/Renderers/DX11/GSDevice11.cpp | 21 ++++------------ pcsx2/GS/Renderers/DX11/GSDevice11.h | 3 +-- pcsx2/GS/Renderers/DX12/GSDevice12.cpp | 24 +++++-------------- pcsx2/GS/Renderers/DX12/GSDevice12.h | 3 +-- pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 6 ++--- pcsx2/GS/Renderers/HW/GSTextureCache.cpp | 4 ++-- pcsx2/GS/Renderers/Metal/GSDeviceMTL.h | 2 +- pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm | 4 ++-- pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp | 4 ++-- pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h | 2 +- pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp | 29 +++++++---------------- pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h | 3 +-- 13 files changed, 35 insertions(+), 72 deletions(-) diff --git a/pcsx2/GS/Renderers/Common/GSDevice.h b/pcsx2/GS/Renderers/Common/GSDevice.h index 8996419276..156272eb28 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.h +++ b/pcsx2/GS/Renderers/Common/GSDevice.h @@ -717,7 +717,7 @@ public: /// Must be called to free resources after calling `DownloadTexture` or `DownloadTextureConvert` virtual void DownloadTextureComplete() {} - virtual void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r) {} + virtual void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) {} virtual void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) {} virtual void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha) {} diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp index 37bd2af213..73c89c2a41 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp @@ -515,7 +515,7 @@ bool GSDevice11::DownloadTexture(GSTexture* src, const GSVector4i& rect, GSTextu m_download_tex.reset(static_cast(CreateOffscreen(rect.width(), rect.height(), src->GetFormat()))); if (!m_download_tex) return false; - CopyRect(src, m_download_tex.get(), rect); + CopyRect(src, m_download_tex.get(), rect, 0, 0); return m_download_tex->Map(out_map); } @@ -528,22 +528,11 @@ void GSDevice11::DownloadTextureComplete() } } -void GSDevice11::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r) -{ - if (!sTex || !dTex) - { - ASSERT(0); - return; - } - - CopyRect(sTex, r, dTex, 0, 0); -} - -void GSDevice11::CopyRect(GSTexture* sTex, const GSVector4i& sRect, GSTexture* dTex, u32 destX, u32 destY) +void GSDevice11::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) { g_perfmon.Put(GSPerfMon::TextureCopies, 1); - D3D11_BOX box = {(UINT)sRect.left, (UINT)sRect.top, 0U, (UINT)sRect.right, (UINT)sRect.bottom, 1U}; + D3D11_BOX box = {(UINT)r.left, (UINT)r.top, 0U, (UINT)r.right, (UINT)r.bottom, 1U}; // DX api isn't happy if we pass a box for depth copy // It complains that depth/multisample must be a full copy @@ -565,12 +554,12 @@ void GSDevice11::CloneTexture(GSTexture* src, GSTexture** dest, const GSVector4i { // DX11 requires that you copy the entire depth buffer. *dest = CreateDepthStencil(w, h, src->GetFormat(), false); - CopyRect(src, GSVector4i(0, 0, w, h), *dest, 0, 0); + CopyRect(src, *dest, GSVector4i(0, 0, w, h), 0, 0); } else { *dest = CreateRenderTarget(w, h, src->GetFormat(), false); - CopyRect(src, rect, *dest, rect.left, rect.top); + CopyRect(src, *dest, rect, rect.left, rect.top); } } diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.h b/pcsx2/GS/Renderers/DX11/GSDevice11.h index 1033ac5604..05ba453f8b 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.h +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.h @@ -251,8 +251,7 @@ public: void CloneTexture(GSTexture* src, GSTexture** dest, const GSVector4i& rect); - void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r); - void CopyRect(GSTexture* sTex, const GSVector4i& sRect, GSTexture* dTex, u32 destX, u32 destY); + void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override; void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) final; void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, bool linear = true); diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index 58781ed60b..d582a7fa00 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -401,19 +401,7 @@ void GSDevice12::DownloadTextureComplete() UnmapStagingBuffer(); } -void GSDevice12::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r) -{ - if (!sTex || !dTex) - { - ASSERT(0); - return; - } - - const GSVector4i dst_rc(r - r.xyxy()); - DoCopyRect(sTex, dTex, r, dst_rc); -} - -void GSDevice12::DoCopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, const GSVector4i& dst_rc) +void GSDevice12::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) { g_perfmon.Put(GSPerfMon::TextureCopies, 1); @@ -426,7 +414,7 @@ void GSDevice12::DoCopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& // source is cleared. if destination is a render target, we can carry the clear forward if (dTexVK->IsRenderTargetOrDepthStencil()) { - if (dtex_rc.eq(dst_rc)) + if (dtex_rc.eq(r)) { // pass it forward if we're clearing the whole thing if (sTexVK->IsDepthStencil()) @@ -464,7 +452,7 @@ void GSDevice12::DoCopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& // if the destination has been cleared, and we're not overwriting the whole thing, commit the clear first // (the area outside of where we're copying to) - if (dTexVK->GetState() == GSTexture::State::Cleared && !dtex_rc.eq(dst_rc)) + if (dTexVK->GetState() == GSTexture::State::Cleared && !dtex_rc.eq(r)) dTexVK->CommitClear(); EndRenderPass(); @@ -485,7 +473,7 @@ void GSDevice12::DoCopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& const D3D12_BOX srcbox{static_cast(r.left), static_cast(r.top), 0u, static_cast(r.right), static_cast(r.bottom), 1u}; g_d3d12_context->GetCommandList()->CopyTextureRegion( - &dstloc, dst_rc.left, dst_rc.top, 0, + &dstloc, destX, destY, 0, &srcloc, &srcbox); dTexVK->SetState(GSTexture::State::Dirty); @@ -2514,7 +2502,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config) config.drawarea.width(), config.drawarea.height()); draw_rt_clone->SetState(GSTexture::State::Invalidated); - DoCopyRect(draw_rt, draw_rt_clone, config.drawarea, config.drawarea); + CopyRect(draw_rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top); PSSetShaderResource(2, draw_rt_clone, true); } } @@ -2532,7 +2520,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config) config.drawarea.width(), config.drawarea.height()); copy_ds->SetState(GSTexture::State::Invalidated); - DoCopyRect(config.ds, copy_ds, config.drawarea, config.drawarea); + CopyRect(config.ds, copy_ds, config.drawarea, config.drawarea.left, config.drawarea.top); PSSetShaderResource(0, copy_ds, true); } } diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.h b/pcsx2/GS/Renderers/DX12/GSDevice12.h index 883818fb79..8c7302638f 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.h +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.h @@ -248,8 +248,7 @@ public: GSTexture* DrawForReadback(GSTexture* src, const GSVector4& sRect, int w, int h, int format = 0, int ps_shader = 0); bool ReadbackTexture(GSTexture* src, const GSVector4i& rect, u32 level, GSTexture::GSMap* dst); - void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r) override; - void DoCopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, const GSVector4i& dst_rc); + void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override; void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) override; diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index a93211365e..f71390beb0 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -1571,7 +1571,7 @@ void GSRendererHW::Draw() g_gs_device->CreateSparseRenderTarget(new_w, new_h, tex->GetFormat()) : g_gs_device->CreateSparseDepthStencil(new_w, new_h, tex->GetFormat()); const GSVector4i r{ 0, 0, w, h }; - g_gs_device->CopyRect(tex, t->m_texture, r); + g_gs_device->CopyRect(tex, t->m_texture, r, 0, 0); g_gs_device->Recycle(tex); t->m_texture->SetScale(up_s); (is_rt ? rt_tex : ds_tex) = t->m_texture; @@ -2017,11 +2017,11 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc const GSVector4i r_full(0, 0, tw, th); if (GSTexture* rt = g_gs_device->CreateRenderTarget(tw, th, GSTexture::Format::Color)) { - g_gs_device->CopyRect(tex->m_texture, rt, r_full); + g_gs_device->CopyRect(tex->m_texture, rt, r_full, 0, 0); g_gs_device->StretchRect(tex->m_texture, sRect, rt, dRect); - g_gs_device->CopyRect(rt, tex->m_texture, r_full); + g_gs_device->CopyRect(rt, tex->m_texture, r_full, 0, 0); g_gs_device->Recycle(rt); } diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index 9a23ac3bc2..723c5b0d52 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -1188,7 +1188,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con GSTexture* dTex = g_gs_device->CreateTexture(w, h, false, GSTexture::Format::Color, true); GSVector4i area(x, y, x + w, y + h); - g_gs_device->CopyRect(sTex, dTex, area); + g_gs_device->CopyRect(sTex, dTex, area, 0, 0); // Keep a trace of origin of the texture src->m_texture = dTex; @@ -1380,7 +1380,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con if (use_texture) { - g_gs_device->CopyRect(sTex, dTex, sRect); + g_gs_device->CopyRect(sTex, dTex, sRect, 0, 0); } else { diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h index b39797a4cb..bf2ee3f937 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h @@ -349,7 +349,7 @@ public: bool DownloadTexture(GSTexture* src, const GSVector4i& rect, GSTexture::GSMap& out_map) override; - void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r) override; + void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override; void DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, id pipeline, bool linear, LoadAction load_action, void* frag_uniform, size_t frag_uniform_len); void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds); /// Copy from a position in sTex to the same position in the currently active render encoder using the given fs pipeline and rect diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm index 35d4aa0e74..2f59d51581 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm @@ -941,7 +941,7 @@ bool GSDeviceMTL::DownloadTexture(GSTexture* src, const GSVector4i& rect, GSText return true; }} -void GSDeviceMTL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r) +void GSDeviceMTL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) { @autoreleasepool { g_perfmon.Put(GSPerfMon::TextureCopies, 1); @@ -971,7 +971,7 @@ void GSDeviceMTL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r toTexture:dT->GetTexture() destinationSlice:0 destinationLevel:0 - destinationOrigin:MTLOriginMake(0, 0, 0)]; + destinationOrigin:MTLOriginMake((int)destX, (int)destY, 0)]; [encoder endEncoding]; }} diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp index d66c19687f..bb69eb025a 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp @@ -1145,7 +1145,7 @@ void GSDeviceOGL::BlitRect(GSTexture* sTex, const GSVector4i& r, const GSVector2 } // Copy a sub part of a texture into another -void GSDeviceOGL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r) +void GSDeviceOGL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) { ASSERT(sTex && dTex); if (!(sTex && dTex)) @@ -1167,7 +1167,7 @@ void GSDeviceOGL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r glCopyImageSubData(sid, GL_TEXTURE_2D, 0, r.x, r.y, 0, did, GL_TEXTURE_2D, - 0, 0, 0, 0, + 0, destX, destY, 0, r.width(), r.height(), 1); } diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h index 49cde9a4ef..a249c16422 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h @@ -341,7 +341,7 @@ public: bool DownloadTexture(GSTexture* src, const GSVector4i& rect, GSTexture::GSMap& out_map) final; - void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r) final; + void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) final; void PushDebugGroup(const char* fmt, ...) final; void PopDebugGroup() final; diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index 68c33b139a..872d296935 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -489,19 +489,7 @@ bool GSDeviceVK::DownloadTexture(GSTexture* src, const GSVector4i& rect, GSTextu void GSDeviceVK::DownloadTextureComplete() {} -void GSDeviceVK::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r) -{ - if (!sTex || !dTex) - { - ASSERT(0); - return; - } - - const GSVector4i dst_rc(r - r.xyxy()); - DoCopyRect(sTex, dTex, r, dst_rc); -} - -void GSDeviceVK::DoCopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, const GSVector4i& dst_rc) +void GSDeviceVK::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) { g_perfmon.Put(GSPerfMon::TextureCopies, 1); @@ -514,7 +502,7 @@ void GSDeviceVK::DoCopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& // source is cleared. if destination is a render target, we can carry the clear forward if (dTexVK->IsRenderTargetOrDepthStencil()) { - if (dtex_rc.eq(dst_rc)) + if (dtex_rc.eq(r)) { // pass it forward if we're clearing the whole thing if (sTexVK->IsDepthStencil()) @@ -529,7 +517,7 @@ void GSDeviceVK::DoCopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& // otherwise we need to do an attachment clear const bool depth = (dTexVK->GetType() == GSTexture::Type::DepthStencil); OMSetRenderTargets(depth ? nullptr : dTexVK, depth ? dTexVK : nullptr, dtex_rc, false); - BeginRenderPassForStretchRect(dTexVK, dtex_rc, dst_rc); + BeginRenderPassForStretchRect(dTexVK, dtex_rc, GSVector4i(destX, destY, destX + r.width(), destY + r.height())); // so use an attachment clear VkClearAttachment ca; @@ -539,7 +527,7 @@ void GSDeviceVK::DoCopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& ca.clearValue.depthStencil.stencil = 0; ca.colorAttachment = 0; - const VkClearRect cr = { {{0, 0}, {static_cast(dst_rc.width()), static_cast(dst_rc.height())}}, 0u, 1u }; + const VkClearRect cr = { {{0, 0}, {static_cast(r.width()), static_cast(r.height())}}, 0u, 1u }; vkCmdClearAttachments(g_vulkan_context->GetCurrentCommandBuffer(), 1, &ca, 1, &cr); return; } @@ -551,13 +539,14 @@ void GSDeviceVK::DoCopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& // if the destination has been cleared, and we're not overwriting the whole thing, commit the clear first // (the area outside of where we're copying to) - if (dTexVK->GetState() == GSTexture::State::Cleared && !dtex_rc.eq(dst_rc)) + if (dTexVK->GetState() == GSTexture::State::Cleared && !dtex_rc.eq(r)) dTexVK->CommitClear(); // *now* we can do a normal image copy. const VkImageAspectFlags src_aspect = (sTexVK->IsDepthStencil()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; const VkImageAspectFlags dst_aspect = (dTexVK->IsDepthStencil()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; - const VkImageCopy ic = {{src_aspect, 0u, 0u, 1u}, {r.left, r.top, 0u}, {dst_aspect, 0u, 0u, 1u}, {dst_rc.left, dst_rc.top, 0u}, + const VkImageCopy ic = {{src_aspect, 0u, 0u, 1u}, {r.left, r.top, 0u}, {dst_aspect, 0u, 0u, 1u}, + {static_cast(destX), static_cast(destY), 0u}, {static_cast(r.width()), static_cast(r.height()), 1u}}; EndRenderPass(); @@ -2925,7 +2914,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config) config.drawarea.left, config.drawarea.top, config.drawarea.width(), config.drawarea.height()); - DoCopyRect(draw_rt, draw_rt_clone, config.drawarea, config.drawarea); + CopyRect(draw_rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top); PSSetShaderResource(2, draw_rt_clone, true); } } @@ -2942,7 +2931,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config) config.drawarea.left, config.drawarea.top, config.drawarea.width(), config.drawarea.height()); - DoCopyRect(config.ds, copy_ds, config.drawarea, config.drawarea); + CopyRect(config.ds, copy_ds, config.drawarea, config.drawarea.left, config.drawarea.top); PSSetShaderResource(0, copy_ds, true); } } diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h index f9d1c439ab..1110270da9 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h @@ -223,8 +223,7 @@ public: bool DownloadTexture(GSTexture* src, const GSVector4i& rect, GSTexture::GSMap& out_map) override; void DownloadTextureComplete() override; - void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r) override; - void DoCopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, const GSVector4i& dst_rc); + void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override; void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) override;