Ver Fonte

Support inline index buffer data (#1351)

* Support inline index buffer data

* Sort usings
gdkchan há 5 anos atrás
pai
commit
dbeb50684d

+ 1 - 1
Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs

@@ -74,7 +74,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
             if (_isLinear && _params.LineCount == 1)
             {
-                ulong address = _context.MemoryManager.Translate( _params.DstAddress.Pack());
+                ulong address = _context.MemoryManager.Translate(_params.DstAddress.Pack());
 
                 _context.PhysicalMemory.Write(address, data);
             }

+ 125 - 3
Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs

@@ -1,5 +1,9 @@
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Common;
+using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Gpu.Image;
+using Ryujinx.Graphics.Gpu.State;
+using System;
+using System.Runtime.InteropServices;
 
 namespace Ryujinx.Graphics.Gpu.Engine
 {
@@ -22,6 +26,10 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
         private int _instanceIndex;
 
+        private BufferHandle _inlineIndexBuffer = BufferHandle.Null;
+        private int _inlineIndexBufferSize;
+        private int _inlineIndexCount;
+
         /// <summary>
         /// Primitive type of the current draw.
         /// </summary>
@@ -87,10 +95,25 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
             int firstInstance = state.Get<int>(MethodOffset.FirstInstance);
 
-            if (_drawIndexed)
+            if (_inlineIndexCount != 0)
             {
-                _drawIndexed = false;
+                int firstVertex = state.Get<int>(MethodOffset.FirstVertex);
+
+                BufferRange br = new BufferRange(_inlineIndexBuffer, 0, _inlineIndexCount * 4);
+
+                _context.Methods.BufferManager.SetIndexBuffer(br, IndexType.UInt);
+
+                _context.Renderer.Pipeline.DrawIndexed(
+                    _inlineIndexCount,
+                    1,
+                    _firstIndex,
+                    firstVertex,
+                    firstInstance);
 
+                _inlineIndexCount = 0;
+            }
+            else if (_drawIndexed)
+            {
                 int firstVertex = state.Get<int>(MethodOffset.FirstVertex);
 
                 _context.Renderer.Pipeline.DrawIndexed(
@@ -111,6 +134,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
                     firstInstance);
             }
 
+            _drawIndexed = false;
+
             if (renderEnable == ConditionalRenderEnabled.Host)
             {
                 _context.Renderer.Pipeline.EndHostConditionalRendering();
@@ -154,6 +179,103 @@ namespace Ryujinx.Graphics.Gpu.Engine
             _drawIndexed = true;
         }
 
+        /// <summary>
+        /// Pushes four 8-bit index buffer elements.
+        /// </summary>
+        /// <param name="state">Current GPU state</param>
+        /// <param name="argument">Method call argument</param>
+        private void VbElementU8(GpuState state, int argument)
+        {
+            byte i0 = (byte)argument;
+            byte i1 = (byte)(argument >> 8);
+            byte i2 = (byte)(argument >> 16);
+            byte i3 = (byte)(argument >> 24);
+
+            Span<uint> data = stackalloc uint[4];
+
+            data[0] = i0;
+            data[1] = i1;
+            data[2] = i2;
+            data[3] = i3;
+
+            int offset = _inlineIndexCount * 4;
+
+            _context.Renderer.SetBufferData(GetInlineIndexBuffer(offset), offset, MemoryMarshal.Cast<uint, byte>(data));
+
+            _inlineIndexCount += 4;
+        }
+
+        /// <summary>
+        /// Pushes two 16-bit index buffer elements.
+        /// </summary>
+        /// <param name="state">Current GPU state</param>
+        /// <param name="argument">Method call argument</param>
+        private void VbElementU16(GpuState state, int argument)
+        {
+            ushort i0 = (ushort)argument;
+            ushort i1 = (ushort)(argument >> 16);
+
+            Span<uint> data = stackalloc uint[2];
+
+            data[0] = i0;
+            data[1] = i1;
+
+            int offset = _inlineIndexCount * 4;
+
+            _context.Renderer.SetBufferData(GetInlineIndexBuffer(offset), offset, MemoryMarshal.Cast<uint, byte>(data));
+
+            _inlineIndexCount += 2;
+        }
+
+        /// <summary>
+        /// Pushes one 32-bit index buffer element.
+        /// </summary>
+        /// <param name="state">Current GPU state</param>
+        /// <param name="argument">Method call argument</param>
+        private void VbElementU32(GpuState state, int argument)
+        {
+            uint i0 = (uint)argument;
+
+            Span<uint> data = stackalloc uint[1];
+
+            data[0] = i0;
+
+            int offset = _inlineIndexCount++ * 4;
+
+            _context.Renderer.SetBufferData(GetInlineIndexBuffer(offset), offset, MemoryMarshal.Cast<uint, byte>(data));
+        }
+
+        /// <summary>
+        /// Gets the handle of a buffer large enough to hold the data that will be written to <paramref name="offset"/>.
+        /// </summary>
+        /// <param name="offset">Offset where the data will be written</param>
+        /// <returns>Buffer handle</returns>
+        private BufferHandle GetInlineIndexBuffer(int offset)
+        {
+            // Calculate a reasonable size for the buffer that can fit all the data,
+            // and that also won't require frequent resizes if we need to push more data.
+            int size = BitUtils.AlignUp(offset + 0x10, 0x200);
+
+            if (_inlineIndexBuffer == BufferHandle.Null)
+            {
+                _inlineIndexBuffer = _context.Renderer.CreateBuffer(size);
+                _inlineIndexBufferSize = size;
+            }
+            else if (_inlineIndexBufferSize < size)
+            {
+                BufferHandle oldBuffer = _inlineIndexBuffer;
+                int oldSize = _inlineIndexBufferSize;
+
+                _inlineIndexBuffer = _context.Renderer.CreateBuffer(size);
+                _inlineIndexBufferSize = size;
+
+                _context.Renderer.Pipeline.CopyBuffer(oldBuffer, _inlineIndexBuffer, 0, 0, oldSize);
+                _context.Renderer.DeleteBuffer(oldBuffer);
+            }
+
+            return _inlineIndexBuffer;
+        }
+
         /// <summary>
         /// Perform any deferred draws.
         /// This is used for instanced draws.

+ 5 - 1
Ryujinx.Graphics.Gpu/Engine/Methods.cs

@@ -78,6 +78,10 @@ namespace Ryujinx.Graphics.Gpu.Engine
             state.RegisterCallback(MethodOffset.InvalidateTextures,  InvalidateTextures);
             state.RegisterCallback(MethodOffset.TextureBarrierTiled, TextureBarrierTiled);
 
+            state.RegisterCallback(MethodOffset.VbElementU8,  VbElementU8);
+            state.RegisterCallback(MethodOffset.VbElementU16, VbElementU16);
+            state.RegisterCallback(MethodOffset.VbElementU32, VbElementU32);
+
             state.RegisterCallback(MethodOffset.ResetCounter, ResetCounter);
 
             state.RegisterCallback(MethodOffset.DrawEnd,   DrawEnd);
@@ -726,7 +730,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
                 ulong size;
 
-                if (_drawIndexed || stride == 0 || instanced)
+                if (_inlineIndexCount != 0 || _drawIndexed || stride == 0 || instanced)
                 {
                     // This size may be (much) larger than the real vertex buffer size.
                     // Avoid calculating it this way, unless we don't have any other option.

+ 12 - 0
Ryujinx.Graphics.Gpu/Memory/BufferManager.cs

@@ -103,6 +103,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
             _indexBufferDirty = true;
         }
 
+        /// <summary>
+        /// Sets a new index buffer that overrides the one set on the call to <see cref="CommitGraphicsBindings"/>.
+        /// </summary>
+        /// <param name="buffer">Buffer to be used as index buffer</param>
+        /// <param name="type">Type of each index buffer element</param>
+        public void SetIndexBuffer(BufferRange buffer, IndexType type)
+        {
+            _context.Renderer.Pipeline.SetIndexBuffer(buffer, type);
+
+            _indexBufferDirty = true;
+        }
+
         /// <summary>
         /// Sets the memory range with vertex buffer data, to be used for subsequent draw calls.
         /// </summary>

+ 3 - 0
Ryujinx.Graphics.Gpu/State/MethodOffset.cs

@@ -58,6 +58,7 @@ namespace Ryujinx.Graphics.Gpu.State
         DepthTestEnable                 = 0x4b3,
         BlendIndependent                = 0x4b9,
         DepthWriteEnable                = 0x4ba,
+        VbElementU8                     = 0x4c1,
         DepthTestFunc                   = 0x4c3,
         BlendConstant                   = 0x4c7,
         BlendStateCommon                = 0x4cf,
@@ -78,6 +79,8 @@ namespace Ryujinx.Graphics.Gpu.State
         StencilBackTestState            = 0x565,
         DepthBiasUnits                  = 0x56f,
         RtMsaaMode                      = 0x574,
+        VbElementU32                    = 0x57a,
+        VbElementU16                    = 0x57c,
         ShaderBaseAddress               = 0x582,
         DrawEnd                         = 0x585,
         DrawBegin                       = 0x586,