From bd28ce90e6df04b5b15a5c1149523f3742af38cc Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 23 Sep 2020 20:48:34 -0300 Subject: Implement small indexed draws and other fixes to make guest Vulkan work (#1558) --- Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs | 214 +++++++++++++++++------------- 1 file changed, 122 insertions(+), 92 deletions(-) (limited to 'Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs') diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs b/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs index 5d41dafd..88f2e8fe 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs @@ -1,9 +1,6 @@ -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 { @@ -11,9 +8,6 @@ namespace Ryujinx.Graphics.Gpu.Engine { private bool _drawIndexed; - private int _firstIndex; - private int _indexCount; - private bool _instancedDrawPending; private bool _instancedIndexed; @@ -26,22 +20,34 @@ namespace Ryujinx.Graphics.Gpu.Engine private int _instanceIndex; - private BufferHandle _inlineIndexBuffer = BufferHandle.Null; - private int _inlineIndexBufferSize; - private int _inlineIndexCount; + private IbStreamer _ibStreamer; /// - /// Primitive type of the current draw. + /// Primitive topology of the current draw. /// - public PrimitiveType PrimitiveType { get; private set; } + public PrimitiveTopology Topology { get; private set; } /// - /// Finishes draw call. + /// Finishes the draw call. /// This draws geometry on the bound buffers based on the current GPU state. /// /// Current GPU state /// Method call argument private void DrawEnd(GpuState state, int argument) + { + var indexBuffer = state.Get(MethodOffset.IndexBufferState); + + DrawEnd(state, indexBuffer.First, indexBuffer.Count); + } + + /// + /// Finishes the draw call. + /// This draws geometry on the bound buffers based on the current GPU state. + /// + /// Current GPU state + /// Index of the first index buffer element used on the draw + /// Number of index buffer elements used on the draw + private void DrawEnd(GpuState state, int firstIndex, int indexCount) { ConditionalRenderEnabled renderEnable = GetRenderEnable(state); @@ -62,7 +68,7 @@ namespace Ryujinx.Graphics.Gpu.Engine return; } - UpdateState(state); + UpdateState(state, firstIndex, indexCount); bool instanced = _vsUsesInstanceId || _isAnyVbInstanced; @@ -72,11 +78,11 @@ namespace Ryujinx.Graphics.Gpu.Engine _instancedIndexed = _drawIndexed; - _instancedFirstIndex = _firstIndex; - _instancedFirstVertex = state.Get(MethodOffset.FirstVertex); + _instancedFirstIndex = firstIndex; + _instancedFirstVertex = state.Get(MethodOffset.FirstVertex); _instancedFirstInstance = state.Get(MethodOffset.FirstInstance); - _instancedIndexCount = _indexCount; + _instancedIndexCount = indexCount; var drawState = state.Get(MethodOffset.VertexBufferDrawState); @@ -95,31 +101,31 @@ namespace Ryujinx.Graphics.Gpu.Engine int firstInstance = state.Get(MethodOffset.FirstInstance); - if (_inlineIndexCount != 0) + int inlineIndexCount = _ibStreamer.GetAndResetInlineIndexCount(); + + if (inlineIndexCount != 0) { int firstVertex = state.Get(MethodOffset.FirstVertex); - BufferRange br = new BufferRange(_inlineIndexBuffer, 0, _inlineIndexCount * 4); + BufferRange br = new BufferRange(_ibStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4); _context.Methods.BufferManager.SetIndexBuffer(br, IndexType.UInt); _context.Renderer.Pipeline.DrawIndexed( - _inlineIndexCount, + inlineIndexCount, 1, - _firstIndex, + firstIndex, firstVertex, firstInstance); - - _inlineIndexCount = 0; } else if (_drawIndexed) { int firstVertex = state.Get(MethodOffset.FirstVertex); _context.Renderer.Pipeline.DrawIndexed( - _indexCount, + indexCount, 1, - _firstIndex, + firstIndex, firstVertex, firstInstance); } @@ -150,22 +156,46 @@ namespace Ryujinx.Graphics.Gpu.Engine /// Method call argument private void DrawBegin(GpuState state, int argument) { - if ((argument & (1 << 26)) != 0) + bool incrementInstance = (argument & (1 << 26)) != 0; + bool resetInstance = (argument & (1 << 27)) == 0; + + PrimitiveType type = (PrimitiveType)(argument & 0xffff); + + PrimitiveTypeOverride typeOverride = state.Get(MethodOffset.PrimitiveTypeOverride); + + if (typeOverride != PrimitiveTypeOverride.Invalid) + { + DrawBegin(incrementInstance, resetInstance, typeOverride.Convert()); + } + else + { + DrawBegin(incrementInstance, resetInstance, type.Convert()); + } + } + + /// + /// Starts draw. + /// This sets primitive type and instanced draw parameters. + /// + /// Indicates if the current instance should be incremented + /// Indicates if the current instance should be set to zero + /// Primitive topology + private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveTopology topology) + { + if (incrementInstance) { _instanceIndex++; } - else if ((argument & (1 << 27)) == 0) + else if (resetInstance) { PerformDeferredDraws(); _instanceIndex = 0; } - PrimitiveType type = (PrimitiveType)(argument & 0xffff); - - _context.Renderer.Pipeline.SetPrimitiveTopology(type.Convert()); + _context.Renderer.Pipeline.SetPrimitiveTopology(topology); - PrimitiveType = type; + Topology = topology; } /// @@ -180,100 +210,100 @@ namespace Ryujinx.Graphics.Gpu.Engine } /// - /// Pushes four 8-bit index buffer elements. + /// Performs a indexed draw with a low number of index buffer elements. /// /// Current GPU state /// Method call argument - private void VbElementU8(GpuState state, int argument) + private void DrawIndexedSmall(GpuState state, int argument) { - byte i0 = (byte)argument; - byte i1 = (byte)(argument >> 8); - byte i2 = (byte)(argument >> 16); - byte i3 = (byte)(argument >> 24); - - Span data = stackalloc uint[4]; - - data[0] = i0; - data[1] = i1; - data[2] = i2; - data[3] = i3; + DrawIndexedSmall(state, argument, false); + } - int offset = _inlineIndexCount * 4; + /// + /// Performs a indexed draw with a low number of index buffer elements. + /// + /// Current GPU state + /// Method call argument + private void DrawIndexedSmall2(GpuState state, int argument) + { + DrawIndexedSmall(state, argument); + } - _context.Renderer.SetBufferData(GetInlineIndexBuffer(offset), offset, MemoryMarshal.Cast(data)); + /// + /// Performs a indexed draw with a low number of index buffer elements, + /// while also pre-incrementing the current instance value. + /// + /// Current GPU state + /// Method call argument + private void DrawIndexedSmallIncInstance(GpuState state, int argument) + { + DrawIndexedSmall(state, argument, true); + } - _inlineIndexCount += 4; + /// + /// Performs a indexed draw with a low number of index buffer elements, + /// while also pre-incrementing the current instance value. + /// + /// Current GPU state + /// Method call argument + private void DrawIndexedSmallIncInstance2(GpuState state, int argument) + { + DrawIndexedSmallIncInstance(state, argument); } /// - /// Pushes two 16-bit index buffer elements. + /// Performs a indexed draw with a low number of index buffer elements, + /// while optionally also pre-incrementing the current instance value. /// /// Current GPU state /// Method call argument - private void VbElementU16(GpuState state, int argument) + /// True to increment the current instance value, false otherwise + private void DrawIndexedSmall(GpuState state, int argument, bool instanced) { - ushort i0 = (ushort)argument; - ushort i1 = (ushort)(argument >> 16); + PrimitiveTypeOverride typeOverride = state.Get(MethodOffset.PrimitiveTypeOverride); + + DrawBegin(instanced, !instanced, typeOverride.Convert()); - Span data = stackalloc uint[2]; + int firstIndex = argument & 0xffff; + int indexCount = (argument >> 16) & 0xfff; - data[0] = i0; - data[1] = i1; + bool oldDrawIndexed = _drawIndexed; - int offset = _inlineIndexCount * 4; + _drawIndexed = true; - _context.Renderer.SetBufferData(GetInlineIndexBuffer(offset), offset, MemoryMarshal.Cast(data)); + DrawEnd(state, firstIndex, indexCount); - _inlineIndexCount += 2; + _drawIndexed = oldDrawIndexed; } /// - /// Pushes one 32-bit index buffer element. + /// Pushes four 8-bit index buffer elements. /// /// Current GPU state /// Method call argument - private void VbElementU32(GpuState state, int argument) + private void VbElementU8(GpuState state, int argument) { - uint i0 = (uint)argument; - - Span data = stackalloc uint[1]; - - data[0] = i0; - - int offset = _inlineIndexCount++ * 4; - - _context.Renderer.SetBufferData(GetInlineIndexBuffer(offset), offset, MemoryMarshal.Cast(data)); + _ibStreamer.VbElementU8(_context.Renderer, argument); } /// - /// Gets the handle of a buffer large enough to hold the data that will be written to . + /// Pushes two 16-bit index buffer elements. /// - /// Offset where the data will be written - /// Buffer handle - private BufferHandle GetInlineIndexBuffer(int offset) + /// Current GPU state + /// Method call argument + private void VbElementU16(GpuState state, int argument) { - // 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); - } + _ibStreamer.VbElementU16(_context.Renderer, argument); + } - return _inlineIndexBuffer; + /// + /// Pushes one 32-bit index buffer element. + /// + /// Current GPU state + /// Method call argument + private void VbElementU32(GpuState state, int argument) + { + _ibStreamer.VbElementU32(_context.Renderer, argument); } /// -- cgit v1.2.3