From ec4cd57ccfada247d720b41c1913299abc733c74 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Fri, 16 Dec 2022 12:06:38 -0300
Subject: [PATCH] Implement another non-indexed draw method on GPU (#4123)

---
 .../Engine/Threed/DrawManager.cs              | 141 ++++++++++++++----
 .../Engine/Threed/StateUpdater.cs             |   2 -
 .../Engine/Threed/ThreedClass.cs              |  74 +++++++--
 .../Engine/Threed/ThreedClassState.cs         |  17 ++-
 4 files changed, 177 insertions(+), 57 deletions(-)

diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
index 5a659f5550..cd14259a68 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
@@ -98,7 +98,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
         /// <param name="argument">Method call argument</param>
         public void DrawEnd(ThreedClass engine, int argument)
         {
-            DrawEnd(engine, _state.State.IndexBufferState.First, (int)_state.State.IndexBufferCount);
+            DrawEnd(
+                engine,
+                _state.State.IndexBufferState.First,
+                (int)_state.State.IndexBufferCount,
+                _state.State.VertexBufferDrawState.First,
+                _state.State.VertexBufferDrawState.Count);
         }
 
         /// <summary>
@@ -108,7 +113,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
         /// <param name="engine">3D engine where this method is being called</param>
         /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
         /// <param name="indexCount">Number of index buffer elements used on the draw</param>
-        private void DrawEnd(ThreedClass engine, int firstIndex, int indexCount)
+        /// <param name="drawFirstVertex">Index of the first vertex used on the draw</param>
+        /// <param name="drawVertexCount">Number of vertices used on the draw</param>
+        private void DrawEnd(ThreedClass engine, int firstIndex, int indexCount, int drawFirstVertex, int drawVertexCount)
         {
             ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
                 _context,
@@ -195,7 +202,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
             {
                 var drawState = _state.State.VertexBufferDrawState;
 
-                _context.Renderer.Pipeline.Draw(drawState.Count, 1, drawState.First, firstInstance);
+                _context.Renderer.Pipeline.Draw(drawVertexCount, 1, drawFirstVertex, firstInstance);
             }
 
             _drawState.DrawIndexed = false;
@@ -216,16 +223,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
             bool incrementInstance = (argument & (1 << 26)) != 0;
             bool resetInstance = (argument & (1 << 27)) == 0;
 
-            if (_state.State.PrimitiveTypeOverrideEnable)
-            {
-                PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
-                DrawBegin(incrementInstance, resetInstance, typeOverride.Convert());
-            }
-            else
-            {
-                PrimitiveType type = (PrimitiveType)(argument & 0xffff);
-                DrawBegin(incrementInstance, resetInstance, type.Convert());
-            }
+            PrimitiveType type = (PrimitiveType)(argument & 0xffff);
+            DrawBegin(incrementInstance, resetInstance, type);
         }
 
         /// <summary>
@@ -234,8 +233,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
         /// </summary>
         /// <param name="incrementInstance">Indicates if the current instance should be incremented</param>
         /// <param name="resetInstance">Indicates if the current instance should be set to zero</param>
-        /// <param name="topology">Primitive topology</param>
-        private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveTopology topology)
+        /// <param name="primitiveType">Primitive type</param>
+        private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveType primitiveType)
         {
             if (incrementInstance)
             {
@@ -248,6 +247,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
                 _instanceIndex = 0;
             }
 
+            PrimitiveTopology topology;
+
+            if (_state.State.PrimitiveTypeOverrideEnable)
+            {
+                PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
+                topology = typeOverride.Convert();
+            }
+            else
+            {
+                topology = primitiveType.Convert();
+            }
+
             UpdateTopology(topology);
         }
 
@@ -276,46 +287,70 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
             _drawState.DrawIndexed = true;
         }
 
+        // TODO: Verify if the index type is implied from the method that is called,
+        // or if it uses the state index type on hardware.
+
         /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements.
+        /// Performs a indexed draw with 8-bit index buffer elements.
         /// </summary>
         /// <param name="engine">3D engine where this method is being called</param>
         /// <param name="argument">Method call argument</param>
-        public void DrawIndexedSmall(ThreedClass engine, int argument)
+        public void DrawIndexBuffer8BeginEndInstanceFirst(ThreedClass engine, int argument)
         {
-            DrawIndexedSmall(engine, argument, false);
+            DrawIndexBufferBeginEndInstance(engine, argument, false);
         }
 
         /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements.
+        /// Performs a indexed draw with 16-bit index buffer elements.
         /// </summary>
         /// <param name="engine">3D engine where this method is being called</param>
         /// <param name="argument">Method call argument</param>
-        public void DrawIndexedSmall2(ThreedClass engine, int argument)
+        public void DrawIndexBuffer16BeginEndInstanceFirst(ThreedClass engine, int argument)
         {
-            DrawIndexedSmall(engine, argument);
+            DrawIndexBufferBeginEndInstance(engine, argument, false);
         }
 
         /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements,
+        /// Performs a indexed draw with 32-bit index buffer elements.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        public void DrawIndexBuffer32BeginEndInstanceFirst(ThreedClass engine, int argument)
+        {
+            DrawIndexBufferBeginEndInstance(engine, argument, false);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with 8-bit index buffer elements,
         /// while also pre-incrementing the current instance value.
         /// </summary>
         /// <param name="engine">3D engine where this method is being called</param>
         /// <param name="argument">Method call argument</param>
-        public void DrawIndexedSmallIncInstance(ThreedClass engine, int argument)
+        public void DrawIndexBuffer8BeginEndInstanceSubsequent(ThreedClass engine, int argument)
         {
-            DrawIndexedSmall(engine, argument, true);
+            DrawIndexBufferBeginEndInstance(engine, argument, true);
         }
 
         /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements,
+        /// Performs a indexed draw with 16-bit index buffer elements,
         /// while also pre-incrementing the current instance value.
         /// </summary>
         /// <param name="engine">3D engine where this method is being called</param>
         /// <param name="argument">Method call argument</param>
-        public void DrawIndexedSmallIncInstance2(ThreedClass engine, int argument)
+        public void DrawIndexBuffer16BeginEndInstanceSubsequent(ThreedClass engine, int argument)
         {
-            DrawIndexedSmallIncInstance(engine, argument);
+            DrawIndexBufferBeginEndInstance(engine, argument, true);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with 32-bit index buffer elements,
+        /// while also pre-incrementing the current instance value.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        public void DrawIndexBuffer32BeginEndInstanceSubsequent(ThreedClass engine, int argument)
+        {
+            DrawIndexBufferBeginEndInstance(engine, argument, true);
         }
 
         /// <summary>
@@ -325,11 +360,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
         /// <param name="engine">3D engine where this method is being called</param>
         /// <param name="argument">Method call argument</param>
         /// <param name="instanced">True to increment the current instance value, false otherwise</param>
-        private void DrawIndexedSmall(ThreedClass engine, int argument, bool instanced)
+        private void DrawIndexBufferBeginEndInstance(ThreedClass engine, int argument, bool instanced)
         {
-            PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
-
-            DrawBegin(instanced, !instanced, typeOverride.Convert());
+            DrawBegin(instanced, !instanced, (PrimitiveType)((argument >> 28) & 0xf));
 
             int firstIndex = argument & 0xffff;
             int indexCount = (argument >> 16) & 0xfff;
@@ -339,7 +372,51 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
             _drawState.DrawIndexed = true;
             engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
 
-            DrawEnd(engine, firstIndex, indexCount);
+            DrawEnd(engine, firstIndex, indexCount, 0, 0);
+
+            _drawState.DrawIndexed = oldDrawIndexed;
+        }
+
+        /// <summary>
+        /// Performs a non-indexed draw with the specified topology, index and count.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        public void DrawVertexArrayBeginEndInstanceFirst(ThreedClass engine, int argument)
+        {
+            DrawVertexArrayBeginEndInstance(engine, argument, false);
+        }
+
+        /// <summary>
+        /// Performs a non-indexed draw with the specified topology, index and count,
+        /// while incrementing the current instance.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        public void DrawVertexArrayBeginEndInstanceSubsequent(ThreedClass engine, int argument)
+        {
+            DrawVertexArrayBeginEndInstance(engine, argument, true);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with a low number of index buffer elements,
+        /// while optionally also pre-incrementing the current instance value.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        /// <param name="instanced">True to increment the current instance value, false otherwise</param>
+        private void DrawVertexArrayBeginEndInstance(ThreedClass engine, int argument, bool instanced)
+        {
+            DrawBegin(instanced, !instanced, (PrimitiveType)((argument >> 28) & 0xf));
+
+            int firstVertex = argument & 0xffff;
+            int vertexCount = (argument >> 16) & 0xfff;
+
+            bool oldDrawIndexed = _drawState.DrawIndexed;
+
+            _drawState.DrawIndexed = false;
+
+            DrawEnd(engine, 0, 0, firstVertex, vertexCount);
 
             _drawState.DrawIndexed = oldDrawIndexed;
         }
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
index e6b4734507..014a173988 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
@@ -1,7 +1,5 @@
 using Ryujinx.Common.Logging;
-using Ryujinx.Common.Memory;
 using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Engine.GPFifo;
 using Ryujinx.Graphics.Gpu.Engine.Types;
 using Ryujinx.Graphics.Gpu.Image;
 using Ryujinx.Graphics.Gpu.Shader;
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
index b254e95e0c..87dd1d8eef 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
@@ -42,6 +42,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
                 { nameof(ThreedClassState.TextureBarrier), new RwCallback(TextureBarrier, null) },
                 { nameof(ThreedClassState.TextureBarrierTiled), new RwCallback(TextureBarrierTiled, null) },
                 { nameof(ThreedClassState.DrawTextureSrcY), new RwCallback(DrawTexture, null) },
+                { nameof(ThreedClassState.DrawVertexArrayBeginEndInstanceFirst), new RwCallback(DrawVertexArrayBeginEndInstanceFirst, null) },
+                { nameof(ThreedClassState.DrawVertexArrayBeginEndInstanceSubsequent), new RwCallback(DrawVertexArrayBeginEndInstanceSubsequent, null) },
                 { nameof(ThreedClassState.VbElementU8), new RwCallback(VbElementU8, null) },
                 { nameof(ThreedClassState.VbElementU16), new RwCallback(VbElementU16, null) },
                 { nameof(ThreedClassState.VbElementU32), new RwCallback(VbElementU32, null) },
@@ -49,10 +51,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
                 { nameof(ThreedClassState.RenderEnableCondition), new RwCallback(null, Zero) },
                 { nameof(ThreedClassState.DrawEnd), new RwCallback(DrawEnd, null) },
                 { nameof(ThreedClassState.DrawBegin), new RwCallback(DrawBegin, null) },
-                { nameof(ThreedClassState.DrawIndexedSmall), new RwCallback(DrawIndexedSmall, null) },
-                { nameof(ThreedClassState.DrawIndexedSmall2), new RwCallback(DrawIndexedSmall2, null) },
-                { nameof(ThreedClassState.DrawIndexedSmallIncInstance), new RwCallback(DrawIndexedSmallIncInstance, null) },
-                { nameof(ThreedClassState.DrawIndexedSmallIncInstance2), new RwCallback(DrawIndexedSmallIncInstance2, null) },
+                { nameof(ThreedClassState.DrawIndexBuffer32BeginEndInstanceFirst), new RwCallback(DrawIndexBuffer32BeginEndInstanceFirst, null) },
+                { nameof(ThreedClassState.DrawIndexBuffer16BeginEndInstanceFirst), new RwCallback(DrawIndexBuffer16BeginEndInstanceFirst, null) },
+                { nameof(ThreedClassState.DrawIndexBuffer8BeginEndInstanceFirst), new RwCallback(DrawIndexBuffer8BeginEndInstanceFirst, null) },
+                { nameof(ThreedClassState.DrawIndexBuffer32BeginEndInstanceSubsequent), new RwCallback(DrawIndexBuffer32BeginEndInstanceSubsequent, null) },
+                { nameof(ThreedClassState.DrawIndexBuffer16BeginEndInstanceSubsequent), new RwCallback(DrawIndexBuffer16BeginEndInstanceSubsequent, null) },
+                { nameof(ThreedClassState.DrawIndexBuffer8BeginEndInstanceSubsequent), new RwCallback(DrawIndexBuffer8BeginEndInstanceSubsequent, null) },
                 { nameof(ThreedClassState.IndexBufferCount), new RwCallback(SetIndexBufferCount, null) },
                 { nameof(ThreedClassState.Clear), new RwCallback(Clear, null) },
                 { nameof(ThreedClassState.SemaphoreControl), new RwCallback(Report, null) },
@@ -303,6 +307,25 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
             _drawManager.DrawTexture(this, argument);
         }
 
+        /// <summary>
+        /// Performs a non-indexed draw with the specified topology, index and count.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void DrawVertexArrayBeginEndInstanceFirst(int argument)
+        {
+            _drawManager.DrawVertexArrayBeginEndInstanceFirst(this, argument);
+        }
+
+        /// <summary>
+        /// Performs a non-indexed draw with the specified topology, index and count,
+        /// while incrementing the current instance.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void DrawVertexArrayBeginEndInstanceSubsequent(int argument)
+        {
+            _drawManager.DrawVertexArrayBeginEndInstanceSubsequent(this, argument);
+        }
+
         /// <summary>
         /// Pushes four 8-bit index buffer elements.
         /// </summary>
@@ -370,41 +393,60 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
         }
 
         /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements.
+        /// Performs a indexed draw with 8-bit index buffer elements.
         /// </summary>
         /// <param name="argument">Method call argument</param>
-        private void DrawIndexedSmall(int argument)
+        private void DrawIndexBuffer8BeginEndInstanceFirst(int argument)
         {
-            _drawManager.DrawIndexedSmall(this, argument);
+            _drawManager.DrawIndexBuffer8BeginEndInstanceFirst(this, argument);
         }
 
         /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements.
+        /// Performs a indexed draw with 16-bit index buffer elements.
         /// </summary>
         /// <param name="argument">Method call argument</param>
-        private void DrawIndexedSmall2(int argument)
+        private void DrawIndexBuffer16BeginEndInstanceFirst(int argument)
         {
-            _drawManager.DrawIndexedSmall2(this, argument);
+            _drawManager.DrawIndexBuffer16BeginEndInstanceFirst(this, argument);
         }
 
         /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements,
+        /// Performs a indexed draw with 32-bit index buffer elements.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void DrawIndexBuffer32BeginEndInstanceFirst(int argument)
+        {
+            _drawManager.DrawIndexBuffer32BeginEndInstanceFirst(this, argument);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with 8-bit index buffer elements,
         /// while also pre-incrementing the current instance value.
         /// </summary>
         /// <param name="argument">Method call argument</param>
-        private void DrawIndexedSmallIncInstance(int argument)
+        private void DrawIndexBuffer8BeginEndInstanceSubsequent(int argument)
         {
-            _drawManager.DrawIndexedSmallIncInstance(this, argument);
+            _drawManager.DrawIndexBuffer8BeginEndInstanceSubsequent(this, argument);
         }
 
         /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements,
+        /// Performs a indexed draw with 16-bit index buffer elements,
         /// while also pre-incrementing the current instance value.
         /// </summary>
         /// <param name="argument">Method call argument</param>
-        private void DrawIndexedSmallIncInstance2(int argument)
+        private void DrawIndexBuffer16BeginEndInstanceSubsequent(int argument)
         {
-            _drawManager.DrawIndexedSmallIncInstance2(this, argument);
+            _drawManager.DrawIndexBuffer16BeginEndInstanceSubsequent(this, argument);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with 32-bit index buffer elements,
+        /// while also pre-incrementing the current instance value.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void DrawIndexBuffer32BeginEndInstanceSubsequent(int argument)
+        {
+            _drawManager.DrawIndexBuffer32BeginEndInstanceSubsequent(this, argument);
         }
 
         /// <summary>
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
index e416cd583c..1498e27ba1 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
@@ -813,7 +813,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
         public uint ClearFlags;
         public fixed uint Reserved10FC[25];
         public Array32<VertexAttribState> VertexAttribState;
-        public fixed uint Reserved11E0[15];
+        public fixed uint Reserved11E0[13];
+        public uint DrawVertexArrayBeginEndInstanceFirst;
+        public uint DrawVertexArrayBeginEndInstanceSubsequent;
         public RtControl RtControl;
         public fixed uint Reserved1220[2];
         public Size3D RtDepthStencilSize;
@@ -888,12 +890,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
         public fixed uint Reserved164C[95];
         public IndexBufferState IndexBufferState;
         public uint IndexBufferCount;
-        public uint DrawIndexedSmall;
-        public uint DrawIndexedSmall2;
-        public uint Reserved17EC;
-        public uint DrawIndexedSmallIncInstance;
-        public uint DrawIndexedSmallIncInstance2;
-        public fixed uint Reserved17F8[33];
+        public uint DrawIndexBuffer32BeginEndInstanceFirst;
+        public uint DrawIndexBuffer16BeginEndInstanceFirst;
+        public uint DrawIndexBuffer8BeginEndInstanceFirst;
+        public uint DrawIndexBuffer32BeginEndInstanceSubsequent;
+        public uint DrawIndexBuffer16BeginEndInstanceSubsequent;
+        public uint DrawIndexBuffer8BeginEndInstanceSubsequent;
+        public fixed uint Reserved17FC[32];
         public float DepthBiasClamp;
         public Array16<Boolean32> VertexBufferInstanced;
         public fixed uint Reserved18C0[20];