aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Memory/VirtualBufferCache.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2024-02-22 11:03:07 -0300
committerGitHub <noreply@github.com>2024-02-22 11:03:07 -0300
commit167f50bbcd5b2378a038e540877be4d9b71a12f6 (patch)
treeb425e6529ade9eb52dca3f9ff92ea767a78224b4 /src/Ryujinx.Graphics.Gpu/Memory/VirtualBufferCache.cs
parentba91f5d401bf5f9fc39aaa30feac2600e82f9c42 (diff)
Implement virtual buffer dependencies (#6190)
* Implement virtual buffer copies * Introduce TranslateAndCreateMultiBuffersPhysicalOnly, use it for copy and clear * Rename VirtualBufferCache to VirtualRangeCache * Fix potential issue where virtual range could exist in the cache, without a physical buffer * Fix bug that could cause copy with negative size on CopyToDependantVirtualBuffer * Remove virtual copy back for SyncAction * GetData XML docs * Make field readonly * Fix virtual buffer modification tracking * Remove CopyFromDependantVirtualBuffers from ExternalFlush * Move things around a little to avoid perf impact - Inline null check for CopyFromDependantVirtualBuffers - Remove extra method call for SynchronizeMemoryWithVirtualCopyBack, prefer calling CopyFromDependantVirtualBuffers separately * Fix up XML doc --------- Co-authored-by: riperiperi <rhy3756547@hotmail.com>
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Memory/VirtualBufferCache.cs')
-rw-r--r--src/Ryujinx.Graphics.Gpu/Memory/VirtualBufferCache.cs238
1 files changed, 0 insertions, 238 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Memory/VirtualBufferCache.cs b/src/Ryujinx.Graphics.Gpu/Memory/VirtualBufferCache.cs
deleted file mode 100644
index 858c5e3b..00000000
--- a/src/Ryujinx.Graphics.Gpu/Memory/VirtualBufferCache.cs
+++ /dev/null
@@ -1,238 +0,0 @@
-using Ryujinx.Memory.Range;
-using System;
-using System.Collections.Concurrent;
-using System.Threading;
-
-namespace Ryujinx.Graphics.Gpu.Memory
-{
- /// <summary>
- /// Virtual buffer cache.
- /// </summary>
- class VirtualBufferCache
- {
- private readonly MemoryManager _memoryManager;
-
- /// <summary>
- /// Represents a GPU virtual memory range.
- /// </summary>
- private readonly struct VirtualRange : IRange
- {
- /// <summary>
- /// GPU virtual address where the range starts.
- /// </summary>
- public ulong Address { get; }
-
- /// <summary>
- /// Size of the range in bytes.
- /// </summary>
- public ulong Size { get; }
-
- /// <summary>
- /// GPU virtual address where the range ends.
- /// </summary>
- public ulong EndAddress => Address + Size;
-
- /// <summary>
- /// Physical regions where the GPU virtual region is mapped.
- /// </summary>
- public MultiRange Range { get; }
-
- /// <summary>
- /// Creates a new virtual memory range.
- /// </summary>
- /// <param name="address">GPU virtual address where the range starts</param>
- /// <param name="size">Size of the range in bytes</param>
- /// <param name="range">Physical regions where the GPU virtual region is mapped</param>
- public VirtualRange(ulong address, ulong size, MultiRange range)
- {
- Address = address;
- Size = size;
- Range = range;
- }
-
- /// <summary>
- /// Checks if a given range overlaps with the buffer.
- /// </summary>
- /// <param name="address">Start address of the range</param>
- /// <param name="size">Size in bytes of the range</param>
- /// <returns>True if the range overlaps, false otherwise</returns>
- public bool OverlapsWith(ulong address, ulong size)
- {
- return Address < address + size && address < EndAddress;
- }
- }
-
- private readonly RangeList<VirtualRange> _virtualRanges;
- private VirtualRange[] _virtualRangeOverlaps;
- private readonly ConcurrentQueue<VirtualRange> _deferredUnmaps;
- private int _hasDeferredUnmaps;
-
- /// <summary>
- /// Creates a new instance of the virtual buffer cache.
- /// </summary>
- /// <param name="memoryManager">Memory manager that the virtual buffer cache belongs to</param>
- public VirtualBufferCache(MemoryManager memoryManager)
- {
- _memoryManager = memoryManager;
- _virtualRanges = new RangeList<VirtualRange>();
- _virtualRangeOverlaps = new VirtualRange[BufferCache.OverlapsBufferInitialCapacity];
- _deferredUnmaps = new ConcurrentQueue<VirtualRange>();
- }
-
- /// <summary>
- /// Handles removal of buffers written to a memory region being unmapped.
- /// </summary>
- /// <param name="sender">Sender object</param>
- /// <param name="e">Event arguments</param>
- public void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
- {
- void EnqueueUnmap()
- {
- _deferredUnmaps.Enqueue(new VirtualRange(e.Address, e.Size, default));
-
- Interlocked.Exchange(ref _hasDeferredUnmaps, 1);
- }
-
- e.AddRemapAction(EnqueueUnmap);
- }
-
- /// <summary>
- /// Tries to get a existing, cached physical range for the specified virtual region.
- /// If no cached range is found, a new one is created and added.
- /// </summary>
- /// <param name="gpuVa">GPU virtual address to get the physical range from</param>
- /// <param name="size">Size in bytes of the region</param>
- /// <param name="supportsSparse">Indicates host support for sparse buffer mapping of non-contiguous ranges</param>
- /// <param name="range">Physical range for the specified GPU virtual region</param>
- /// <returns>True if the range already existed, false if a new one was created and added</returns>
- public bool TryGetOrAddRange(ulong gpuVa, ulong size, bool supportsSparse, out MultiRange range)
- {
- VirtualRange[] overlaps = _virtualRangeOverlaps;
- int overlapsCount;
-
- if (Interlocked.Exchange(ref _hasDeferredUnmaps, 0) != 0)
- {
- while (_deferredUnmaps.TryDequeue(out VirtualRange unmappedRange))
- {
- overlapsCount = _virtualRanges.FindOverlapsNonOverlapping(unmappedRange.Address, unmappedRange.Size, ref overlaps);
-
- for (int index = 0; index < overlapsCount; index++)
- {
- _virtualRanges.Remove(overlaps[index]);
- }
- }
- }
-
- bool found = false;
-
- ulong originalVa = gpuVa;
-
- overlapsCount = _virtualRanges.FindOverlapsNonOverlapping(gpuVa, size, ref overlaps);
-
- if (overlapsCount != 0)
- {
- // The virtual range already exists. We just need to check if our range fits inside
- // the existing one, and if not, we must extend the existing one.
-
- ulong endAddress = gpuVa + size;
- VirtualRange overlap0 = overlaps[0];
-
- if (overlap0.Address > gpuVa || overlap0.EndAddress < endAddress)
- {
- for (int index = 0; index < overlapsCount; index++)
- {
- VirtualRange virtualRange = overlaps[index];
-
- gpuVa = Math.Min(gpuVa, virtualRange.Address);
- endAddress = Math.Max(endAddress, virtualRange.EndAddress);
-
- _virtualRanges.Remove(virtualRange);
- }
-
- ulong newSize = endAddress - gpuVa;
- MultiRange newRange = _memoryManager.GetPhysicalRegions(gpuVa, newSize);
-
- _virtualRanges.Add(new(gpuVa, newSize, newRange));
-
- range = newRange.Slice(originalVa - gpuVa, size);
- }
- else
- {
- found = true;
- range = overlap0.Range.Slice(gpuVa - overlap0.Address, size);
- }
- }
- else
- {
- // No overlap, just create a new virtual range.
- range = _memoryManager.GetPhysicalRegions(gpuVa, size);
-
- VirtualRange virtualRange = new(gpuVa, size, range);
-
- _virtualRanges.Add(virtualRange);
- }
-
- ShrinkOverlapsBufferIfNeeded();
-
- // If the the range is not properly aligned for sparse mapping,
- // or if the host does not support sparse mapping, let's just
- // force it to a single range.
- // This might cause issues in some applications that uses sparse
- // mappings.
- if (!IsSparseAligned(range) || !supportsSparse)
- {
- range = new MultiRange(range.GetSubRange(0).Address, size);
- }
-
- return found;
- }
-
- /// <summary>
- /// Checks if the physical memory ranges are valid for sparse mapping,
- /// which requires all sub-ranges to be 64KB aligned.
- /// </summary>
- /// <param name="range">Range to check</param>
- /// <returns>True if the range is valid for sparse mapping, false otherwise</returns>
- private static bool IsSparseAligned(MultiRange range)
- {
- if (range.Count == 1)
- {
- return (range.GetSubRange(0).Address & (BufferCache.SparseBufferAlignmentSize - 1)) == 0;
- }
-
- for (int i = 0; i < range.Count; i++)
- {
- MemoryRange subRange = range.GetSubRange(i);
-
- // Check if address is aligned. The address of the first sub-range can
- // be misaligned as it is at the start.
- if (i > 0 &&
- subRange.Address != MemoryManager.PteUnmapped &&
- (subRange.Address & (BufferCache.SparseBufferAlignmentSize - 1)) != 0)
- {
- return false;
- }
-
- // Check if the size is aligned. The size of the last sub-range can
- // be misaligned as it is at the end.
- if (i < range.Count - 1 && (subRange.Size & (BufferCache.SparseBufferAlignmentSize - 1)) != 0)
- {
- return false;
- }
- }
-
- return true;
- }
-
- /// <summary>
- /// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
- /// </summary>
- private void ShrinkOverlapsBufferIfNeeded()
- {
- if (_virtualRangeOverlaps.Length > BufferCache.OverlapsBufferMaxCapacity)
- {
- Array.Resize(ref _virtualRangeOverlaps, BufferCache.OverlapsBufferMaxCapacity);
- }
- }
- }
-}