diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index 63d221508..14b177aa4 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Memory private const ulong BufferAlignmentSize = 0x1000; private const ulong BufferAlignmentMask = BufferAlignmentSize - 1; + private const ulong MaxDynamicGrowthSize = 0x100000; + private readonly GpuContext _context; 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 // that fits all the data we need. We also need to copy the contents from the // old buffer(s) to the new buffer. + ulong endAddress = address + size; 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++) { 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) { @@ -202,7 +231,7 @@ namespace Ryujinx.Graphics.Gpu.Memory buffer.DisposeData(); } - newBuffer.SynchronizeMemory(address, endAddress - address); + newBuffer.SynchronizeMemory(address, newSize); // Existing buffers were modified, we need to rebind everything. NotifyBuffersModified?.Invoke();