diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
index 61f227d93..7438ba03b 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
@@ -180,7 +180,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int firstInstance = (int)_state.State.FirstInstance;
- int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount();
+ int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer);
if (inlineIndexCount != 0)
{
@@ -670,7 +670,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
if (indexedInline)
{
- int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount();
+ int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer);
BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
_channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs
index 4862bca18..80d8c00b9 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs
@@ -11,9 +11,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
///
struct IbStreamer
{
+ private const int BufferCapacity = 256; // Must be a power of 2.
+
private BufferHandle _inlineIndexBuffer;
private int _inlineIndexBufferSize;
private int _inlineIndexCount;
+ private uint[] _buffer;
+ private int _bufferOffset;
///
/// Indicates if any index buffer data has been pushed.
@@ -38,9 +42,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Gets the number of elements on the current inline index buffer,
/// while also reseting it to zero for the next draw.
///
+ /// Host renderer
/// Inline index bufffer count
- public int GetAndResetInlineIndexCount()
+ public int GetAndResetInlineIndexCount(IRenderer renderer)
{
+ UpdateRemaining(renderer);
int temp = _inlineIndexCount;
_inlineIndexCount = 0;
return temp;
@@ -58,16 +64,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
byte i2 = (byte)(argument >> 16);
byte i3 = (byte)(argument >> 24);
- Span data = stackalloc uint[4];
+ int offset = _inlineIndexCount;
- data[0] = i0;
- data[1] = i1;
- data[2] = i2;
- data[3] = i3;
-
- int offset = _inlineIndexCount * 4;
-
- renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast(data));
+ PushData(renderer, offset, i0);
+ PushData(renderer, offset + 1, i1);
+ PushData(renderer, offset + 2, i2);
+ PushData(renderer, offset + 3, i3);
_inlineIndexCount += 4;
}
@@ -82,14 +84,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
ushort i0 = (ushort)argument;
ushort i1 = (ushort)(argument >> 16);
- Span data = stackalloc uint[2];
+ int offset = _inlineIndexCount;
- data[0] = i0;
- data[1] = i1;
-
- int offset = _inlineIndexCount * 4;
-
- renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast(data));
+ PushData(renderer, offset, i0);
+ PushData(renderer, offset + 1, i1);
_inlineIndexCount += 2;
}
@@ -103,13 +101,61 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
uint i0 = (uint)argument;
- Span data = stackalloc uint[1];
+ int offset = _inlineIndexCount++;
- data[0] = i0;
+ PushData(renderer, offset, i0);
+ }
- int offset = _inlineIndexCount++ * 4;
+ ///
+ /// Pushes a 32-bit value to the index buffer.
+ ///
+ /// Host renderer
+ /// Offset where the data should be written, in 32-bit words
+ /// Index value to be written
+ private void PushData(IRenderer renderer, int offset, uint value)
+ {
+ if (_buffer == null)
+ {
+ _buffer = new uint[BufferCapacity];
+ }
- renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast(data));
+ // We upload data in chunks.
+ // If we are at the start of a chunk, then the buffer might be full,
+ // in that case we need to submit any existing data before overwriting the buffer.
+ int subOffset = offset & (BufferCapacity - 1);
+
+ if (subOffset == 0 && offset != 0)
+ {
+ int baseOffset = (offset - BufferCapacity) * sizeof(uint);
+ BufferHandle buffer = GetInlineIndexBuffer(renderer, baseOffset, BufferCapacity * sizeof(uint));
+ renderer.SetBufferData(buffer, baseOffset, MemoryMarshal.Cast(_buffer));
+ }
+
+ _buffer[subOffset] = value;
+ }
+
+ ///
+ /// Makes sure that any pending data is submitted to the GPU before the index buffer is used.
+ ///
+ /// Host renderer
+ private void UpdateRemaining(IRenderer renderer)
+ {
+ int offset = _inlineIndexCount;
+ if (offset == 0)
+ {
+ return;
+ }
+
+ int count = offset & (BufferCapacity - 1);
+ if (count == 0)
+ {
+ count = BufferCapacity;
+ }
+
+ int baseOffset = (offset - count) * sizeof(uint);
+ int length = count * sizeof(uint);
+ BufferHandle buffer = GetInlineIndexBuffer(renderer, baseOffset, length);
+ renderer.SetBufferData(buffer, baseOffset, MemoryMarshal.Cast(_buffer).Slice(0, length));
}
///
@@ -117,12 +163,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
///
/// Host renderer
/// Offset where the data will be written
+ /// Number of bytes that will be written
/// Buffer handle
- private BufferHandle GetInlineIndexBuffer(IRenderer renderer, int offset)
+ private BufferHandle GetInlineIndexBuffer(IRenderer renderer, int offset, int length)
{
// 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);
+ int size = BitUtils.AlignUp(offset + length + 0x10, 0x200);
if (_inlineIndexBuffer == BufferHandle.Null)
{