mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-10-02 01:28:58 +02:00
Memory changes 2.2
A few more internal changes to the RangeList systems. * No longer using a QuickAccess dictionary. * The performance of the dictionary wasn't much faster than just doing binary searches. * Using just binary searches allows us to take advantage of span and array returns as they're are faster than linked lists when iterating or copying the overlaps. Small code optimizations. Fixes a few leftover crashes.
This commit is contained in:
@@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
ulong size,
|
||||
BufferStage stage,
|
||||
bool sparseCompatible,
|
||||
List<Buffer> baseBuffers)
|
||||
RangeItem<Buffer>[] baseBuffers)
|
||||
{
|
||||
_context = context;
|
||||
_physicalMemory = physicalMemory;
|
||||
@@ -128,18 +128,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
List<IRegionHandle> baseHandles = null;
|
||||
|
||||
if (baseBuffers.Count != 0)
|
||||
if (baseBuffers.Length != 0)
|
||||
{
|
||||
baseHandles = new List<IRegionHandle>();
|
||||
foreach (Buffer buffer in baseBuffers)
|
||||
foreach (RangeItem<Buffer> item in baseBuffers)
|
||||
{
|
||||
if (buffer._useGranular)
|
||||
if (item.Value._useGranular)
|
||||
{
|
||||
baseHandles.AddRange((buffer._memoryTrackingGranular.GetHandles()));
|
||||
baseHandles.AddRange((item.Value._memoryTrackingGranular.GetHandles()));
|
||||
}
|
||||
else
|
||||
{
|
||||
baseHandles.Add(buffer._memoryTracking);
|
||||
baseHandles.Add(item.Value._memoryTracking);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Memory.Range;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -56,7 +57,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// <param name="parent">Parent buffer</param>
|
||||
/// <param name="stage">Initial buffer stage</param>
|
||||
/// <param name="baseBuffers">Buffers to inherit state from</param>
|
||||
public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, List<Buffer> baseBuffers)
|
||||
public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, RangeItem<Buffer>[] baseBuffers)
|
||||
{
|
||||
_size = (int)parent.Size;
|
||||
_systemMemoryType = context.Capabilities.MemoryType;
|
||||
@@ -72,7 +73,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
BufferStage storageFlags = stage & BufferStage.StorageMask;
|
||||
|
||||
if (parent.Size > DeviceLocalSizeThreshold && baseBuffers.Count == 0)
|
||||
if (parent.Size > DeviceLocalSizeThreshold && baseBuffers.Length == 0)
|
||||
{
|
||||
_desiredType = BufferBackingType.DeviceMemory;
|
||||
}
|
||||
@@ -100,11 +101,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
// TODO: Might be nice to force atomic access to be device local for any stage.
|
||||
}
|
||||
|
||||
if (baseBuffers.Count != 0)
|
||||
if (baseBuffers.Length != 0)
|
||||
{
|
||||
foreach (Buffer buffer in baseBuffers)
|
||||
foreach (RangeItem<Buffer> item in baseBuffers)
|
||||
{
|
||||
CombineState(buffer.BackingState);
|
||||
CombineState(item.Value.BackingState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,13 +81,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
MemoryRange subRange = range.GetSubRange(index);
|
||||
|
||||
_buffers.Lock.EnterReadLock();
|
||||
(RangeItem<Buffer> first, RangeItem<Buffer> last) = _buffers.FindOverlaps(subRange.Address, subRange.Size);
|
||||
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(subRange.Address, subRange.Size);
|
||||
|
||||
RangeItem<Buffer> current = first;
|
||||
while (last != null && current != last.Next)
|
||||
for (int i = 0; i < overlaps.Length; i++)
|
||||
{
|
||||
current.Value.Unmapped(subRange.Address, subRange.Size);
|
||||
current = current.Next;
|
||||
overlaps[i].Value.Unmapped(subRange.Address, subRange.Size);
|
||||
}
|
||||
|
||||
_buffers.Lock.ExitReadLock();
|
||||
@@ -490,9 +488,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage)
|
||||
{
|
||||
_buffers.Lock.EnterWriteLock();
|
||||
(RangeItem<Buffer> first, RangeItem<Buffer> last) = _buffers.FindOverlaps(address, size);
|
||||
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(address, size);
|
||||
|
||||
if (first is not null)
|
||||
if (overlaps.Length > 0)
|
||||
{
|
||||
// The buffer already exists. We can just return the existing buffer
|
||||
// if the buffer we need is fully contained inside the overlapping buffer.
|
||||
@@ -502,7 +500,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
ulong endAddress = address + size;
|
||||
|
||||
if (first.Address > address || first.EndAddress < endAddress)
|
||||
if (overlaps[0].Address > address || overlaps[0].EndAddress < endAddress)
|
||||
{
|
||||
bool anySparseCompatible = false;
|
||||
|
||||
@@ -515,39 +513,40 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
// 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 (first == last &&
|
||||
address >= first.Address &&
|
||||
endAddress - first.EndAddress <= BufferAlignmentSize * 2)
|
||||
if (overlaps.Length == 1 &&
|
||||
address >= overlaps[0].Address &&
|
||||
endAddress - overlaps[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 = first.Value.Size;
|
||||
ulong existingSize = overlaps[0].Value.Size;
|
||||
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
|
||||
|
||||
size = Math.Max(size, growthSize);
|
||||
endAddress = address + size;
|
||||
|
||||
(first, last) = _buffers.FindOverlaps(address, size);
|
||||
overlaps = _buffers.FindOverlapsAsSpan(address, size);
|
||||
}
|
||||
|
||||
address = Math.Min(address, first.Address);
|
||||
endAddress = Math.Max(endAddress, last.EndAddress);
|
||||
address = Math.Min(address, overlaps[0].Address);
|
||||
endAddress = Math.Max(endAddress, overlaps[^1].EndAddress);
|
||||
|
||||
List<Buffer> overlaps = [];
|
||||
RangeItem<Buffer>[] overlapsArray = overlaps.ToArray();
|
||||
|
||||
RangeItem<Buffer> current = first;
|
||||
while (current != last.Next)
|
||||
for (int i = 0; i < overlaps.Length; i++)
|
||||
{
|
||||
anySparseCompatible |= current.Value.SparseCompatible;
|
||||
overlaps.Add(current.Value);
|
||||
_buffers.Remove(current.Value);
|
||||
|
||||
current = current.Next;
|
||||
anySparseCompatible |= overlaps[i].Value.SparseCompatible;
|
||||
}
|
||||
|
||||
_buffers.RemoveRange(overlaps[0], overlaps[^1]);
|
||||
|
||||
_buffers.Lock.ExitWriteLock();
|
||||
|
||||
ulong newSize = endAddress - address;
|
||||
|
||||
Buffer newBuffer = CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlaps);
|
||||
Buffer newBuffer = CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlapsArray);
|
||||
|
||||
_buffers.Lock.EnterWriteLock();
|
||||
|
||||
_buffers.Add(newBuffer);
|
||||
}
|
||||
@@ -577,19 +576,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
bool sparseAligned = alignment >= SparseBufferAlignmentSize;
|
||||
|
||||
_buffers.Lock.EnterWriteLock();
|
||||
(RangeItem<Buffer> first, RangeItem<Buffer> last) = _buffers.FindOverlaps(address, size);
|
||||
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(address, size);
|
||||
|
||||
if (first is not null)
|
||||
if (overlaps.Length > 0)
|
||||
{
|
||||
// If the buffer already exists, make sure if covers the entire range,
|
||||
// and make sure it is properly aligned, otherwise sparse mapping may fail.
|
||||
|
||||
ulong endAddress = address + size;
|
||||
|
||||
if (first.Address > address ||
|
||||
first.EndAddress < endAddress ||
|
||||
(first.Address & (alignment - 1)) != 0 ||
|
||||
(!first.Value.SparseCompatible && sparseAligned))
|
||||
if (overlaps[0].Address > address ||
|
||||
overlaps[0].EndAddress < endAddress ||
|
||||
(overlaps[0].Address & (alignment - 1)) != 0 ||
|
||||
(!overlaps[0].Value.SparseCompatible && sparseAligned))
|
||||
{
|
||||
// We need to make sure the new buffer is properly aligned.
|
||||
// However, after the range is aligned, it is possible that it
|
||||
@@ -597,33 +596,30 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
// and ensure we cover all overlaps.
|
||||
|
||||
RangeItem<Buffer> oldFirst;
|
||||
endAddress = Math.Max(endAddress, last.EndAddress);
|
||||
endAddress = Math.Max(endAddress, overlaps[^1].EndAddress);
|
||||
|
||||
do
|
||||
{
|
||||
address = Math.Min(address, first.Address);
|
||||
address = Math.Min(address, overlaps[0].Address);
|
||||
|
||||
address &= ~(alignment - 1);
|
||||
|
||||
oldFirst = first;
|
||||
(first, last) = _buffers.FindOverlaps(address, endAddress - address);
|
||||
oldFirst = overlaps[0];
|
||||
overlaps = _buffers.FindOverlapsAsSpan(address, endAddress - address);
|
||||
}
|
||||
while (oldFirst != first);
|
||||
while (oldFirst != overlaps[0]);
|
||||
|
||||
ulong newSize = endAddress - address;
|
||||
|
||||
List<Buffer> overlaps = [];
|
||||
RangeItem<Buffer>[] overlapsArray = overlaps.ToArray();
|
||||
|
||||
RangeItem<Buffer> current = first;
|
||||
while (current != last.Next)
|
||||
{
|
||||
overlaps.Add(current.Value);
|
||||
_buffers.Remove(current.Value);
|
||||
|
||||
current = current.Next;
|
||||
}
|
||||
_buffers.RemoveRange(overlaps[0], overlaps[^1]);
|
||||
|
||||
Buffer newBuffer = CreateBufferAligned(address, newSize, stage, sparseAligned, overlaps);
|
||||
_buffers.Lock.ExitWriteLock();
|
||||
|
||||
Buffer newBuffer = CreateBufferAligned(address, newSize, stage, sparseAligned, overlapsArray);
|
||||
|
||||
_buffers.Lock.EnterWriteLock();
|
||||
|
||||
_buffers.Add(newBuffer);
|
||||
}
|
||||
@@ -635,6 +631,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
_buffers.Add(buffer);
|
||||
}
|
||||
|
||||
_buffers.Lock.ExitWriteLock();
|
||||
}
|
||||
|
||||
@@ -648,13 +645,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// <param name="stage">The type of usage that created the buffer</param>
|
||||
/// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param>
|
||||
/// <param name="overlaps">Buffers overlapping the range</param>
|
||||
private Buffer CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, List<Buffer> overlaps)
|
||||
private Buffer CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, RangeItem<Buffer>[] overlaps)
|
||||
{
|
||||
Buffer newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible, overlaps);
|
||||
|
||||
for (int index = 0; index < overlaps.Count; index++)
|
||||
for (int index = 0; index < overlaps.Length; index++)
|
||||
{
|
||||
Buffer buffer = overlaps[index];
|
||||
Buffer buffer = overlaps[index].Value;
|
||||
|
||||
int dstOffset = (int)(buffer.Address - newBuffer.Address);
|
||||
|
||||
@@ -930,7 +927,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
buffer = _buffers.FindOverlapFast(address, size).Value;
|
||||
buffer = _buffers.FindOverlap(address, size).Value;
|
||||
|
||||
buffer.CopyFromDependantVirtualBuffers();
|
||||
buffer.SynchronizeMemory(address, size);
|
||||
|
||||
@@ -80,8 +80,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
private BufferMigration _source;
|
||||
private BufferModifiedRangeList _migrationTarget;
|
||||
|
||||
private List<RangeItem<BufferModifiedRange>> _overlaps;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the modified range list has any entries or not.
|
||||
@@ -108,7 +106,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
_context = context;
|
||||
_parent = parent;
|
||||
_flushAction = flushAction;
|
||||
_overlaps = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -122,12 +119,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
// Slices a given region using the modified regions in the list. Calls the action for the new slices.
|
||||
Lock.EnterReadLock();
|
||||
|
||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
|
||||
Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
|
||||
|
||||
RangeItem<BufferModifiedRange> current = first;
|
||||
while (last != null && current != last.Next)
|
||||
for (int i = 0; i < overlaps.Length; i++)
|
||||
{
|
||||
BufferModifiedRange overlap = current.Value;
|
||||
BufferModifiedRange overlap = overlaps[i].Value;
|
||||
|
||||
if (overlap.Address > address)
|
||||
{
|
||||
@@ -138,7 +134,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
// Remaining region is after this overlap.
|
||||
size -= overlap.EndAddress - address;
|
||||
address = overlap.EndAddress;
|
||||
current = current.Next;
|
||||
}
|
||||
|
||||
Lock.ExitReadLock();
|
||||
@@ -158,12 +153,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// <param name="size">Size of the modified region in bytes</param>
|
||||
public void SignalModified(ulong address, ulong size)
|
||||
{
|
||||
// We may overlap with some existing modified regions. They must be cut into by the new entry.
|
||||
Lock.EnterWriteLock();
|
||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
|
||||
|
||||
ulong endAddress = address + size;
|
||||
ulong syncNumber = _context.SyncNumber;
|
||||
// We may overlap with some existing modified regions. They must be cut into by the new entry.
|
||||
Lock.EnterWriteLock();
|
||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
|
||||
|
||||
if (first is null)
|
||||
{
|
||||
@@ -172,8 +166,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (first == last)
|
||||
{
|
||||
if (first.Address == address && first.EndAddress == endAddress)
|
||||
@@ -260,19 +252,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
public void GetRangesAtSync(ulong address, ulong size, ulong syncNumber, Action<ulong, ulong> rangeAction)
|
||||
{
|
||||
Lock.EnterReadLock();
|
||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
|
||||
Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
|
||||
|
||||
RangeItem<BufferModifiedRange> current = first;
|
||||
while (last != null && current != last.Next)
|
||||
for (int i = 0; i < overlaps.Length; i++)
|
||||
{
|
||||
BufferModifiedRange overlap = current.Value;
|
||||
BufferModifiedRange overlap = overlaps[i].Value;
|
||||
|
||||
if (overlap.SyncNumber == syncNumber)
|
||||
{
|
||||
rangeAction(overlap.Address, overlap.Size);
|
||||
}
|
||||
|
||||
current = current.Next;
|
||||
}
|
||||
|
||||
Lock.ExitReadLock();
|
||||
@@ -288,22 +277,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
// We use the non-span method here because keeping the lock will cause a deadlock.
|
||||
Lock.EnterReadLock();
|
||||
|
||||
_overlaps.Clear();
|
||||
|
||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
|
||||
|
||||
RangeItem<BufferModifiedRange> current = first;
|
||||
while (last != null && current != last.Next)
|
||||
{
|
||||
_overlaps.Add(current);
|
||||
current = current.Next;
|
||||
}
|
||||
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
|
||||
Lock.ExitReadLock();
|
||||
|
||||
for (int i = 0; i < _overlaps.Count; i++)
|
||||
for (int i = 0; i < overlaps.Length; i++)
|
||||
{
|
||||
BufferModifiedRange overlap = _overlaps[i].Value;
|
||||
BufferModifiedRange overlap = overlaps[i].Value;
|
||||
rangeAction(overlap.Address, overlap.Size);
|
||||
}
|
||||
}
|
||||
@@ -404,8 +383,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
ulong endAddress = address + size;
|
||||
ulong currentSync = _context.SyncNumber;
|
||||
|
||||
List<RangeItem<BufferModifiedRange>> overlaps = [];
|
||||
|
||||
// Range list must be consistent for this operation
|
||||
if (_migrationTarget != null)
|
||||
{
|
||||
@@ -416,16 +393,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
Lock.EnterWriteLock();
|
||||
// We use the non-span method here because the array is partially modified by the code, which would invalidate a span.
|
||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
|
||||
|
||||
RangeItem<BufferModifiedRange> current = first;
|
||||
while (last != null && current != last.Next)
|
||||
{
|
||||
overlaps.Add(current);
|
||||
current = current.Next;
|
||||
}
|
||||
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
|
||||
|
||||
int rangeCount = overlaps.Count;
|
||||
int rangeCount = overlaps.Length;
|
||||
|
||||
if (rangeCount == 0)
|
||||
{
|
||||
@@ -582,7 +552,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
ulong endAddress = address + size;
|
||||
Lock.EnterWriteLock();
|
||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
|
||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
|
||||
|
||||
if (first is null)
|
||||
{
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
ulong originalVa = gpuVa;
|
||||
|
||||
_virtualRanges.Lock.EnterWriteLock();
|
||||
(RangeItem<VirtualRange> first, RangeItem<VirtualRange> last) = _virtualRanges.FindOverlaps(gpuVa, size);
|
||||
(RangeItem<VirtualRange> first, RangeItem<VirtualRange> last) = _virtualRanges.FindOverlapsAsNodes(gpuVa, size);
|
||||
|
||||
if (first is not null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user