From c8c86a3854fb3c96e65eca6b59d6058270a21a17 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Fri, 13 Apr 2018 15:12:58 -0300
Subject: [PATCH] Fix for current framebuffer issues (#78)

[GPU] Fix some of the current framebuffer issues
---
 .../OsHle/Services/Nv/INvDrvServices.cs       |   2 +
 Ryujinx.Core/OsHle/Services/Nv/NvMap.cs       |   1 +
 Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs   | 125 +++---
 Ryujinx.Graphics/Gal/GalBlendEquation.cs      |  10 +-
 Ryujinx.Graphics/Gal/GalBlendFactor.cs        |  38 +-
 Ryujinx.Graphics/Gal/IGalRenderer.cs          |  29 +-
 Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs    | 250 ------------
 .../Gal/OpenGL/OGLEnumConverter.cs            |  61 ++-
 Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs | 207 ++++++++--
 Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs     |  20 +-
 Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs |  89 +++--
 Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs |   2 +-
 .../Gal/Shader/ShaderDecodeMem.cs             |  21 +-
 Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs  |   4 +-
 Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs  |   5 -
 .../Gal/Shader/ShaderOptExprProp.cs           | 366 ------------------
 Ryujinx.Graphics/Gpu/NsGpu.cs                 |   6 +-
 Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs         | 103 +++--
 Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs      |   8 +
 Ryujinx.Graphics/Gpu/Texture.cs               |  20 +-
 Ryujinx.Graphics/Gpu/TextureReader.cs         |   2 +-
 Ryujinx.Graphics/Gpu/TextureSwizzle.cs        |   2 +-
 Ryujinx/Ui/GLScreen.cs                        |   2 -
 23 files changed, 482 insertions(+), 891 deletions(-)
 delete mode 100644 Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs
 delete mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs

diff --git a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs
index e41f03a430..abda5b862f 100644
--- a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs
+++ b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs
@@ -244,6 +244,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
 
             Context.Memory.WriteInt64(Position + 0x20, Offset);
 
+            Map.GpuAddress = Offset;
+
             return 0;
         }
 
diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs
index 9ef703196a..f3dd1f4718 100644
--- a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs
+++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs
@@ -8,5 +8,6 @@ namespace Ryujinx.Core.OsHle.Services.Nv
         public int  Align;
         public int  Kind;
         public long CpuAddress;
+        public long GpuAddress;
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
index 45b99ead1b..4ab64e2ae0 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
@@ -2,6 +2,7 @@ using ChocolArm64.Memory;
 using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Services.Nv;
 using Ryujinx.Graphics.Gal;
+using Ryujinx.Graphics.Gpu;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -63,13 +64,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
 
         private ManualResetEvent WaitBufferFree;
 
-        private object RenderQueueLock;
-
-        private int RenderQueueCount;
-
-        private bool NvFlingerDisposed;
-
-        private bool KeepRunning;
+        private bool Disposed;
 
         public NvFlinger(IGalRenderer Renderer, KEvent ReleaseEvent)
         {
@@ -92,10 +87,6 @@ namespace Ryujinx.Core.OsHle.Services.Android
             BufferQueue = new BufferEntry[0x40];
 
             WaitBufferFree = new ManualResetEvent(false);
-
-            RenderQueueLock = new object();
-
-            KeepRunning = true;
         }
 
         public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code)
@@ -285,35 +276,24 @@ namespace Ryujinx.Core.OsHle.Services.Android
             return 0;
         }
 
-        private unsafe void SendFrameBuffer(ServiceCtx Context, int Slot)
+        private void SendFrameBuffer(ServiceCtx Context, int Slot)
         {
-            int FbWidth  = BufferQueue[Slot].Data.Width;
-            int FbHeight = BufferQueue[Slot].Data.Height;
-
-            long FbSize = (uint)FbWidth * FbHeight * 4;
+            int FbWidth  = 1280;
+            int FbHeight = 720;
 
             NvMap Map = GetNvMap(Context, Slot);
 
             NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0);
 
-            long Address = Map.CpuAddress;
+            long CpuAddr = Map.CpuAddress;
+            long GpuAddr = Map.GpuAddress;
 
             if (MapFb.HasBufferOffset(Slot))
             {
-                Address += MapFb.GetBufferOffset(Slot);
-            }
+                CpuAddr += MapFb.GetBufferOffset(Slot);
 
-            if ((ulong)(Address + FbSize) > AMemoryMgr.AddrSize)
-            {
-                Logging.Error($"Frame buffer address {Address:x16} is invalid!");
-
-                BufferQueue[Slot].State = BufferState.Free;
-
-                ReleaseEvent.Handle.Set();
-
-                WaitBufferFree.Set();
-
-                return;
+                //TODO: Enable once the frame buffers problems are fixed.
+                //GpuAddr += MapFb.GetBufferOffset(Slot);
             }
 
             BufferQueue[Slot].State = BufferState.Acquired;
@@ -367,41 +347,28 @@ namespace Ryujinx.Core.OsHle.Services.Android
                 Rotate = -MathF.PI * 0.5f;
             }
 
-            lock (RenderQueueLock)
-            {
-                if (NvFlingerDisposed)
-                {
-                    return;
-                }
+            Renderer.SetFrameBufferTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY);
 
-                Interlocked.Increment(ref RenderQueueCount);
+            //TODO: Support double buffering here aswell, it is broken for GPU
+            //frame buffers because it seems to be completely out of sync.
+            if (Context.Ns.Gpu.Engine3d.IsFrameBufferPosition(GpuAddr))
+            {
+                //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(GpuAddr);
+            }
+            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(CpuAddr, FbWidth, FbHeight);
+
+                byte[] Data = TextureReader.Read(Context.Memory, Texture);
+
+                Renderer.SetFrameBuffer(Data, FbWidth, FbHeight);
             }
 
-            byte* Fb = (byte*)Context.Memory.Ram + Address;
-
-            Context.Ns.Gpu.Renderer.QueueAction(delegate()
-            {
-                Context.Ns.Gpu.Renderer.SetFrameBuffer(
-                    Fb,
-                    FbWidth,
-                    FbHeight,
-                    ScaleX,
-                    ScaleY,
-                    OffsX,
-                    OffsY,
-                    Rotate);
-
-                BufferQueue[Slot].State = BufferState.Free;
-
-                Interlocked.Decrement(ref RenderQueueCount);
-
-                ReleaseEvent.Handle.Set();
-
-                lock (WaitBufferFree)
-                {
-                    WaitBufferFree.Set();
-                }
-            });
+            Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot));
         }
 
         private NvMap GetNvMap(ServiceCtx Context, int Slot)
@@ -420,6 +387,18 @@ namespace Ryujinx.Core.OsHle.Services.Android
             return INvDrvServices.NvMaps.GetData<NvMap>(Context.Process, NvMapHandle);
         }
 
+        private void ReleaseBuffer(int Slot)
+        {
+            BufferQueue[Slot].State = BufferState.Free;
+
+            ReleaseEvent.Handle.Set();
+
+            lock (WaitBufferFree)
+            {
+                WaitBufferFree.Set();
+            }
+        }
+
         private int GetFreeSlotBlocking(int Width, int Height)
         {
             int Slot;
@@ -435,7 +414,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
 
                     Logging.Debug("Waiting for a free BufferQueue slot...");
 
-                    if (!KeepRunning)
+                    if (Disposed)
                     {
                         break;
                     }
@@ -445,7 +424,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
 
                 WaitBufferFree.WaitOne();
             }
-            while (KeepRunning);
+            while (!Disposed);
 
             Logging.Debug($"Found free BufferQueue slot {Slot}!");
 
@@ -485,26 +464,12 @@ namespace Ryujinx.Core.OsHle.Services.Android
 
         protected virtual void Dispose(bool Disposing)
         {
-            if (Disposing && !NvFlingerDisposed)
+            if (Disposing && !Disposed)
             {
-                lock (RenderQueueLock)
-                {
-                    NvFlingerDisposed = true;
-                }
-
-                //Ensure that all pending actions was sent before
-                //we can safely assume that the class was disposed.
-                while (RenderQueueCount > 0)
-                {
-                    Thread.Yield();
-                }
-
-                Renderer.ResetFrameBuffer();
+                Disposed = true;
 
                 lock (WaitBufferFree)
                 {
-                    KeepRunning = false;
-
                     WaitBufferFree.Set();
                 }
 
diff --git a/Ryujinx.Graphics/Gal/GalBlendEquation.cs b/Ryujinx.Graphics/Gal/GalBlendEquation.cs
index d9f8e79934..7fd4ba5fa6 100644
--- a/Ryujinx.Graphics/Gal/GalBlendEquation.cs
+++ b/Ryujinx.Graphics/Gal/GalBlendEquation.cs
@@ -2,10 +2,10 @@ namespace Ryujinx.Graphics.Gal
 {
     public enum GalBlendEquation
     {
-        FuncAdd             = 0x8006,
-        Min                 = 0x8007,
-        Max                 = 0x8008,
-        FuncSubtract        = 0x800a,
-        FuncReverseSubtract = 0x800b
+        FuncAdd             = 1,
+        FuncSubtract        = 2,
+        FuncReverseSubtract = 3,
+        Min                 = 4,
+        Max                 = 5
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalBlendFactor.cs b/Ryujinx.Graphics/Gal/GalBlendFactor.cs
index de7d45fd48..7237c4edac 100644
--- a/Ryujinx.Graphics/Gal/GalBlendFactor.cs
+++ b/Ryujinx.Graphics/Gal/GalBlendFactor.cs
@@ -2,24 +2,24 @@ namespace Ryujinx.Graphics.Gal
 {
     public enum GalBlendFactor
     {
-        Zero                  = 0x4000,
-        One                   = 0x4001,
-        SrcColor              = 0x4300,
-        OneMinusSrcColor      = 0x4301,
-        SrcAlpha              = 0x4302,
-        OneMinusSrcAlpha      = 0x4303,
-        DstAlpha              = 0x4304,
-        OneMinusDstAlpha      = 0x4305,
-        DstColor              = 0x4306,
-        OneMinusDstColor      = 0x4307,
-        SrcAlphaSaturate      = 0x4308,
-        ConstantColor         = 0xc001,
-        OneMinusConstantColor = 0xc002,
-        ConstantAlpha         = 0xc003,
-        OneMinusConstantAlpha = 0xc004,
-        Src1Color             = 0xc900,
-        OneMinusSrc1Color     = 0xc901,
-        Src1Alpha             = 0xc902,
-        OneMinusSrc1Alpha     = 0xc903
+        Zero                  = 0x1,
+        One                   = 0x2,
+        SrcColor              = 0x3,
+        OneMinusSrcColor      = 0x4,
+        SrcAlpha              = 0x5,
+        OneMinusSrcAlpha      = 0x6,
+        DstAlpha              = 0x7,
+        OneMinusDstAlpha      = 0x8,
+        DstColor              = 0x9,
+        OneMinusDstColor      = 0xa,
+        SrcAlphaSaturate      = 0xb,
+        Src1Color             = 0x10,
+        OneMinusSrc1Color     = 0x11,
+        Src1Alpha             = 0x12,
+        OneMinusSrc1Alpha     = 0x13,
+        ConstantColor         = 0x61,
+        OneMinusConstantColor = 0x62,
+        ConstantAlpha         = 0x63,
+        OneMinusConstantAlpha = 0x64
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs
index 99534672d1..c30c79fb38 100644
--- a/Ryujinx.Graphics/Gal/IGalRenderer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs
@@ -6,21 +6,12 @@ namespace Ryujinx.Graphics.Gal
     public unsafe interface IGalRenderer
     {
         void QueueAction(Action ActionMthd);
+
         void RunActions();
 
-        void InitializeFrameBuffer();
-        void ResetFrameBuffer();
         void Render();
+
         void SetWindowSize(int Width, int Height);
-        void SetFrameBuffer(
-            byte* Fb,
-            int   Width,
-            int   Height,
-            float ScaleX,
-            float ScaleY,
-            float OffsX,
-            float OffsY,
-            float Rotate);
 
         //Blend
         void SetBlendEnable(bool Enable);
@@ -39,11 +30,17 @@ namespace Ryujinx.Graphics.Gal
             GalBlendFactor   FuncDstAlpha);
 
         //Frame Buffer
-        void SetFb(int FbIndex, int Width, int Height);
+        void CreateFrameBuffer(long Tag, int Width, int Height);
 
-        void BindFrameBuffer(int FbIndex);
+        void BindFrameBuffer(long Tag);
 
-        void DrawFrameBuffer(int FbIndex);
+        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);
 
         //Rasterizer
         void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
@@ -70,8 +67,8 @@ namespace Ryujinx.Graphics.Gal
         void BindProgram();
 
         //Texture
-        void SetTexture(int Index, GalTexture Tex);
+        void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler);
 
-        void SetSampler(int Index, GalTextureSampler Sampler);
+        void BindTexture(int Index);
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs
deleted file mode 100644
index 7e7725d611..0000000000
--- a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs
+++ /dev/null
@@ -1,250 +0,0 @@
-using OpenTK;
-using OpenTK.Graphics.OpenGL;
-using System;
-
-namespace Ryujinx.Graphics.Gal.OpenGL
-{
-    unsafe class FrameBuffer
-    {
-        public int WindowWidth  { get; set; }
-        public int WindowHeight { get; set; }
-
-        private int VtxShaderHandle;
-        private int FragShaderHandle;
-        private int PrgShaderHandle;
-
-        private int TexHandle;
-        private int TexWidth;
-        private int TexHeight;
-
-        private int VaoHandle;
-        private int VboHandle;
-
-        private int[] Pixels;
-
-        private byte* FbPtr;
-
-        private object FbPtrLock;
-
-        public FrameBuffer(int Width, int Height)
-        {
-            if (Width < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Width));
-            }
-
-            if (Height < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Height));
-            }
-
-            FbPtrLock = new object();
-
-            TexWidth  = Width;
-            TexHeight = Height;
-
-            WindowWidth  = Width;
-            WindowHeight = Height;
-
-            SetupShaders();
-            SetupTexture();
-            SetupVertex();
-        }
-
-        private void SetupShaders()
-        {
-            VtxShaderHandle  = GL.CreateShader(ShaderType.VertexShader);
-            FragShaderHandle = GL.CreateShader(ShaderType.FragmentShader);
-
-            string VtxShaderSource  = EmbeddedResource.GetString("GlFbVtxShader");
-            string FragShaderSource = EmbeddedResource.GetString("GlFbFragShader");
-
-            GL.ShaderSource(VtxShaderHandle, VtxShaderSource);
-            GL.ShaderSource(FragShaderHandle, FragShaderSource);
-            GL.CompileShader(VtxShaderHandle);
-            GL.CompileShader(FragShaderHandle);
-
-            PrgShaderHandle = GL.CreateProgram();
-
-            GL.AttachShader(PrgShaderHandle, VtxShaderHandle);
-            GL.AttachShader(PrgShaderHandle, FragShaderHandle);
-            GL.LinkProgram(PrgShaderHandle);
-            GL.UseProgram(PrgShaderHandle);
-
-            int TexUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "tex");
-
-            GL.Uniform1(TexUniformLocation, 0);
-
-            int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size");
-
-            GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f));
-        }
-
-        private void SetupTexture()
-        {
-            Pixels = new int[TexWidth * TexHeight];
-
-            if (TexHandle == 0)
-            {
-                TexHandle = GL.GenTexture();
-            }
-
-            GL.BindTexture(TextureTarget.Texture2D, TexHandle);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
-            GL.TexImage2D(TextureTarget.Texture2D,
-                0,
-                PixelInternalFormat.Rgba,
-                TexWidth,
-                TexHeight,
-                0,
-                PixelFormat.Rgba,
-                PixelType.UnsignedByte,
-                IntPtr.Zero);
-        }
-
-        private void SetupVertex()
-        {
-            VaoHandle = GL.GenVertexArray();
-            VboHandle = GL.GenBuffer();
-
-            float[] Buffer = new float[]
-            {
-                -1,  1,  0,  0,
-                 1,  1,  1,  0,
-                -1, -1,  0,  1,
-                 1, -1,  1,  1
-            };
-
-            IntPtr Length = new IntPtr(Buffer.Length * 4);
-
-            GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
-            GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
-            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
-
-            GL.BindVertexArray(VaoHandle);
-
-            GL.EnableVertexAttribArray(0);
-
-            GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
-
-            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 16, 0);
-
-            GL.EnableVertexAttribArray(1);
-
-            GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
-
-            GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8);
-
-            GL.BindVertexArray(0);
-        }
-
-        public unsafe void Set(byte* Fb, int Width, int Height, Matrix2 Transform, Vector2 Offs)
-        {
-            if (Fb == null)
-            {
-                throw new ArgumentNullException(nameof(Fb));
-            }
-
-            if (Width < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Width));
-            }
-
-            if (Height < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Height));
-            }
-
-            lock (FbPtrLock)
-            {
-                FbPtr = Fb;
-            }
-
-            if (Width  != TexWidth ||
-                Height != TexHeight)
-            {
-                TexWidth  = Width;
-                TexHeight = Height;
-
-                SetupTexture();
-            }
-
-            GL.UseProgram(PrgShaderHandle);
-
-            int TransformUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "transform");
-
-            GL.UniformMatrix2(TransformUniformLocation, false, ref Transform);
-
-            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 Reset()
-        {
-            lock (FbPtrLock)
-            {
-                FbPtr = null;
-            }
-        }
-
-        public void Render()
-        {
-            lock (FbPtrLock)
-            {
-                if (FbPtr == null)
-                {
-                    return;
-                }
-
-                for (int Y = 0; Y < TexHeight; Y++)
-                for (int X = 0; X < TexWidth;  X++)
-                {
-                    Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y)));
-                }
-            }
-
-            GL.BindTexture(TextureTarget.Texture2D, TexHandle);
-            GL.TexSubImage2D(TextureTarget.Texture2D,
-                0,
-                0,
-                0,
-                TexWidth,
-                TexHeight,
-                PixelFormat.Rgba,
-                PixelType.UnsignedByte,
-                Pixels);
-
-            GL.ActiveTexture(TextureUnit.Texture0);
-
-            GL.BindVertexArray(VaoHandle);
-
-            GL.UseProgram(PrgShaderHandle);
-
-            GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
-        }
-
-        private int GetSwizzleOffset(int X, int Y)
-        {
-            int Pos;
-
-            Pos  = (Y & 0x7f) >> 4;
-            Pos += (X >> 4) << 3;
-            Pos += (Y >> 7) * ((TexWidth >> 4) << 3);
-            Pos *= 1024;
-            Pos += ((Y & 0xf) >> 3) << 9;
-            Pos += ((X & 0xf) >> 3) << 8;
-            Pos += ((Y & 0x7) >> 1) << 6;
-            Pos += ((X & 0x7) >> 2) << 5;
-            Pos += ((Y & 0x1) >> 0) << 4;
-            Pos += ((X & 0x3) >> 0) << 2;
-
-            return Pos;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
index 17bf629166..4cc0a03975 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
@@ -127,17 +127,72 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
         {
-            return (BlendEquationMode)BlendEquation;
+            switch (BlendEquation)
+            {
+                case GalBlendEquation.FuncAdd:             return BlendEquationMode.FuncAdd;
+                case GalBlendEquation.FuncSubtract:        return BlendEquationMode.FuncSubtract;
+                case GalBlendEquation.FuncReverseSubtract: return BlendEquationMode.FuncReverseSubtract;
+                case GalBlendEquation.Min:                 return BlendEquationMode.Min;
+                case GalBlendEquation.Max:                 return BlendEquationMode.Max;
+            }
+
+            throw new ArgumentException(nameof(BlendEquation));
         }
 
         public static BlendingFactorSrc GetBlendFactorSrc(GalBlendFactor BlendFactor)
         {
-            return (BlendingFactorSrc)(BlendFactor - 0x4000);
+            switch (BlendFactor)
+            {
+                case GalBlendFactor.Zero:                  return BlendingFactorSrc.Zero;
+                case GalBlendFactor.One:                   return BlendingFactorSrc.One;
+                case GalBlendFactor.SrcColor:              return BlendingFactorSrc.SrcColor;
+                case GalBlendFactor.OneMinusSrcColor:      return BlendingFactorSrc.OneMinusSrcColor;
+                case GalBlendFactor.DstColor:              return BlendingFactorSrc.DstColor;
+                case GalBlendFactor.OneMinusDstColor:      return BlendingFactorSrc.OneMinusDstColor;
+                case GalBlendFactor.SrcAlpha:              return BlendingFactorSrc.SrcAlpha;
+                case GalBlendFactor.OneMinusSrcAlpha:      return BlendingFactorSrc.OneMinusSrcAlpha;
+                case GalBlendFactor.DstAlpha:              return BlendingFactorSrc.DstAlpha;
+                case GalBlendFactor.OneMinusDstAlpha:      return BlendingFactorSrc.OneMinusDstAlpha;
+                case GalBlendFactor.ConstantColor:         return BlendingFactorSrc.ConstantColor;
+                case GalBlendFactor.OneMinusConstantColor: return BlendingFactorSrc.OneMinusConstantColor;
+                case GalBlendFactor.ConstantAlpha:         return BlendingFactorSrc.ConstantAlpha;
+                case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactorSrc.OneMinusConstantAlpha;
+                case GalBlendFactor.SrcAlphaSaturate:      return BlendingFactorSrc.SrcAlphaSaturate;
+                case GalBlendFactor.Src1Color:             return BlendingFactorSrc.Src1Color;
+                case GalBlendFactor.OneMinusSrc1Color:     return BlendingFactorSrc.OneMinusSrc1Color;
+                case GalBlendFactor.Src1Alpha:             return BlendingFactorSrc.Src1Alpha;
+                case GalBlendFactor.OneMinusSrc1Alpha:     return BlendingFactorSrc.OneMinusSrc1Alpha;
+            }
+
+            throw new ArgumentException(nameof(BlendFactor));
         }
 
         public static BlendingFactorDest GetBlendFactorDst(GalBlendFactor BlendFactor)
         {
-            return (BlendingFactorDest)(BlendFactor - 0x4000);
+            switch (BlendFactor)
+            {
+                case GalBlendFactor.Zero:                  return BlendingFactorDest.Zero;
+                case GalBlendFactor.One:                   return BlendingFactorDest.One;
+                case GalBlendFactor.SrcColor:              return BlendingFactorDest.SrcColor;
+                case GalBlendFactor.OneMinusSrcColor:      return BlendingFactorDest.OneMinusSrcColor;
+                case GalBlendFactor.DstColor:              return BlendingFactorDest.DstColor;
+                case GalBlendFactor.OneMinusDstColor:      return BlendingFactorDest.OneMinusDstColor;
+                case GalBlendFactor.SrcAlpha:              return BlendingFactorDest.SrcAlpha;
+                case GalBlendFactor.OneMinusSrcAlpha:      return BlendingFactorDest.OneMinusSrcAlpha;
+                case GalBlendFactor.DstAlpha:              return BlendingFactorDest.DstAlpha;
+                case GalBlendFactor.OneMinusDstAlpha:      return BlendingFactorDest.OneMinusDstAlpha;
+                case GalBlendFactor.ConstantColor:         return BlendingFactorDest.ConstantColor;
+                case GalBlendFactor.OneMinusConstantColor: return BlendingFactorDest.OneMinusConstantColor;
+                case GalBlendFactor.ConstantAlpha:         return BlendingFactorDest.ConstantAlpha;
+                case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactorDest.OneMinusConstantAlpha;
+                case GalBlendFactor.SrcAlphaSaturate:      return BlendingFactorDest.SrcAlphaSaturate;
+                case GalBlendFactor.Src1Color:             return BlendingFactorDest.Src1Color;
+                case GalBlendFactor.OneMinusSrc1Color:     return BlendingFactorDest.OneMinusSrc1Color;
+                case GalBlendFactor.Src1Alpha:             return BlendingFactorDest.Src1Alpha;
+                case GalBlendFactor.OneMinusSrc1Alpha:     return BlendingFactorDest.OneMinusSrc1Alpha;
+            }
+
+            throw new ArgumentException(nameof(BlendFactor));
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
index f9c42ae014..818af3b3a8 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
@@ -1,16 +1,30 @@
 using OpenTK;
 using OpenTK.Graphics.OpenGL;
 using System;
+using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
 {
     class OGLFrameBuffer
     {
-        private struct FrameBuffer
+        private class FrameBuffer
         {
-            public int FbHandle;
-            public int RbHandle;
-            public int TexHandle;
+            public int Width  { get; set; }
+            public int Height { get; set; }
+
+            public int Handle    { get; private set; }
+            public int RbHandle  { get; private set; }
+            public int TexHandle { get; private set; }
+
+            public FrameBuffer(int Width, int Height)
+            {
+                this.Width  = Width;
+                this.Height = Height;
+
+                Handle    = GL.GenFramebuffer();
+                RbHandle  = GL.GenRenderbuffer();
+                TexHandle = GL.GenTexture();
+            }
         }
 
         private struct ShaderProgram
@@ -20,83 +34,175 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             public int FpHandle;
         }
 
-        private FrameBuffer[] Fbs;
+        private Dictionary<long, FrameBuffer> Fbs;
 
         private ShaderProgram Shader;
 
         private bool IsInitialized;
 
+        private int RawFbTexWidth;
+        private int RawFbTexHeight;
+        private int RawFbTexHandle;
+
+        private int CurrFbHandle;
+        private int CurrTexHandle;
+
         private int VaoHandle;
         private int VboHandle;
 
         public OGLFrameBuffer()
         {
-            Fbs = new FrameBuffer[16];
+            Fbs = new Dictionary<long, FrameBuffer>();
 
             Shader = new ShaderProgram();
         }
 
-        public void Set(int Index, int Width, int Height)
+        public void Create(long Tag, int Width, int Height)
         {
-            if (Fbs[Index].FbHandle != 0)
+            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
             {
+                if (Fb.Width  != Width ||
+                    Fb.Height != Height)
+                {
+                    SetupTexture(Fb.TexHandle, Width, Height);
+
+                    Fb.Width  = Width;
+                    Fb.Height = Height;
+                }
+
                 return;
             }
 
-            Fbs[Index].FbHandle  = GL.GenFramebuffer();
-            Fbs[Index].RbHandle  = GL.GenRenderbuffer();
-            Fbs[Index].TexHandle = GL.GenTexture();
+            Fb = new FrameBuffer(Width, Height);
 
-            GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle);
+            SetupTexture(Fb.TexHandle, Width, Height);
 
-            GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle);
+            GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
 
-            GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, 1280, 720);
+            GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fb.RbHandle);
 
-            GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle);
+            GL.RenderbufferStorage(
+                RenderbufferTarget.Renderbuffer,
+                RenderbufferStorage.Depth24Stencil8,
+                Width,
+                Height);
 
-            GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle);
+            GL.FramebufferRenderbuffer(
+                FramebufferTarget.Framebuffer,
+                FramebufferAttachment.DepthStencilAttachment,
+                RenderbufferTarget.Renderbuffer,
+                Fb.RbHandle);
 
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
-
-            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, 1280, 720, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
-
-            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Fbs[Index].TexHandle, 0);
+            GL.FramebufferTexture(
+                FramebufferTarget.Framebuffer,
+                FramebufferAttachment.ColorAttachment0,
+                Fb.TexHandle,
+                0);
 
             GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
+
+            Fbs.Add(Tag, Fb);
         }
 
-        public void Bind(int Index)
+        public void Bind(long Tag)
         {
-            if (Fbs[Index].FbHandle == 0)
+            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
             {
-                return;
-            }
+                GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
 
-            GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle);
+                CurrFbHandle = Fb.Handle;
+            }
         }
 
-        public void Draw(int Index)
+        public void BindTexture(long Tag, int Index)
         {
-            if (Fbs[Index].FbHandle == 0)
+            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
             {
-                return;
+                GL.ActiveTexture(TextureUnit.Texture0 + Index);
+
+                GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle);
+            }
+        }
+
+        public void Set(long Tag)
+        {
+            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
+            {
+                CurrTexHandle = Fb.TexHandle;
+            }
+        }
+
+        public void Set(byte[] Data, int Width, int Height)
+        {
+            if (RawFbTexHandle == 0)
+            {
+                RawFbTexHandle = GL.GenTexture();
             }
 
-            EnsureInitialized();
+            if (RawFbTexWidth  != Width ||
+                RawFbTexHeight != Height)
+            {
+                SetupTexture(RawFbTexHandle, Width, Height);
 
-            GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
-
-            GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle);
+                RawFbTexWidth  = Width;
+                RawFbTexHeight = Height;
+            }
 
             GL.ActiveTexture(TextureUnit.Texture0);
 
-            GL.BindVertexArray(VaoHandle);
+            GL.BindTexture(TextureTarget.Texture2D, RawFbTexHandle);
+
+            (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
+
+            GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, Format, Type, Data);
+
+            CurrTexHandle = RawFbTexHandle;
+        }
+
+        public void SetTransform(Matrix2 Transform, Vector2 Offs)
+        {
+            EnsureInitialized();
+
+            int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
 
             GL.UseProgram(Shader.Handle);
 
-            GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
+            int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform");
+
+            GL.UniformMatrix2(TransformUniformLocation, false, ref Transform);
+
+            int OffsetUniformLocation = GL.GetUniformLocation(Shader.Handle, "offset");
+
+            GL.Uniform2(OffsetUniformLocation, ref Offs);
+
+            GL.UseProgram(CurrentProgram);
+        }
+
+        public void Render()
+        {
+            if (CurrTexHandle != 0)
+            {
+                EnsureInitialized();
+
+                GL.ActiveTexture(TextureUnit.Texture0);
+
+                GL.BindTexture(TextureTarget.Texture2D, CurrTexHandle);
+
+                int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
+
+                GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
+
+                GL.BindVertexArray(VaoHandle);
+
+                GL.UseProgram(Shader.Handle);
+
+                GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
+
+                //Restore the original state.
+                GL.BindFramebuffer(FramebufferTarget.Framebuffer, CurrFbHandle);
+
+                GL.UseProgram(CurrentProgram);
+            }
         }
 
         private void EnsureInitialized()
@@ -130,7 +236,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.LinkProgram(Shader.Handle);
             GL.UseProgram(Shader.Handle);
 
-            Matrix2 Transform = Matrix2.CreateScale(1, -1);
+            Matrix2 Transform = Matrix2.Identity;
 
             int TexUniformLocation = GL.GetUniformLocation(Shader.Handle, "tex");
 
@@ -178,5 +284,32 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8);
         }
+
+        private void SetupTexture(int Handle, int Width, int Height)
+        {
+            GL.BindTexture(TextureTarget.Texture2D, Handle);
+
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+
+            (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
+
+            const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
+
+            const int Level  = 0;
+            const int Border = 0;
+
+            GL.TexImage2D(
+                TextureTarget.Texture2D,
+                Level,
+                InternalFmt,
+                Width,
+                Height,
+                Border,
+                Format,
+                Type,
+                IntPtr.Zero);
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
index b7c8999ee1..9ea25056cd 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
@@ -15,10 +15,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             GL.ActiveTexture(TextureUnit.Texture0 + Index);
 
-            int Handle = EnsureTextureInitialized(Index);
-
-            GL.BindTexture(TextureTarget.Texture2D, Handle);
+            Bind(Index);
 
+            const int Level  = 0; //TODO: Support mipmap textures.
             const int Border = 0;
 
             if (IsCompressedTextureFormat(Texture.Format))
@@ -27,7 +26,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
                 GL.CompressedTexImage2D(
                     TextureTarget.Texture2D,
-                    0,
+                    Level,
                     InternalFmt,
                     Texture.Width,
                     Texture.Height,
@@ -39,27 +38,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             {
                 const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
 
-                (PixelFormat, PixelType) Format = OGLEnumConverter.GetTextureFormat(Texture.Format);
+                (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(Texture.Format);
 
                 GL.TexImage2D(
                     TextureTarget.Texture2D,
-                    0,
+                    Level,
                     InternalFmt,
                     Texture.Width,
                     Texture.Height,
                     Border,
-                    Format.Item1,
-                    Format.Item2,
+                    Format,
+                    Type,
                     Texture.Data);
             }
         }
 
-        public void Set(int Index, GalTextureSampler Sampler)
+        public void Bind(int Index)
         {
             int Handle = EnsureTextureInitialized(Index);
 
             GL.BindTexture(TextureTarget.Texture2D, Handle);
+        }
 
+        public static void Set(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
index 0b7bf92ac4..b3ccae5f87 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
@@ -19,8 +19,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         private ConcurrentQueue<Action> ActionsQueue;
 
-        private FrameBuffer FbRenderer;
-
         public OpenGLRenderer()
         {
             Blend = new OGLBlend();
@@ -36,16 +34,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             ActionsQueue = new ConcurrentQueue<Action>();
         }
 
-        public void InitializeFrameBuffer()
-        {
-            FbRenderer = new FrameBuffer(1280, 720);
-        }
-
-        public void ResetFrameBuffer()
-        {
-            FbRenderer.Reset();
-        }
-
         public void QueueAction(Action ActionMthd)
         {
             ActionsQueue.Enqueue(ActionMthd);
@@ -63,33 +51,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         public void Render()
         {
-            FbRenderer.Render();
+            FrameBuffer.Render();
         }
 
         public void SetWindowSize(int Width, int Height)
         {
-            FbRenderer.WindowWidth  = Width;
-            FbRenderer.WindowHeight = Height;
-        }
-
-        public unsafe void SetFrameBuffer(
-            byte* Fb,
-            int   Width,
-            int   Height,
-            float ScaleX,
-            float ScaleY,
-            float OffsX,
-            float OffsY,
-            float Rotate)
-        {
-            Matrix2 Transform;
-
-            Transform  = Matrix2.CreateScale(ScaleX, ScaleY);
-            Transform *= Matrix2.CreateRotation(Rotate);
-
-            Vector2 Offs = new Vector2(OffsX, OffsY);
-
-            FbRenderer.Set(Fb, Width, Height, Transform, Offs);
+            //TODO
         }
 
         public void SetBlendEnable(bool Enable)
@@ -132,19 +99,46 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             });
         }
 
-        public void SetFb(int FbIndex, int Width, int Height)
+        public void CreateFrameBuffer(long Tag, int Width, int Height)
         {
-            ActionsQueue.Enqueue(() => FrameBuffer.Set(FbIndex, Width, Height));
+            ActionsQueue.Enqueue(() => FrameBuffer.Create(Tag, Width, Height));
         }
 
-        public void BindFrameBuffer(int FbIndex)
+        public void BindFrameBuffer(long Tag)
         {
-            ActionsQueue.Enqueue(() => FrameBuffer.Bind(FbIndex));
+            ActionsQueue.Enqueue(() => FrameBuffer.Bind(Tag));
         }
 
-        public void DrawFrameBuffer(int FbIndex)
+        public void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler)
         {
-            ActionsQueue.Enqueue(() => FrameBuffer.Draw(FbIndex));
+            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 ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
@@ -239,14 +233,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             ActionsQueue.Enqueue(() => Shader.BindProgram());
         }
 
-        public void SetTexture(int Index, GalTexture Tex)
+        public void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler)
         {
-            ActionsQueue.Enqueue(() => Texture.Set(Index, Tex));
+            ActionsQueue.Enqueue(() =>
+            {
+                this.Texture.Set(Index, Texture);
+
+                OGLTexture.Set(Sampler);
+            });
         }
 
-        public void SetSampler(int Index, GalTextureSampler Sampler)
+        public void BindTexture(int Index)
         {
-            ActionsQueue.Enqueue(() => Texture.Set(Index, Sampler));
+            ActionsQueue.Enqueue(() => Texture.Bind(Index));
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
index e155e47539..82dbff9fac 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
@@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public GlslProgram Decompile(int[] Code, GalShaderType ShaderType)
         {
-            ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, ShaderType);
+            ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0);
 
             ShaderIrNode[] Nodes = Block.GetNodes();
 
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
index 6553cfcfd3..33f5823160 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
@@ -74,11 +74,9 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             for (int Ch = 0; Ch < 4; Ch++)
             {
-                ShaderIrOperGpr Dst = (Ch >> 1) != 0
-                    ? GetOperGpr28(OpCode)
-                    : GetOperGpr0 (OpCode);
-
-                Dst.Index += Ch & 1;
+                //Assign it to a temp because the destination registers
+                //may be used as texture coord input aswell.
+                ShaderIrOperGpr Dst = new ShaderIrOperGpr(0x100 + Ch);
 
                 ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
 
@@ -86,6 +84,19 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                 Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
             }
+
+            for (int Ch = 0; Ch < 4; Ch++)
+            {
+                ShaderIrOperGpr Src = new ShaderIrOperGpr(0x100 + Ch);
+
+                ShaderIrOperGpr Dst = (Ch >> 1) != 0
+                    ? GetOperGpr28(OpCode)
+                    : GetOperGpr0 (OpCode);
+
+                Dst.Index += Ch & 1;
+
+                Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode));
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
index 7bebea6250..e44d5b7d75 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
@@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 {
     static class ShaderDecoder
     {
-        public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset, GalShaderType ShaderType)
+        public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset)
         {
             ShaderIrBlock Block = new ShaderIrBlock();
 
@@ -37,8 +37,6 @@ namespace Ryujinx.Graphics.Gal.Shader
                 }
             }
 
-            Block.RunOptimizationPasses(ShaderType);
-
             return Block;
         }
 
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
index 1a96d3be90..c920d9fa50 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
@@ -16,11 +16,6 @@ namespace Ryujinx.Graphics.Gal.Shader
             Nodes.Add(Node);
         }
 
-        public void RunOptimizationPasses(GalShaderType ShaderType)
-        {
-            ShaderOptExprProp.Optimize(Nodes, ShaderType);
-        }
-
         public ShaderIrNode[] GetNodes()
         {
             return Nodes.ToArray();
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs
deleted file mode 100644
index 9ce7cbe312..0000000000
--- a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs
+++ /dev/null
@@ -1,366 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.Gal.Shader
-{
-    static class ShaderOptExprProp
-    {
-        private struct UseSite
-        {
-            public ShaderIrNode Parent { get; private set; }
-            public ShaderIrCond Cond   { get; private set; }
-
-            public int UseIndex { get; private set; }
-
-            public int OperIndex { get; private set; }
-
-            public UseSite(
-                ShaderIrNode Parent,
-                ShaderIrCond Cond,
-                int          UseIndex,
-                int          OperIndex)
-            {
-                this.Parent    = Parent;
-                this.Cond      = Cond;
-                this.UseIndex  = UseIndex;
-                this.OperIndex = OperIndex;
-            }
-        }
-
-        private class RegUse
-        {
-            public ShaderIrAsg Asg { get; private set; }
-
-            public int AsgIndex { get; private set; }
-
-            public int LastSiteIndex { get; private set; }
-
-            public ShaderIrCond Cond { get; private set; }
-
-            private List<UseSite> Sites;
-
-            public RegUse()
-            {
-                Sites = new List<UseSite>();
-            }
-
-            public void AddUseSite(UseSite Site)
-            {
-                if (LastSiteIndex < Site.UseIndex)
-                {
-                    LastSiteIndex = Site.UseIndex;
-                }
-
-                Sites.Add(Site);
-            }
-
-            public bool TryPropagate()
-            {
-                //This happens when a untiliazied register is used,
-                //this usually indicates a decoding error, but may also
-                //be caused by bogus programs (?). In any case, we just
-                //keep the unitialized access and avoid trying to propagate
-                //the expression (since we can't propagate what doesn't yet exist).
-                if (Asg == null)
-                {
-                    return false;
-                }
-
-                if (Cond != null)
-                {
-                    //If the assignment is conditional, we can only propagate
-                    //to the use sites that shares the same condition of the assignment.
-                    foreach (UseSite Site in Sites)
-                    {
-                        if (!IsSameCondition(Cond, Site.Cond))
-                        {
-                            return false;
-                        }
-                    }
-                }
-
-                if (Sites.Count > 0)
-                {
-                    foreach (UseSite Site in Sites)
-                    {
-                        if (Site.Parent is ShaderIrCond Cond)
-                        {
-                            switch (Site.OperIndex)
-                            {
-                                case 0: Cond.Pred  = Asg.Src; break;
-                                case 1: Cond.Child = Asg.Src; break;
-
-                                default: throw new InvalidOperationException();
-                            }
-                        }
-                        else if (Site.Parent is ShaderIrOp Op)
-                        {
-                            switch (Site.OperIndex)
-                            {
-                                case 0: Op.OperandA = Asg.Src; break;
-                                case 1: Op.OperandB = Asg.Src; break;
-                                case 2: Op.OperandC = Asg.Src; break;
-
-                                default: throw new InvalidOperationException();
-                            }
-                        }
-                        else if (Site.Parent is ShaderIrAsg SiteAsg)
-                        {
-                            SiteAsg.Src = Asg.Src;
-                        }
-                        else
-                        {
-                            throw new InvalidOperationException();
-                        }
-                    }
-                }
-
-                return true;
-            }
-
-            public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, ShaderIrCond Cond)
-            {
-                this.Asg      = Asg;
-                this.AsgIndex = AsgIndex;
-                this.Cond     = Cond;
-
-                LastSiteIndex = 0;
-
-                Sites.Clear();
-            }
-        }
-
-        public static void Optimize(List<ShaderIrNode> Nodes, GalShaderType ShaderType)
-        {
-            Dictionary<int, RegUse> Uses = new Dictionary<int, RegUse>();
-
-            RegUse GetUse(int Key)
-            {
-                RegUse Use;
-
-                if (!Uses.TryGetValue(Key, out Use))
-                {
-                    Use = new RegUse();
-
-                    Uses.Add(Key, Use);
-                }
-
-                return Use;
-            }
-
-            int GetGprKey(int GprIndex)
-            {
-                return GprIndex;
-            }
-
-            int GetPredKey(int PredIndex)
-            {
-                return PredIndex | 0x10000000;
-            }
-
-            RegUse GetGprUse(int GprIndex)
-            {
-                return GetUse(GetGprKey(GprIndex));
-            }
-
-            RegUse GetPredUse(int PredIndex)
-            {
-                return GetUse(GetPredKey(PredIndex));
-            }
-
-            void RemoveUse(RegUse Use)
-            {
-                if (!Nodes.Remove((ShaderIrNode)Use.Cond ?? Use.Asg))
-                {
-                    throw new InvalidOperationException();
-                }
-            }
-
-            void FindRegUses(
-                List<(int, UseSite)> UseList,
-                ShaderIrNode         Parent,
-                ShaderIrNode         Node,
-                ShaderIrCond         CondNode,
-                int                  UseIndex,
-                int                  OperIndex = 0)
-            {
-                if (Node is ShaderIrAsg Asg)
-                {
-                    FindRegUses(UseList, Asg, Asg.Src, CondNode, UseIndex);
-                }
-                else if (Node is ShaderIrCond Cond)
-                {
-                    FindRegUses(UseList, Cond, Cond.Pred,  CondNode, UseIndex, 0);
-                    FindRegUses(UseList, Cond, Cond.Child, CondNode, UseIndex, 1);
-                }
-                else if (Node is ShaderIrOp Op)
-                {
-                    FindRegUses(UseList, Op, Op.OperandA, CondNode, UseIndex, 0);
-                    FindRegUses(UseList, Op, Op.OperandB, CondNode, UseIndex, 1);
-                    FindRegUses(UseList, Op, Op.OperandC, CondNode, UseIndex, 2);
-                }
-                else if (Node is ShaderIrOperGpr Gpr && !Gpr.IsConst)
-                {
-                    UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
-                }
-                else if (Node is ShaderIrOperPred Pred)
-                {
-                    UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
-                }
-            }
-
-            void TryAddRegUseSite(ShaderIrNode Node, ShaderIrCond CondNode, int UseIndex)
-            {
-                List<(int, UseSite)> UseList = new List<(int, UseSite)>();
-
-                FindRegUses(UseList, null, Node, CondNode, UseIndex);
-
-                foreach ((int Key, UseSite Site) in UseList)
-                {
-                    GetUse(Key).AddUseSite(Site);
-                }
-            }
-
-            bool TryPropagate(RegUse Use)
-            {
-                //We can only propagate if the registers that the expression depends
-                //on weren't assigned after the original expression assignment
-                //to a register took place. We traverse the expression tree to find
-                //all registers being used, if any of those registers was assigned
-                //after the assignment to be propagated, then we can't propagate.
-                if (Use?.Asg == null)
-                {
-                    return false;
-                }
-
-                List<(int, UseSite)> UseList = new List<(int, UseSite)>();
-
-                if (Use.Cond != null)
-                {
-                    FindRegUses(UseList, null, Use.Cond, null, 0);
-                }
-                else
-                {
-                    FindRegUses(UseList, Use.Asg, Use.Asg.Src, null, 0);
-                }
-
-                foreach ((int Key, UseSite Site) in UseList)
-                {
-                    //TODO: Build an assignment list inside RegUse,
-                    //and check if there is an assignment inside the
-                    //range of Use.AsgIndex and Use.LastSiteIndex,
-                    //and if that's the case, then we should return false.
-                    //The current method is too conservative.
-                    if (GetUse(Key).AsgIndex >= Use.AsgIndex)
-                    {
-                        return false;
-                    }
-                }
-
-                return Use.TryPropagate();
-            }
-
-            for (int Index = 0, IterCount = 0; Index < Nodes.Count; Index++, IterCount++)
-            {
-                ShaderIrNode Node = Nodes[Index];
-
-                ShaderIrCond CondNode = null;
-
-                if (Node is ShaderIrCond)
-                {
-                    CondNode = (ShaderIrCond)Node;
-                }
-
-                TryAddRegUseSite(Node, CondNode, IterCount);;
-
-                while (Node is ShaderIrCond Cond)
-                {
-                    Node = Cond.Child;
-                }
-
-                if (!(Node is ShaderIrAsg Asg))
-                {
-                    continue;
-                }
-
-                RegUse Use = null;
-
-                if (Asg.Dst is ShaderIrOperGpr Gpr && !Gpr.IsConst)
-                {
-                    Use = GetGprUse(Gpr.Index);
-                }
-                else if (Asg.Dst is ShaderIrOperPred Pred)
-                {
-                    Use = GetPredUse(Pred.Index);
-                }
-
-                bool CanRemoveAsg = CondNode == null;
-
-                CanRemoveAsg |= IsSameCondition(CondNode, Use?.Cond);
-
-                if (CanRemoveAsg && TryPropagate(Use))
-                {
-                    RemoveUse(Use);
-
-                    //Note: Only decrement if the removal was successful.
-                    //RemoveUse throws when this is not the case so we should be good.
-                    Index--;
-                }
-
-                //All nodes inside conditional nodes can't be propagated,
-                //as we don't even know if they will be executed to begin with.
-                Use?.SetNewAsg(Asg, IterCount, CondNode);
-            }
-
-            foreach (RegUse Use in Uses.Values)
-            {
-                //Gprs 0-3 are the color output on fragment shaders,
-                //so we can't remove the last assignments to those registers.
-                if (ShaderType == GalShaderType.Fragment)
-                {
-                    if (Use.Asg?.Dst is ShaderIrOperGpr Gpr && Gpr.Index < 4)
-                    {
-                        continue;
-                    }
-                }
-
-                if (TryPropagate(Use))
-                {
-                    RemoveUse(Use);
-                }
-            }
-        }
-
-        private static bool IsSameCondition(ShaderIrCond CondA, ShaderIrCond CondB)
-        {
-            if (CondA == null || CondB == null)
-            {
-                return CondA == CondB;
-            }
-
-            if (CondA.Not != CondB.Not)
-            {
-                return false;
-            }
-
-            if (CondA.Pred is ShaderIrOperPred PredA)
-            {
-                if (!(CondB.Pred is ShaderIrOperPred PredB))
-                {
-                    return false;
-                }
-
-                if (PredA.Index != PredB.Index)
-                {
-                    return false;
-                }
-            }
-            else if (CondA.Pred != CondB.Pred)
-            {
-                return false;
-            }
-
-            return true;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NsGpu.cs b/Ryujinx.Graphics/Gpu/NsGpu.cs
index 5738050255..9a2e901288 100644
--- a/Ryujinx.Graphics/Gpu/NsGpu.cs
+++ b/Ryujinx.Graphics/Gpu/NsGpu.cs
@@ -9,9 +9,9 @@ namespace Ryujinx.Graphics.Gpu
 
         internal NsGpuMemoryMgr MemoryMgr { get; private set; }
 
-        public NvGpuFifo Fifo;
+        public NvGpuFifo Fifo { get; private set; }
 
-        internal NvGpuEngine3d Engine3d;
+        public NvGpuEngine3d Engine3d { get; private set; }
 
         private Thread FifoProcessing;
 
@@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Gpu
 
             KeepRunning = true;
 
-            FifoProcessing = new Thread(ProcessFifo);            
+            FifoProcessing = new Thread(ProcessFifo);
 
             FifoProcessing.Start();
         }
diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
index f4486f46cf..1142e4aa69 100644
--- a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
+++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
@@ -5,7 +5,7 @@ using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Gpu
 {
-    class NvGpuEngine3d : INvGpuEngine
+    public class NvGpuEngine3d : INvGpuEngine
     {
         public int[] Registers { get; private set; }
 
@@ -20,9 +20,9 @@ namespace Ryujinx.Graphics.Gpu
             public int  Size;
         }
 
-        private ConstBuffer[] Cbs;
+        private ConstBuffer[] ConstBuffers;
 
-        private bool HasDataToRender;
+        private HashSet<long> FrameBuffers;
 
         public NvGpuEngine3d(NsGpu Gpu)
         {
@@ -48,7 +48,9 @@ namespace Ryujinx.Graphics.Gpu
             AddMethod(0x8e4, 16, 1, CbData);
             AddMethod(0x904,  1, 1, CbBind);
 
-            Cbs = new ConstBuffer[18];
+            ConstBuffers = new ConstBuffer[18];
+
+            FrameBuffers = new HashSet<long>();
         }
 
         public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry)
@@ -76,19 +78,10 @@ namespace Ryujinx.Graphics.Gpu
             UploadTextures(Memory, Tags);
             UploadUniforms(Memory);
             UploadVertexArrays(Memory);
-
-            HasDataToRender = true;
         }
 
         private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry)
         {
-            if (HasDataToRender)
-            {
-                HasDataToRender = false;
-
-                Gpu.Renderer.DrawFrameBuffer(0);
-            }
-
             int Arg0 = PBEntry.Arguments[0];
 
             int FbIndex = (Arg0 >> 6) & 0xf;
@@ -99,16 +92,23 @@ namespace Ryujinx.Graphics.Gpu
 
             SetFrameBuffer(0);
 
-            Gpu.Renderer.ClearBuffers(Layer, Flags);
+            //TODO: Enable this once the frame buffer problems are fixed.
+            //Gpu.Renderer.ClearBuffers(Layer, Flags);
         }
 
         private void SetFrameBuffer(int FbIndex)
         {
-            int Width   = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth  + FbIndex * 0x10);
-            int Height  = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
+            long Address = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10);
 
-            Gpu.Renderer.SetFb(FbIndex, Width, Height);
-            Gpu.Renderer.BindFrameBuffer(FbIndex);
+            FrameBuffers.Add(Address);
+
+            int Width  = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth  + FbIndex * 0x10);
+            int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
+
+            //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(Address, 1280, 720);
+            Gpu.Renderer.BindFrameBuffer(Address);
         }
 
         private long[] UploadShaders(AMemory Memory)
@@ -167,23 +167,24 @@ namespace Ryujinx.Graphics.Gpu
 
         private void SetAlphaBlending()
         {
-            bool BlendEnableMaster = (ReadRegister(NvGpuEngine3dReg.BlendEnableMaster) & 1) != 0;
+            //TODO: Support independent blend properly.
+            bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendEnable) & 1) != 0;
 
-            Gpu.Renderer.SetBlendEnable(BlendEnableMaster);
+            Gpu.Renderer.SetBlendEnable(Enable);
 
-            bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.BlendSeparateAlpha) & 1) != 0;
+            bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0;
 
-            GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.BlendEquationRgb);
+            GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb);
 
-            GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncSrcRgb);
-            GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncDstRgb);
+            GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb);
+            GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb);
 
             if (BlendSeparateAlpha)
             {
-                GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.BlendEquationAlpha);
+                GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationAlpha);
 
-                GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncSrcAlpha);
-                GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncDstAlpha);
+                GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha);
+                GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha);
 
                 Gpu.Renderer.SetBlendSeparate(
                     EquationRgb,
@@ -205,11 +206,13 @@ namespace Ryujinx.Graphics.Gpu
 
             int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
 
-            long BasePosition = Cbs[TextureCbIndex].Position;
+            long BasePosition = ConstBuffers[TextureCbIndex].Position;
 
-            long Size = (uint)Cbs[TextureCbIndex].Size;
+            long Size = (uint)ConstBuffers[TextureCbIndex].Size;
 
-            int TexIndex = 0;
+            //Note: On the emulator renderer, Texture Unit 0 is
+            //reserved for drawing the frame buffer.
+            int TexIndex = 1;
 
             for (int Index = 0; Index < Tags.Length; Index++)
             {
@@ -241,8 +244,25 @@ namespace Ryujinx.Graphics.Gpu
             TicPosition += TicIndex * 0x20;
             TscPosition += TscIndex * 0x20;
 
-            Gpu.Renderer.SetTexture(TexIndex, TextureFactory.MakeTexture(Gpu, Memory, TicPosition));
-            Gpu.Renderer.SetSampler(TexIndex, TextureFactory.MakeSampler(Gpu, Memory, TscPosition));
+            GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Memory, TscPosition);
+
+            long TextureAddress = Memory.ReadInt64(TicPosition + 4) & 0xffffffffffff;
+
+            if (FrameBuffers.Contains(TextureAddress))
+            {
+                //This texture is a frame buffer texture,
+                //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);
+            }
+            else
+            {
+                GalTexture Texture = TextureFactory.MakeTexture(Gpu, Memory, TicPosition);
+
+                Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler);
+                Gpu.Renderer.BindTexture(TexIndex);
+            }
         }
 
         private void UploadUniforms(AMemory Memory)
@@ -262,9 +282,9 @@ namespace Ryujinx.Graphics.Gpu
                     continue;
                 }
 
-                for (int Cbuf = 0; Cbuf < Cbs.Length; Cbuf++)
+                for (int Cbuf = 0; Cbuf < ConstBuffers.Length; Cbuf++)
                 {
-                    ConstBuffer Cb = Cbs[Cbuf];
+                    ConstBuffer Cb = ConstBuffers[Cbuf];
 
                     if (Cb.Enabled)
                     {
@@ -379,7 +399,7 @@ namespace Ryujinx.Graphics.Gpu
 
                 if (Mode == 0)
                 {
-                    //Write.
+                    //Write mode.
                     Memory.WriteInt32(Position, Seq);
                 }
             }
@@ -414,16 +434,16 @@ namespace Ryujinx.Graphics.Gpu
 
             if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position))
             {
-                Cbs[Index].Position = Position;
-                Cbs[Index].Enabled  = Enabled;
+                ConstBuffers[Index].Position = Position;
+                ConstBuffers[Index].Enabled  = Enabled;
 
-                Cbs[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize);
+                ConstBuffers[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize);
             }
         }
 
         private int ReadCb(AMemory Memory, int Cbuf, int Offset)
         {
-            long Position = Cbs[Cbuf].Position;
+            long Position = ConstBuffers[Cbuf].Position;
 
             int Value = Memory.ReadInt32(Position + Offset);
 
@@ -465,5 +485,10 @@ namespace Ryujinx.Graphics.Gpu
         {
             Registers[(int)Reg] = Value;
         }
+
+        public bool IsFrameBufferPosition(long Position)
+        {
+            return FrameBuffers.Contains(Position);
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs
index 4bba9abe0d..cb0b9d983d 100644
--- a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs
+++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs
@@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Gpu
         FrameBufferNHeight   = 0x203,
         FrameBufferNFormat   = 0x204,
         VertexAttribNFormat  = 0x458,
+        IBlendEnable         = 0x4b9,
         BlendSeparateAlpha   = 0x4cf,
         BlendEquationRgb     = 0x4d0,
         BlendFuncSrcRgb      = 0x4d1,
@@ -31,6 +32,13 @@ namespace Ryujinx.Graphics.Gpu
         VertexArrayNControl  = 0x700,
         VertexArrayNAddress  = 0x701,
         VertexArrayNDivisor  = 0x703,
+        IBlendNSeparateAlpha = 0x780,
+        IBlendNEquationRgb   = 0x781,
+        IBlendNFuncSrcRgb    = 0x782,
+        IBlendNFuncDstRgb    = 0x783,
+        IBlendNEquationAlpha = 0x784,
+        IBlendNFuncSrcAlpha  = 0x785,
+        IBlendNFuncDstAlpha  = 0x786,
         VertexArrayNEndAddr  = 0x7c0,
         ShaderNControl       = 0x800,
         ShaderNOffset        = 0x801,
diff --git a/Ryujinx.Graphics/Gpu/Texture.cs b/Ryujinx.Graphics/Gpu/Texture.cs
index 831c664d4f..cbfa683dc9 100644
--- a/Ryujinx.Graphics/Gpu/Texture.cs
+++ b/Ryujinx.Graphics/Gpu/Texture.cs
@@ -2,7 +2,7 @@ using Ryujinx.Graphics.Gal;
 
 namespace Ryujinx.Graphics.Gpu
 {
-    struct Texture
+    public struct Texture
     {
         public long Position { get; private set; }
 
@@ -16,6 +16,24 @@ namespace Ryujinx.Graphics.Gpu
 
         public GalTextureFormat Format { get; private set; }
 
+        public Texture(
+            long Position,
+            int  Width,
+            int  Height)
+        {
+            this.Position = Position;
+            this.Width    = Width;
+            this.Height   = Height;
+
+            Pitch = 0;
+
+            BlockHeight = 16;
+
+            Swizzle = TextureSwizzle.BlockLinear;
+
+            Format = GalTextureFormat.A8B8G8R8;
+        }
+
         public Texture(
             long             Position,
             int              Width,
diff --git a/Ryujinx.Graphics/Gpu/TextureReader.cs b/Ryujinx.Graphics/Gpu/TextureReader.cs
index b3b016ed9a..4c3b4fb17e 100644
--- a/Ryujinx.Graphics/Gpu/TextureReader.cs
+++ b/Ryujinx.Graphics/Gpu/TextureReader.cs
@@ -4,7 +4,7 @@ using System;
 
 namespace Ryujinx.Graphics.Gpu
 {
-    static class TextureReader
+    public static class TextureReader
     {
         public static byte[] Read(AMemory Memory, Texture Texture)
         {
diff --git a/Ryujinx.Graphics/Gpu/TextureSwizzle.cs b/Ryujinx.Graphics/Gpu/TextureSwizzle.cs
index 2142e2c20a..7d99279cd4 100644
--- a/Ryujinx.Graphics/Gpu/TextureSwizzle.cs
+++ b/Ryujinx.Graphics/Gpu/TextureSwizzle.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.Gpu
 {
-    enum TextureSwizzle
+    public enum TextureSwizzle
     {
         _1dBuffer           = 0,
         PitchColorKey       = 1,
diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs
index d6a33edbfd..49338247e2 100644
--- a/Ryujinx/Ui/GLScreen.cs
+++ b/Ryujinx/Ui/GLScreen.cs
@@ -38,8 +38,6 @@ namespace Ryujinx
         protected override void OnLoad(EventArgs e)
         {
             VSync = VSyncMode.On;
-
-            Renderer.InitializeFrameBuffer();
         }
 
         protected override void OnUpdateFrame(FrameEventArgs e)