diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
index 3e45c44196..b745867e92 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
@@ -35,12 +35,22 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
             Acquired
         }
 
+        private struct Rect
+        {
+            public int Top;
+            public int Left;
+            public int Right;
+            public int Bottom;
+        }
+
         private struct BufferEntry
         {
             public BufferState State;
 
             public HalTransform Transform;
 
+            public Rect Crop;
+
             public GbpBuffer Data;
         }
 
@@ -168,6 +178,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
 
             BufferQueue[Slot].Transform = (HalTransform)Transform;
 
+            BufferQueue[Slot].Crop.Top    = CropTop;
+            BufferQueue[Slot].Crop.Left   = CropLeft;
+            BufferQueue[Slot].Crop.Right  = CropRight;
+            BufferQueue[Slot].Crop.Bottom = CropBottom;
+
             BufferQueue[Slot].State = BufferState.Queued;
 
             SendFrameBuffer(Context, Slot);
@@ -256,20 +271,44 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
                 return;
             }
 
-            BufferQueue[Slot].State = BufferState.Acquired; 
+            BufferQueue[Slot].State = BufferState.Acquired;
+
+            Rect Crop = BufferQueue[Slot].Crop;
+
+            int RealWidth  = FbWidth;
+            int RealHeight = FbHeight;
 
             float ScaleX = 1;
             float ScaleY = 1;
+            float OffsX = 0;
+            float OffsY = 0;
+
+            if (Crop.Right  != 0 &&
+                Crop.Bottom != 0)
+            {
+                RealWidth  = Crop.Right  - Crop.Left;
+                RealHeight = Crop.Bottom - Crop.Top;
+
+                ScaleX = (float)FbWidth  / RealWidth;
+                ScaleY = (float)FbHeight / RealHeight;
+
+                OffsX = -(float)Crop.Left / Crop.Right;
+                OffsY = -(float)Crop.Top  / Crop.Bottom;
+
+                OffsX += ScaleX - 1;
+                OffsY += ScaleY - 1;
+            }
+
             float Rotate = 0;
 
             if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX))
             {
-                ScaleX = -1;
+                ScaleX = -ScaleX;
             }
 
             if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY))
             {
-                ScaleY = -1;
+                ScaleY = -ScaleY;
             }
 
             if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90))
@@ -287,6 +326,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
                     FbHeight,
                     ScaleX,
                     ScaleY,
+                    OffsX,
+                    OffsY,
                     Rotate);
 
                 BufferQueue[Slot].State = BufferState.Free;
diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs
index 5854c54a6b..83d2c699f8 100644
--- a/Ryujinx.Graphics/Gal/IGalRenderer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs
@@ -10,9 +10,20 @@ namespace Ryujinx.Graphics.Gal
         void InitializeFrameBuffer();
         void Render();
         void SetWindowSize(int Width, int Height);
-        void SetFrameBuffer(byte* Fb, int Width, int Height, float SX, float SY, float R);
+        void SetFrameBuffer(
+            byte* Fb,
+            int   Width,
+            int   Height,
+            float ScaleX,
+            float ScaleY,
+            float OffsX,
+            float OffsY,
+            float Rotate);
+
         void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs);
+
         void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height);
+
         void BindTexture(int Index);
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl b/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl
index 933fa6aa97..35d560c09b 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl
+++ b/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl
@@ -2,8 +2,9 @@
 
 precision highp float;
 
-uniform vec2 window_size;
 uniform mat2 transform;
+uniform vec2 window_size;
+uniform vec2 offset;
 
 layout(location = 0) in vec2 in_position;
 layout(location = 1) in vec2 in_tex_coord;
@@ -22,5 +23,6 @@ vec2 get_scale_ratio(void) {
 
 void main(void) {
     tex_coord = in_tex_coord;
-    gl_Position = vec4((transform * in_position) * get_scale_ratio(), 0, 1);
+    vec2 t_pos = (transform * in_position) + offset;
+    gl_Position = vec4(t_pos * get_scale_ratio(), 0, 1);
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs
index c66c0cb754..7dc4bffe34 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs
@@ -135,7 +135,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.BindVertexArray(0);
         }
 
-        public unsafe void Set(byte* Fb, int Width, int Height, Matrix2 Transform)
+        public unsafe void Set(byte* Fb, int Width, int Height, Matrix2 Transform, Vector2 Offs)
         {
             if (Fb == null)
             {
@@ -172,6 +172,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size");
 
             GL.Uniform2(WindowSizeUniformLocation, new Vector2(WindowWidth, WindowHeight));
+
+            int OffsetUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "offset");
+
+            GL.Uniform2(OffsetUniformLocation, Offs);
         }
 
         public void Render()
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
index 5ae5e22597..bdedfc1a87 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
@@ -1,6 +1,7 @@
 using OpenTK;
 using OpenTK.Graphics.OpenGL;
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
@@ -24,7 +25,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         private Texture[] Textures;
 
-        private Queue<Action> ActionsQueue;
+        private ConcurrentQueue<Action> ActionsQueue;
 
         private FrameBuffer FbRenderer;
 
@@ -34,7 +35,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             Textures = new Texture[8];
 
-            ActionsQueue = new Queue<Action>();
+            ActionsQueue = new ConcurrentQueue<Action>();
         }
 
         public void InitializeFrameBuffer()
@@ -51,9 +52,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             int Count = ActionsQueue.Count;
 
-            while (Count-- > 0)
+            while (Count-- > 0 && ActionsQueue.TryDequeue(out Action RenderAction))
             {
-                ActionsQueue.Dequeue()();
+                RenderAction();
             }
         }
 
@@ -86,6 +87,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             int   Height,
             float ScaleX,
             float ScaleY,
+            float OffsX,
+            float OffsY,
             float Rotate)
         {
             Matrix2 Transform;
@@ -93,7 +96,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             Transform  = Matrix2.CreateScale(ScaleX, ScaleY);
             Transform *= Matrix2.CreateRotation(Rotate);
 
-            FbRenderer.Set(Fb, Width, Height, Transform);
+            Vector2 Offs = new Vector2(OffsX, OffsY);
+
+            FbRenderer.Set(Fb, Width, Height, Transform, Offs);
         }
 
         public void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs)