diff --git a/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs b/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs index 79432395..f6a99440 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs +++ b/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); } diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs b/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs index d70402e9..5d41dafd 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs +++ b/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; + /// /// Primitive type of the current draw. /// @@ -87,10 +95,25 @@ namespace Ryujinx.Graphics.Gpu.Engine int firstInstance = state.Get(MethodOffset.FirstInstance); - if (_drawIndexed) + if (_inlineIndexCount != 0) { - _drawIndexed = false; + int firstVertex = state.Get(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(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; } + /// + /// Pushes four 8-bit index buffer elements. + /// + /// Current GPU state + /// Method call argument + 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 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(data)); + + _inlineIndexCount += 4; + } + + /// + /// Pushes two 16-bit index buffer elements. + /// + /// Current GPU state + /// Method call argument + private void VbElementU16(GpuState state, int argument) + { + ushort i0 = (ushort)argument; + ushort i1 = (ushort)(argument >> 16); + + Span data = stackalloc uint[2]; + + data[0] = i0; + data[1] = i1; + + int offset = _inlineIndexCount * 4; + + _context.Renderer.SetBufferData(GetInlineIndexBuffer(offset), offset, MemoryMarshal.Cast(data)); + + _inlineIndexCount += 2; + } + + /// + /// Pushes one 32-bit index buffer element. + /// + /// Current GPU state + /// Method call argument + private void VbElementU32(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)); + } + + /// + /// Gets the handle of a buffer large enough to hold the data that will be written to . + /// + /// Offset where the data will be written + /// Buffer handle + 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; + } + /// /// Perform any deferred draws. /// This is used for instanced draws. diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index fc933131..af32c6bc 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/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. diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 39d1cd6f..533b0576 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -103,6 +103,18 @@ namespace Ryujinx.Graphics.Gpu.Memory _indexBufferDirty = true; } + /// + /// Sets a new index buffer that overrides the one set on the call to . + /// + /// Buffer to be used as index buffer + /// Type of each index buffer element + public void SetIndexBuffer(BufferRange buffer, IndexType type) + { + _context.Renderer.Pipeline.SetIndexBuffer(buffer, type); + + _indexBufferDirty = true; + } + /// /// Sets the memory range with vertex buffer data, to be used for subsequent draw calls. /// diff --git a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs index da54a469..2e0a197e 100644 --- a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs +++ b/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,