aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Memory
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Memory')
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/Buffer.cs74
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/BufferManager.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/IRange.cs13
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs4
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs41
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/RangeList.cs343
6 files changed, 80 insertions, 397 deletions
diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
index 2394f90d..3cc96432 100644
--- a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
@@ -1,4 +1,6 @@
+using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.GAL;
+using Ryujinx.Memory.Range;
using System;
namespace Ryujinx.Graphics.Gpu.Memory
@@ -8,6 +10,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
class Buffer : IRange, IDisposable
{
+ private static ulong GranularBufferThreshold = 4096;
+
private readonly GpuContext _context;
/// <summary>
@@ -30,9 +34,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public ulong EndAddress => Address + Size;
- private readonly (ulong, ulong)[] _modifiedRanges;
+ private CpuSmartMultiRegionHandle _memoryTrackingGranular;
+ private CpuRegionHandle _memoryTracking;
+ private int _sequenceNumber;
- private readonly int[] _sequenceNumbers;
+ private bool _useGranular;
/// <summary>
/// Creates a new instance of the buffer.
@@ -48,9 +54,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
Handle = context.Renderer.CreateBuffer((int)size);
- _modifiedRanges = new (ulong, ulong)[size / PhysicalMemory.PageSize];
+ _useGranular = size > GranularBufferThreshold;
- _sequenceNumbers = new int[size / MemoryManager.PageSize];
+ if (_useGranular)
+ {
+ _memoryTrackingGranular = context.PhysicalMemory.BeginSmartGranularTracking(address, size);
+ }
+ else
+ {
+ _memoryTracking = context.PhysicalMemory.BeginTracking(address, size);
+ }
}
/// <summary>
@@ -91,41 +104,35 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the range to synchronize</param>
public void SynchronizeMemory(ulong address, ulong size)
{
- int currentSequenceNumber = _context.SequenceNumber;
-
- bool needsSync = false;
-
- ulong buffOffset = address - Address;
-
- ulong buffEndOffset = (buffOffset + size + MemoryManager.PageMask) & ~MemoryManager.PageMask;
-
- int startIndex = (int)(buffOffset / MemoryManager.PageSize);
- int endIndex = (int)(buffEndOffset / MemoryManager.PageSize);
-
- for (int index = startIndex; index < endIndex; index++)
+ if (_useGranular)
{
- if (_sequenceNumbers[index] != currentSequenceNumber)
+ _memoryTrackingGranular.QueryModified(address, size, (ulong mAddress, ulong mSize) =>
{
- _sequenceNumbers[index] = currentSequenceNumber;
+ if (mAddress < Address)
+ {
+ mAddress = Address;
+ }
- needsSync = true;
- }
- }
+ ulong maxSize = Address + Size - mAddress;
- if (!needsSync)
- {
- return;
- }
+ if (mSize > maxSize)
+ {
+ mSize = maxSize;
+ }
- int count = _context.PhysicalMemory.QueryModified(address, size, ResourceName.Buffer, _modifiedRanges);
+ int offset = (int)(mAddress - Address);
- for (int index = 0; index < count; index++)
+ _context.Renderer.SetBufferData(Handle, offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize));
+ }, _context.SequenceNumber);
+ }
+ else
{
- (ulong mAddress, ulong mSize) = _modifiedRanges[index];
-
- int offset = (int)(mAddress - Address);
-
- _context.Renderer.SetBufferData(Handle, offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize));
+ if (_memoryTracking.Dirty && _context.SequenceNumber != _sequenceNumber)
+ {
+ _memoryTracking.Reprotect();
+ _context.Renderer.SetBufferData(Handle, 0, _context.PhysicalMemory.GetSpan(Address, (int)Size));
+ _sequenceNumber = _context.SequenceNumber;
+ }
}
}
@@ -161,6 +168,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void Dispose()
{
_context.Renderer.DeleteBuffer(Handle);
+
+ _memoryTrackingGranular?.Dispose();
+ _memoryTracking?.Dispose();
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
index 41067a11..ee1be74b 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
@@ -2,6 +2,7 @@ using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Shader;
+using Ryujinx.Memory.Range;
using System;
namespace Ryujinx.Graphics.Gpu.Memory
@@ -407,6 +408,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
Buffer newBuffer = new Buffer(_context, address, endAddress - address);
+ newBuffer.SynchronizeMemory(address, endAddress - address);
_buffers.Add(newBuffer);
diff --git a/Ryujinx.Graphics.Gpu/Memory/IRange.cs b/Ryujinx.Graphics.Gpu/Memory/IRange.cs
deleted file mode 100644
index 9d5eee0b..00000000
--- a/Ryujinx.Graphics.Gpu/Memory/IRange.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Memory
-{
- /// <summary>
- /// Range of memory.
- /// </summary>
- interface IRange
- {
- ulong Address { get; }
- ulong Size { get; }
-
- bool OverlapsWith(ulong address, ulong size);
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
index 59b6d1e5..517dacef 100644
--- a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
@@ -125,6 +125,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
lock (_pageTable)
{
+ MemoryUnmapped?.Invoke(this, new UnmapEventArgs(va, size));
+
for (ulong offset = 0; offset < size; offset += PageSize)
{
SetPte(va + offset, pa + offset);
@@ -201,6 +203,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
lock (_pageTable)
{
+ MemoryUnmapped?.Invoke(this, new UnmapEventArgs(va, size));
+
for (ulong offset = 0; offset < size; offset += PageSize)
{
if (IsPageInUse(va + offset))
diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
index ed325369..3ebf2fd7 100644
--- a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
@@ -1,4 +1,5 @@
using Ryujinx.Cpu;
+using Ryujinx.Cpu.Tracking;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -29,10 +30,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <param name="address">Start address of the range</param>
/// <param name="size">Size in bytes to be range</param>
+ /// <param name="tracked">True if read tracking is triggered on the span</param>
/// <returns>A read only span of the data at the specified memory location</returns>
- public ReadOnlySpan<byte> GetSpan(ulong address, int size)
+ public ReadOnlySpan<byte> GetSpan(ulong address, int size, bool tracked = false)
{
- return _cpuMemory.GetSpan(address, size);
+ return _cpuMemory.GetSpan(address, size, tracked);
}
/// <summary>
@@ -78,17 +80,38 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
- /// Checks if a specified virtual memory region has been modified by the CPU since the last call.
+ /// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with.
/// </summary>
/// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param>
- /// <param name="name">Resource name</param>
- /// <param name="modifiedRanges">Optional array where the modified ranges should be written</param>
- /// <returns>The number of modified ranges</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public int QueryModified(ulong address, ulong size, ResourceName name, (ulong, ulong)[] modifiedRanges = null)
+ /// <returns>The memory tracking handle</returns>
+ public CpuRegionHandle BeginTracking(ulong address, ulong size)
{
- return _cpuMemory.QueryModified(address, size, (int)name, modifiedRanges);
+ return _cpuMemory.BeginTracking(address, size);
+ }
+
+ /// <summary>
+ /// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
+ /// </summary>
+ /// <param name="address">CPU virtual address of the region</param>
+ /// <param name="size">Size of the region</param>
+ /// <param name="granularity">Desired granularity of write tracking</param>
+ /// <returns>The memory tracking handle</returns>
+ public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, ulong granularity = 4096)
+ {
+ return _cpuMemory.BeginGranularTracking(address, size, granularity);
+ }
+
+ /// <summary>
+ /// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
+ /// </summary>
+ /// <param name="address">CPU virtual address of the region</param>
+ /// <param name="size">Size of the region</param>
+ /// <param name="granularity">Desired granularity of write tracking</param>
+ /// <returns>The memory tracking handle</returns>
+ public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity = 4096)
+ {
+ return _cpuMemory.BeginSmartGranularTracking(address, size, granularity);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Memory/RangeList.cs b/Ryujinx.Graphics.Gpu/Memory/RangeList.cs
deleted file mode 100644
index 6af440c0..00000000
--- a/Ryujinx.Graphics.Gpu/Memory/RangeList.cs
+++ /dev/null
@@ -1,343 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.Gpu.Memory
-{
- /// <summary>
- /// List of GPU resources with data on guest memory.
- /// </summary>
- /// <typeparam name="T">Type of the GPU resource</typeparam>
- class RangeList<T> : IEnumerable<T> where T : IRange
- {
- private const int ArrayGrowthSize = 32;
-
- private readonly List<T> _items;
-
- /// <summary>
- /// Creates a new GPU resources list.
- /// </summary>
- public RangeList()
- {
- _items = new List<T>();
- }
-
- /// <summary>
- /// Adds a new item to the list.
- /// </summary>
- /// <param name="item">The item to be added</param>
- public void Add(T item)
- {
- int index = BinarySearch(item.Address);
-
- if (index < 0)
- {
- index = ~index;
- }
-
- _items.Insert(index, item);
- }
-
- /// <summary>
- /// Removes an item from the list.
- /// </summary>
- /// <param name="item">The item to be removed</param>
- /// <returns>True if the item was removed, or false if it was not found</returns>
- public bool Remove(T item)
- {
- int index = BinarySearch(item.Address);
-
- if (index >= 0)
- {
- while (index > 0 && _items[index - 1].Address == item.Address)
- {
- index--;
- }
-
- while (index < _items.Count)
- {
- if (_items[index].Equals(item))
- {
- _items.RemoveAt(index);
-
- return true;
- }
-
- if (_items[index].Address > item.Address)
- {
- break;
- }
-
- index++;
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Gets the first item on the list overlapping in memory with the specified item.
- /// </summary>
- /// <remarks>
- /// Despite the name, this has no ordering guarantees of the returned item.
- /// It only ensures that the item returned overlaps the specified item.
- /// </remarks>
- /// <param name="item">Item to check for overlaps</param>
- /// <returns>The overlapping item, or the default value for the type if none found</returns>
- public T FindFirstOverlap(T item)
- {
- return FindFirstOverlap(item.Address, item.Size);
- }
-
- /// <summary>
- /// Gets the first item on the list overlapping the specified memory range.
- /// </summary>
- /// <remarks>
- /// Despite the name, this has no ordering guarantees of the returned item.
- /// It only ensures that the item returned overlaps the specified memory range.
- /// </remarks>
- /// <param name="address">Start address of the range</param>
- /// <param name="size">Size in bytes of the range</param>
- /// <returns>The overlapping item, or the default value for the type if none found</returns>
- public T FindFirstOverlap(ulong address, ulong size)
- {
- int index = BinarySearch(address, size);
-
- if (index < 0)
- {
- return default(T);
- }
-
- return _items[index];
- }
-
- /// <summary>
- /// Gets all items overlapping with the specified item in memory.
- /// </summary>
- /// <param name="item">Item to check for overlaps</param>
- /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
- /// <returns>The number of overlapping items found</returns>
- public int FindOverlaps(T item, ref T[] output)
- {
- return FindOverlaps(item.Address, item.Size, ref output);
- }
-
- /// <summary>
- /// Gets all items on the list overlapping the specified memory range.
- /// </summary>
- /// <param name="address">Start address of the range</param>
- /// <param name="size">Size in bytes of the range</param>
- /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
- /// <returns>The number of overlapping items found</returns>
- public int FindOverlaps(ulong address, ulong size, ref T[] output)
- {
- int outputIndex = 0;
-
- ulong endAddress = address + size;
-
- lock (_items)
- {
- foreach (T item in _items)
- {
- if (item.Address >= endAddress)
- {
- break;
- }
-
- if (item.OverlapsWith(address, size))
- {
- if (outputIndex == output.Length)
- {
- Array.Resize(ref output, outputIndex + ArrayGrowthSize);
- }
-
- output[outputIndex++] = item;
- }
- }
- }
-
- return outputIndex;
- }
-
- /// <summary>
- /// Gets all items overlapping with the specified item in memory.
- /// </summary>
- /// <remarks>
- /// This method only returns correct results if none of the items on the list overlaps with
- /// each other. If that is not the case, this method should not be used.
- /// This method is faster than the regular method to find all overlaps.
- /// </remarks>
- /// <param name="item">Item to check for overlaps</param>
- /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
- /// <returns>The number of overlapping items found</returns>
- public int FindOverlapsNonOverlapping(T item, ref T[] output)
- {
- return FindOverlapsNonOverlapping(item.Address, item.Size, ref output);
- }
-
- /// <summary>
- /// Gets all items on the list overlapping the specified memory range.
- /// </summary>
- /// <remarks>
- /// This method only returns correct results if none of the items on the list overlaps with
- /// each other. If that is not the case, this method should not be used.
- /// This method is faster than the regular method to find all overlaps.
- /// </remarks>
- /// <param name="address">Start address of the range</param>
- /// <param name="size">Size in bytes of the range</param>
- /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
- /// <returns>The number of overlapping items found</returns>
- public int FindOverlapsNonOverlapping(ulong address, ulong size, ref T[] output)
- {
- // This is a bit faster than FindOverlaps, but only works
- // when none of the items on the list overlaps with each other.
- int outputIndex = 0;
-
- int index = BinarySearch(address, size);
-
- if (index >= 0)
- {
- while (index > 0 && _items[index - 1].OverlapsWith(address, size))
- {
- index--;
- }
-
- do
- {
- if (outputIndex == output.Length)
- {
- Array.Resize(ref output, outputIndex + ArrayGrowthSize);
- }
-
- output[outputIndex++] = _items[index++];
- }
- while (index < _items.Count && _items[index].OverlapsWith(address, size));
- }
-
- return outputIndex;
- }
-
- /// <summary>
- /// Gets all items on the list with the specified memory address.
- /// </summary>
- /// <param name="address">Address to find</param>
- /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
- /// <returns>The number of matches found</returns>
- public int FindOverlaps(ulong address, ref T[] output)
- {
- int index = BinarySearch(address);
-
- int outputIndex = 0;
-
- if (index >= 0)
- {
- while (index > 0 && _items[index - 1].Address == address)
- {
- index--;
- }
-
- while (index < _items.Count)
- {
- T overlap = _items[index++];
-
- if (overlap.Address != address)
- {
- break;
- }
-
- if (outputIndex == output.Length)
- {
- Array.Resize(ref output, outputIndex + ArrayGrowthSize);
- }
-
- output[outputIndex++] = overlap;
- }
- }
-
- return outputIndex;
- }
-
- /// <summary>
- /// Performs binary search on the internal list of items.
- /// </summary>
- /// <param name="address">Address to find</param>
- /// <returns>List index of the item, or complement index of nearest item with lower value on the list</returns>
- private int BinarySearch(ulong address)
- {
- int left = 0;
- int right = _items.Count - 1;
-
- while (left <= right)
- {
- int range = right - left;
-
- int middle = left + (range >> 1);
-
- T item = _items[middle];
-
- if (item.Address == address)
- {
- return middle;
- }
-
- if (address < item.Address)
- {
- right = middle - 1;
- }
- else
- {
- left = middle + 1;
- }
- }
-
- return ~left;
- }
-
- /// <summary>
- /// Performs binary search for items overlapping a given memory range.
- /// </summary>
- /// <param name="address">Start address of the range</param>
- /// <param name="size">Size in bytes of the range</param>
- /// <returns>List index of the item, or complement index of nearest item with lower value on the list</returns>
- private int BinarySearch(ulong address, ulong size)
- {
- int left = 0;
- int right = _items.Count - 1;
-
- while (left <= right)
- {
- int range = right - left;
-
- int middle = left + (range >> 1);
-
- T item = _items[middle];
-
- if (item.OverlapsWith(address, size))
- {
- return middle;
- }
-
- if (address < item.Address)
- {
- right = middle - 1;
- }
- else
- {
- left = middle + 1;
- }
- }
-
- return ~left;
- }
-
- public IEnumerator<T> GetEnumerator()
- {
- return _items.GetEnumerator();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return _items.GetEnumerator();
- }
- }
-} \ No newline at end of file