diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs
index 6a407baf..d626622a 100644
--- a/ChocolArm64/Instruction/ASoftFallback.cs
+++ b/ChocolArm64/Instruction/ASoftFallback.cs
@@ -1,6 +1,5 @@
 using ChocolArm64.Translation;
 using System;
-using System.Numerics;
 
 namespace ChocolArm64.Instruction
 {
diff --git a/Ryujinx.Graphics/Gal/GalConsts.cs b/Ryujinx.Graphics/Gal/GalConsts.cs
deleted file mode 100644
index 6c8857c6..00000000
--- a/Ryujinx.Graphics/Gal/GalConsts.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-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/IGalBlend.cs b/Ryujinx.Graphics/Gal/IGalBlend.cs
new file mode 100644
index 00000000..5c96a492
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/IGalBlend.cs
@@ -0,0 +1,22 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public interface IGalBlend
+    {
+        void Enable();
+
+        void Disable();
+
+        void Set(
+            GalBlendEquation Equation,
+            GalBlendFactor   FuncSrc,
+            GalBlendFactor   FuncDst);
+
+        void SetSeparate(
+            GalBlendEquation EquationRgb,
+            GalBlendEquation EquationAlpha,
+            GalBlendFactor   FuncSrcRgb,
+            GalBlendFactor   FuncDstRgb,
+            GalBlendFactor   FuncSrcAlpha,
+            GalBlendFactor   FuncDstAlpha);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs b/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs
new file mode 100644
index 00000000..eaae0a49
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace Ryujinx.Graphics.Gal
+{
+    public interface IGalFrameBuffer
+    {
+        void Create(long Key, int Width, int Height);
+
+        void Bind(long Key);
+
+        void BindTexture(long Key, int Index);
+
+        void Set(long Key);
+
+        void Set(byte[] Data, int Width, int Height);
+
+        void SetTransform(float SX, float SY, float Rotate, float TX, float TY);
+
+        void SetWindowSize(int Width, int Height);
+
+        void SetViewport(int X, int Y, int Width, int Height);
+
+        void Render();
+
+        void GetBufferData(long Key, Action<byte[]> Callback);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs
new file mode 100644
index 00000000..81c922be
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs
@@ -0,0 +1,23 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public interface IGalRasterizer
+    {
+        void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
+
+        bool IsVboCached(long Key, long DataSize);
+
+        bool IsIboCached(long Key, long DataSize);
+
+        void CreateVbo(long Key, byte[] Buffer);
+
+        void CreateIbo(long Key, byte[] Buffer);
+
+        void SetVertexArray(int VbIndex, int Stride, long VboKey, GalVertexAttrib[] Attribs);
+
+        void SetIndexArray(long Key, int Size, GalIndexFormat Format);
+
+        void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType);
+
+        void DrawElements(long IboKey, int First, GalPrimitiveType PrimType);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs
index b8f83469..c6324c4a 100644
--- a/Ryujinx.Graphics/Gal/IGalRenderer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs
@@ -1,90 +1,21 @@
 using System;
-using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Gal
 {
-    public unsafe interface IGalRenderer
+    public interface IGalRenderer
     {
         void QueueAction(Action ActionMthd);
 
         void RunActions();
 
-        void Render();
+        IGalBlend Blend { get; }
 
-        void SetWindowSize(int Width, int Height);
+        IGalFrameBuffer FrameBuffer { get; }
 
-        //Blend
-        void SetBlendEnable(bool Enable);
+        IGalRasterizer Rasterizer { get; }
 
-        void SetBlend(
-            GalBlendEquation Equation,
-            GalBlendFactor   FuncSrc,
-            GalBlendFactor   FuncDst);
+        IGalShader Shader { get; }
 
-        void SetBlendSeparate(
-            GalBlendEquation EquationRgb,
-            GalBlendEquation EquationAlpha,
-            GalBlendFactor   FuncSrcRgb,
-            GalBlendFactor   FuncDstRgb,
-            GalBlendFactor   FuncSrcAlpha,
-            GalBlendFactor   FuncDstAlpha);
-
-        //Frame Buffer
-        void CreateFrameBuffer(long Tag, int Width, int Height);
-
-        void BindFrameBuffer(long Tag);
-
-        void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler);
-
-        void SetFrameBuffer(long Tag);
-
-        void SetFrameBuffer(byte[] Data, int Width, int Height);
-
-        void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY);
-
-        void SetViewport(int X, int Y, int Width, int Height);
-
-        void GetFrameBufferData(long Tag, Action<byte[]> Callback);
-
-        //Rasterizer
-        void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
-
-        bool IsVboCached(long Tag, long DataSize);
-
-        bool IsIboCached(long Tag, long DataSize);
-
-        void CreateVbo(long Tag, byte[] Buffer);
-
-        void CreateIbo(long Tag, byte[] Buffer);
-
-        void SetVertexArray(int VbIndex, int Stride, long VboTag, GalVertexAttrib[] Attribs);
-
-        void SetIndexArray(long Tag, int Size, GalIndexFormat Format);
-
-        void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType);
-
-        void DrawElements(long IboTag, int First, GalPrimitiveType PrimType);
-
-        //Shader
-        void CreateShader(IGalMemory Memory, long Tag, GalShaderType Type);
-
-        IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag);
-
-        void SetConstBuffer(long Tag, int Cbuf, byte[] Data);
-
-        void SetUniform1(string UniformName, int Value);
-
-        void SetUniform2F(string UniformName, float X, float Y);
-
-        void BindShader(long Tag);
-
-        void BindProgram();
-
-        //Texture
-        void SetTextureAndSampler(long Tag, byte[] Data, GalTexture Texture, GalTextureSampler Sampler);
-
-        bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture);
-
-        void BindTexture(long Tag, int Index);
+        IGalTexture Texture { get; }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalShader.cs b/Ryujinx.Graphics/Gal/IGalShader.cs
new file mode 100644
index 00000000..79e77c0a
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/IGalShader.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Gal
+{
+    public interface IGalShader
+    {
+        void Create(IGalMemory Memory, long Key, GalShaderType Type);
+
+        IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key);
+
+        void SetConstBuffer(long Key, int Cbuf, byte[] Data);
+
+        void EnsureTextureBinding(string UniformName, int Value);
+
+        void SetFlip(float X, float Y);
+
+        void Bind(long Key);
+
+        void BindProgram();
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalTexture.cs b/Ryujinx.Graphics/Gal/IGalTexture.cs
new file mode 100644
index 00000000..6379e73a
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/IGalTexture.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public interface IGalTexture
+    {
+        void Create(long Key, byte[] Data, GalTexture Texture);
+
+        bool TryGetCachedTexture(long Key, long DataSize, out GalTexture Texture);
+
+        void Bind(long Key, int Index);
+
+        void SetSampler(GalTextureSampler Sampler);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs
index e33851e5..7175e3a0 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs
@@ -2,7 +2,7 @@ using OpenTK.Graphics.OpenGL;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
 {
-    class OGLBlend
+    public class OGLBlend : IGalBlend
     {
         public void Enable()
         {
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
index 8f265f54..4d91ff97 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
@@ -5,7 +5,7 @@ using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
 {
-    class OGLFrameBuffer
+    public class OGLFrameBuffer : IGalFrameBuffer
     {
         private struct Rect
         {
@@ -16,9 +16,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             public Rect(int X, int Y, int Width, int Height)
             {
-                this.X     = X;
-                this.Y     = Y;
-                this.Width = Width;
+                this.X      = X;
+                this.Y      = Y;
+                this.Width  = Width;
                 this.Height = Height;
             }
         }
@@ -76,14 +76,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             Shader = new ShaderProgram();
         }
 
-        public void Create(long Tag, int Width, int Height)
+        public void Create(long Key, int Width, int Height)
         {
             //TODO: We should either use the original frame buffer size,
             //or just remove the Width/Height arguments.
             Width  = Window.Width;
             Height = Window.Height;
 
-            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
+            if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
             {
                 if (Fb.Width  != Width ||
                     Fb.Height != Height)
@@ -127,12 +127,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             GL.Viewport(0, 0, Width, Height);
 
-            Fbs.Add(Tag, Fb);
+            Fbs.Add(Key, Fb);
         }
 
-        public void Bind(long Tag)
+        public void Bind(long Key)
         {
-            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
+            if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
             {
                 GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
 
@@ -140,9 +140,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             }
         }
 
-        public void BindTexture(long Tag, int Index)
+        public void BindTexture(long Key, int Index)
         {
-            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
+            if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
             {
                 GL.ActiveTexture(TextureUnit.Texture0 + Index);
 
@@ -150,9 +150,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             }
         }
 
-        public void Set(long Tag)
+        public void Set(long Key)
         {
-            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
+            if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
             {
                 CurrTexHandle = Fb.TexHandle;
             }
@@ -185,10 +185,17 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             CurrTexHandle = RawFbTexHandle;
         }
 
-        public void SetTransform(Matrix2 Transform, Vector2 Offs)
+        public void SetTransform(float SX, float SY, float Rotate, float TX, float TY)
         {
             EnsureInitialized();
 
+            Matrix2 Transform;
+
+            Transform  = Matrix2.CreateScale(SX, SY);
+            Transform *= Matrix2.CreateRotation(Rotate);
+
+            Vector2 Offs = new Vector2(TX, TY);
+
             int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
 
             GL.UseProgram(Shader.Handle);
@@ -270,9 +277,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             }
         }
 
-        public void GetBufferData(long Tag, Action<byte[]> Callback)
+        public void GetBufferData(long Key, Action<byte[]> Callback)
         {
-            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
+            if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
             {
                 GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, Fb.Handle);
 
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
index b63c8b35..bdf22b9c 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
@@ -4,7 +4,7 @@ using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
 {
-    class OGLRasterizer
+    public class OGLRasterizer : IGalRasterizer
     {
         private static Dictionary<GalVertexAttribSize, int> AttribElements =
                    new Dictionary<GalVertexAttribSize, int>()
@@ -74,8 +74,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             ClearBufferMask Mask = 0;
 
-            //OpenGL doesn't support clearing just a single color channel,
-            //so we can't just clear all channels...
+            //TODO: Use glColorMask to clear just the specified channels.
             if (Flags.HasFlag(GalClearBufferFlags.ColorRed)   &&
                 Flags.HasFlag(GalClearBufferFlags.ColorGreen) &&
                 Flags.HasFlag(GalClearBufferFlags.ColorBlue)  &&
@@ -97,45 +96,43 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.Clear(Mask);
         }
 
-        public bool IsVboCached(long Tag, long DataSize)
+        public bool IsVboCached(long Key, long DataSize)
         {
-            return VboCache.TryGetSize(Tag, out long Size) && Size == DataSize;
+            return VboCache.TryGetSize(Key, out long Size) && Size == DataSize;
         }
 
-        public bool IsIboCached(long Tag, long DataSize)
+        public bool IsIboCached(long Key, long DataSize)
         {
-            return IboCache.TryGetSize(Tag, out long Size) && Size == DataSize;
+            return IboCache.TryGetSize(Key, out long Size) && Size == DataSize;
         }
 
-        public void CreateVbo(long Tag, byte[] Buffer)
+        public void CreateVbo(long Key, byte[] Buffer)
         {
             int Handle = GL.GenBuffer();
 
-            VboCache.AddOrUpdate(Tag, Handle, (uint)Buffer.Length);
+            VboCache.AddOrUpdate(Key, Handle, (uint)Buffer.Length);
 
             IntPtr Length = new IntPtr(Buffer.Length);
 
             GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
             GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
-            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
         }
 
-        public void CreateIbo(long Tag, byte[] Buffer)
+        public void CreateIbo(long Key, byte[] Buffer)
         {
             int Handle = GL.GenBuffer();
 
-            IboCache.AddOrUpdate(Tag, Handle, (uint)Buffer.Length);
+            IboCache.AddOrUpdate(Key, Handle, (uint)Buffer.Length);
 
             IntPtr Length = new IntPtr(Buffer.Length);
 
             GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
             GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
-            GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
         }
 
-        public void SetVertexArray(int VbIndex, int Stride, long VboTag, GalVertexAttrib[] Attribs)
+        public void SetVertexArray(int VbIndex, int Stride, long VboKey, GalVertexAttrib[] Attribs)
         {
-            if (!VboCache.TryGetValue(VboTag, out int VboHandle))
+            if (!VboCache.TryGetValue(VboKey, out int VboHandle))
             {
                 return;
             }
@@ -178,11 +175,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
                 GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Stride, Offset);
             }
-
-            GL.BindVertexArray(0);
         }
 
-        public void SetIndexArray(long Tag, int Size, GalIndexFormat Format)
+        public void SetIndexArray(long Key, int Size, GalIndexFormat Format)
         {
             IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format);
 
@@ -201,9 +196,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, PrimCount);
         }
 
-        public void DrawElements(long IboTag, int First, GalPrimitiveType PrimType)
+        public void DrawElements(long IboKey, int First, GalPrimitiveType PrimType)
         {
-            if (!IboCache.TryGetValue(IboTag, out int IboHandle))
+            if (!IboCache.TryGetValue(IboKey, out int IboHandle))
             {
                 return;
             }
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs
new file mode 100644
index 00000000..ca70d4f6
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Concurrent;
+
+namespace Ryujinx.Graphics.Gal.OpenGL
+{
+    public class OGLRenderer : IGalRenderer
+    {
+        public IGalBlend Blend { get; private set; }
+
+        public IGalFrameBuffer FrameBuffer { get; private set; }
+
+        public IGalRasterizer Rasterizer { get; private set; }
+
+        public IGalShader Shader { get; private set; }
+
+        public IGalTexture Texture { get; private set; }
+
+        private ConcurrentQueue<Action> ActionsQueue;
+
+        public OGLRenderer()
+        {
+            Blend = new OGLBlend();
+
+            FrameBuffer = new OGLFrameBuffer();
+
+            Rasterizer = new OGLRasterizer();
+
+            Shader = new OGLShader();
+
+            Texture = new OGLTexture();
+
+            ActionsQueue = new ConcurrentQueue<Action>();
+        }
+
+        public void QueueAction(Action ActionMthd)
+        {
+            ActionsQueue.Enqueue(ActionMthd);
+        }
+
+        public void RunActions()
+        {
+            int Count = ActionsQueue.Count;
+
+            while (Count-- > 0 && ActionsQueue.TryDequeue(out Action RenderAction))
+            {
+                RenderAction();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
index be1bb20b..5760d172 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
@@ -7,7 +7,7 @@ using System.Linq;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
 {
-    class OGLShader
+    public class OGLShader : IGalShader
     {
         private class ShaderStage : IDisposable
         {
@@ -84,9 +84,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             Programs = new Dictionary<ShaderProgram, int>();
         }
 
-        public void Create(IGalMemory Memory, long Tag, GalShaderType Type)
+        public void Create(IGalMemory Memory, long Key, GalShaderType Type)
         {
-            Stages.GetOrAdd(Tag, (Key) => ShaderStageFactory(Memory, Tag, Type));
+            Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, Key, Type));
         }
 
         private ShaderStage ShaderStageFactory(IGalMemory Memory, long Position, GalShaderType Type)
@@ -107,9 +107,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             return Decompiler.Decompile(Memory, Position + 0x50, Type);
         }
 
-        public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag)
+        public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key)
         {
-            if (Stages.TryGetValue(Tag, out ShaderStage Stage))
+            if (Stages.TryGetValue(Key, out ShaderStage Stage))
             {
                 return Stage.TextureUsage;
             }
@@ -117,11 +117,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             return Enumerable.Empty<ShaderDeclInfo>();
         }
 
-        public void SetConstBuffer(long Tag, int Cbuf, byte[] Data)
+        public void SetConstBuffer(long Key, int Cbuf, byte[] Data)
         {
             BindProgram();
 
-            if (Stages.TryGetValue(Tag, out ShaderStage Stage))
+            if (Stages.TryGetValue(Key, out ShaderStage Stage))
             {
                 foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf))
                 {
@@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             }
         }
 
-        public void SetUniform1(string UniformName, int Value)
+        public void EnsureTextureBinding(string UniformName, int Value)
         {
             BindProgram();
 
@@ -153,18 +153,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.Uniform1(Location, Value);
         }
 
-        public void SetUniform2F(string UniformName, float X, float Y)
+        public void SetFlip(float X, float Y)
         {
             BindProgram();
 
-            int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName);
+            int Location = GL.GetUniformLocation(CurrentProgramHandle, GlslDecl.FlipUniformName);
 
             GL.Uniform2(Location, X, Y);
         }
 
-        public void Bind(long Tag)
+        public void Bind(long Key)
         {
-            if (Stages.TryGetValue(Tag, out ShaderStage Stage))
+            if (Stages.TryGetValue(Key, out ShaderStage Stage))
             {
                 Bind(Stage);
             }
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
index 56f157e5..c50bdd71 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
@@ -4,7 +4,7 @@ using System;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
 {
-    class OGLTexture
+    public class OGLTexture : IGalTexture
     {
         private class TCE
         {
@@ -31,11 +31,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.DeleteTexture(CachedTexture.Handle);
         }
 
-        public void Create(long Tag, byte[] Data, GalTexture Texture)
+        public void Create(long Key, byte[] Data, GalTexture Texture)
         {
             int Handle = GL.GenTexture();
 
-            TextureCache.AddOrUpdate(Tag, new TCE(Handle, Texture), (uint)Data.Length);
+            TextureCache.AddOrUpdate(Key, new TCE(Handle, Texture), (uint)Data.Length);
 
             GL.BindTexture(TextureTarget.Texture2D, Handle);
 
@@ -146,11 +146,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             throw new ArgumentException(nameof(Format));
         }
 
-        public bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture)
+        public bool TryGetCachedTexture(long Key, long DataSize, out GalTexture Texture)
         {
-            if (TextureCache.TryGetSize(Tag, out long Size) && Size == DataSize)
+            if (TextureCache.TryGetSize(Key, out long Size) && Size == DataSize)
             {
-                if (TextureCache.TryGetValue(Tag, out TCE CachedTexture))
+                if (TextureCache.TryGetValue(Key, out TCE CachedTexture))
                 {
                     Texture = CachedTexture.Texture;
 
@@ -163,9 +163,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             return false;
         }
 
-        public void Bind(long Tag, int Index)
+        public void Bind(long Key, int Index)
         {
-            if (TextureCache.TryGetValue(Tag, out TCE CachedTexture))
+            if (TextureCache.TryGetValue(Key, out TCE CachedTexture))
             {
                 GL.ActiveTexture(TextureUnit.Texture0 + Index);
 
@@ -173,7 +173,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             }
         }
 
-        public static void Set(GalTextureSampler Sampler)
+        public void SetSampler(GalTextureSampler Sampler)
         {
             int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
             int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
deleted file mode 100644
index 4c4bd2ca..00000000
--- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
+++ /dev/null
@@ -1,284 +0,0 @@
-using OpenTK;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.Gal.OpenGL
-{
-    public class OpenGLRenderer : IGalRenderer
-    {
-        private OGLBlend Blend;
-
-        private OGLFrameBuffer FrameBuffer;
-
-        private OGLRasterizer Rasterizer;
-
-        private OGLShader Shader;
-
-        private OGLTexture Texture;
-
-        private ConcurrentQueue<Action> ActionsQueue;
-
-        public OpenGLRenderer()
-        {
-            Blend = new OGLBlend();
-
-            FrameBuffer = new OGLFrameBuffer();
-
-            Rasterizer = new OGLRasterizer();
-
-            Shader = new OGLShader();
-
-            Texture = new OGLTexture();
-
-            ActionsQueue = new ConcurrentQueue<Action>();
-        }
-
-        public void QueueAction(Action ActionMthd)
-        {
-            ActionsQueue.Enqueue(ActionMthd);
-        }
-
-        public void RunActions()
-        {
-            int Count = ActionsQueue.Count;
-
-            while (Count-- > 0 && ActionsQueue.TryDequeue(out Action RenderAction))
-            {
-                RenderAction();
-            }
-        }
-
-        public void Render()
-        {
-            FrameBuffer.Render();
-        }
-
-        public void SetWindowSize(int Width, int Height)
-        {
-            FrameBuffer.SetWindowSize(Width, Height);
-        }
-
-        public void SetBlendEnable(bool Enable)
-        {
-            if (Enable)
-            {
-                ActionsQueue.Enqueue(() => Blend.Enable());
-            }
-            else
-            {
-                ActionsQueue.Enqueue(() => Blend.Disable());
-            }
-        }
-
-        public void SetBlend(
-            GalBlendEquation Equation,
-            GalBlendFactor   FuncSrc,
-            GalBlendFactor   FuncDst)
-        {
-            ActionsQueue.Enqueue(() => Blend.Set(Equation, FuncSrc, FuncDst));
-        }
-
-        public void SetBlendSeparate(
-            GalBlendEquation EquationRgb,
-            GalBlendEquation EquationAlpha,
-            GalBlendFactor   FuncSrcRgb,
-            GalBlendFactor   FuncDstRgb,
-            GalBlendFactor   FuncSrcAlpha,
-            GalBlendFactor   FuncDstAlpha)
-        {
-            ActionsQueue.Enqueue(() =>
-            {
-                Blend.SetSeparate(
-                    EquationRgb,
-                    EquationAlpha,
-                    FuncSrcRgb,
-                    FuncDstRgb,
-                    FuncSrcAlpha,
-                    FuncDstAlpha);
-            });
-        }
-
-        public void CreateFrameBuffer(long Tag, int Width, int Height)
-        {
-            ActionsQueue.Enqueue(() => FrameBuffer.Create(Tag, Width, Height));
-        }
-
-        public void BindFrameBuffer(long Tag)
-        {
-            ActionsQueue.Enqueue(() => FrameBuffer.Bind(Tag));
-        }
-
-        public void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler)
-        {
-            ActionsQueue.Enqueue(() =>
-            {
-                FrameBuffer.BindTexture(Tag, Index);
-
-                OGLTexture.Set(Sampler);
-            });
-        }
-
-        public void SetFrameBuffer(long Tag)
-        {
-            ActionsQueue.Enqueue(() => FrameBuffer.Set(Tag));
-        }
-
-        public void SetFrameBuffer(byte[] Data, int Width, int Height)
-        {
-            ActionsQueue.Enqueue(() => FrameBuffer.Set(Data, Width, Height));
-        }
-
-        public void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY)
-        {
-            Matrix2 Transform;
-
-            Transform  = Matrix2.CreateScale(SX, SY);
-            Transform *= Matrix2.CreateRotation(Rotate);
-
-            Vector2 Offs = new Vector2(TX, TY);
-
-            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 GetFrameBufferData(long Tag, Action<byte[]> Callback)
-        {
-            ActionsQueue.Enqueue(() => FrameBuffer.GetBufferData(Tag, Callback));
-        }
-
-        public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
-        {
-            ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags));
-        }
-
-        public bool IsVboCached(long Tag, long DataSize)
-        {
-            return Rasterizer.IsVboCached(Tag, DataSize);
-        }
-
-        public bool IsIboCached(long Tag, long DataSize)
-        {
-            return Rasterizer.IsIboCached(Tag, DataSize);
-        }
-
-        public void CreateVbo(long Tag, byte[] Buffer)
-        {
-            ActionsQueue.Enqueue(() => Rasterizer.CreateVbo(Tag, Buffer));
-        }
-
-        public void CreateIbo(long Tag, byte[] Buffer)
-        {
-            ActionsQueue.Enqueue(() => Rasterizer.CreateIbo(Tag, Buffer));
-        }
-
-        public void SetVertexArray(int VbIndex, int Stride, long VboTag, GalVertexAttrib[] Attribs)
-        {
-            if ((uint)VbIndex > 31)
-            {
-                throw new ArgumentOutOfRangeException(nameof(VbIndex));
-            }
-
-            if (Attribs == null)
-            {
-                throw new ArgumentNullException(nameof(Attribs));
-            }
-
-            ActionsQueue.Enqueue(() => Rasterizer.SetVertexArray(VbIndex, Stride, VboTag, Attribs));
-        }
-
-        public void SetIndexArray(long Tag, int Size, GalIndexFormat Format)
-        {
-            ActionsQueue.Enqueue(() => Rasterizer.SetIndexArray(Tag, Size, Format));
-        }
-
-        public void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType)
-        {
-            ActionsQueue.Enqueue(() => Rasterizer.DrawArrays(First, PrimCount, PrimType));
-        }
-
-        public void DrawElements(long IboTag, int First, GalPrimitiveType PrimType)
-        {
-            ActionsQueue.Enqueue(() => Rasterizer.DrawElements(IboTag, First, PrimType));
-        }
-
-        public void CreateShader(IGalMemory Memory, long Tag, GalShaderType Type)
-        {
-            if (Memory == null)
-            {
-                throw new ArgumentNullException(nameof(Memory));
-            }
-
-            Shader.Create(Memory, Tag, Type);
-        }
-
-        public void SetConstBuffer(long Tag, int Cbuf, byte[] Data)
-        {
-            if (Data == null)
-            {
-                throw new ArgumentNullException(nameof(Data));
-            }
-
-            ActionsQueue.Enqueue(() => Shader.SetConstBuffer(Tag, Cbuf, Data));
-        }
-
-        public void SetUniform1(string UniformName, int Value)
-        {
-            if (UniformName == null)
-            {
-                throw new ArgumentNullException(nameof(UniformName));
-            }
-
-            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);
-        }
-
-        public void BindShader(long Tag)
-        {
-            ActionsQueue.Enqueue(() => Shader.Bind(Tag));
-        }
-
-        public void BindProgram()
-        {
-            ActionsQueue.Enqueue(() => Shader.BindProgram());
-        }
-
-        public void SetTextureAndSampler(long Tag, byte[] Data, GalTexture Texture, GalTextureSampler Sampler)
-        {
-            ActionsQueue.Enqueue(() =>
-            {
-                this.Texture.Create(Tag, Data, Texture);
-
-                OGLTexture.Set(Sampler);
-            });
-        }
-
-        public bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture)
-        {
-            return this.Texture.TryGetCachedTexture(Tag, DataSize, out Texture);
-        }
-
-        public void BindTexture(long Tag, int Index)
-        {
-            ActionsQueue.Enqueue(() => Texture.Bind(Tag, Index));
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
index 4002c29a..86838ab2 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
@@ -26,6 +26,8 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public const string FragmentOutputName = "FragColor";
 
+        public const string FlipUniformName = "flip";
+
         private string[] StagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" };
 
         private string StagePrefix;
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
index 77f16b81..24db303f 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
@@ -140,7 +140,7 @@ namespace Ryujinx.Graphics.Gal.Shader
         {
             if (Decl.ShaderType == GalShaderType.Vertex)
             {
-                SB.AppendLine("uniform vec2 " + GalConsts.FlipUniformName + ";");
+                SB.AppendLine("uniform vec2 " + GlslDecl.FlipUniformName + ";");
             }
 
             foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector))
diff --git a/Ryujinx.HLE/Gpu/INvGpuEngine.cs b/Ryujinx.HLE/Gpu/Engines/INvGpuEngine.cs
similarity index 67%
rename from Ryujinx.HLE/Gpu/INvGpuEngine.cs
rename to Ryujinx.HLE/Gpu/Engines/INvGpuEngine.cs
index 62307f58..068878a9 100644
--- a/Ryujinx.HLE/Gpu/INvGpuEngine.cs
+++ b/Ryujinx.HLE/Gpu/Engines/INvGpuEngine.cs
@@ -1,4 +1,6 @@
-namespace Ryujinx.HLE.Gpu
+using Ryujinx.HLE.Gpu.Memory;
+
+namespace Ryujinx.HLE.Gpu.Engines
 {
     interface INvGpuEngine
     {
diff --git a/Ryujinx.HLE/Gpu/MacroInterpreter.cs b/Ryujinx.HLE/Gpu/Engines/MacroInterpreter.cs
similarity index 94%
rename from Ryujinx.HLE/Gpu/MacroInterpreter.cs
rename to Ryujinx.HLE/Gpu/Engines/MacroInterpreter.cs
index c333046a..aef2eb4c 100644
--- a/Ryujinx.HLE/Gpu/MacroInterpreter.cs
+++ b/Ryujinx.HLE/Gpu/Engines/MacroInterpreter.cs
@@ -1,10 +1,16 @@
+using Ryujinx.HLE.Gpu.Exceptions;
+using Ryujinx.HLE.Gpu.Memory;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Engines
 {
     class MacroInterpreter
     {
+        private const int MaxCallCountPerRun = 500;
+
+        private int CallCount;
+
         private enum AssignmentOperation
         {
             IgnoreAndFetch                  = 0,
@@ -96,6 +102,8 @@ namespace Ryujinx.HLE.Gpu
             MethIncr = 0;
 
             Carry = false;
+
+            CallCount = 0;
         }
 
         private bool Step(NvGpuVmm Vmm, int[] Mme)
@@ -407,6 +415,15 @@ namespace Ryujinx.HLE.Gpu
 
         private void Send(NvGpuVmm Vmm, int Value)
         {
+            //This is an artificial limit that prevents excessive calls
+            //to VertexEndGl since that triggers rendering, and in the
+            //case that something is bugged and causes an absurd amount of
+            //draw calls, this prevents the system from freezing (and throws instead).
+            if (MethAddr == 0x585 && ++CallCount > MaxCallCountPerRun)
+            {
+                GpuExceptionHelper.ThrowCallCoundExceeded();
+            }
+
             NvGpuPBEntry PBEntry = new NvGpuPBEntry(MethAddr, 0, Value);
 
             Engine.CallMethod(Vmm, PBEntry);
diff --git a/Ryujinx.HLE/Gpu/NvGpuEngine.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine.cs
similarity index 82%
rename from Ryujinx.HLE/Gpu/NvGpuEngine.cs
rename to Ryujinx.HLE/Gpu/Engines/NvGpuEngine.cs
index 41697ed6..f9d6342c 100644
--- a/Ryujinx.HLE/Gpu/NvGpuEngine.cs
+++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Engines
 {
     enum NvGpuEngine
     {
diff --git a/Ryujinx.HLE/Gpu/NvGpuEngine2d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs
similarity index 90%
rename from Ryujinx.HLE/Gpu/NvGpuEngine2d.cs
rename to Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs
index 15667eeb..f150b3f5 100644
--- a/Ryujinx.HLE/Gpu/NvGpuEngine2d.cs
+++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs
@@ -1,7 +1,9 @@
 using Ryujinx.Graphics.Gal;
+using Ryujinx.HLE.Gpu.Memory;
+using Ryujinx.HLE.Gpu.Texture;
 using System.Collections.Generic;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Engines
 {
     class NvGpuEngine2d : INvGpuEngine
     {
@@ -75,19 +77,19 @@ namespace Ryujinx.HLE.Gpu
 
             int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf);
 
-            long Tag = Vmm.GetPhysicalAddress(MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress));
+            long Key = Vmm.GetPhysicalAddress(MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress));
 
             long SrcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress);
             long DstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress);
 
-            bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Tag);
+            bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Key);
 
             if (IsFbTexture && DstLinear)
             {
                 DstSwizzle = TextureSwizzle.BlockLinear;
             }
 
-            Texture DstTexture = new Texture(
+            TextureInfo DstTexture = new TextureInfo(
                 DstAddress,
                 DstWidth,
                 DstHeight,
@@ -103,7 +105,7 @@ namespace Ryujinx.HLE.Gpu
                 SrcWidth  = 1280;
                 SrcHeight = 720;
 
-                Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) =>
+                Gpu.Renderer.FrameBuffer.GetBufferData(Key, (byte[] Buffer) =>
                 {
                     CopyTexture(
                         Vmm,
@@ -129,11 +131,11 @@ namespace Ryujinx.HLE.Gpu
         }
 
         private void CopyTexture(
-            NvGpuVmm Vmm,
-            Texture  Texture,
-            byte[]   Buffer,
-            int      Width,
-            int      Height)
+            NvGpuVmm    Vmm,
+            TextureInfo Texture,
+            byte[]      Buffer,
+            int         Width,
+            int         Height)
         {
             TextureWriter.Write(Vmm, Texture, Buffer, Width, Height);
         }
diff --git a/Ryujinx.HLE/Gpu/NvGpuEngine2dReg.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2dReg.cs
similarity index 95%
rename from Ryujinx.HLE/Gpu/NvGpuEngine2dReg.cs
rename to Ryujinx.HLE/Gpu/Engines/NvGpuEngine2dReg.cs
index 1039e368..29d66d46 100644
--- a/Ryujinx.HLE/Gpu/NvGpuEngine2dReg.cs
+++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2dReg.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Engines
 {
     enum NvGpuEngine2dReg
     {
diff --git a/Ryujinx.HLE/Gpu/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs
similarity index 82%
rename from Ryujinx.HLE/Gpu/NvGpuEngine3d.cs
rename to Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs
index 6d03e6b8..02461d5d 100644
--- a/Ryujinx.HLE/Gpu/NvGpuEngine3d.cs
+++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs
@@ -1,8 +1,10 @@
 using Ryujinx.Graphics.Gal;
+using Ryujinx.HLE.Gpu.Memory;
+using Ryujinx.HLE.Gpu.Texture;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Engines
 {
     class NvGpuEngine3d : INvGpuEngine
     {
@@ -73,13 +75,13 @@ namespace Ryujinx.HLE.Gpu
         {
             SetFrameBuffer(Vmm, 0);
 
-            long[] Tags = UploadShaders(Vmm);
+            long[] Keys = UploadShaders(Vmm);
 
-            Gpu.Renderer.BindProgram();
+            Gpu.Renderer.Shader.BindProgram();
 
             SetAlphaBlending();
 
-            UploadTextures(Vmm, Tags);
+            UploadTextures(Vmm, Keys);
             UploadUniforms(Vmm);
             UploadVertexArrays(Vmm);
         }
@@ -113,13 +115,13 @@ namespace Ryujinx.HLE.Gpu
 
             //Note: Using the Width/Height results seems to give incorrect results.
             //Maybe the size of all frame buffers is hardcoded to screen size? This seems unlikely.
-            Gpu.Renderer.CreateFrameBuffer(PA, 1280, 720);
-            Gpu.Renderer.BindFrameBuffer(PA);
+            Gpu.Renderer.FrameBuffer.Create(PA, 1280, 720);
+            Gpu.Renderer.FrameBuffer.Bind(PA);
         }
 
         private long[] UploadShaders(NvGpuVmm Vmm)
         {
-            long[] Tags = new long[5];
+            long[] Keys = new long[5];
 
             long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
 
@@ -136,14 +138,14 @@ namespace Ryujinx.HLE.Gpu
                     continue;
                 }
 
-                long Tag = BasePosition + (uint)Offset;
+                long Key = BasePosition + (uint)Offset;
 
                 GalShaderType ShaderType = GetTypeFromProgram(Index);
 
-                Tags[(int)ShaderType] = Tag;
+                Keys[(int)ShaderType] = Key;
 
-                Gpu.Renderer.CreateShader(Vmm, Tag, ShaderType);
-                Gpu.Renderer.BindShader(Tag);
+                Gpu.Renderer.Shader.Create(Vmm, Key, ShaderType);
+                Gpu.Renderer.Shader.Bind(Key);
             }
 
             int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX);
@@ -155,9 +157,9 @@ namespace Ryujinx.HLE.Gpu
             float SignX = MathF.Sign(SX);
             float SignY = MathF.Sign(SY);
 
-            Gpu.Renderer.SetUniform2F(GalConsts.FlipUniformName, SignX, SignY);
+            Gpu.Renderer.Shader.SetFlip(SignX, SignY);
 
-            return Tags;
+            return Keys;
         }
 
         private static GalShaderType GetTypeFromProgram(int Program)
@@ -180,7 +182,14 @@ namespace Ryujinx.HLE.Gpu
             //TODO: Support independent blend properly.
             bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0;
 
-            Gpu.Renderer.SetBlendEnable(Enable);
+            if (Enable)
+            {
+                Gpu.Renderer.Blend.Enable();
+            }
+            else
+            {
+                Gpu.Renderer.Blend.Disable();
+            }
 
             if (!Enable)
             {
@@ -203,7 +212,7 @@ namespace Ryujinx.HLE.Gpu
                 GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha);
                 GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha);
 
-                Gpu.Renderer.SetBlendSeparate(
+                Gpu.Renderer.Blend.SetSeparate(
                     EquationRgb,
                     EquationAlpha,
                     FuncSrcRgb,
@@ -213,11 +222,11 @@ namespace Ryujinx.HLE.Gpu
             }
             else
             {
-                Gpu.Renderer.SetBlend(EquationRgb, FuncSrcRgb, FuncDstRgb);
+                Gpu.Renderer.Blend.Set(EquationRgb, FuncSrcRgb, FuncDstRgb);
             }
         }
 
-        private void UploadTextures(NvGpuVmm Vmm, long[] Tags)
+        private void UploadTextures(NvGpuVmm Vmm, long[] Keys)
         {
             long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
 
@@ -227,15 +236,15 @@ namespace Ryujinx.HLE.Gpu
             //reserved for drawing the frame buffer.
             int TexIndex = 1;
 
-            for (int Index = 0; Index < Tags.Length; Index++)
+            for (int Index = 0; Index < Keys.Length; Index++)
             {
-                foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index]))
+                foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.Shader.GetTextureUsage(Keys[Index]))
                 {
                     long Position = ConstBuffers[Index][TextureCbIndex].Position;
 
                     UploadTexture(Vmm, Position, TexIndex, DeclInfo.Index);
 
-                    Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex);
+                    Gpu.Renderer.Shader.EnsureTextureBinding(DeclInfo.Name, TexIndex);
 
                     TexIndex++;
                 }
@@ -270,7 +279,7 @@ namespace Ryujinx.HLE.Gpu
 
             long TextureAddress = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff;
 
-            long Tag = TextureAddress;
+            long Key = TextureAddress;
 
             TextureAddress = Vmm.GetPhysicalAddress(TextureAddress);
 
@@ -280,7 +289,7 @@ namespace Ryujinx.HLE.Gpu
                 //we shouldn't read anything from memory and bind
                 //the frame buffer texture instead, since we're not
                 //really writing anything to memory.
-                Gpu.Renderer.BindFrameBufferTexture(TextureAddress, TexIndex, Sampler);
+                Gpu.Renderer.FrameBuffer.BindTexture(TextureAddress, TexIndex);
             }
             else
             {
@@ -288,22 +297,29 @@ namespace Ryujinx.HLE.Gpu
 
                 long Size = (uint)TextureHelper.GetTextureSize(NewTexture);
 
-                if (Gpu.Renderer.TryGetCachedTexture(Tag, Size, out GalTexture Texture))
-                {
-                    if (NewTexture.Equals(Texture) && !Vmm.IsRegionModified(Tag, Size, NvGpuBufferType.Texture))
-                    {
-                        Gpu.Renderer.BindTexture(Tag, TexIndex);
+                bool HasCachedTexture = false;
 
-                        return;
+                if (Gpu.Renderer.Texture.TryGetCachedTexture(Key, Size, out GalTexture Texture))
+                {
+                    if (NewTexture.Equals(Texture) && !Vmm.IsRegionModified(Key, Size, NvGpuBufferType.Texture))
+                    {
+                        Gpu.Renderer.Texture.Bind(Key, TexIndex);
+
+                        HasCachedTexture = true;
                     }
                 }
 
-                byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition);
+                if (!HasCachedTexture)
+                {
+                    byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition);
 
-                Gpu.Renderer.SetTextureAndSampler(Tag, Data, NewTexture, Sampler);
+                    Gpu.Renderer.Texture.Create(Key, Data, NewTexture);
+                }
 
-                Gpu.Renderer.BindTexture(Tag, TexIndex);
+                Gpu.Renderer.Texture.Bind(Key, TexIndex);
             }
+
+            Gpu.Renderer.Texture.SetSampler(Sampler);
         }
 
         private void UploadUniforms(NvGpuVmm Vmm)
@@ -331,7 +347,7 @@ namespace Ryujinx.HLE.Gpu
                     {
                         byte[] Data = Vmm.ReadBytes(Cb.Position, (uint)Cb.Size);
 
-                        Gpu.Renderer.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data);
+                        Gpu.Renderer.Shader.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data);
                     }
                 }
             }
@@ -341,33 +357,33 @@ namespace Ryujinx.HLE.Gpu
         {
             long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress);
 
-            int IndexSize  = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
-            int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst);
-            int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount);
+            int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
+            int IndexFirst    = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst);
+            int IndexCount    = ReadRegister(NvGpuEngine3dReg.IndexBatchCount);
 
-            GalIndexFormat IndexFormat = (GalIndexFormat)IndexSize;
+            GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt;
 
-            IndexSize = 1 << IndexSize;
+            int IndexEntrySize = 1 << IndexEntryFmt;
 
-            if (IndexSize > 4)
+            if (IndexEntrySize > 4)
             {
                 throw new InvalidOperationException();
             }
 
             if (IndexCount != 0)
             {
-                int IbSize = IndexCount * IndexSize;
+                int IbSize = IndexCount * IndexEntrySize;
 
-                bool IboCached = Gpu.Renderer.IsIboCached(IndexPosition, (uint)IbSize);
+                bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IndexPosition, (uint)IbSize);
 
                 if (!IboCached || Vmm.IsRegionModified(IndexPosition, (uint)IbSize, NvGpuBufferType.Index))
                 {
                     byte[] Data = Vmm.ReadBytes(IndexPosition, (uint)IbSize);
 
-                    Gpu.Renderer.CreateIbo(IndexPosition, Data);
+                    Gpu.Renderer.Rasterizer.CreateIbo(IndexPosition, Data);
                 }
 
-                Gpu.Renderer.SetIndexArray(IndexPosition, IbSize, IndexFormat);
+                Gpu.Renderer.Rasterizer.SetIndexArray(IndexPosition, IbSize, IndexFormat);
             }
 
             List<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[32];
@@ -429,27 +445,27 @@ namespace Ryujinx.HLE.Gpu
                     VbSize = VertexCount * Stride;
                 }
 
-                bool VboCached = Gpu.Renderer.IsVboCached(VertexPosition, VbSize);
+                bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VertexPosition, VbSize);
 
                 if (!VboCached || Vmm.IsRegionModified(VertexPosition, VbSize, NvGpuBufferType.Vertex))
                 {
                     byte[] Data = Vmm.ReadBytes(VertexPosition, VbSize);
 
-                    Gpu.Renderer.CreateVbo(VertexPosition, Data);
+                    Gpu.Renderer.Rasterizer.CreateVbo(VertexPosition, Data);
                 }
 
-                Gpu.Renderer.SetVertexArray(Index, Stride, VertexPosition, Attribs[Index].ToArray());
+                Gpu.Renderer.Rasterizer.SetVertexArray(Index, Stride, VertexPosition, Attribs[Index].ToArray());
             }
 
             GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
 
             if (IndexCount != 0)
             {
-                Gpu.Renderer.DrawElements(IndexPosition, IndexFirst, PrimType);
+                Gpu.Renderer.Rasterizer.DrawElements(IndexPosition, IndexFirst, PrimType);
             }
             else
             {
-                Gpu.Renderer.DrawArrays(VertexFirst, VertexCount, PrimType);
+                Gpu.Renderer.Rasterizer.DrawArrays(VertexFirst, VertexCount, PrimType);
             }
         }
 
diff --git a/Ryujinx.HLE/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs
similarity index 98%
rename from Ryujinx.HLE/Gpu/NvGpuEngine3dReg.cs
rename to Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs
index e0de4777..de2b2eef 100644
--- a/Ryujinx.HLE/Gpu/NvGpuEngine3dReg.cs
+++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Engines
 {
     enum NvGpuEngine3dReg
     {
diff --git a/Ryujinx.HLE/Gpu/NvGpuEngineDma.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngineDma.cs
similarity index 97%
rename from Ryujinx.HLE/Gpu/NvGpuEngineDma.cs
rename to Ryujinx.HLE/Gpu/Engines/NvGpuEngineDma.cs
index ed7819e9..7e355e8d 100644
--- a/Ryujinx.HLE/Gpu/NvGpuEngineDma.cs
+++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngineDma.cs
@@ -1,6 +1,8 @@
+using Ryujinx.HLE.Gpu.Memory;
+using Ryujinx.HLE.Gpu.Texture;
 using System.Collections.Generic;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Engines
 {
     class NvGpuEngineDma : INvGpuEngine
     {
diff --git a/Ryujinx.HLE/Gpu/NvGpuEngineDmaReg.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngineDmaReg.cs
similarity index 93%
rename from Ryujinx.HLE/Gpu/NvGpuEngineDmaReg.cs
rename to Ryujinx.HLE/Gpu/Engines/NvGpuEngineDmaReg.cs
index 55b404c5..835a822d 100644
--- a/Ryujinx.HLE/Gpu/NvGpuEngineDmaReg.cs
+++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngineDmaReg.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Engines
 {
     enum NvGpuEngineDmaReg
     {
diff --git a/Ryujinx.HLE/Gpu/NvGpuFifo.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs
similarity index 98%
rename from Ryujinx.HLE/Gpu/NvGpuFifo.cs
rename to Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs
index 361c8bce..0bc682a7 100644
--- a/Ryujinx.HLE/Gpu/NvGpuFifo.cs
+++ b/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs
@@ -1,6 +1,7 @@
+using Ryujinx.HLE.Gpu.Memory;
 using System.Collections.Concurrent;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Engines
 {
     class NvGpuFifo
     {
diff --git a/Ryujinx.HLE/Gpu/NvGpuFifoMeth.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuFifoMeth.cs
similarity index 86%
rename from Ryujinx.HLE/Gpu/NvGpuFifoMeth.cs
rename to Ryujinx.HLE/Gpu/Engines/NvGpuFifoMeth.cs
index 247a7bfc..ffd179f2 100644
--- a/Ryujinx.HLE/Gpu/NvGpuFifoMeth.cs
+++ b/Ryujinx.HLE/Gpu/Engines/NvGpuFifoMeth.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Engines
 {
     enum NvGpuFifoMeth
     {
diff --git a/Ryujinx.HLE/Gpu/NvGpuMethod.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuMethod.cs
similarity index 51%
rename from Ryujinx.HLE/Gpu/NvGpuMethod.cs
rename to Ryujinx.HLE/Gpu/Engines/NvGpuMethod.cs
index f7ff6647..04c92f2a 100644
--- a/Ryujinx.HLE/Gpu/NvGpuMethod.cs
+++ b/Ryujinx.HLE/Gpu/Engines/NvGpuMethod.cs
@@ -1,4 +1,6 @@
-namespace Ryujinx.HLE.Gpu
+using Ryujinx.HLE.Gpu.Memory;
+
+namespace Ryujinx.HLE.Gpu.Engines
 {
     delegate void NvGpuMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry);
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/Gpu/Exceptions/GpuException.cs b/Ryujinx.HLE/Gpu/Exceptions/GpuException.cs
new file mode 100644
index 00000000..c0bce5a5
--- /dev/null
+++ b/Ryujinx.HLE/Gpu/Exceptions/GpuException.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Ryujinx.HLE.Gpu.Exceptions
+{
+    class GpuException : Exception
+    {
+        public GpuException() : base() { }
+
+        public GpuException(string ExMsg) : base(ExMsg) { }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/Gpu/Exceptions/GpuExceptionHelper.cs b/Ryujinx.HLE/Gpu/Exceptions/GpuExceptionHelper.cs
new file mode 100644
index 00000000..aeab9a29
--- /dev/null
+++ b/Ryujinx.HLE/Gpu/Exceptions/GpuExceptionHelper.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.HLE.Gpu.Exceptions
+{
+    static class GpuExceptionHelper
+    {
+        private const string CallCountExceeded = "Method call count exceeded the limit allowed per run!";
+
+        public static void ThrowCallCoundExceeded()
+        {
+            throw new GpuException(CallCountExceeded);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/Gpu/NvGpuBufferType.cs b/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs
similarity index 72%
rename from Ryujinx.HLE/Gpu/NvGpuBufferType.cs
rename to Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs
index a44a772d..7474aa33 100644
--- a/Ryujinx.HLE/Gpu/NvGpuBufferType.cs
+++ b/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Memory
 {
     enum NvGpuBufferType
     {
diff --git a/Ryujinx.HLE/Gpu/NvGpuPBEntry.cs b/Ryujinx.HLE/Gpu/Memory/NvGpuPBEntry.cs
similarity index 94%
rename from Ryujinx.HLE/Gpu/NvGpuPBEntry.cs
rename to Ryujinx.HLE/Gpu/Memory/NvGpuPBEntry.cs
index 2cd663fe..aba89e3c 100644
--- a/Ryujinx.HLE/Gpu/NvGpuPBEntry.cs
+++ b/Ryujinx.HLE/Gpu/Memory/NvGpuPBEntry.cs
@@ -1,7 +1,7 @@
 using System;
 using System.Collections.ObjectModel;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Memory
 {
     struct NvGpuPBEntry
     {
diff --git a/Ryujinx.HLE/Gpu/NvGpuPushBuffer.cs b/Ryujinx.HLE/Gpu/Memory/NvGpuPushBuffer.cs
similarity index 99%
rename from Ryujinx.HLE/Gpu/NvGpuPushBuffer.cs
rename to Ryujinx.HLE/Gpu/Memory/NvGpuPushBuffer.cs
index 2d4f0c1a..6121b3e6 100644
--- a/Ryujinx.HLE/Gpu/NvGpuPushBuffer.cs
+++ b/Ryujinx.HLE/Gpu/Memory/NvGpuPushBuffer.cs
@@ -1,7 +1,7 @@
 using System.Collections.Generic;
 using System.IO;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Memory
 {
     static class NvGpuPushBuffer
     {
diff --git a/Ryujinx.HLE/Gpu/NvGpuVmm.cs b/Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs
similarity index 99%
rename from Ryujinx.HLE/Gpu/NvGpuVmm.cs
rename to Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs
index b0ba3e90..36f6406a 100644
--- a/Ryujinx.HLE/Gpu/NvGpuVmm.cs
+++ b/Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs
@@ -2,7 +2,7 @@ using ChocolArm64.Memory;
 using Ryujinx.Graphics.Gal;
 using System.Collections.Concurrent;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Memory
 {
     class NvGpuVmm : IAMemory, IGalMemory
     {
diff --git a/Ryujinx.HLE/Gpu/NvGpuVmmCache.cs b/Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs
similarity index 99%
rename from Ryujinx.HLE/Gpu/NvGpuVmmCache.cs
rename to Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs
index 38b25e4f..c7108f00 100644
--- a/Ryujinx.HLE/Gpu/NvGpuVmmCache.cs
+++ b/Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs
@@ -2,7 +2,7 @@ using ChocolArm64.Memory;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Memory
 {
     class NvGpuVmmCache
     {
diff --git a/Ryujinx.HLE/Gpu/NvGpu.cs b/Ryujinx.HLE/Gpu/NvGpu.cs
index 0301fcf2..625cb727 100644
--- a/Ryujinx.HLE/Gpu/NvGpu.cs
+++ b/Ryujinx.HLE/Gpu/NvGpu.cs
@@ -1,5 +1,5 @@
 using Ryujinx.Graphics.Gal;
-using System.Threading;
+using Ryujinx.HLE.Gpu.Engines;
 
 namespace Ryujinx.HLE.Gpu
 {
@@ -13,10 +13,6 @@ namespace Ryujinx.HLE.Gpu
         public NvGpuEngine3d  Engine3d  { get; private set; }
         public NvGpuEngineDma EngineDma { get; private set; }
 
-        private Thread FifoProcessing;
-
-        private bool KeepRunning;
-
         public NvGpu(IGalRenderer Renderer)
         {
             this.Renderer = Renderer;
@@ -26,22 +22,6 @@ namespace Ryujinx.HLE.Gpu
             Engine2d  = new NvGpuEngine2d(this);
             Engine3d  = new NvGpuEngine3d(this);
             EngineDma = new NvGpuEngineDma(this);
-
-            KeepRunning = true;
-
-            FifoProcessing = new Thread(ProcessFifo);
-
-            FifoProcessing.Start();
-        }
-
-        private void ProcessFifo()
-        {
-            while (KeepRunning)
-            {
-                Fifo.DispatchCalls();
-
-                Thread.Yield();
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/Gpu/BlockLinearSwizzle.cs b/Ryujinx.HLE/Gpu/Texture/BlockLinearSwizzle.cs
similarity index 97%
rename from Ryujinx.HLE/Gpu/BlockLinearSwizzle.cs
rename to Ryujinx.HLE/Gpu/Texture/BlockLinearSwizzle.cs
index 366f5740..e66d7613 100644
--- a/Ryujinx.HLE/Gpu/BlockLinearSwizzle.cs
+++ b/Ryujinx.HLE/Gpu/Texture/BlockLinearSwizzle.cs
@@ -1,6 +1,6 @@
 using System;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Texture
 {
     class BlockLinearSwizzle : ISwizzle
     {
diff --git a/Ryujinx.HLE/Gpu/ISwizzle.cs b/Ryujinx.HLE/Gpu/Texture/ISwizzle.cs
similarity index 70%
rename from Ryujinx.HLE/Gpu/ISwizzle.cs
rename to Ryujinx.HLE/Gpu/Texture/ISwizzle.cs
index 525707ba..222aab16 100644
--- a/Ryujinx.HLE/Gpu/ISwizzle.cs
+++ b/Ryujinx.HLE/Gpu/Texture/ISwizzle.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Texture
 {
     interface ISwizzle
     {
diff --git a/Ryujinx.HLE/Gpu/LinearSwizzle.cs b/Ryujinx.HLE/Gpu/Texture/LinearSwizzle.cs
similarity index 91%
rename from Ryujinx.HLE/Gpu/LinearSwizzle.cs
rename to Ryujinx.HLE/Gpu/Texture/LinearSwizzle.cs
index 995866ad..720f7832 100644
--- a/Ryujinx.HLE/Gpu/LinearSwizzle.cs
+++ b/Ryujinx.HLE/Gpu/Texture/LinearSwizzle.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Texture
 {
     class LinearSwizzle : ISwizzle
     {
diff --git a/Ryujinx.HLE/Gpu/TextureFactory.cs b/Ryujinx.HLE/Gpu/Texture/TextureFactory.cs
similarity index 96%
rename from Ryujinx.HLE/Gpu/TextureFactory.cs
rename to Ryujinx.HLE/Gpu/Texture/TextureFactory.cs
index 94c6eb18..9df0b600 100644
--- a/Ryujinx.HLE/Gpu/TextureFactory.cs
+++ b/Ryujinx.HLE/Gpu/Texture/TextureFactory.cs
@@ -1,7 +1,8 @@
 using Ryujinx.Graphics.Gal;
+using Ryujinx.HLE.Gpu.Memory;
 using System;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Texture
 {
     static class TextureFactory
     {
@@ -61,7 +62,7 @@ namespace Ryujinx.HLE.Gpu
             int Width  = (Tic[4] & 0xffff) + 1;
             int Height = (Tic[5] & 0xffff) + 1;
 
-            Texture Texture = new Texture(
+            TextureInfo Texture = new TextureInfo(
                 TextureAddress,
                 Width,
                 Height,
diff --git a/Ryujinx.HLE/Gpu/TextureHelper.cs b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs
similarity index 94%
rename from Ryujinx.HLE/Gpu/TextureHelper.cs
rename to Ryujinx.HLE/Gpu/Texture/TextureHelper.cs
index 237d87ab..ac8f75c5 100644
--- a/Ryujinx.HLE/Gpu/TextureHelper.cs
+++ b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs
@@ -1,12 +1,13 @@
 using ChocolArm64.Memory;
 using Ryujinx.Graphics.Gal;
+using Ryujinx.HLE.Gpu.Memory;
 using System;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Texture
 {
     static class TextureHelper
     {
-        public static ISwizzle GetSwizzle(Texture Texture, int Width, int Bpp)
+        public static ISwizzle GetSwizzle(TextureInfo Texture, int Width, int Bpp)
         {
             switch (Texture.Swizzle)
             {
diff --git a/Ryujinx.HLE/Gpu/Texture.cs b/Ryujinx.HLE/Gpu/Texture/TextureInfo.cs
similarity index 92%
rename from Ryujinx.HLE/Gpu/Texture.cs
rename to Ryujinx.HLE/Gpu/Texture/TextureInfo.cs
index 1de7f302..31784bbc 100644
--- a/Ryujinx.HLE/Gpu/Texture.cs
+++ b/Ryujinx.HLE/Gpu/Texture/TextureInfo.cs
@@ -1,8 +1,8 @@
 using Ryujinx.Graphics.Gal;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Texture
 {
-    struct Texture
+    struct TextureInfo
     {
         public long Position { get; private set; }
 
@@ -16,7 +16,7 @@ namespace Ryujinx.HLE.Gpu
 
         public GalTextureFormat Format { get; private set; }
 
-        public Texture(
+        public TextureInfo(
             long Position,
             int  Width,
             int  Height)
@@ -34,7 +34,7 @@ namespace Ryujinx.HLE.Gpu
             Format = GalTextureFormat.A8B8G8R8;
         }
 
-        public Texture(
+        public TextureInfo(
             long             Position,
             int              Width,
             int              Height,
diff --git a/Ryujinx.HLE/Gpu/TextureReader.cs b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs
similarity index 97%
rename from Ryujinx.HLE/Gpu/TextureReader.cs
rename to Ryujinx.HLE/Gpu/Texture/TextureReader.cs
index 9e9ff783..48bf1a90 100644
--- a/Ryujinx.HLE/Gpu/TextureReader.cs
+++ b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs
@@ -2,11 +2,11 @@ using ChocolArm64.Memory;
 using Ryujinx.Graphics.Gal;
 using System;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Texture
 {
     static class TextureReader
     {
-        public static byte[] Read(IAMemory Memory, Texture Texture)
+        public static byte[] Read(IAMemory Memory, TextureInfo Texture)
         {
             switch (Texture.Format)
             {
@@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Gpu
             throw new NotImplementedException(Texture.Format.ToString());
         }
 
-        private unsafe static byte[] Read1Bpp(IAMemory Memory, Texture Texture)
+        private unsafe static byte[] Read1Bpp(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -64,7 +64,7 @@ namespace Ryujinx.HLE.Gpu
             return Output;
         }
 
-        private unsafe static byte[] Read5551(IAMemory Memory, Texture Texture)
+        private unsafe static byte[] Read5551(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -102,7 +102,7 @@ namespace Ryujinx.HLE.Gpu
             return Output;
         }
 
-        private unsafe static byte[] Read565(IAMemory Memory, Texture Texture)
+        private unsafe static byte[] Read565(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -139,7 +139,7 @@ namespace Ryujinx.HLE.Gpu
             return Output;
         }
 
-        private unsafe static byte[] Read2Bpp(IAMemory Memory, Texture Texture)
+        private unsafe static byte[] Read2Bpp(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -172,7 +172,7 @@ namespace Ryujinx.HLE.Gpu
             return Output;
         }
 
-        private unsafe static byte[] Read4Bpp(IAMemory Memory, Texture Texture)
+        private unsafe static byte[] Read4Bpp(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -205,7 +205,7 @@ namespace Ryujinx.HLE.Gpu
             return Output;
         }
 
-        private unsafe static byte[] Read8Bpp(IAMemory Memory, Texture Texture)
+        private unsafe static byte[] Read8Bpp(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -238,7 +238,7 @@ namespace Ryujinx.HLE.Gpu
             return Output;
         }
 
-        private unsafe static byte[] Read16Bpp(IAMemory Memory, Texture Texture)
+        private unsafe static byte[] Read16Bpp(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -273,7 +273,7 @@ namespace Ryujinx.HLE.Gpu
             return Output;
         }
 
-        private unsafe static byte[] Read8Bpt4x4(IAMemory Memory, Texture Texture)
+        private unsafe static byte[] Read8Bpt4x4(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = (Texture.Width  + 3) / 4;
             int Height = (Texture.Height + 3) / 4;
@@ -306,7 +306,7 @@ namespace Ryujinx.HLE.Gpu
             return Output;
         }
 
-        private unsafe static byte[] Read16Bpt4x4(IAMemory Memory, Texture Texture)
+        private unsafe static byte[] Read16Bpt4x4(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = (Texture.Width  + 3) / 4;
             int Height = (Texture.Height + 3) / 4;
diff --git a/Ryujinx.HLE/Gpu/TextureSwizzle.cs b/Ryujinx.HLE/Gpu/Texture/TextureSwizzle.cs
similarity index 85%
rename from Ryujinx.HLE/Gpu/TextureSwizzle.cs
rename to Ryujinx.HLE/Gpu/Texture/TextureSwizzle.cs
index 5e32f4c7..076df97a 100644
--- a/Ryujinx.HLE/Gpu/TextureSwizzle.cs
+++ b/Ryujinx.HLE/Gpu/Texture/TextureSwizzle.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Texture
 {
     enum TextureSwizzle
     {
diff --git a/Ryujinx.HLE/Gpu/TextureWriter.cs b/Ryujinx.HLE/Gpu/Texture/TextureWriter.cs
similarity index 77%
rename from Ryujinx.HLE/Gpu/TextureWriter.cs
rename to Ryujinx.HLE/Gpu/Texture/TextureWriter.cs
index ad92961c..b64302a5 100644
--- a/Ryujinx.HLE/Gpu/TextureWriter.cs
+++ b/Ryujinx.HLE/Gpu/Texture/TextureWriter.cs
@@ -2,16 +2,16 @@ using ChocolArm64.Memory;
 using Ryujinx.Graphics.Gal;
 using System;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.HLE.Gpu.Texture
 {
     static class TextureWriter
     {
         public static void Write(
-            IAMemory Memory,
-            Texture  Texture,
-            byte[]   Data,
-            int      Width,
-            int      Height)
+            IAMemory    Memory,
+            TextureInfo Texture,
+            byte[]      Data,
+            int         Width,
+            int         Height)
         {
             switch (Texture.Format)
             {
@@ -22,11 +22,11 @@ namespace Ryujinx.HLE.Gpu
         }
 
         private unsafe static void Write4Bpp(
-            IAMemory Memory,
-            Texture  Texture,
-            byte[]   Data,
-            int      Width,
-            int      Height)
+            IAMemory    Memory,
+            TextureInfo Texture,
+            byte[]      Data,
+            int         Width,
+            int         Height)
         {
             ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4);
 
diff --git a/Ryujinx.HLE/Loaders/Npdm/ACI0.cs b/Ryujinx.HLE/Loaders/Npdm/ACI0.cs
index 1f1b810e..47b30a3c 100644
--- a/Ryujinx.HLE/Loaders/Npdm/ACI0.cs
+++ b/Ryujinx.HLE/Loaders/Npdm/ACI0.cs
@@ -1,4 +1,3 @@
-using Ryujinx.HLE.OsHle.Utilities;
 using System;
 using System.IO;
 
@@ -20,7 +19,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
         public KernelAccessControl  KernelAccessControl;
 
         public const long ACI0Magic = 'A' << 0 | 'C' << 8 | 'I' << 16 | '0' << 24;
-        
+
         public ACI0(Stream ACI0Stream, int Offset)
         {
             ACI0Stream.Seek(Offset, SeekOrigin.Begin);
diff --git a/Ryujinx.HLE/Loaders/Npdm/ACID.cs b/Ryujinx.HLE/Loaders/Npdm/ACID.cs
index d0f0acdd..09768a92 100644
--- a/Ryujinx.HLE/Loaders/Npdm/ACID.cs
+++ b/Ryujinx.HLE/Loaders/Npdm/ACID.cs
@@ -1,4 +1,3 @@
-using Ryujinx.HLE.OsHle.Utilities;
 using System;
 using System.IO;
 
@@ -24,7 +23,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
         public FSAccessControl      FSAccessControl;
         public ServiceAccessControl ServiceAccessControl;
         public KernelAccessControl  KernelAccessControl;
-        
+
         public const long ACIDMagic = 'A' << 0 | 'C' << 8 | 'I' << 16 | 'D' << 24;
 
         public ACID(Stream ACIDStream, int Offset)
diff --git a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
index d255e668..eaa662f0 100644
--- a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
+++ b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
@@ -1,6 +1,4 @@
 using Ryujinx.HLE.OsHle.Utilities;
-using System;
-using System.Collections.Generic;
 using System.IO;
 using System.Text;
 
@@ -29,7 +27,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
 
         public ACI0 ACI0;
         public ACID ACID;
-        
+
         public const long NpdmMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24;
 
         public Npdm(Stream NPDMStream)
@@ -61,7 +59,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
             // ProcessCategory (0: regular title, 1: kernel built-in). Should be 0 here.
             ProcessCategory = EndianSwap.Swap32(Reader.ReadInt32());
 
-            // Main entrypoint stack size 
+            // Main entrypoint stack size
             // (Should(?) be page-aligned. In non-nspwn scenarios, values of 0 can also rarely break in Horizon.
             // This might be something auto-adapting or a security feature of some sort ?)
             MainEntrypointStackSize = Reader.ReadInt32();
diff --git a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs b/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
index fd33841a..ddd7d7ed 100644
--- a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
+++ b/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.IO;
 using System.Text;
 
@@ -25,7 +24,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
 
                 int Length             = ((ControlByte & 0x07)) + 1;
                 bool RegisterAllowed   = ((ControlByte & 0x80) != 0);
-                
+
                 Services.Add((Encoding.ASCII.GetString(Reader.ReadBytes(Length), 0, Length), RegisterAllowed));
 
                 ByteReaded += Length + 1;
diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
index c96c04c8..fcc478a4 100644
--- a/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
+++ b/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
@@ -1,5 +1,5 @@
 using ChocolArm64.Memory;
-using Ryujinx.HLE.Gpu;
+using Ryujinx.HLE.Gpu.Memory;
 using Ryujinx.HLE.Logging;
 using Ryujinx.HLE.OsHle.Services.Nv.NvMap;
 using System;
diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
index c461fa28..8f3d3cd7 100644
--- a/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
+++ b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
@@ -1,5 +1,5 @@
 using ChocolArm64.Memory;
-using Ryujinx.HLE.Gpu;
+using Ryujinx.HLE.Gpu.Memory;
 using Ryujinx.HLE.Logging;
 using Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS;
 using System;
diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs
index 43de4edb..ec10a375 100644
--- a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs
+++ b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs
@@ -1,5 +1,5 @@
 using ChocolArm64.Memory;
-using Ryujinx.HLE.Gpu;
+using Ryujinx.HLE.Gpu.Memory;
 using Ryujinx.HLE.Logging;
 using Ryujinx.HLE.OsHle.Utilities;
 using System.Collections.Concurrent;
diff --git a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs
index b45dac6b..a3ed3ab5 100644
--- a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs
+++ b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs
@@ -1,5 +1,5 @@
 using Ryujinx.Graphics.Gal;
-using Ryujinx.HLE.Gpu;
+using Ryujinx.HLE.Gpu.Texture;
 using Ryujinx.HLE.Logging;
 using Ryujinx.HLE.OsHle.Handles;
 using Ryujinx.HLE.OsHle.Services.Nv.NvMap;
@@ -8,6 +8,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.Text;
 using System.Threading;
+
 using static Ryujinx.HLE.OsHle.Services.Android.Parcel;
 
 namespace Ryujinx.HLE.OsHle.Services.Android
@@ -339,7 +340,7 @@ namespace Ryujinx.HLE.OsHle.Services.Android
                 Rotate = -MathF.PI * 0.5f;
             }
 
-            Renderer.SetFrameBufferTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY);
+            Renderer.QueueAction(() => Renderer.FrameBuffer.SetTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY));
 
             //TODO: Support double buffering here aswell, it is broken for GPU
             //frame buffers because it seems to be completely out of sync.
@@ -347,17 +348,17 @@ namespace Ryujinx.HLE.OsHle.Services.Android
             {
                 //Frame buffer is rendered to by the GPU, we can just
                 //bind the frame buffer texture, it's not necessary to read anything.
-                Renderer.SetFrameBuffer(FbAddr);
+                Renderer.QueueAction(() => Renderer.FrameBuffer.Set(FbAddr));
             }
             else
             {
                 //Frame buffer is not set on the GPU registers, in this case
                 //assume that the app is manually writing to it.
-                Texture Texture = new Texture(FbAddr, FbWidth, FbHeight);
+                TextureInfo Texture = new TextureInfo(FbAddr, FbWidth, FbHeight);
 
                 byte[] Data = TextureReader.Read(Context.Memory, Texture);
 
-                Renderer.SetFrameBuffer(Data, FbWidth, FbHeight);
+                Renderer.QueueAction(() => Renderer.FrameBuffer.Set(Data, FbWidth, FbHeight));
             }
 
             Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot));
diff --git a/Ryujinx.HLE/PerformanceStatistics.cs b/Ryujinx.HLE/PerformanceStatistics.cs
index bbcdc645..9bc3d6b4 100644
--- a/Ryujinx.HLE/PerformanceStatistics.cs
+++ b/Ryujinx.HLE/PerformanceStatistics.cs
@@ -1,84 +1,119 @@
 using System.Diagnostics;
 using System.Timers;
 
+
 namespace Ryujinx.HLE
 {
     public class PerformanceStatistics
     {
-        Stopwatch ExecutionTime = new Stopwatch();
-        Timer ResetTimer = new Timer(1000);
+        private const double FrameRateWeight = 0.5;
 
-        long CurrentGameFrameEnded;
-        long CurrentSystemFrameEnded;
-        long CurrentSystemFrameStart;
-        long LastGameFrameEnded;
-        long LastSystemFrameEnded;
+        private const int FrameTypeSystem = 0;
+        private const int FrameTypeGame   = 1;
 
-        double AccumulatedGameFrameTime;
-        double AccumulatedSystemFrameTime;
-        double CurrentGameFrameTime;
-        double CurrentSystemFrameTime;
-        double PreviousGameFrameTime;
-        double PreviousSystemFrameTime;
-        public double GameFrameRate   { get; private set; }
-        public double SystemFrameRate { get; private set; }
-        public long SystemFramesRendered;
-        public long GameFramesRendered;
-        public long ElapsedMilliseconds => ExecutionTime.ElapsedMilliseconds;
-        public long ElapsedMicroseconds => (long)
-                (((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000);
-        public long ElapsedNanoseconds => (long)
-                (((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000000);
+        private double[] AverageFrameRate;
+        private double[] AccumulatedFrameTime;
+        private double[] PreviousFrameTime;
+
+        private long[] FramesRendered;
+
+        private object[] FrameLock;
+
+        private double TicksToSeconds;
+
+        private Stopwatch ExecutionTime;
+
+        private Timer ResetTimer;
 
         public PerformanceStatistics()
         {
+            AverageFrameRate     = new double[2];
+            AccumulatedFrameTime = new double[2];
+            PreviousFrameTime    = new double[2];
+
+            FramesRendered = new long[2];
+
+            FrameLock = new object[] { new object(), new object() };
+
+            ExecutionTime = new Stopwatch();
+
             ExecutionTime.Start();
+
+            ResetTimer = new Timer(1000);
+
             ResetTimer.Elapsed += ResetTimerElapsed;
+
             ResetTimer.AutoReset = true;
+
             ResetTimer.Start();
+
+            TicksToSeconds = 1.0 / Stopwatch.Frequency;
         }
 
         private void ResetTimerElapsed(object sender, ElapsedEventArgs e)
         {
-            ResetStatistics();
+            CalculateAverageFrameRate(FrameTypeSystem);
+            CalculateAverageFrameRate(FrameTypeGame);
         }
 
-        public void StartSystemFrame()
+        private void CalculateAverageFrameRate(int FrameType)
         {
-            PreviousSystemFrameTime = CurrentSystemFrameTime;
-            LastSystemFrameEnded = CurrentSystemFrameEnded;
-            CurrentSystemFrameStart = ElapsedMicroseconds;
+            double FrameRate = 0;
+
+            if (AccumulatedFrameTime[FrameType] > 0)
+            {
+                FrameRate = FramesRendered[FrameType] / AccumulatedFrameTime[FrameType];
+            }
+
+            lock (FrameLock[FrameType])
+            {
+                AverageFrameRate[FrameType] = LinearInterpolate(AverageFrameRate[FrameType], FrameRate);
+
+                FramesRendered[FrameType] = 0;
+
+                AccumulatedFrameTime[FrameType] = 0;
+            }
         }
 
-        public void EndSystemFrame()
+        private double LinearInterpolate(double Old, double New)
         {
-            CurrentSystemFrameEnded = ElapsedMicroseconds;
-            CurrentSystemFrameTime = CurrentSystemFrameEnded - CurrentSystemFrameStart;
-            AccumulatedSystemFrameTime += CurrentSystemFrameTime;
-            SystemFramesRendered++;
+            return Old * (1.0 - FrameRateWeight) + New * FrameRateWeight;
+        }
+
+        public void RecordSystemFrameTime()
+        {
+            RecordFrameTime(FrameTypeSystem);
         }
 
         public void RecordGameFrameTime()
         {
-            CurrentGameFrameEnded = ElapsedMicroseconds;
-            CurrentGameFrameTime = CurrentGameFrameEnded - LastGameFrameEnded;
-            PreviousGameFrameTime = CurrentGameFrameTime;
-            LastGameFrameEnded = CurrentGameFrameEnded;
-            AccumulatedGameFrameTime += CurrentGameFrameTime;
-            GameFramesRendered++;
+            RecordFrameTime(FrameTypeGame);
         }
 
-        public void ResetStatistics()
+        private void RecordFrameTime(int FrameType)
         {
-            GameFrameRate = 1000 / ((AccumulatedGameFrameTime / GameFramesRendered) / 1000);
-            GameFrameRate = double.IsNaN(GameFrameRate) ? 0 : GameFrameRate;
-            SystemFrameRate = 1000 / ((AccumulatedSystemFrameTime / SystemFramesRendered) / 1000);
-            SystemFrameRate = double.IsNaN(SystemFrameRate) ? 0 : SystemFrameRate;
+            double CurrentFrameTime = ExecutionTime.ElapsedTicks * TicksToSeconds;
 
-            GameFramesRendered = 0;
-            SystemFramesRendered = 0;
-            AccumulatedGameFrameTime = 0;
-            AccumulatedSystemFrameTime = 0;
+            double ElapsedFrameTime = CurrentFrameTime - PreviousFrameTime[FrameType];
+
+            PreviousFrameTime[FrameType] = CurrentFrameTime;
+
+            lock (FrameLock[FrameType])
+            {
+                AccumulatedFrameTime[FrameType] += ElapsedFrameTime;
+
+                FramesRendered[FrameType]++;
+            }
+        }
+
+        public double GetSystemFrameRate()
+        {
+            return AverageFrameRate[FrameTypeSystem];
+        }
+
+        public double GetGameFrameRate()
+        {
+            return AverageFrameRate[FrameTypeGame];
         }
     }
 }
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index f75b2490..f7b263cd 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -71,6 +71,11 @@ namespace Ryujinx.HLE
             Os.LoadProgram(FileName);
         }
 
+        public void ProcessFrame()
+        {
+            Gpu.Fifo.DispatchCalls();
+        }
+
         internal virtual void OnFinish(EventArgs e)
         {
             Finish?.Invoke(this, e);
diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs
index e2024b5b..ab5eaa0f 100644
--- a/Ryujinx/Ui/GLScreen.cs
+++ b/Ryujinx/Ui/GLScreen.cs
@@ -23,7 +23,7 @@ namespace Ryujinx
         private KeyboardState? Keyboard = null;
 
         private MouseState? Mouse = null;
-        
+
         public GLScreen(Switch Ns, IGalRenderer Renderer)
             : base(1280, 720,
             new GraphicsMode(), "Ryujinx", 0,
@@ -42,7 +42,7 @@ namespace Ryujinx
         {
             VSync = VSyncMode.On;
 
-            Renderer.SetWindowSize(Width, Height);
+            Renderer.FrameBuffer.SetWindowSize(Width, Height);
         }
 
         protected override void OnUpdateFrame(FrameEventArgs e)
@@ -55,7 +55,7 @@ namespace Ryujinx
             int LeftJoystickDY = 0;
             int RightJoystickDX = 0;
             int RightJoystickDY = 0;
-            
+
             if (Keyboard.HasValue)
             {
                 KeyboardState Keyboard = this.Keyboard.Value;
@@ -83,7 +83,7 @@ namespace Ryujinx
                 if (Keyboard[(Key)Config.FakeJoyCon.Right.StickDown])  RightJoystickDY = -short.MaxValue;
                 if (Keyboard[(Key)Config.FakeJoyCon.Right.StickLeft])  RightJoystickDX = -short.MaxValue;
                 if (Keyboard[(Key)Config.FakeJoyCon.Right.StickRight]) RightJoystickDX = short.MaxValue;
-            
+
                 //RightButtons
                 if (Keyboard[(Key)Config.FakeJoyCon.Right.StickButton]) CurrentButton |= HidControllerButtons.KEY_RSTICK;
                 if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonA])     CurrentButton |= HidControllerButtons.KEY_A;
@@ -179,28 +179,31 @@ namespace Ryujinx
                 CurrentButton,
                 LeftJoystick,
                 RightJoystick);
+
+            Ns.ProcessFrame();
+
+            Renderer.RunActions();
         }
 
         protected override void OnRenderFrame(FrameEventArgs e)
         {
-            Ns.Statistics.StartSystemFrame();
+            Renderer.FrameBuffer.Render();
 
-            Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " +
-                $"{Ns.Statistics.GameFrameRate:0})";
+            Ns.Statistics.RecordSystemFrameTime();
 
-            Renderer.RunActions();
-            Renderer.Render();
+            double HostFps = Ns.Statistics.GetSystemFrameRate();
+            double GameFps = Ns.Statistics.GetGameFrameRate();
+
+            Title = $"Ryujinx | Host FPS: {HostFps:0.0} | Game FPS: {GameFps:0.0}";
 
             SwapBuffers();
 
-            Ns.Statistics.EndSystemFrame();
-
             Ns.Os.SignalVsync();
         }
 
         protected override void OnResize(EventArgs e)
         {
-            Renderer.SetWindowSize(Width, Height);
+            Renderer.FrameBuffer.SetWindowSize(Width, Height);
         }
 
         protected override void OnKeyDown(KeyboardKeyEventArgs e)
diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs
index 3bb16faf..b1489769 100644
--- a/Ryujinx/Ui/Program.cs
+++ b/Ryujinx/Ui/Program.cs
@@ -14,7 +14,7 @@ namespace Ryujinx
         {
             Console.Title = "Ryujinx Console";
 
-            IGalRenderer Renderer = new OpenGLRenderer();
+            IGalRenderer Renderer = new OGLRenderer();
 
             IAalOutput AudioOut = new OpenALAudioOut();
 
@@ -67,7 +67,7 @@ namespace Ryujinx
                     Screen.Exit();
                 };
 
-                Screen.Run(60.0);
+                Screen.Run(0.0, 60.0);
             }
 
             Environment.Exit(0);