diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a1675355e1..2427b8c073 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -906,13 +906,30 @@ void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& t
 }
 
 void RasterizerOpenGL::SyncViewport() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
-    for (std::size_t i = 0; i < Maxwell::NumViewports; ++i) {
-        const auto& src = regs.viewports[i];
-        const Common::Rectangle<f32> rect{regs.viewport_transform[i].GetRect()};
-        glViewportIndexedf(static_cast<GLuint>(i), rect.left, rect.bottom, rect.GetWidth(),
-                           rect.GetHeight());
-        glDepthRangef(src.depth_range_near, src.depth_range_far);
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    const auto& regs = gpu.regs;
+
+    if (flags[Dirty::Viewports]) {
+        flags[Dirty::Viewports] = false;
+
+        const bool force = flags[Dirty::ViewportTransform];
+        flags[Dirty::ViewportTransform] = false;
+
+        for (std::size_t i = 0; i < Maxwell::NumViewports; ++i) {
+            if (!force && !flags[Dirty::Viewport0 + i]) {
+                continue;
+            }
+            flags[Dirty::Viewport0 + i] = false;
+
+            const Common::Rectangle<f32> rect{regs.viewport_transform[i].GetRect()};
+            glViewportIndexedf(static_cast<GLuint>(i), rect.left, rect.bottom, rect.GetWidth(),
+                               rect.GetHeight());
+
+            const auto& src = regs.viewports[i];
+            glDepthRangeIndexed(static_cast<GLuint>(i), static_cast<GLdouble>(src.depth_range_near),
+                                static_cast<GLdouble>(src.depth_range_far));
+        }
     }
 
     bool flip_y = false;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 268b9351e8..6293f61021 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -61,6 +61,22 @@ void SetupDirtyRenderTargets(Tables& tables) {
     }
 }
 
+void SetupDirtyViewports(Tables& tables) {
+    for (std::size_t i = 0; i < Regs::NumViewports; ++i) {
+        const std::size_t transf_offset = OFF(viewport_transform) + i * NUM(viewport_transform[0]);
+        const std::size_t viewport_offset = OFF(viewports) + i * NUM(viewports[0]);
+
+        FillBlock(tables[0], transf_offset, NUM(viewport_transform[0]), Viewport0 + i);
+        FillBlock(tables[0], viewport_offset, NUM(viewports[0]), Viewport0 + i);
+    }
+
+    FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports);
+    FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports);
+
+    tables[0][OFF(viewport_transform_enabled)] = ViewportTransform;
+    tables[1][OFF(viewport_transform_enabled)] = Viewports;
+}
+
 } // Anonymous namespace
 
 StateTracker::StateTracker(Core::System& system) : system{system} {}
@@ -80,6 +96,7 @@ void StateTracker::Initialize() {
 
     auto& tables = dirty.tables;
     SetupDirtyRenderTargets(tables);
+    SetupDirtyViewports(tables);
 }
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 91d4bb8d3a..93c64a44a9 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -23,6 +23,7 @@ enum : u8 {
     VertexInstances,
     Shaders,
     Viewports,
+    ViewportTransform,
     CullTestEnable,
     FrontFace,
     CullFace,
@@ -33,7 +34,8 @@ enum : u8 {
     BlendState,
     PolygonOffset,
 
-    VertexBuffer0 = PolygonOffset + 8,
+    Viewport0,
+    VertexBuffer0 = Viewport0 + 16,
     VertexInstance0 = VertexBuffer0 + 32,
 };
 }
@@ -44,6 +46,12 @@ public:
 
     void Initialize();
 
+    void NotifyViewport0() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::Viewports] = true;
+        flags[OpenGL::Dirty::Viewport0] = true;
+    }
+
     void NotifyFramebuffer() {
         auto& flags = system.GPU().Maxwell3D().dirty.flags;
         flags[VideoCommon::Dirty::RenderTargets] = true;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 36c634e0d3..73d2d90273 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -576,6 +576,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
 
     // TODO: Signal state tracker about these changes
+    state_tracker.NotifyViewport0();
     state_tracker.NotifyFramebuffer();
 
     program_manager.UseVertexShader(vertex_program.handle);
@@ -601,7 +602,9 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glFrontFace(GL_CW);
     glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
     glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
-    glViewport(0, 0, layout.width, layout.height);
+    glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
+                       static_cast<GLfloat>(layout.height));
+    glDepthRangeIndexed(0, 0.0, 0.0);
 
     glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
                          offsetof(ScreenRectVertex, position));