diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index b44e5a40ac..1e6e7d5290 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -258,10 +258,10 @@ bool Renderer::CheckForResize() return false; } -void Renderer::SetScissorRect(const EFBRectangle& rc) +void Renderer::SetScissorRect(const MathUtil::Rectangle& rc) { - TargetRectangle trc = ConvertEFBRectangle(rc); - D3D::context->RSSetScissorRects(1, trc.AsRECT()); + const RECT rect = {rc.left, rc.top, rc.right, rc.bottom}; + D3D::context->RSSetScissorRects(1, &rect); } // This function allows the CPU to directly access the EFB. @@ -445,59 +445,17 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num RestoreAPIState(); } -void Renderer::SetViewport() +void Renderer::SetViewport(float x, float y, float width, float height, float near_depth, + float far_depth) { - // reversed gxsetviewport(xorig, yorig, width, height, nearz, farz) - // [0] = width/2 - // [1] = height/2 - // [2] = 16777215 * (farz - nearz) - // [3] = xorig + width/2 + 342 - // [4] = yorig + height/2 + 342 - // [5] = 16777215 * farz - - // D3D crashes for zero viewports - if (xfmem.viewport.wd == 0 || xfmem.viewport.ht == 0) - return; - - int scissorXOff = bpmem.scissorOffset.x * 2; - int scissorYOff = bpmem.scissorOffset.y * 2; - - float X = Renderer::EFBToScaledXf(xfmem.viewport.xOrig - xfmem.viewport.wd - scissorXOff); - float Y = Renderer::EFBToScaledYf(xfmem.viewport.yOrig + xfmem.viewport.ht - scissorYOff); - float Wd = Renderer::EFBToScaledXf(2.0f * xfmem.viewport.wd); - float Ht = Renderer::EFBToScaledYf(-2.0f * xfmem.viewport.ht); - float min_depth = (xfmem.viewport.farZ - xfmem.viewport.zRange) / 16777216.0f; - float max_depth = xfmem.viewport.farZ / 16777216.0f; - if (Wd < 0.0f) - { - X += Wd; - Wd = -Wd; - } - if (Ht < 0.0f) - { - Y += Ht; - Ht = -Ht; - } - - // If an inverted or oversized depth range is used, we need to calculate the depth range in the - // vertex shader. - if (UseVertexDepthRange()) - { - // We need to ensure depth values are clamped the maximum value supported by the console GPU. - min_depth = 0.0f; - max_depth = GX_MAX_DEPTH; - } - // In D3D, the viewport rectangle must fit within the render target. - X = (X >= 0.f) ? X : 0.f; - Y = (Y >= 0.f) ? Y : 0.f; - Wd = (X + Wd <= GetTargetWidth()) ? Wd : (GetTargetWidth() - X); - Ht = (Y + Ht <= GetTargetHeight()) ? Ht : (GetTargetHeight() - Y); - - // We use an inverted depth range here to apply the Reverse Z trick. - // This trick makes sure we match the precision provided by the 1:0 - // clipping depth range on the hardware. - D3D11_VIEWPORT vp = CD3D11_VIEWPORT(X, Y, Wd, Ht, 1.0f - max_depth, 1.0f - min_depth); + D3D11_VIEWPORT vp; + vp.TopLeftX = MathUtil::Clamp(x, 0.0f, static_cast(m_target_width - 1)); + vp.TopLeftY = MathUtil::Clamp(y, 0.0f, static_cast(m_target_height - 1)); + vp.Width = MathUtil::Clamp(width, 1.0f, static_cast(m_target_width) - vp.TopLeftX); + vp.Height = MathUtil::Clamp(height, 1.0f, static_cast(m_target_height) - vp.TopLeftY); + vp.MinDepth = near_depth; + vp.MaxDepth = far_depth; D3D::context->RSSetViewports(1, &vp); } @@ -673,7 +631,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region // begin next frame RestoreAPIState(); FramebufferManager::BindEFBRenderTarget(); - SetViewport(); } // ALWAYS call RestoreAPIState for each ResetAPIState call you're doing @@ -690,7 +647,7 @@ void Renderer::RestoreAPIState() D3D::stateman->PopBlendState(); D3D::stateman->PopDepthState(); D3D::stateman->PopRasterizerState(); - SetViewport(); + BPFunctions::SetViewport(); BPFunctions::SetScissor(); } diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index 05144d2d2b..986d80c3ce 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -27,14 +27,15 @@ public: CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; void SetBlendingState(const BlendingState& state) override; - void SetScissorRect(const EFBRectangle& rc) override; + void SetScissorRect(const MathUtil::Rectangle& rc) override; void SetRasterizationState(const RasterizationState& state) override; void SetDepthState(const DepthState& state) override; void SetTexture(u32 index, const AbstractTexture* texture) override; void SetSamplerState(u32 index, const SamplerState& state) override; void UnbindTexture(const AbstractTexture* texture) override; void SetInterlacingMode() override; - void SetViewport() override; + void SetViewport(float x, float y, float width, float height, float near_depth, + float far_depth) override; void SetFullscreen(bool enable_fullscreen) override; bool IsFullscreen() const override; diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index 04c64b80f0..22bfd58982 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -863,20 +863,9 @@ TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc) return result; } -// Function: This function handles the OpenGL glScissor() function -// ---------------------------- -// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg() -// case 0x52 > SetScissorRect() -// ---------------------------- -// bpmem.scissorTL.x, y = 342x342 -// bpmem.scissorBR.x, y = 981x821 -// Renderer::GetTargetHeight() = the fixed ini file setting -// donkopunchstania - it appears scissorBR is the bottom right pixel inside the scissor box -// therefore the width and height are (scissorBR + 1) - scissorTL -void Renderer::SetScissorRect(const EFBRectangle& rc) +void Renderer::SetScissorRect(const MathUtil::Rectangle& rc) { - TargetRectangle trc = ConvertEFBRectangle(rc); - glScissor(trc.left, trc.bottom, trc.GetWidth(), trc.GetHeight()); + glScissor(rc.left, rc.bottom, rc.GetWidth(), rc.GetHeight()); } void ClearEFBCache() @@ -1136,75 +1125,23 @@ void Renderer::BBoxWrite(int index, u16 _value) BoundingBox::Set(index, value); } -void Renderer::SetViewport() +void Renderer::SetViewport(float x, float y, float width, float height, float near_depth, + float far_depth) { - // reversed gxsetviewport(xorig, yorig, width, height, nearz, farz) - // [0] = width/2 - // [1] = height/2 - // [2] = 16777215 * (farz - nearz) - // [3] = xorig + width/2 + 342 - // [4] = yorig + height/2 + 342 - // [5] = 16777215 * farz - - int scissorXOff = bpmem.scissorOffset.x * 2; - int scissorYOff = bpmem.scissorOffset.y * 2; - - // TODO: ceil, floor or just cast to int? - float X = EFBToScaledXf(xfmem.viewport.xOrig - xfmem.viewport.wd - (float)scissorXOff); - float Y = EFBToScaledYf((float)EFB_HEIGHT - xfmem.viewport.yOrig + xfmem.viewport.ht + - (float)scissorYOff); - float Width = EFBToScaledXf(2.0f * xfmem.viewport.wd); - float Height = EFBToScaledYf(-2.0f * xfmem.viewport.ht); - float min_depth = (xfmem.viewport.farZ - xfmem.viewport.zRange) / 16777216.0f; - float max_depth = xfmem.viewport.farZ / 16777216.0f; - if (Width < 0) - { - X += Width; - Width *= -1; - } - if (Height < 0) - { - Y += Height; - Height *= -1; - } - - // Update the view port + // The x/y parameters here assume a upper-left origin. glViewport takes an offset from the + // lower-left of the framebuffer, so we must set y to the distance from the lower-left. + y = static_cast(m_target_height) - y - height; if (g_ogl_config.bSupportViewportFloat) { - glViewportIndexedf(0, X, Y, Width, Height); + glViewportIndexedf(0, x, y, width, height); } else { - auto iceilf = [](float f) { return static_cast(ceilf(f)); }; - glViewport(iceilf(X), iceilf(Y), iceilf(Width), iceilf(Height)); + auto iceilf = [](float f) { return static_cast(std::ceil(f)); }; + glViewport(iceilf(x), iceilf(y), iceilf(width), iceilf(height)); } - if (!g_ActiveConfig.backend_info.bSupportsDepthClamp) - { - // There's no way to support oversized depth ranges in this situation. Let's just clamp the - // range to the maximum value supported by the console GPU and hope for the best. - min_depth = MathUtil::Clamp(min_depth, 0.0f, GX_MAX_DEPTH); - max_depth = MathUtil::Clamp(max_depth, 0.0f, GX_MAX_DEPTH); - } - - if (UseVertexDepthRange()) - { - // We need to ensure depth values are clamped the maximum value supported by the console GPU. - // Taking into account whether the depth range is inverted or not. - if (xfmem.viewport.zRange < 0.0f) - { - min_depth = GX_MAX_DEPTH; - max_depth = 0.0f; - } - else - { - min_depth = 0.0f; - max_depth = GX_MAX_DEPTH; - } - } - - // Set the reversed depth range. - glDepthRangef(max_depth, min_depth); + glDepthRangef(near_depth, far_depth); } void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, @@ -1563,9 +1500,9 @@ void Renderer::RestoreAPIState() } BPFunctions::SetGenerationMode(); BPFunctions::SetScissor(); + BPFunctions::SetViewport(); BPFunctions::SetDepthMode(); BPFunctions::SetBlendMode(); - SetViewport(); ProgramShaderCache::BindLastVertexFormat(); const VertexManager* const vm = static_cast(g_vertex_manager.get()); diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index 993057b2d9..68e173f568 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -91,14 +91,15 @@ public: CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; void SetBlendingState(const BlendingState& state) override; - void SetScissorRect(const EFBRectangle& rc) override; + void SetScissorRect(const MathUtil::Rectangle& rc) override; void SetRasterizationState(const RasterizationState& state) override; void SetDepthState(const DepthState& state) override; void SetTexture(u32 index, const AbstractTexture* texture) override; void SetSamplerState(u32 index, const SamplerState& state) override; void UnbindTexture(const AbstractTexture* texture) override; void SetInterlacingMode() override; - void SetViewport() override; + void SetViewport(float x, float y, float width, float height, float near_depth, + float far_depth) override; void RenderText(const std::string& text, int left, int top, u32 color) override; diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index e4fb76c37c..413f4bb750 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -810,7 +810,7 @@ void Renderer::RecreateEFBFramebuffer() BindEFBToStateTracker(); // Viewport and scissor rect have to be reset since they will be scaled differently. - SetViewport(); + BPFunctions::SetViewport(); BPFunctions::SetScissor(); } @@ -899,53 +899,18 @@ void Renderer::SetInterlacingMode() { } -void Renderer::SetScissorRect(const EFBRectangle& rc) +void Renderer::SetScissorRect(const MathUtil::Rectangle& rc) { - TargetRectangle target_rc = ConvertEFBRectangle(rc); - - VkRect2D scissor = { - {target_rc.left, target_rc.top}, - {static_cast(target_rc.GetWidth()), static_cast(target_rc.GetHeight())}}; - + VkRect2D scissor = {{rc.left, rc.top}, + {static_cast(rc.GetWidth()), static_cast(rc.GetHeight())}}; StateTracker::GetInstance()->SetScissor(scissor); } -void Renderer::SetViewport() +void Renderer::SetViewport(float x, float y, float width, float height, float near_depth, + float far_depth) { - int scissor_x_offset = bpmem.scissorOffset.x * 2; - int scissor_y_offset = bpmem.scissorOffset.y * 2; - - float x = Renderer::EFBToScaledXf(xfmem.viewport.xOrig - xfmem.viewport.wd - scissor_x_offset); - float y = Renderer::EFBToScaledYf(xfmem.viewport.yOrig + xfmem.viewport.ht - scissor_y_offset); - float width = Renderer::EFBToScaledXf(2.0f * xfmem.viewport.wd); - float height = Renderer::EFBToScaledYf(-2.0f * xfmem.viewport.ht); - float min_depth = (xfmem.viewport.farZ - xfmem.viewport.zRange) / 16777216.0f; - float max_depth = xfmem.viewport.farZ / 16777216.0f; - if (width < 0.0f) - { - x += width; - width = -width; - } - if (height < 0.0f) - { - y += height; - height = -height; - } - - // If an oversized or inverted depth range is used, we need to calculate the depth range in the - // vertex shader. - // TODO: Inverted depth ranges are bugged in all drivers, which should be added to DriverDetails. - if (UseVertexDepthRange()) - { - // We need to ensure depth values are clamped the maximum value supported by the console GPU. - min_depth = 0.0f; - max_depth = GX_MAX_DEPTH; - } - - // We use an inverted depth range here to apply the Reverse Z trick. - // This trick makes sure we match the precision provided by the 1:0 - // clipping depth range on the hardware. - VkViewport viewport = {x, y, width, height, 1.0f - max_depth, 1.0f - min_depth}; + VkViewport viewport = {x, y, std::max(width, 1.0f), std::max(height, 1.0f), + near_depth, far_depth}; StateTracker::GetInstance()->SetViewport(viewport); } diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index 813a3c1335..d5a2e9ae1d 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -60,14 +60,15 @@ public: void RestoreAPIState() override; void SetBlendingState(const BlendingState& state) override; - void SetScissorRect(const EFBRectangle& rc) override; + void SetScissorRect(const MathUtil::Rectangle& rc) override; void SetRasterizationState(const RasterizationState& state) override; void SetDepthState(const DepthState& state) override; void SetTexture(u32 index, const AbstractTexture* texture) override; void SetSamplerState(u32 index, const SamplerState& state) override; void UnbindTexture(const AbstractTexture* texture) override; void SetInterlacingMode() override; - void SetViewport() override; + void SetViewport(float x, float y, float width, float height, float near_depth, + float far_depth) override; void ChangeSurface(void* new_surface_handle) override; diff --git a/Source/Core/VideoCommon/BPFunctions.cpp b/Source/Core/VideoCommon/BPFunctions.cpp index a937a9037e..0e4551da09 100644 --- a/Source/Core/VideoCommon/BPFunctions.cpp +++ b/Source/Core/VideoCommon/BPFunctions.cpp @@ -12,6 +12,7 @@ #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" +#include "VideoCommon/XFMemory.h" namespace BPFunctions { @@ -48,24 +49,82 @@ void SetScissor() const int xoff = bpmem.scissorOffset.x * 2; const int yoff = bpmem.scissorOffset.y * 2; - EFBRectangle rc(bpmem.scissorTL.x - xoff, bpmem.scissorTL.y - yoff, bpmem.scissorBR.x - xoff + 1, - bpmem.scissorBR.y - yoff + 1); + EFBRectangle native_rc(bpmem.scissorTL.x - xoff, bpmem.scissorTL.y - yoff, + bpmem.scissorBR.x - xoff + 1, bpmem.scissorBR.y - yoff + 1); + native_rc.ClampUL(0, 0, EFB_WIDTH, EFB_HEIGHT); - if (rc.left < 0) - rc.left = 0; - if (rc.top < 0) - rc.top = 0; - if (rc.right > EFB_WIDTH) - rc.right = EFB_WIDTH; - if (rc.bottom > EFB_HEIGHT) - rc.bottom = EFB_HEIGHT; + TargetRectangle target_rc = g_renderer->ConvertEFBRectangle(native_rc); + g_renderer->SetScissorRect(target_rc); +} - if (rc.left > rc.right) - rc.right = rc.left; - if (rc.top > rc.bottom) - rc.bottom = rc.top; +void SetViewport() +{ + int scissor_x_off = bpmem.scissorOffset.x * 2; + int scissor_y_off = bpmem.scissorOffset.y * 2; + float x = g_renderer->EFBToScaledXf(xfmem.viewport.xOrig - xfmem.viewport.wd - scissor_x_off); + float y = g_renderer->EFBToScaledYf(xfmem.viewport.yOrig + xfmem.viewport.ht - scissor_y_off); - g_renderer->SetScissorRect(rc); + float width = g_renderer->EFBToScaledXf(2.0f * xfmem.viewport.wd); + float height = g_renderer->EFBToScaledYf(-2.0f * xfmem.viewport.ht); + float min_depth = (xfmem.viewport.farZ - xfmem.viewport.zRange) / 16777216.0f; + float max_depth = xfmem.viewport.farZ / 16777216.0f; + if (width < 0.f) + { + x += width; + width *= -1; + } + if (height < 0.f) + { + y += height; + height *= -1; + } + + // The maximum depth that is written to the depth buffer should never exceed this value. + // This is necessary because we use a 2^24 divisor for all our depth values to prevent + // floating-point round-trip errors. However the console GPU doesn't ever write a value + // to the depth buffer that exceeds 2^24 - 1. + constexpr float GX_MAX_DEPTH = 16777215.0f / 16777216.0f; + if (!g_ActiveConfig.backend_info.bSupportsDepthClamp) + { + // There's no way to support oversized depth ranges in this situation. Let's just clamp the + // range to the maximum value supported by the console GPU and hope for the best. + min_depth = MathUtil::Clamp(min_depth, 0.0f, GX_MAX_DEPTH); + max_depth = MathUtil::Clamp(max_depth, 0.0f, GX_MAX_DEPTH); + } + + if (g_renderer->UseVertexDepthRange()) + { + // We need to ensure depth values are clamped the maximum value supported by the console GPU. + // Taking into account whether the depth range is inverted or not. + if (xfmem.viewport.zRange < 0.0f && g_ActiveConfig.backend_info.bSupportsReversedDepthRange) + { + min_depth = GX_MAX_DEPTH; + max_depth = 0.0f; + } + else + { + min_depth = 0.0f; + max_depth = GX_MAX_DEPTH; + } + } + + float near_depth, far_depth; + if (g_ActiveConfig.backend_info.bSupportsReversedDepthRange) + { + // Set the reversed depth range. + near_depth = max_depth; + far_depth = min_depth; + } + else + { + // We use an inverted depth range here to apply the Reverse Z trick. + // This trick makes sure we match the precision provided by the 1:0 + // clipping depth range on the hardware. + near_depth = 1.0f - max_depth; + far_depth = 1.0f - min_depth; + } + + g_renderer->SetViewport(x, y, width, height, near_depth, far_depth); } void SetDepthMode() diff --git a/Source/Core/VideoCommon/BPFunctions.h b/Source/Core/VideoCommon/BPFunctions.h index b89c02340e..24ebee7a21 100644 --- a/Source/Core/VideoCommon/BPFunctions.h +++ b/Source/Core/VideoCommon/BPFunctions.h @@ -17,6 +17,7 @@ namespace BPFunctions void FlushPipeline(); void SetGenerationMode(); void SetScissor(); +void SetViewport(); void SetDepthMode(); void SetBlendMode(); void ClearScreen(const EFBRectangle& rc); diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index 609f82903a..6a78dc6dd7 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -130,6 +130,7 @@ static void BPWritten(const BPCmd& bp) case BPMEM_SCISSORBR: // Scissor Rectable Bottom, Right case BPMEM_SCISSOROFFSET: // Scissor Offset SetScissor(); + SetViewport(); VertexShaderManager::SetViewportChanged(); GeometryShaderManager::SetViewportChanged(); return; @@ -1415,6 +1416,7 @@ void BPReload() // note that PixelShaderManager is already covered since it has its own DoState. SetGenerationMode(); SetScissor(); + SetViewport(); SetDepthMode(); SetBlendMode(); OnPixelFormatChange(); diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 11cd9b4f75..9179fe0fa6 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -71,12 +71,6 @@ static int OSDTime; std::unique_ptr g_renderer; -// The maximum depth that is written to the depth buffer should never exceed this value. -// This is necessary because we use a 2^24 divisor for all our depth values to prevent -// floating-point round-trip errors. However the console GPU doesn't ever write a value -// to the depth buffer that exceeds 2^24 - 1. -const float Renderer::GX_MAX_DEPTH = 16777215.0f / 16777216.0f; - static float AspectToWidescreen(float aspect) { return aspect * ((16.0f / 9.0f) / (4.0f / 3.0f)); diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 423707166b..9ffd378d67 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -70,14 +70,17 @@ public: }; virtual void SetBlendingState(const BlendingState& state) {} - virtual void SetScissorRect(const EFBRectangle& rc) {} + virtual void SetScissorRect(const MathUtil::Rectangle& rc) {} virtual void SetRasterizationState(const RasterizationState& state) {} virtual void SetDepthState(const DepthState& state) {} virtual void SetTexture(u32 index, const AbstractTexture* texture) {} virtual void SetSamplerState(u32 index, const SamplerState& state) {} virtual void UnbindTexture(const AbstractTexture* texture) {} virtual void SetInterlacingMode() {} - virtual void SetViewport() {} + virtual void SetViewport(float x, float y, float width, float height, float near_depth, + float far_depth) + { + } virtual void SetFullscreen(bool enable_fullscreen) {} virtual bool IsFullscreen() const { return false; } virtual void ApplyState() {} @@ -184,8 +187,6 @@ protected: std::unique_ptr m_post_processor; - static const float GX_MAX_DEPTH; - void* m_surface_handle = nullptr; void* m_new_surface_handle = nullptr; Common::Flag m_surface_needs_change; diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index 7a7d3c03cb..9d3c25ca13 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -16,6 +16,7 @@ #include "Common/MathUtil.h" #include "Core/ConfigManager.h" #include "Core/Core.h" +#include "VideoCommon/BPFunctions.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/CPMemory.h" #include "VideoCommon/RenderBase.h" @@ -420,8 +421,7 @@ void VertexShaderManager::SetConstants() } dirty = true; - // This is so implementation-dependent that we can't have it here. - g_renderer->SetViewport(); + BPFunctions::SetViewport(); // Update projection if the viewport isn't 1:1 useable if (!g_ActiveConfig.backend_info.bSupportsOversizedViewports)