From aa1cd849cfe254dc7c8c9a0783a546a4b9b3c0ab Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Fri, 12 Oct 2018 22:37:01 -0300
Subject: [PATCH] Quads, QuadStrip, const attributes and half-float attributes
 support (#447)

* Quads, QuadStrip and const attributes support

* Add support for half float attributes and fix texture pitch alignment

* Throw when an unsupported float type is used as const attribute aswell
---
 Ryujinx.Graphics/Gal/GalPipelineState.cs      |  12 --
 Ryujinx.Graphics/Gal/GalVertexAttrib.cs       |  11 +-
 Ryujinx.Graphics/Gal/GalVertexBinding.cs      |  14 ++
 Ryujinx.Graphics/Gal/IGalRasterizer.cs        |   1 +
 .../Gal/OpenGL/OGLEnumConverter.cs            |  28 ++-
 Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs    | 193 ++++++++++++++++--
 Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs  |  33 ++-
 .../Gal/OpenGL/OGLRenderTarget.cs             |   2 +-
 Ryujinx.Graphics/NvGpuEngine3d.cs             |  83 +++++++-
 Ryujinx.Graphics/QuadHelper.cs                |  81 ++++++++
 Ryujinx.Graphics/Texture/ImageUtils.cs        |   6 +-
 Ryujinx.Graphics/Texture/TextureFactory.cs    |   9 +-
 12 files changed, 420 insertions(+), 53 deletions(-)
 create mode 100644 Ryujinx.Graphics/Gal/GalVertexBinding.cs
 create mode 100644 Ryujinx.Graphics/QuadHelper.cs

diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs
index 7b0f17d17..8837eb8c4 100644
--- a/Ryujinx.Graphics/Gal/GalPipelineState.cs
+++ b/Ryujinx.Graphics/Gal/GalPipelineState.cs
@@ -1,17 +1,5 @@
 namespace Ryujinx.Graphics.Gal
 {
-    public struct GalVertexBinding
-    {
-        //VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3
-
-        public bool Enabled;
-        public int Stride;
-        public long VboKey;
-        public bool Instanced;
-        public int Divisor;
-        public GalVertexAttrib[] Attribs;
-    }
-
     public class GalPipelineState
     {
         public const int Stages = 5;
diff --git a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
index dd0400602..fa9a391f7 100644
--- a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
+++ b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
@@ -1,10 +1,13 @@
+using System;
+
 namespace Ryujinx.Graphics.Gal
 {
     public struct GalVertexAttrib
     {
-        public int  Index   { get; private set; }
-        public bool IsConst { get; private set; }
-        public int  Offset  { get; private set; }
+        public int    Index   { get; private set; }
+        public bool   IsConst { get; private set; }
+        public int    Offset  { get; private set; }
+        public IntPtr Pointer { get; private set; }
 
         public GalVertexAttribSize Size { get; private set; }
         public GalVertexAttribType Type { get; private set; }
@@ -15,12 +18,14 @@ namespace Ryujinx.Graphics.Gal
             int                 Index,
             bool                IsConst,
             int                 Offset,
+            IntPtr              Pointer,
             GalVertexAttribSize Size,
             GalVertexAttribType Type,
             bool                IsBgra)
         {
             this.Index   = Index;
             this.IsConst = IsConst;
+            this.Pointer = Pointer;
             this.Offset  = Offset;
             this.Size    = Size;
             this.Type    = Type;
diff --git a/Ryujinx.Graphics/Gal/GalVertexBinding.cs b/Ryujinx.Graphics/Gal/GalVertexBinding.cs
new file mode 100644
index 000000000..0c3c845d4
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalVertexBinding.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public struct GalVertexBinding
+    {
+        //VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3
+
+        public bool Enabled;
+        public int Stride;
+        public long VboKey;
+        public bool Instanced;
+        public int Divisor;
+        public GalVertexAttrib[] Attribs;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs
index 1ee630e23..1572efa8f 100644
--- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs
@@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Gal
         void CreateVbo(long Key, int DataSize, IntPtr HostAddress);
 
         void CreateIbo(long Key, int DataSize, IntPtr HostAddress);
+        void CreateIbo(long Key, int DataSize, byte[] Buffer);
 
         void SetIndexArray(int Size, GalIndexFormat Format);
 
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
index 7f9e9fbe3..388e06b2d 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalFrontFace.CCW: return FrontFaceDirection.Ccw;
             }
 
-            throw new ArgumentException(nameof(FrontFace));
+            throw new ArgumentException(nameof(FrontFace) + " \"" + FrontFace + "\" is not valid!");
         }
 
         public static CullFaceMode GetCullFace(GalCullFace CullFace)
@@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalCullFace.FrontAndBack: return CullFaceMode.FrontAndBack;
             }
 
-            throw new ArgumentException(nameof(CullFace));
+            throw new ArgumentException(nameof(CullFace) + " \"" + CullFace + "\" is not valid!");
         }
 
         public static StencilOp GetStencilOp(GalStencilOp Op)
@@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalStencilOp.DecrWrap: return StencilOp.DecrWrap;
             }
 
-            throw new ArgumentException(nameof(Op));
+            throw new ArgumentException(nameof(Op) + " \"" + Op + "\" is not valid!");
         }
 
         public static DepthFunction GetDepthFunc(GalComparisonOp Func)
@@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalComparisonOp.Always:   return DepthFunction.Always;
             }
 
-            throw new ArgumentException(nameof(Func));
+            throw new ArgumentException(nameof(Func) + " \"" + Func + "\" is not valid!");
         }
 
         public static StencilFunction GetStencilFunc(GalComparisonOp Func)
@@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalIndexFormat.Int32: return DrawElementsType.UnsignedInt;
             }
 
-            throw new ArgumentException(nameof(Format));
+            throw new ArgumentException(nameof(Format) + " \"" + Format + "\" is not valid!");
         }
 
         public static PrimitiveType GetPrimitiveType(GalPrimitiveType Type)
@@ -98,8 +98,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalPrimitiveType.Triangles:              return PrimitiveType.Triangles;
                 case GalPrimitiveType.TriangleStrip:          return PrimitiveType.TriangleStrip;
                 case GalPrimitiveType.TriangleFan:            return PrimitiveType.TriangleFan;
-                case GalPrimitiveType.Quads:                  return PrimitiveType.Quads;
-                case GalPrimitiveType.QuadStrip:              return PrimitiveType.QuadStrip;
                 case GalPrimitiveType.Polygon:                return PrimitiveType.Polygon;
                 case GalPrimitiveType.LinesAdjacency:         return PrimitiveType.LinesAdjacency;
                 case GalPrimitiveType.LineStripAdjacency:     return PrimitiveType.LineStripAdjacency;
@@ -108,7 +106,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalPrimitiveType.Patches:                return PrimitiveType.Patches;
             }
 
-            throw new ArgumentException(nameof(Type));
+            throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!");
         }
 
         public static ShaderType GetShaderType(GalShaderType Type)
@@ -122,7 +120,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalShaderType.Fragment:       return ShaderType.FragmentShader;
             }
 
-            throw new ArgumentException(nameof(Type));
+            throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!");
         }
 
         public static (PixelInternalFormat, PixelFormat, PixelType) GetImageFormat(GalImageFormat Format)
@@ -211,7 +209,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalTextureSource.OneFloat: return All.One;
             }
 
-            throw new ArgumentException(nameof(Source));
+            throw new ArgumentException(nameof(Source) + " \"" + Source + "\" is not valid!");
         }
 
         public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap)
@@ -245,7 +243,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 }
             }
 
-            throw new ArgumentException(nameof(Wrap));
+            throw new ArgumentException(nameof(Wrap) + " \"" + Wrap + "\" is not valid!");
         }
 
         public static TextureMinFilter GetTextureMinFilter(
@@ -259,7 +257,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalTextureFilter.Linear:  return TextureMinFilter.Linear;
             }
 
-            throw new ArgumentException(nameof(MinFilter));
+            throw new ArgumentException(nameof(MinFilter) + " \"" + MinFilter + "\" is not valid!");
         }
 
         public static TextureMagFilter GetTextureMagFilter(GalTextureFilter Filter)
@@ -270,7 +268,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalTextureFilter.Linear:  return TextureMagFilter.Linear;
             }
 
-            throw new ArgumentException(nameof(Filter));
+            throw new ArgumentException(nameof(Filter) + " \"" + Filter + "\" is not valid!");
         }
 
         public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
@@ -284,7 +282,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalBlendEquation.Max:                 return BlendEquationMode.Max;
             }
 
-            throw new ArgumentException(nameof(BlendEquation));
+            throw new ArgumentException(nameof(BlendEquation) + " \"" + BlendEquation + "\" is not valid!");
         }
 
         public static BlendingFactor GetBlendFactor(GalBlendFactor BlendFactor)
@@ -315,7 +313,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                     return BlendingFactor.ConstantColor;
             }
 
-            throw new ArgumentException(nameof(BlendFactor));
+            throw new ArgumentException(nameof(BlendFactor) + " \"" + BlendFactor + "\" is not valid!");
         }
     }
 }
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
index 20e92ff2c..96da17f8e 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
@@ -25,6 +25,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             { GalVertexAttribSize._11_11_10,    3 }
         };
 
+        private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> FloatAttribTypes =
+                   new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
+        {
+            { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Float     },
+            { GalVertexAttribSize._32_32_32,    VertexAttribPointerType.Float     },
+            { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.HalfFloat },
+            { GalVertexAttribSize._32_32,       VertexAttribPointerType.Float     },
+            { GalVertexAttribSize._16_16_16,    VertexAttribPointerType.HalfFloat },
+            { GalVertexAttribSize._16_16,       VertexAttribPointerType.HalfFloat },
+            { GalVertexAttribSize._32,          VertexAttribPointerType.Float     },
+            { GalVertexAttribSize._16,          VertexAttribPointerType.HalfFloat }
+        };
+
         private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> SignedAttribTypes =
                    new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
         {
@@ -356,8 +369,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                         continue;
                     }
 
-                    GL.EnableVertexAttribArray(Attrib.Index);
-
                     GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
 
                     bool Unsigned =
@@ -373,35 +384,50 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
                     if (Attrib.Type == GalVertexAttribType.Float)
                     {
-                        Type = VertexAttribPointerType.Float;
+                        Type = GetType(FloatAttribTypes, Attrib);
                     }
                     else
                     {
                         if (Unsigned)
                         {
-                            Type = UnsignedAttribTypes[Attrib.Size];
+                            Type = GetType(UnsignedAttribTypes, Attrib);
                         }
                         else
                         {
-                            Type = SignedAttribTypes[Attrib.Size];
+                            Type = GetType(SignedAttribTypes, Attrib);
                         }
                     }
 
-                    int Size = AttribElements[Attrib.Size];
+                    if (!AttribElements.TryGetValue(Attrib.Size, out int Size))
+                    {
+                        throw new InvalidOperationException("Invalid attribute size \"" + Attrib.Size + "\"!");
+                    }
+
                     int Offset = Attrib.Offset;
 
-                    if (Attrib.Type == GalVertexAttribType.Sint ||
-                        Attrib.Type == GalVertexAttribType.Uint)
+                    if (Binding.Stride != 0)
                     {
-                        IntPtr Pointer = new IntPtr(Offset);
+                        GL.EnableVertexAttribArray(Attrib.Index);
 
-                        VertexAttribIntegerType IType = (VertexAttribIntegerType)Type;
+                        if (Attrib.Type == GalVertexAttribType.Sint ||
+                            Attrib.Type == GalVertexAttribType.Uint)
+                        {
+                            IntPtr Pointer = new IntPtr(Offset);
 
-                        GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer);
+                            VertexAttribIntegerType IType = (VertexAttribIntegerType)Type;
+
+                            GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer);
+                        }
+                        else
+                        {
+                            GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset);
+                        }
                     }
                     else
                     {
-                        GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset);
+                        GL.DisableVertexAttribArray(Attrib.Index);
+
+                        SetConstAttrib(Attrib);
                     }
 
                     if (Binding.Instanced && Binding.Divisor != 0)
@@ -416,6 +442,149 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             }
         }
 
+        private static VertexAttribPointerType GetType(Dictionary<GalVertexAttribSize, VertexAttribPointerType> Dict, GalVertexAttrib Attrib)
+        {
+            if (!Dict.TryGetValue(Attrib.Size, out VertexAttribPointerType Type))
+            {
+                throw new NotImplementedException("Unsupported size \"" + Attrib.Size + "\" on type \"" + Attrib.Type + "\"!");
+            }
+
+            return Type;
+        }
+
+        private unsafe static void SetConstAttrib(GalVertexAttrib Attrib)
+        {
+            void Unsupported()
+            {
+                throw new NotImplementedException("Constant attribute " + Attrib.Size + " not implemented!");
+            }
+
+            if (Attrib.Size == GalVertexAttribSize._10_10_10_2 ||
+                Attrib.Size == GalVertexAttribSize._11_11_10)
+            {
+                Unsupported();
+            }
+
+            if (Attrib.Type == GalVertexAttribType.Unorm)
+            {
+                switch (Attrib.Size)
+                {
+                    case GalVertexAttribSize._8:
+                    case GalVertexAttribSize._8_8:
+                    case GalVertexAttribSize._8_8_8:
+                    case GalVertexAttribSize._8_8_8_8:
+                        GL.VertexAttrib4N((uint)Attrib.Index, (byte*)Attrib.Pointer);
+                        break;
+
+                    case GalVertexAttribSize._16:
+                    case GalVertexAttribSize._16_16:
+                    case GalVertexAttribSize._16_16_16:
+                    case GalVertexAttribSize._16_16_16_16:
+                        GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Attrib.Pointer);
+                        break;
+
+                    case GalVertexAttribSize._32:
+                    case GalVertexAttribSize._32_32:
+                    case GalVertexAttribSize._32_32_32:
+                    case GalVertexAttribSize._32_32_32_32:
+                        GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Attrib.Pointer);
+                        break;
+                }
+            }
+            else if (Attrib.Type == GalVertexAttribType.Snorm)
+            {
+                switch (Attrib.Size)
+                {
+                    case GalVertexAttribSize._8:
+                    case GalVertexAttribSize._8_8:
+                    case GalVertexAttribSize._8_8_8:
+                    case GalVertexAttribSize._8_8_8_8:
+                        GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Attrib.Pointer);
+                        break;
+
+                    case GalVertexAttribSize._16:
+                    case GalVertexAttribSize._16_16:
+                    case GalVertexAttribSize._16_16_16:
+                    case GalVertexAttribSize._16_16_16_16:
+                        GL.VertexAttrib4N((uint)Attrib.Index, (short*)Attrib.Pointer);
+                        break;
+
+                    case GalVertexAttribSize._32:
+                    case GalVertexAttribSize._32_32:
+                    case GalVertexAttribSize._32_32_32:
+                    case GalVertexAttribSize._32_32_32_32:
+                        GL.VertexAttrib4N((uint)Attrib.Index, (int*)Attrib.Pointer);
+                        break;
+                }
+            }
+            else if (Attrib.Type == GalVertexAttribType.Uint)
+            {
+                switch (Attrib.Size)
+                {
+                    case GalVertexAttribSize._8:
+                    case GalVertexAttribSize._8_8:
+                    case GalVertexAttribSize._8_8_8:
+                    case GalVertexAttribSize._8_8_8_8:
+                        GL.VertexAttribI4((uint)Attrib.Index, (byte*)Attrib.Pointer);
+                        break;
+
+                    case GalVertexAttribSize._16:
+                    case GalVertexAttribSize._16_16:
+                    case GalVertexAttribSize._16_16_16:
+                    case GalVertexAttribSize._16_16_16_16:
+                        GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Attrib.Pointer);
+                        break;
+
+                    case GalVertexAttribSize._32:
+                    case GalVertexAttribSize._32_32:
+                    case GalVertexAttribSize._32_32_32:
+                    case GalVertexAttribSize._32_32_32_32:
+                        GL.VertexAttribI4((uint)Attrib.Index, (uint*)Attrib.Pointer);
+                        break;
+                }
+            }
+            else if (Attrib.Type == GalVertexAttribType.Sint)
+            {
+                switch (Attrib.Size)
+                {
+                    case GalVertexAttribSize._8:
+                    case GalVertexAttribSize._8_8:
+                    case GalVertexAttribSize._8_8_8:
+                    case GalVertexAttribSize._8_8_8_8:
+                        GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Attrib.Pointer);
+                        break;
+
+                    case GalVertexAttribSize._16:
+                    case GalVertexAttribSize._16_16:
+                    case GalVertexAttribSize._16_16_16:
+                    case GalVertexAttribSize._16_16_16_16:
+                        GL.VertexAttribI4((uint)Attrib.Index, (short*)Attrib.Pointer);
+                        break;
+
+                    case GalVertexAttribSize._32:
+                    case GalVertexAttribSize._32_32:
+                    case GalVertexAttribSize._32_32_32:
+                    case GalVertexAttribSize._32_32_32_32:
+                        GL.VertexAttribI4((uint)Attrib.Index, (int*)Attrib.Pointer);
+                        break;
+                }
+            }
+            else if (Attrib.Type == GalVertexAttribType.Float)
+            {
+                switch (Attrib.Size)
+                {
+                    case GalVertexAttribSize._32:
+                    case GalVertexAttribSize._32_32:
+                    case GalVertexAttribSize._32_32_32:
+                    case GalVertexAttribSize._32_32_32_32:
+                        GL.VertexAttrib4(Attrib.Index, (float*)Attrib.Pointer);
+                        break;
+
+                    default: Unsupported(); break;
+                }
+            }
+        }
+
         private void Enable(EnableCap Cap, bool Enabled)
         {
             if (Enabled)
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
index a74aee077..7b435c455 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
@@ -119,6 +119,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.BufferData(BufferTarget.ElementArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw);
         }
 
+        public void CreateIbo(long Key, int DataSize, byte[] Buffer)
+        {
+            int Handle = GL.GenBuffer();
+
+            IboCache.AddOrUpdate(Key, Handle, (uint)DataSize);
+
+            IntPtr Length = new IntPtr(Buffer.Length);
+
+            GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
+            GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
+        }
+
         public void SetIndexArray(int Size, GalIndexFormat Format)
         {
             IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format);
@@ -135,7 +147,26 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 return;
             }
 
-            GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, Count);
+            if (PrimType == GalPrimitiveType.Quads)
+            {
+                for (int Offset = 0; Offset < Count; Offset += 4)
+                {
+                    GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4);
+                }
+            }
+            else if (PrimType == GalPrimitiveType.QuadStrip)
+            {
+                GL.DrawArrays(PrimitiveType.TriangleFan, First, 4);
+
+                for (int Offset = 2; Offset < Count; Offset += 2)
+                {
+                    GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4);
+                }
+            }
+            else
+            {
+                GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, Count);
+            }
         }
 
         public void DrawElements(long IboKey, int First, int VertexBase, GalPrimitiveType PrimType)
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
index 78cf5d2fa..0de070b54 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
@@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             Attachments.Zeta = Key;
         }
-        
+
         public void UnbindZeta()
         {
             Attachments.Zeta = 0;
diff --git a/Ryujinx.Graphics/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs
index 22c093777..3dd7746da 100644
--- a/Ryujinx.Graphics/NvGpuEngine3d.cs
+++ b/Ryujinx.Graphics/NvGpuEngine3d.cs
@@ -433,7 +433,7 @@ namespace Ryujinx.Graphics
 
         private void SetRenderTargets()
         {
-            //Commercial games do not seem to 
+            //Commercial games do not seem to
             //bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData);
 
             uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl));
@@ -568,12 +568,15 @@ namespace Ryujinx.Graphics
 
         private void UploadVertexArrays(NvGpuVmm Vmm, GalPipelineState State)
         {
-            long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress);
+            long IbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress);
 
-            long IboKey = Vmm.GetPhysicalAddress(IndexPosition);
+            long IboKey = Vmm.GetPhysicalAddress(IbPosition);
 
             int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
             int IndexCount    = ReadRegister(NvGpuEngine3dReg.IndexBatchCount);
+            int PrimCtrl      = ReadRegister(NvGpuEngine3dReg.VertexBeginGl);
+
+            GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
 
             GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt;
 
@@ -590,14 +593,50 @@ namespace Ryujinx.Graphics
 
                 bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IboKey, (uint)IbSize);
 
+                bool UsesLegacyQuads =
+                    PrimType == GalPrimitiveType.Quads ||
+                    PrimType == GalPrimitiveType.QuadStrip;
+
                 if (!IboCached || QueryKeyUpload(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index))
                 {
-                    IntPtr DataAddress = Vmm.GetHostAddress(IndexPosition, IbSize);
+                    if (!UsesLegacyQuads)
+                    {
+                        IntPtr DataAddress = Vmm.GetHostAddress(IbPosition, IbSize);
 
-                    Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress);
+                        Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress);
+                    }
+                    else
+                    {
+                        byte[] Buffer = Vmm.ReadBytes(IbPosition, IbSize);
+
+                        if (PrimType == GalPrimitiveType.Quads)
+                        {
+                            Buffer = QuadHelper.ConvertIbQuadsToTris(Buffer, IndexEntrySize, IndexCount);
+                        }
+                        else /* if (PrimType == GalPrimitiveType.QuadStrip) */
+                        {
+                            Buffer = QuadHelper.ConvertIbQuadStripToTris(Buffer, IndexEntrySize, IndexCount);
+                        }
+
+                        Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Buffer);
+                    }
                 }
 
-                Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat);
+                if (!UsesLegacyQuads)
+                {
+                    Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat);
+                }
+                else
+                {
+                    if (PrimType == GalPrimitiveType.Quads)
+                    {
+                        Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadsToTris(IbSize), IndexFormat);
+                    }
+                    else /* if (PrimType == GalPrimitiveType.QuadStrip) */
+                    {
+                        Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadStripToTris(IbSize), IndexFormat);
+                    }
+                }
             }
 
             List<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[32];
@@ -613,10 +652,19 @@ namespace Ryujinx.Graphics
                     Attribs[ArrayIndex] = new List<GalVertexAttrib>();
                 }
 
+                long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4);
+
+                int Offset = (Packed >> 7) & 0x3fff;
+
+                //Note: 16 is the maximum size of an attribute,
+                //having a component size of 32-bits with 4 elements (a vec4).
+                IntPtr Pointer = Vmm.GetHostAddress(VertexPosition + Offset, 16);
+
                 Attribs[ArrayIndex].Add(new GalVertexAttrib(
                                            Attr,
                                          ((Packed >>  6) & 0x1) != 0,
-                                          (Packed >>  7) & 0x3fff,
+                                           Offset,
+                                           Pointer,
                     (GalVertexAttribSize)((Packed >> 21) & 0x3f),
                     (GalVertexAttribType)((Packed >> 27) & 0x7),
                                          ((Packed >> 31) & 0x1) != 0));
@@ -722,6 +770,27 @@ namespace Ryujinx.Graphics
 
                 long IboKey = Vmm.GetPhysicalAddress(IndexPosition);
 
+                //Quad primitive types were deprecated on OpenGL 3.x,
+                //they are converted to a triangles index buffer on IB creation,
+                //so we should use the triangles type here too.
+                if (PrimType == GalPrimitiveType.Quads ||
+                    PrimType == GalPrimitiveType.QuadStrip)
+                {
+                    PrimType = GalPrimitiveType.Triangles;
+
+                    //Note: We assume that index first points to the first
+                    //vertex of a quad, if it points to the middle of a
+                    //quad (First % 4 != 0 for Quads) then it will not work properly.
+                    if (PrimType == GalPrimitiveType.Quads)
+                    {
+                        IndexFirst = QuadHelper.ConvertIbSizeQuadsToTris(IndexFirst);
+                    }
+                    else /* if (PrimType == GalPrimitiveType.QuadStrip) */
+                    {
+                        IndexFirst = QuadHelper.ConvertIbSizeQuadStripToTris(IndexFirst);
+                    }
+                }
+
                 Gpu.Renderer.Rasterizer.DrawElements(IboKey, IndexFirst, VertexBase, PrimType);
             }
             else
diff --git a/Ryujinx.Graphics/QuadHelper.cs b/Ryujinx.Graphics/QuadHelper.cs
new file mode 100644
index 000000000..0dfffce0b
--- /dev/null
+++ b/Ryujinx.Graphics/QuadHelper.cs
@@ -0,0 +1,81 @@
+using System;
+
+namespace Ryujinx.Graphics
+{
+    static class QuadHelper
+    {
+        public static int ConvertIbSizeQuadsToTris(int Size)
+        {
+            return Size <= 0 ? 0 : (Size / 4) * 6;
+        }
+
+        public static int ConvertIbSizeQuadStripToTris(int Size)
+        {
+            return Size <= 1 ? 0 : ((Size - 2) / 2) * 6;
+        }
+
+        public static byte[] ConvertIbQuadsToTris(byte[] Data, int EntrySize, int Count)
+        {
+            int PrimitivesCount = Count / 4;
+
+            int QuadPrimSize = 4 * EntrySize;
+            int TrisPrimSize = 6 * EntrySize;
+
+            byte[] Output = new byte[PrimitivesCount * 6 * EntrySize];
+
+            for (int Prim = 0; Prim < PrimitivesCount; Prim++)
+            {
+                void AssignIndex(int Src, int Dst, int CopyCount = 1)
+                {
+                    Src = Prim * QuadPrimSize + Src * EntrySize;
+                    Dst = Prim * TrisPrimSize + Dst * EntrySize;
+
+                    Buffer.BlockCopy(Data, Src, Output, Dst, CopyCount * EntrySize);
+                }
+
+                //0 1 2 -> 0 1 2.
+                AssignIndex(0, 0, 3);
+
+                //2 3 -> 3 4.
+                AssignIndex(2, 3, 2);
+
+                //0 -> 5.
+                AssignIndex(0, 5);
+            }
+
+            return Output;
+        }
+
+        public static byte[] ConvertIbQuadStripToTris(byte[] Data, int EntrySize, int Count)
+        {
+            int PrimitivesCount = (Count - 2) / 2;
+
+            int QuadPrimSize = 2 * EntrySize;
+            int TrisPrimSize = 6 * EntrySize;
+
+            byte[] Output = new byte[PrimitivesCount * 6 * EntrySize];
+
+            for (int Prim = 0; Prim < PrimitivesCount; Prim++)
+            {
+                void AssignIndex(int Src, int Dst, int CopyCount = 1)
+                {
+                    Src = Prim * QuadPrimSize + Src * EntrySize + 2 * EntrySize;
+                    Dst = Prim * TrisPrimSize + Dst * EntrySize;
+
+                    Buffer.BlockCopy(Data, Src, Output, Dst, CopyCount * EntrySize);
+                }
+
+                //-2 -1 0 -> 0 1 2.
+                AssignIndex(-2, 0, 3);
+
+                //0 1 -> 3 4.
+                AssignIndex(0, 3, 2);
+
+                //-2 -> 5.
+                AssignIndex(-2, 5);
+            }
+
+            return Output;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Texture/ImageUtils.cs b/Ryujinx.Graphics/Texture/ImageUtils.cs
index 18a179fbf..1b043245e 100644
--- a/Ryujinx.Graphics/Texture/ImageUtils.cs
+++ b/Ryujinx.Graphics/Texture/ImageUtils.cs
@@ -289,7 +289,11 @@ namespace Ryujinx.Graphics.Texture
         {
             ImageDescriptor Desc = GetImageDescriptor(Format);
 
-            return Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth);
+            int Pitch = Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth);
+
+            Pitch = (Pitch + 0x1f) & ~0x1f;
+
+            return Pitch;
         }
 
         public static int GetBlockWidth(GalImageFormat Format)
diff --git a/Ryujinx.Graphics/Texture/TextureFactory.cs b/Ryujinx.Graphics/Texture/TextureFactory.cs
index 766c53da4..c0c53b06e 100644
--- a/Ryujinx.Graphics/Texture/TextureFactory.cs
+++ b/Ryujinx.Graphics/Texture/TextureFactory.cs
@@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Texture
             int Width  = (Tic[4] & 0xffff) + 1;
             int Height = (Tic[5] & 0xffff) + 1;
 
-            return new GalImage(
+            GalImage Image = new GalImage(
                 Width,
                 Height,
                 TileWidth,
@@ -51,6 +51,13 @@ namespace Ryujinx.Graphics.Texture
                 YSource,
                 ZSource,
                 WSource);
+
+            if (Layout == GalMemoryLayout.Pitch)
+            {
+                Image.Pitch = (Tic[3] & 0xffff) << 5;
+            }
+
+            return Image;
         }
 
         public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition)