From 1344a47c774ab2b7491c59e363e04a499fa432f3 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 23 Jul 2018 11:21:05 -0300
Subject: [PATCH] Blit framebuffer without shaders (#229)

* Blit framebuffer without shaders

* De-hardcode native size values

* Adapt to dehardcoded framebuffers and address feedback

* Remove framebuffer rebinding
---
 Ryujinx.Graphics/Gal/IGalFrameBuffer.cs       |   2 +-
 Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl |  13 -
 Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl  |  28 --
 Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs | 284 ++++++------------
 Ryujinx.Graphics/Ryujinx.Graphics.csproj      |   9 -
 Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs    |  53 +---
 6 files changed, 101 insertions(+), 288 deletions(-)
 delete mode 100644 Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl
 delete mode 100644 Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl

diff --git a/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs b/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs
index 1f62bdb379..c0287ef8be 100644
--- a/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs
+++ b/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs
@@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Gal
 
         void Set(byte[] Data, int Width, int Height);
 
-        void SetTransform(float SX, float SY, float Rotate, float TX, float TY);
+        void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom);
 
         void SetWindowSize(int Width, int Height);
 
diff --git a/Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl b/Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl
deleted file mode 100644
index 74e33bd7c0..0000000000
--- a/Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl
+++ /dev/null
@@ -1,13 +0,0 @@
-#version 330 core
-
-precision highp float;
-
-uniform sampler2D tex;
-
-in vec2 tex_coord;
-
-out vec4 out_frag_color;
-
-void main(void) {
-    out_frag_color = texture(tex, tex_coord);
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl b/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl
deleted file mode 100644
index 35d560c09b..0000000000
--- a/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl
+++ /dev/null
@@ -1,28 +0,0 @@
-#version 330 core
-
-precision highp float;
-
-uniform mat2 transform;
-uniform vec2 window_size;
-uniform vec2 offset;
-
-layout(location = 0) in vec2 in_position;
-layout(location = 1) in vec2 in_tex_coord;
-
-out vec2 tex_coord;
-
-// Have a fixed aspect ratio, fit the image within the available space.
-vec2 get_scale_ratio(void) {
-    vec2 native_size = vec2(1280, 720);
-    vec2 ratio = vec2(
-        (window_size.y * native_size.x) / (native_size.y * window_size.x),
-        (window_size.x * native_size.y) / (native_size.x * window_size.y)
-    );
-    return min(ratio, 1);
-}
-
-void main(void) {
-    tex_coord = in_tex_coord;
-    vec2 t_pos = (transform * in_position) + offset;
-    gl_Position = vec4(t_pos * get_scale_ratio(), 0, 1);
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
index cd52762c79..30a3de64a0 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
@@ -32,48 +32,45 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             public int RbHandle  { get; private set; }
             public int TexHandle { get; private set; }
 
-            public FrameBuffer(int Width, int Height)
+            public FrameBuffer(int Width, int Height, bool HasRenderBuffer)
             {
                 this.Width  = Width;
                 this.Height = Height;
 
                 Handle    = GL.GenFramebuffer();
-                RbHandle  = GL.GenRenderbuffer();
                 TexHandle = GL.GenTexture();
+
+                if (HasRenderBuffer)
+                {
+                    RbHandle = GL.GenRenderbuffer();
+                }
             }
         }
 
-        private struct ShaderProgram
-        {
-            public int Handle;
-            public int VpHandle;
-            public int FpHandle;
-        }
+        private const int NativeWidth  = 1280;
+        private const int NativeHeight = 720;
 
         private Dictionary<long, FrameBuffer> Fbs;
 
-        private ShaderProgram Shader;
-
         private Rect Viewport;
         private Rect Window;
 
-        private bool IsInitialized;
+        private FrameBuffer CurrFb;
+        private FrameBuffer CurrReadFb;
 
-        private int RawFbTexWidth;
-        private int RawFbTexHeight;
-        private int RawFbTexHandle;
+        private FrameBuffer RawFb;
 
-        private int CurrFbHandle;
-        private int CurrTexHandle;
+        private bool FlipX;
+        private bool FlipY;
 
-        private int VaoHandle;
-        private int VboHandle;
+        private int CropTop;
+        private int CropLeft;
+        private int CropRight;
+        private int CropBottom;
 
         public OGLFrameBuffer()
         {
             Fbs = new Dictionary<long, FrameBuffer>();
-
-            Shader = new ShaderProgram();
         }
 
         public void Create(long Key, int Width, int Height)
@@ -92,7 +89,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 return;
             }
 
-            Fb = new FrameBuffer(Width, Height);
+            Fb = new FrameBuffer(Width, Height, true);
 
             SetupTexture(Fb.TexHandle, Width, Height);
 
@@ -129,7 +126,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             {
                 GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
 
-                CurrFbHandle = Fb.Handle;
+                CurrFb = Fb;
             }
         }
 
@@ -147,75 +144,50 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
             {
-                CurrTexHandle = Fb.TexHandle;
+                CurrReadFb = Fb;
             }
         }
 
         public void Set(byte[] Data, int Width, int Height)
         {
-            if (RawFbTexHandle == 0)
+            if (RawFb == null)
             {
-                RawFbTexHandle = GL.GenTexture();
+                CreateRawFb(Width, Height);
             }
 
-            if (RawFbTexWidth  != Width ||
-                RawFbTexHeight != Height)
+            if (RawFb.Width  != Width ||
+                RawFb.Height != Height)
             {
-                SetupTexture(RawFbTexHandle, Width, Height);
+                SetupTexture(RawFb.TexHandle, Width, Height);
 
-                RawFbTexWidth  = Width;
-                RawFbTexHeight = Height;
+                RawFb.Width  = Width;
+                RawFb.Height = Height;
             }
 
             GL.ActiveTexture(TextureUnit.Texture0);
 
-            GL.BindTexture(TextureTarget.Texture2D, RawFbTexHandle);
+            GL.BindTexture(TextureTarget.Texture2D, RawFb.TexHandle);
 
             (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
 
             GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, Format, Type, Data);
 
-            CurrTexHandle = RawFbTexHandle;
+            CurrReadFb = RawFb;
         }
 
-        public void SetTransform(float SX, float SY, float Rotate, float TX, float TY)
+        public void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom)
         {
-            EnsureInitialized();
+            this.FlipX = FlipX;
+            this.FlipY = FlipY;
 
-            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);
-
-            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);
+            CropTop    = Top;
+            CropLeft   = Left;
+            CropRight  = Right;
+            CropBottom = Bottom;
         }
 
         public void SetWindowSize(int Width, int Height)
         {
-            int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
-
-            GL.UseProgram(Shader.Handle);
-
-            int WindowSizeUniformLocation = GL.GetUniformLocation(Shader.Handle, "window_size");
-
-            GL.Uniform2(WindowSizeUniformLocation, new Vector2(Width, Height));
-
-            GL.UseProgram(CurrentProgram);
-
             Window = new Rect(0, 0, Width, Height);
         }
 
@@ -237,72 +209,59 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         public void Render()
         {
-            if (CurrTexHandle != 0)
+            if (CurrReadFb != null)
             {
-                EnsureInitialized();
+                int SrcX0, SrcX1, SrcY0, SrcY1;
 
-                //bool CullFaceEnable = GL.IsEnabled(EnableCap.CullFace);
+                if (CropLeft == 0 && CropRight == 0)
+                {
+                    SrcX0 = 0;
+                    SrcX1 = CurrReadFb.Width;
+                }
+                else
+                {
+                    SrcX0 = CropLeft;
+                    SrcX1 = CropRight;
+                }
 
-                bool DepthTestEnable = GL.IsEnabled(EnableCap.DepthTest);
+                if (CropTop == 0 && CropBottom == 0)
+                {
+                    SrcY0 = 0;
+                    SrcY1 = CurrReadFb.Height;
+                }
+                else
+                {
+                    SrcY0 = CropTop;
+                    SrcY1 = CropBottom;
+                }
 
-                bool StencilTestEnable = GL.IsEnabled(EnableCap.StencilTest);
+                float RatioX = MathF.Min(1f, (Window.Height * (float)NativeWidth)  / ((float)NativeHeight * Window.Width));
+                float RatioY = MathF.Min(1f, (Window.Width  * (float)NativeHeight) / ((float)NativeWidth  * Window.Height));
 
-                bool AlphaBlendEnable = GL.IsEnabled(EnableCap.Blend);
+                int DstWidth  = (int)(Window.Width  * RatioX);
+                int DstHeight = (int)(Window.Height * RatioY);
 
-                //GL.Disable(EnableCap.CullFace);
+                int DstPaddingX = (Window.Width  - DstWidth)  / 2;
+                int DstPaddingY = (Window.Height - DstHeight) / 2;
 
-                GL.Disable(EnableCap.DepthTest);
+                int DstX0 = FlipX ? Window.Width - DstPaddingX : DstPaddingX;
+                int DstX1 = FlipX ? DstPaddingX : Window.Width - DstPaddingX;
 
-                GL.Disable(EnableCap.StencilTest);
-
-                GL.Disable(EnableCap.Blend);
-
-                GL.ActiveTexture(TextureUnit.Texture0);
-
-                GL.BindTexture(TextureTarget.Texture2D, CurrTexHandle);
-
-                int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
+                int DstY0 = FlipY ? DstPaddingY : Window.Height - DstPaddingY;
+                int DstY1 = FlipY ? Window.Height - DstPaddingY : DstPaddingY;
 
                 GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
 
-                SetViewport(Window);
+                GL.Viewport(0, 0, Window.Width, Window.Height);
 
-                GL.Clear(
-                    ClearBufferMask.ColorBufferBit |
-                    ClearBufferMask.DepthBufferBit);
+                GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, CurrReadFb.Handle);
 
-                GL.BindVertexArray(VaoHandle);
+                GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
-                GL.UseProgram(Shader.Handle);
-
-                GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
-
-                //Restore the original state.
-                GL.BindFramebuffer(FramebufferTarget.Framebuffer, CurrFbHandle);
-
-                GL.UseProgram(CurrentProgram);
-
-                //if (CullFaceEnable)
-                //{
-                //    GL.Enable(EnableCap.CullFace);
-                //}
-
-                if (DepthTestEnable)
-                {
-                    GL.Enable(EnableCap.DepthTest);
-                }
-
-                if (StencilTestEnable)
-                {
-                    GL.Enable(EnableCap.StencilTest);
-                }
-
-                if (AlphaBlendEnable)
-                {
-                    GL.Enable(EnableCap.Blend);
-                }
-
-                SetViewport(Viewport);
+                GL.BlitFramebuffer(
+                    SrcX0, SrcY0, SrcX1, SrcY1,
+                    DstX0, DstY0, DstX1, DstY1,
+                    ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear);
             }
         }
 
@@ -354,8 +313,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                     Data);
 
                 Callback(Data);
-
-                GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, CurrFbHandle);
             }
         }
 
@@ -390,86 +347,29 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             }
         }
 
-        private void EnsureInitialized()
+        private void CreateRawFb(int Width, int Height)
         {
-            if (!IsInitialized)
+            if (RawFb == null)
             {
-                IsInitialized = true;
+                RawFb = new FrameBuffer(Width, Height, false);
 
-                SetupShader();
-                SetupVertex();
+                SetupTexture(RawFb.TexHandle, Width, Height);
+
+                RawFb.Width = Width;
+                RawFb.Height = Height;
+
+                GL.BindFramebuffer(FramebufferTarget.Framebuffer, RawFb.Handle);
+
+                GL.FramebufferTexture(
+                    FramebufferTarget.Framebuffer,
+                    FramebufferAttachment.ColorAttachment0,
+                    RawFb.TexHandle,
+                    0);
+
+                GL.Viewport(0, 0, Width, Height);
             }
         }
 
-        private void SetupShader()
-        {
-            Shader.VpHandle = GL.CreateShader(ShaderType.VertexShader);
-            Shader.FpHandle = GL.CreateShader(ShaderType.FragmentShader);
-
-            string VpSource = EmbeddedResource.GetString("GlFbVtxShader");
-            string FpSource = EmbeddedResource.GetString("GlFbFragShader");
-
-            GL.ShaderSource(Shader.VpHandle, VpSource);
-            GL.ShaderSource(Shader.FpHandle, FpSource);
-            GL.CompileShader(Shader.VpHandle);
-            GL.CompileShader(Shader.FpHandle);
-
-            Shader.Handle = GL.CreateProgram();
-
-            GL.AttachShader(Shader.Handle, Shader.VpHandle);
-            GL.AttachShader(Shader.Handle, Shader.FpHandle);
-            GL.LinkProgram(Shader.Handle);
-            GL.UseProgram(Shader.Handle);
-
-            Matrix2 Transform = Matrix2.Identity;
-
-            int TexUniformLocation = GL.GetUniformLocation(Shader.Handle, "tex");
-
-            GL.Uniform1(TexUniformLocation, 0);
-
-            int WindowSizeUniformLocation = GL.GetUniformLocation(Shader.Handle, "window_size");
-
-            GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f));
-
-            int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform");
-
-            GL.UniformMatrix2(TransformUniformLocation, false, ref Transform);
-        }
-
-        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);
-        }
-
         private void SetupTexture(int Handle, int Width, int Height)
         {
             GL.BindTexture(TextureTarget.Texture2D, Handle);
diff --git a/Ryujinx.Graphics/Ryujinx.Graphics.csproj b/Ryujinx.Graphics/Ryujinx.Graphics.csproj
index d0fad10765..7d86cbe134 100644
--- a/Ryujinx.Graphics/Ryujinx.Graphics.csproj
+++ b/Ryujinx.Graphics/Ryujinx.Graphics.csproj
@@ -21,13 +21,4 @@
     <ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
   </ItemGroup>
 
-  <ItemGroup>
-    <EmbeddedResource Include="Gal\OpenGL\FbVtxShader.glsl">
-        <LogicalName>GlFbVtxShader</LogicalName>
-    </EmbeddedResource>
-    <EmbeddedResource Include="Gal\OpenGL\FbFragShader.glsl">
-        <LogicalName>GlFbFragShader</LogicalName>
-    </EmbeddedResource>
-  </ItemGroup>
-
 </Project>
diff --git a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs
index 5307127be4..41f2916f4a 100644
--- a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs
+++ b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs
@@ -293,54 +293,17 @@ namespace Ryujinx.HLE.OsHle.Services.Android
 
             Rect Crop = BufferQueue[Slot].Crop;
 
-            int RealWidth  = FbWidth;
-            int RealHeight = FbHeight;
+            bool FlipX = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX);
+            bool FlipY = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY);
 
-            float XSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX) ? -1 : 1;
-            float YSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY) ? -1 : 1;
+            //Rotation is being ignored
 
-            float ScaleX = 1;
-            float ScaleY = 1;
+            int Top    = Crop.Top;
+            int Left   = Crop.Left;
+            int Right  = Crop.Right;
+            int Bottom = Crop.Bottom;
 
-            float OffsX = 0;
-            float OffsY = 0;
-
-            if (Crop.Right  != 0 &&
-                Crop.Bottom != 0)
-            {
-                //Who knows if this is right, I was never good with math...
-                RealWidth  = Crop.Right  - Crop.Left;
-                RealHeight = Crop.Bottom - Crop.Top;
-
-                if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90))
-                {
-                    ScaleY = (float)FbHeight / RealHeight;
-                    ScaleX = (float)FbWidth  / RealWidth;
-
-                    OffsY = ((-(float)Crop.Left / Crop.Right)  + ScaleX - 1) * -XSign;
-                    OffsX = ((-(float)Crop.Top  / Crop.Bottom) + ScaleY - 1) * -YSign;
-                }
-                else
-                {
-                    ScaleX = (float)FbWidth  / RealWidth;
-                    ScaleY = (float)FbHeight / RealHeight;
-
-                    OffsX = ((-(float)Crop.Left / Crop.Right)  + ScaleX - 1) *  XSign;
-                    OffsY = ((-(float)Crop.Top  / Crop.Bottom) + ScaleY - 1) * -YSign;
-                }
-            }
-
-            ScaleX *= XSign;
-            ScaleY *= YSign;
-
-            float Rotate = 0;
-
-            if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90))
-            {
-                Rotate = -MathF.PI * 0.5f;
-            }
-
-            Renderer.QueueAction(() => Renderer.FrameBuffer.SetTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY));
+            Renderer.QueueAction(() => Renderer.FrameBuffer.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom));
 
             //TODO: Support double buffering here aswell, it is broken for GPU
             //frame buffers because it seems to be completely out of sync.