diff --git a/Ryujinx.Core/LogClass.cs b/Ryujinx.Core/LogClass.cs
index 67fd8559ff..1a3fbb5903 100644
--- a/Ryujinx.Core/LogClass.cs
+++ b/Ryujinx.Core/LogClass.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Ryujinx.Core
+namespace Ryujinx.Core
 {
     public enum LogClass
     {
diff --git a/Ryujinx.Graphics/Gal/GalConsts.cs b/Ryujinx.Graphics/Gal/GalConsts.cs
new file mode 100644
index 0000000000..6c8857c6ed
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalConsts.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public static class GalConsts
+    {
+        public const string FlipUniformName = "flip";
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs
index c30c79fb38..af88412a29 100644
--- a/Ryujinx.Graphics/Gal/IGalRenderer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs
@@ -42,6 +42,8 @@ namespace Ryujinx.Graphics.Gal
 
         void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY);
 
+        void SetViewport(int X, int Y, int Width, int Height);
+
         //Rasterizer
         void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
 
@@ -62,6 +64,8 @@ namespace Ryujinx.Graphics.Gal
 
         void SetUniform1(string UniformName, int Value);
 
+        void SetUniform2F(string UniformName, float X, float Y);
+
         void BindShader(long Tag);
 
         void BindProgram();
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
index cca61e1810..e0da9179a3 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
@@ -7,6 +7,22 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 {
     class OGLFrameBuffer
     {
+        private struct Rect
+        {
+            public int X      { get; private set; }
+            public int Y      { get; private set; }
+            public int Width  { get; private set; }
+            public int Height { get; private set; }
+
+            public Rect(int X, int Y, int Width, int Height)
+            {
+                this.X     = X;
+                this.Y     = Y;
+                this.Width = Width;
+                this.Height = Height;
+            }
+        }
+
         private class FrameBuffer
         {
             public int Width  { get; set; }
@@ -38,6 +54,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         private ShaderProgram Shader;
 
+        private Rect Viewport;
+
         private bool IsInitialized;
 
         private int RawFbTexWidth;
@@ -178,6 +196,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.UseProgram(CurrentProgram);
         }
 
+        public void SetViewport(int X, int Y, int Width, int Height)
+        {
+            Viewport = new Rect(X, Y, Width, Height);
+
+            //TODO
+        }
+
         public void Render()
         {
             if (CurrTexHandle != 0)
@@ -196,6 +221,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
                 GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
 
+                GL.Viewport(0, 0, 1280, 720);
+
                 GL.Clear(
                     ClearBufferMask.ColorBufferBit |
                     ClearBufferMask.DepthBufferBit);
@@ -218,6 +245,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             }
         }
 
+        private void SetViewport()
+        {
+            GL.Viewport(
+                Viewport.X,
+                Viewport.Y,
+                Viewport.Width,
+                Viewport.Height);
+        }
+
         private void EnsureInitialized()
         {
             if (!IsInitialized)
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
index 6d6ac555d6..fff6362b41 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
@@ -158,6 +158,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.Uniform1(Location, Value);
         }
 
+        public void SetUniform2F(string UniformName, float X, float Y)
+        {
+            BindProgram();
+
+            int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName);
+
+            GL.Uniform2(Location, X, Y);
+        }
+
         public void Bind(long Tag)
         {
             if (Stages.TryGetValue(Tag, out ShaderStage Stage))
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
index b3ccae5f87..5e066e3f19 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
@@ -141,6 +141,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             ActionsQueue.Enqueue(() => FrameBuffer.SetTransform(Transform, Offs));
         }
 
+        public void SetViewport(int X, int Y, int Width, int Height)
+        {
+            ActionsQueue.Enqueue(() => FrameBuffer.SetViewport(X, Y, Width, Height));
+        }
+
         public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
         {
             ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags));
@@ -218,6 +223,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             ActionsQueue.Enqueue(() => Shader.SetUniform1(UniformName, Value));
         }
 
+        public void SetUniform2F(string UniformName, float X, float Y)
+        {
+            if (UniformName == null)
+            {
+                throw new ArgumentNullException(nameof(UniformName));
+            }
+
+            ActionsQueue.Enqueue(() => Shader.SetUniform2F(UniformName, X, Y));
+        }
+
         public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag)
         {
             return Shader.GetTextureUsage(Tag);
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
index 7d97ec3342..457192dc2a 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
@@ -115,6 +115,11 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private void PrintDeclUniforms()
         {
+            if (Decl.ShaderType == GalShaderType.Vertex)
+            {
+                SB.AppendLine("uniform vec2 " + GalConsts.FlipUniformName + ";");
+            }
+
             foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector))
             {
                 SB.AppendLine($"uniform {GetDecl(DeclInfo)};");
@@ -270,6 +275,8 @@ namespace Ryujinx.Graphics.Gal.Shader
                         //the shader ends here.
                         if (Decl.ShaderType == GalShaderType.Vertex)
                         {
+                            SB.AppendLine(Identation + "gl_Position.xy *= flip;");
+
                             SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + " = gl_Position;");
                         }
                     }
diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
index fd13367c41..88ad763349 100644
--- a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
+++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
@@ -147,6 +147,17 @@ namespace Ryujinx.Graphics.Gpu
                 Gpu.Renderer.BindShader(Tag);
             }
 
+            int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX);
+            int RawSY = ReadRegister(NvGpuEngine3dReg.ViewportScaleY);
+
+            float SX = BitConverter.Int32BitsToSingle(RawSX);
+            float SY = BitConverter.Int32BitsToSingle(RawSY);
+
+            float SignX = MathF.Sign(SX);
+            float SignY = MathF.Sign(SY);
+
+            Gpu.Renderer.SetUniform2F(GalConsts.FlipUniformName, SignX, SignY);
+
             return Tags;
         }
 
diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs
index 4f1dce94b3..605ca9dab5 100644
--- a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs
+++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs
@@ -6,6 +6,12 @@ namespace Ryujinx.Graphics.Gpu
         FrameBufferNWidth    = 0x202,
         FrameBufferNHeight   = 0x203,
         FrameBufferNFormat   = 0x204,
+        ViewportScaleX       = 0x280,
+        ViewportScaleY       = 0x281,
+        ViewportScaleZ       = 0x282,
+        ViewportTranslateX   = 0x283,
+        ViewportTranslateY   = 0x284,
+        ViewportTranslateZ   = 0x285,
         VertexAttribNFormat  = 0x458,
         IBlendEnable         = 0x4b9,
         BlendSeparateAlpha   = 0x4cf,