Dynamically increase buffer size when resizing (#2861)

* Grow buffers by 1.5x of its size when resizing

* Further restrict the cases where the dynamic expansion is done
This commit is contained in:
gdkchan 2022-03-14 23:33:53 -03:00 committed by GitHub
parent 223172ac0b
commit 79becc4b78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
private const ulong BufferAlignmentSize = 0x1000; private const ulong BufferAlignmentSize = 0x1000;
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1; private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
private const ulong MaxDynamicGrowthSize = 0x100000;
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly PhysicalMemory _physicalMemory; private readonly PhysicalMemory _physicalMemory;
@ -166,10 +168,35 @@ namespace Ryujinx.Graphics.Gpu.Memory
// Otherwise, we must delete the overlapping buffers and create a bigger buffer // Otherwise, we must delete the overlapping buffers and create a bigger buffer
// that fits all the data we need. We also need to copy the contents from the // that fits all the data we need. We also need to copy the contents from the
// old buffer(s) to the new buffer. // old buffer(s) to the new buffer.
ulong endAddress = address + size; ulong endAddress = address + size;
if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress) if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress)
{ {
// Check if the following conditions are met:
// - We have a single overlap.
// - The overlap starts at or before the requested range. That is, the overlap happens at the end.
// - The size delta between the new, merged buffer and the old one is of at most 2 pages.
// In this case, we attempt to extend the buffer further than the requested range,
// this can potentially avoid future resizes if the application keeps using overlapping
// sequential memory.
// Allowing for 2 pages (rather than just one) is necessary to catch cases where the
// range crosses a page, and after alignment, ends having a size of 2 pages.
if (overlapsCount == 1 &&
address >= _bufferOverlaps[0].Address &&
endAddress - _bufferOverlaps[0].EndAddress <= BufferAlignmentSize * 2)
{
// Try to grow the buffer by 1.5x of its current size.
// This improves performance in the cases where the buffer is resized often by small amounts.
ulong existingSize = _bufferOverlaps[0].Size;
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
size = Math.Max(size, growthSize);
endAddress = address + size;
overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
}
for (int index = 0; index < overlapsCount; index++) for (int index = 0; index < overlapsCount; index++)
{ {
Buffer buffer = _bufferOverlaps[index]; Buffer buffer = _bufferOverlaps[index];
@ -183,7 +210,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
} }
Buffer newBuffer = new Buffer(_context, _physicalMemory, address, endAddress - address, _bufferOverlaps.Take(overlapsCount)); ulong newSize = endAddress - address;
Buffer newBuffer = new Buffer(_context, _physicalMemory, address, newSize, _bufferOverlaps.Take(overlapsCount));
lock (_buffers) lock (_buffers)
{ {
@ -202,7 +231,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
buffer.DisposeData(); buffer.DisposeData();
} }
newBuffer.SynchronizeMemory(address, endAddress - address); newBuffer.SynchronizeMemory(address, newSize);
// Existing buffers were modified, we need to rebind everything. // Existing buffers were modified, we need to rebind everything.
NotifyBuffersModified?.Invoke(); NotifyBuffersModified?.Invoke();