From f73a182b20970f993abc1f1329dfab1e5d3b3354 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sun, 29 Apr 2018 17:58:38 -0300
Subject: [PATCH] Properly support multiple vertex buffers, stub 2 ioctls, fix
 a shader issue, change the way how the vertex buffer size is calculated for
 the buffers with limit = 0

---
 .../OsHle/Services/Nv/INvDrvServices.cs       | 29 +++++++
 Ryujinx.Graphics/Gal/GalVertexAttrib.cs       |  3 +
 Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs  | 53 ++++--------
 Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs | 11 ++-
 Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs         | 83 +++++++++++++++++--
 5 files changed, 133 insertions(+), 46 deletions(-)

diff --git a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs
index f41a98d0ab..2652724d5b 100644
--- a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs
+++ b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs
@@ -51,6 +51,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
                 { ("/dev/nvhost-as-gpu",   0x4114), NvGpuAsIoctlRemap                 },
                 { ("/dev/nvhost-ctrl",     0x001b), NvHostIoctlCtrlGetConfig          },
                 { ("/dev/nvhost-ctrl",     0x001d), NvHostIoctlCtrlEventWait          },
+                { ("/dev/nvhost-ctrl",     0x001e), NvHostIoctlCtrlEventWaitAsync     },
+                { ("/dev/nvhost-ctrl",     0x001f), NvHostIoctlCtrlEventRegister      },
                 { ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize         },
                 { ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo            },
                 { ("/dev/nvhost-ctrl-gpu", 0x4703), NvGpuIoctlZbcSetTable             },
@@ -384,6 +386,33 @@ namespace Ryujinx.Core.OsHle.Services.Nv
             return 0;
         }
 
+        private long NvHostIoctlCtrlEventWaitAsync(ServiceCtx Context)
+        {
+            long Position = Context.Request.GetSendBuffPtr();
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            int SyncPtId  = Reader.ReadInt32();
+            int Threshold = Reader.ReadInt32();
+            int Timeout   = Reader.ReadInt32();
+            int Value     = Reader.ReadInt32();
+
+            Context.Memory.WriteInt32(Position + 0xc, 0xcafe);
+
+            return 0;
+        }
+
+        private long NvHostIoctlCtrlEventRegister(ServiceCtx Context)
+        {
+            long Position = Context.Request.GetSendBuffPtr();
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            int UserEventId = Reader.ReadInt32();
+
+            return 0;
+        }
+
         private long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context)
         {
             long Position = Context.Request.GetSendBuffPtr();
diff --git a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
index 563e624d53..dd04006025 100644
--- a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
+++ b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
@@ -2,6 +2,7 @@ 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; }
 
@@ -11,12 +12,14 @@ namespace Ryujinx.Graphics.Gal
         public bool IsBgra { get; private set; }
 
         public GalVertexAttrib(
+            int                 Index,
             bool                IsConst,
             int                 Offset,
             GalVertexAttribSize Size,
             GalVertexAttribType Type,
             bool                IsBgra)
         {
+            this.Index   = Index;
             this.IsConst = IsConst;
             this.Offset  = Offset;
             this.Size    = Size;
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
index b1504563f4..5b115446e7 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
@@ -44,12 +44,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             { GalVertexAttribSize._11_11_10,    VertexAttribPointerType.Int   }  //?
         };
 
-        private struct VbInfo
-        {
-            public int VaoHandle;
-            public int VboHandle;
-        }
-
         private struct IbInfo
         {
             public int IboHandle;
@@ -58,13 +52,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             public DrawElementsType Type;
         }
 
-        private VbInfo[] VertexBuffers;
+        private int VaoHandle;
+
+        private int[] VertexBuffers;
 
         private IbInfo IndexBuffer;
 
         public OGLRasterizer()
         {
-            VertexBuffers = new VbInfo[32];
+            VertexBuffers = new int[32];
 
             IndexBuffer = new IbInfo();
         }
@@ -100,28 +96,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             EnsureVbInitialized(VbIndex);
 
-            VbInfo Vb = VertexBuffers[VbIndex];
-
             IntPtr Length = new IntPtr(Buffer.Length);
 
-            GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
+            GL.BindBuffer(BufferTarget.ArrayBuffer, VertexBuffers[VbIndex]);
             GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
             GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
 
-            GL.BindVertexArray(Vb.VaoHandle);
+            GL.BindVertexArray(VaoHandle);
 
-            for (int Attr = 0; Attr < 16; Attr++)
+            foreach (GalVertexAttrib Attrib in Attribs)
             {
-                GL.DisableVertexAttribArray(Attr);
-            }
+                GL.EnableVertexAttribArray(Attrib.Index);
 
-            for (int Index = 0; Index < Attribs.Length; Index++)
-            {
-                GalVertexAttrib Attrib = Attribs[Index];
-
-                GL.EnableVertexAttribArray(Index);
-
-                GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
+                GL.BindBuffer(BufferTarget.ArrayBuffer, VertexBuffers[VbIndex]);
 
                 bool Unsigned =
                     Attrib.Type == GalVertexAttribType.Unorm ||
@@ -146,7 +133,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 int Size   = AttribElements[Attrib.Size];
                 int Offset = Attrib.Offset;
 
-                GL.VertexAttribPointer(Index, Size, Type, Normalize, Stride, Offset);
+                GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Stride, Offset);
             }
 
             GL.BindVertexArray(0);
@@ -174,20 +161,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 return;
             }
 
-            VbInfo Vb = VertexBuffers[VbIndex];
-
-            GL.BindVertexArray(Vb.VaoHandle);
+            GL.BindVertexArray(VaoHandle);
 
             GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, PrimCount);
         }
 
         public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
         {
-            VbInfo Vb = VertexBuffers[VbIndex];
-
             PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType);
 
-            GL.BindVertexArray(Vb.VaoHandle);
+            GL.BindVertexArray(VaoHandle);
 
             GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexBuffer.IboHandle);
 
@@ -196,19 +179,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         private void EnsureVbInitialized(int VbIndex)
         {
-            VbInfo Vb = VertexBuffers[VbIndex];
-
-            if (Vb.VaoHandle == 0)
+            if (VaoHandle == 0)
             {
-                Vb.VaoHandle = GL.GenVertexArray();
+                VaoHandle = GL.GenVertexArray();
             }
 
-            if (Vb.VboHandle == 0)
+            if (VertexBuffers[VbIndex] == 0)
             {
-                Vb.VboHandle = GL.GenBuffer();
+                VertexBuffers[VbIndex] = GL.GenBuffer();
             }
-
-            VertexBuffers[VbIndex] = Vb;
         }
 
         private void EnsureIbInitialized()
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
index c22f59264c..1e0824d2fb 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
@@ -261,13 +261,16 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                     PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child);
                 }
-                else if (Node is ShaderIrAsg Asg && IsValidOutOper(Asg.Dst))
+                else if (Node is ShaderIrAsg Asg)
                 {
-                    string Expr = GetSrcExpr(Asg.Src, true);
+                    if (IsValidOutOper(Asg.Dst))
+                    {
+                        string Expr = GetSrcExpr(Asg.Src, true);
 
-                    Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr);
+                        Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr);
 
-                    SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";");
+                        SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";");
+                    }
                 }
                 else if (Node is ShaderIrOp Op)
                 {
diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
index bf04db367f..a6696650b6 100644
--- a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
+++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
@@ -351,6 +351,7 @@ namespace Ryujinx.Graphics.Gpu
                 }
 
                 Attribs[ArrayIndex].Add(new GalVertexAttrib(
+                                           Attr,
                                          ((Packed >>  6) & 0x1) != 0,
                                           (Packed >>  7) & 0x3fff,
                     (GalVertexAttribSize)((Packed >> 21) & 0x3f),
@@ -367,18 +368,34 @@ namespace Ryujinx.Graphics.Gpu
 
                 bool Enable = (Control & 0x1000) != 0;
 
+                long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
+
                 if (!Enable)
                 {
                     continue;
                 }
 
-                long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
-                long VertexEndPos   = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 4);
-
-                long Size = (VertexEndPos - VertexPosition) + 1;
-
                 int Stride = Control & 0xfff;
 
+                long Size = 0;
+
+                if (IndexCount != 0)
+                {
+                    Size = GetVertexCountFromIndexBuffer(
+                        Memory,
+                        IndexPosition,
+                        IndexCount,
+                        IndexSize);
+                }
+                else
+                {
+                    Size = VertexCount;
+                }
+
+                //TODO: Support cases where the Stride is 0.
+                //In this case, we need to use the size of the attribute.
+                Size *= Stride;
+
                 VertexPosition = Gpu.GetCpuAddr(VertexPosition);
 
                 byte[] Data = AMemoryHelper.ReadBytes(Memory, VertexPosition, Size);
@@ -402,6 +419,62 @@ namespace Ryujinx.Graphics.Gpu
             }
         }
 
+        private int GetVertexCountFromIndexBuffer(
+            AMemory Memory,
+            long    IndexPosition,
+            int     IndexCount,
+            int     IndexSize)
+        {
+            int MaxIndex = -1;
+
+            if (IndexSize == 2)
+            {
+                while (IndexCount -- > 0)
+                {
+                    ushort Value = Memory.ReadUInt16(IndexPosition);
+
+                    IndexPosition += 2;
+
+                    if (MaxIndex < Value)
+                    {
+                        MaxIndex = Value;
+                    }
+                }
+            }
+            else if (IndexSize == 1)
+            {
+                while (IndexCount -- > 0)
+                {
+                    byte Value = Memory.ReadByte(IndexPosition++);
+
+                    if (MaxIndex < Value)
+                    {
+                        MaxIndex = Value;
+                    }
+                }
+            }
+            else if (IndexSize == 4)
+            {
+                while (IndexCount -- > 0)
+                {
+                    uint Value = Memory.ReadUInt32(IndexPosition);
+
+                    IndexPosition += 2;
+
+                    if (MaxIndex < Value)
+                    {
+                        MaxIndex = (int)Value;
+                    }
+                }
+            }
+            else
+            {
+                throw new ArgumentOutOfRangeException(nameof(IndexSize));
+            }
+
+            return MaxIndex + 1;
+        }
+
         private void QueryControl(AMemory Memory, NsGpuPBEntry PBEntry)
         {
             if (TryGetCpuAddr(NvGpuEngine3dReg.QueryAddress, out long Position))