Batch inline index buffer update (#4587)

This commit is contained in:
gdkchan 2023-03-24 10:19:54 -03:00 committed by GitHub
parent 80519af67d
commit 9ecbee8032
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 25 deletions

View File

@ -180,7 +180,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int firstInstance = (int)_state.State.FirstInstance; int firstInstance = (int)_state.State.FirstInstance;
int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(); int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer);
if (inlineIndexCount != 0) if (inlineIndexCount != 0)
{ {
@ -670,7 +670,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
if (indexedInline) if (indexedInline)
{ {
int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(); int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer);
BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4); BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
_channel.BufferManager.SetIndexBuffer(br, IndexType.UInt); _channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);

View File

@ -11,9 +11,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
struct IbStreamer struct IbStreamer
{ {
private const int BufferCapacity = 256; // Must be a power of 2.
private BufferHandle _inlineIndexBuffer; private BufferHandle _inlineIndexBuffer;
private int _inlineIndexBufferSize; private int _inlineIndexBufferSize;
private int _inlineIndexCount; private int _inlineIndexCount;
private uint[] _buffer;
private int _bufferOffset;
/// <summary> /// <summary>
/// Indicates if any index buffer data has been pushed. /// 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, /// Gets the number of elements on the current inline index buffer,
/// while also reseting it to zero for the next draw. /// while also reseting it to zero for the next draw.
/// </summary> /// </summary>
/// <param name="renderer">Host renderer</param>
/// <returns>Inline index bufffer count</returns> /// <returns>Inline index bufffer count</returns>
public int GetAndResetInlineIndexCount() public int GetAndResetInlineIndexCount(IRenderer renderer)
{ {
UpdateRemaining(renderer);
int temp = _inlineIndexCount; int temp = _inlineIndexCount;
_inlineIndexCount = 0; _inlineIndexCount = 0;
return temp; return temp;
@ -58,16 +64,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
byte i2 = (byte)(argument >> 16); byte i2 = (byte)(argument >> 16);
byte i3 = (byte)(argument >> 24); byte i3 = (byte)(argument >> 24);
Span<uint> data = stackalloc uint[4]; int offset = _inlineIndexCount;
data[0] = i0; PushData(renderer, offset, i0);
data[1] = i1; PushData(renderer, offset + 1, i1);
data[2] = i2; PushData(renderer, offset + 2, i2);
data[3] = i3; PushData(renderer, offset + 3, i3);
int offset = _inlineIndexCount * 4;
renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data));
_inlineIndexCount += 4; _inlineIndexCount += 4;
} }
@ -82,14 +84,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
ushort i0 = (ushort)argument; ushort i0 = (ushort)argument;
ushort i1 = (ushort)(argument >> 16); ushort i1 = (ushort)(argument >> 16);
Span<uint> data = stackalloc uint[2]; int offset = _inlineIndexCount;
data[0] = i0; PushData(renderer, offset, i0);
data[1] = i1; PushData(renderer, offset + 1, i1);
int offset = _inlineIndexCount * 4;
renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data));
_inlineIndexCount += 2; _inlineIndexCount += 2;
} }
@ -103,13 +101,61 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
uint i0 = (uint)argument; uint i0 = (uint)argument;
Span<uint> data = stackalloc uint[1]; int offset = _inlineIndexCount++;
data[0] = i0; PushData(renderer, offset, i0);
}
int offset = _inlineIndexCount++ * 4; /// <summary>
/// Pushes a 32-bit value to the index buffer.
/// </summary>
/// <param name="renderer">Host renderer</param>
/// <param name="offset">Offset where the data should be written, in 32-bit words</param>
/// <param name="value">Index value to be written</param>
private void PushData(IRenderer renderer, int offset, uint value)
{
if (_buffer == null)
{
_buffer = new uint[BufferCapacity];
}
renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(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<uint, byte>(_buffer));
}
_buffer[subOffset] = value;
}
/// <summary>
/// Makes sure that any pending data is submitted to the GPU before the index buffer is used.
/// </summary>
/// <param name="renderer">Host renderer</param>
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<uint, byte>(_buffer).Slice(0, length));
} }
/// <summary> /// <summary>
@ -117,12 +163,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
/// <param name="renderer">Host renderer</param> /// <param name="renderer">Host renderer</param>
/// <param name="offset">Offset where the data will be written</param> /// <param name="offset">Offset where the data will be written</param>
/// <param name="length">Number of bytes that will be written</param>
/// <returns>Buffer handle</returns> /// <returns>Buffer handle</returns>
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, // 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. // 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) if (_inlineIndexBuffer == BufferHandle.Null)
{ {