From 3c64f0c6165f7f998952c9a4829970b143f930cb Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 28 Apr 2019 16:53:16 +1000 Subject: [PATCH 1/2] Renderer: Adjust source rectangle when crop would draw off screen This prevents us from requiring an oversized and/or negative viewport by shrinking the source rectangle instead. --- Source/Core/VideoBackends/D3D/Render.cpp | 8 +++-- Source/Core/VideoBackends/OGL/Render.cpp | 8 +++-- Source/Core/VideoCommon/RenderBase.cpp | 44 +++++++++++++++++++++--- Source/Core/VideoCommon/RenderBase.h | 7 ++++ 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index fc417962a0..4b27358e61 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -338,12 +338,14 @@ void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const MathUtil: // Render to staging texture which is double the width of the backbuffer SetAndClearFramebuffer(m_3d_vision_framebuffer.get()); - const auto target_rc = GetTargetRectangle(); - m_post_processor->BlitFromTexture(target_rc, rc, texture, 0); + auto adjusted_rc = rc; + auto target_rc = GetTargetRectangle(); + AdjustRectanglesToFitBounds(&target_rc, &adjusted_rc, m_backbuffer_width, m_backbuffer_height); + m_post_processor->BlitFromTexture(target_rc, adjusted_rc, texture, 0); m_post_processor->BlitFromTexture( MathUtil::Rectangle(target_rc.left + m_backbuffer_width, target_rc.top, target_rc.right + m_backbuffer_width, target_rc.bottom), - rc, texture, 1); + adjusted_rc, texture, 1); // Copy the left eye to the backbuffer, if Nvidia 3D Vision is enabled it should // recognize the signature and automatically include the right eye frame. diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index 546556c440..152b192abe 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -984,13 +984,15 @@ void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const MathUtil: if (g_ActiveConfig.stereo_mode != StereoMode::QuadBuffer) return ::Renderer::RenderXFBToScreen(texture, rc); - const auto target_rc = GetTargetRectangle(); + auto adjusted_rc = rc; + auto target_rc = GetTargetRectangle(); + AdjustRectanglesToFitBounds(&target_rc, &adjusted_rc, m_backbuffer_width, m_backbuffer_height); glDrawBuffer(GL_BACK_LEFT); - m_post_processor->BlitFromTexture(target_rc, rc, texture, 0); + m_post_processor->BlitFromTexture(target_rc, adjusted_rc, texture, 0); glDrawBuffer(GL_BACK_RIGHT); - m_post_processor->BlitFromTexture(target_rc, rc, texture, 1); + m_post_processor->BlitFromTexture(target_rc, adjusted_rc, texture, 1); glDrawBuffer(GL_BACK); } diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 523a681884..039da1f189 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -561,6 +561,40 @@ float Renderer::CalculateDrawAspectRatio() const } } +void Renderer::AdjustRectanglesToFitBounds(MathUtil::Rectangle* target_rect, + MathUtil::Rectangle* source_rect, int fb_width, + int fb_height) +{ + const int orig_target_width = target_rect->GetWidth(); + const int orig_target_height = target_rect->GetHeight(); + const int orig_source_width = source_rect->GetWidth(); + const int orig_source_height = source_rect->GetHeight(); + if (target_rect->left < 0) + { + const int offset = -target_rect->left; + target_rect->left = 0; + source_rect->left += offset * orig_source_width / orig_target_width; + } + if (target_rect->right > fb_width) + { + const int offset = target_rect->right - fb_width; + target_rect->right -= offset; + source_rect->right -= offset * orig_source_width / orig_target_width; + } + if (target_rect->top < 0) + { + const int offset = -target_rect->top; + target_rect->top = 0; + source_rect->top += offset * orig_source_height / orig_target_height; + } + if (target_rect->bottom > fb_height) + { + const int offset = target_rect->bottom - fb_height; + target_rect->right -= offset; + source_rect->right -= offset * orig_source_height / orig_target_height; + } +} + bool Renderer::IsHeadless() const { return true; @@ -1287,19 +1321,21 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const MathUtil::Rectangle& rc) { - const auto target_rc = GetTargetRectangle(); + auto adjusted_rc = rc; + auto target_rc = GetTargetRectangle(); + AdjustRectanglesToFitBounds(&target_rc, &adjusted_rc, m_backbuffer_width, m_backbuffer_height); if (g_ActiveConfig.stereo_mode == StereoMode::SBS || g_ActiveConfig.stereo_mode == StereoMode::TAB) { MathUtil::Rectangle left_rc, right_rc; std::tie(left_rc, right_rc) = ConvertStereoRectangle(target_rc); - m_post_processor->BlitFromTexture(left_rc, rc, texture, 0); - m_post_processor->BlitFromTexture(right_rc, rc, texture, 1); + m_post_processor->BlitFromTexture(left_rc, adjusted_rc, texture, 0); + m_post_processor->BlitFromTexture(right_rc, adjusted_rc, texture, 1); } else { - m_post_processor->BlitFromTexture(target_rc, rc, texture, 0); + m_post_processor->BlitFromTexture(target_rc, adjusted_rc, texture, 0); } } diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index c10d8d155b..ce31059ee2 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -171,6 +171,13 @@ public: const MathUtil::Rectangle& GetTargetRectangle() const { return m_target_rectangle; } float CalculateDrawAspectRatio() const; + // Crops the target rectangle to the framebuffer dimensions, reducing the size of the source + // rectangle if it is greater. Works even if the source and target rectangles don't have a + // 1:1 pixel mapping, scaling as appropriate. + void AdjustRectanglesToFitBounds(MathUtil::Rectangle* target_rect, + MathUtil::Rectangle* source_rect, int fb_width, + int fb_height); + std::tuple ScaleToDisplayAspectRatio(int width, int height) const; void UpdateDrawRectangle(); From 32359bf2bbeb7ed14f9138719fbe25021a2e90c4 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 28 Apr 2019 17:00:18 +1000 Subject: [PATCH 2/2] Renderer: Adjust target rectangle in the base class --- Source/Core/VideoBackends/D3D/Render.cpp | 13 +++++------ Source/Core/VideoBackends/D3D/Render.h | 5 +++-- Source/Core/VideoBackends/OGL/Render.cpp | 14 +++++------- Source/Core/VideoBackends/OGL/Render.h | 5 +++-- .../VideoBackends/Software/SWRenderer.cpp | 7 +++--- .../Core/VideoBackends/Software/SWRenderer.h | 5 +++-- Source/Core/VideoCommon/RenderBase.cpp | 22 ++++++++++++------- Source/Core/VideoCommon/RenderBase.h | 5 +++-- 8 files changed, 42 insertions(+), 34 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 4b27358e61..8312f6b626 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -327,10 +327,12 @@ void Renderer::WaitForGPUIdle() D3D::context->Flush(); } -void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const MathUtil::Rectangle& rc) +void Renderer::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, + const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) { if (g_ActiveConfig.stereo_mode != StereoMode::Nvidia3DVision) - return ::Renderer::RenderXFBToScreen(texture, rc); + return ::Renderer::RenderXFBToScreen(target_rc, source_texture, source_rc); if (!m_3d_vision_texture) Create3DVisionTexture(m_backbuffer_width, m_backbuffer_height); @@ -338,14 +340,11 @@ void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const MathUtil: // Render to staging texture which is double the width of the backbuffer SetAndClearFramebuffer(m_3d_vision_framebuffer.get()); - auto adjusted_rc = rc; - auto target_rc = GetTargetRectangle(); - AdjustRectanglesToFitBounds(&target_rc, &adjusted_rc, m_backbuffer_width, m_backbuffer_height); - m_post_processor->BlitFromTexture(target_rc, adjusted_rc, texture, 0); + m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 0); m_post_processor->BlitFromTexture( MathUtil::Rectangle(target_rc.left + m_backbuffer_width, target_rc.top, target_rc.right + m_backbuffer_width, target_rc.bottom), - adjusted_rc, texture, 1); + source_rc, source_texture, 1); // Copy the left eye to the backbuffer, if Nvidia 3D Vision is enabled it should // recognize the signature and automatically include the right eye frame. diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index 277cd4f19f..fc981508e5 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -67,8 +67,9 @@ public: void Flush() override; void WaitForGPUIdle() override; - void RenderXFBToScreen(const AbstractTexture* texture, - const MathUtil::Rectangle& rc) override; + void RenderXFBToScreen(const MathUtil::Rectangle& target_rc, + const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) override; void OnConfigChanged(u32 bits) override; private: diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index 152b192abe..f52d0f605c 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -978,21 +978,19 @@ void Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, BPFunctions::SetScissor(); } -void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const MathUtil::Rectangle& rc) +void Renderer::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, + const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) { // Quad-buffered stereo is annoying on GL. if (g_ActiveConfig.stereo_mode != StereoMode::QuadBuffer) - return ::Renderer::RenderXFBToScreen(texture, rc); - - auto adjusted_rc = rc; - auto target_rc = GetTargetRectangle(); - AdjustRectanglesToFitBounds(&target_rc, &adjusted_rc, m_backbuffer_width, m_backbuffer_height); + return ::Renderer::RenderXFBToScreen(target_rc, source_texture, source_rc); glDrawBuffer(GL_BACK_LEFT); - m_post_processor->BlitFromTexture(target_rc, adjusted_rc, texture, 0); + m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 0); glDrawBuffer(GL_BACK_RIGHT); - m_post_processor->BlitFromTexture(target_rc, adjusted_rc, texture, 1); + m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 1); glDrawBuffer(GL_BACK); } diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index 1cd2aa2e2d..a73b8e6f16 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -134,8 +134,9 @@ public: void Flush() override; void WaitForGPUIdle() override; - void RenderXFBToScreen(const AbstractTexture* texture, - const MathUtil::Rectangle& rc) override; + void RenderXFBToScreen(const MathUtil::Rectangle& target_rc, + const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) override; void OnConfigChanged(u32 bits) override; void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index b63e530a03..21ed4618b2 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -94,11 +94,12 @@ std::unique_ptr SWRenderer::CreatePipeline(const AbstractPipel } // Called on the GPU thread -void SWRenderer::RenderXFBToScreen(const AbstractTexture* texture, - const MathUtil::Rectangle& xfb_region) +void SWRenderer::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, + const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) { if (!IsHeadless()) - m_window->ShowImage(texture, xfb_region); + m_window->ShowImage(source_texture, source_rc); } u32 SWRenderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData) diff --git a/Source/Core/VideoBackends/Software/SWRenderer.h b/Source/Core/VideoBackends/Software/SWRenderer.h index 9e478b0f50..49ba251d22 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.h +++ b/Source/Core/VideoBackends/Software/SWRenderer.h @@ -42,8 +42,9 @@ public: u16 BBoxRead(int index) override; void BBoxWrite(int index, u16 value) override; - void RenderXFBToScreen(const AbstractTexture* texture, - const MathUtil::Rectangle& rc) override; + void RenderXFBToScreen(const MathUtil::Rectangle& target_rc, + const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) override; void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 039da1f189..4c84350bb8 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -1251,7 +1251,14 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 { BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}}); UpdateDrawRectangle(); - RenderXFBToScreen(xfb_entry->texture.get(), xfb_rect); + + // Adjust the source rectangle instead of using an oversized viewport to render the XFB. + auto render_target_rc = GetTargetRectangle(); + auto render_source_rc = xfb_rect; + AdjustRectanglesToFitBounds(&render_target_rc, &xfb_rect, m_backbuffer_width, + m_backbuffer_height); + RenderXFBToScreen(render_target_rc, xfb_entry->texture.get(), render_source_rc); + DrawImGui(); // Present to the window system. @@ -1319,23 +1326,22 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 } } -void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const MathUtil::Rectangle& rc) +void Renderer::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, + const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) { - auto adjusted_rc = rc; - auto target_rc = GetTargetRectangle(); - AdjustRectanglesToFitBounds(&target_rc, &adjusted_rc, m_backbuffer_width, m_backbuffer_height); if (g_ActiveConfig.stereo_mode == StereoMode::SBS || g_ActiveConfig.stereo_mode == StereoMode::TAB) { MathUtil::Rectangle left_rc, right_rc; std::tie(left_rc, right_rc) = ConvertStereoRectangle(target_rc); - m_post_processor->BlitFromTexture(left_rc, adjusted_rc, texture, 0); - m_post_processor->BlitFromTexture(right_rc, adjusted_rc, texture, 1); + m_post_processor->BlitFromTexture(left_rc, source_rc, source_texture, 0); + m_post_processor->BlitFromTexture(right_rc, source_rc, source_texture, 1); } else { - m_post_processor->BlitFromTexture(target_rc, adjusted_rc, texture, 0); + m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 0); } } diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index ce31059ee2..3f7fb55368 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -223,8 +223,9 @@ public: // Draws the specified XFB buffer to the screen, performing any post-processing. // Assumes that the backbuffer has already been bound and cleared. - virtual void RenderXFBToScreen(const AbstractTexture* texture, - const MathUtil::Rectangle& rc); + virtual void RenderXFBToScreen(const MathUtil::Rectangle& target_rc, + const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc); // Called when the configuration changes, and backend structures need to be updated. virtual void OnConfigChanged(u32 bits) {}