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.cs83
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs43
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/BufferManager.cs29
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs8
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs24
5 files changed, 179 insertions, 8 deletions
diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
index cdd61b6d..c567e30c 100644
--- a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
@@ -36,6 +36,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
public ulong EndAddress => Address + Size;
/// <summary>
+ /// Increments when the buffer is (partially) unmapped or disposed.
+ /// </summary>
+ public int UnmappedSequence { get; private set; }
+
+ /// <summary>
/// Ranges of the buffer that have been modified on the GPU.
/// Ranges defined here cannot be updated from CPU until a CPU waiting sync point is reached.
/// Then, write tracking will signal, wait for GPU sync (generated at the syncpoint) and flush these regions.
@@ -45,9 +50,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </remarks>
private BufferModifiedRangeList _modifiedRanges = null;
- private CpuMultiRegionHandle _memoryTrackingGranular;
-
- private CpuRegionHandle _memoryTracking;
+ private readonly CpuMultiRegionHandle _memoryTrackingGranular;
+ private readonly CpuRegionHandle _memoryTracking;
private readonly RegionSignal _externalFlushDelegate;
private readonly Action<ulong, ulong> _loadDelegate;
@@ -131,6 +135,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
+ /// Checks if a given range is fully contained in 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 is contained, false otherwise</returns>
+ public bool FullyContains(ulong address, ulong size)
+ {
+ return address >= Address && address + size <= EndAddress;
+ }
+
+ /// <summary>
/// Performs guest to host memory synchronization of the buffer data.
/// </summary>
/// <remarks>
@@ -147,7 +162,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
else
{
- if (_memoryTracking.Dirty && _context.SequenceNumber != _sequenceNumber)
+ if (_context.SequenceNumber != _sequenceNumber && _memoryTracking.DirtyOrVolatile())
{
_memoryTracking.Reprotect();
@@ -166,6 +181,39 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
+ /// Performs guest to host memory synchronization of the buffer data, regardless of sequence number.
+ /// </summary>
+ /// <remarks>
+ /// This causes the buffer data to be overwritten if a write was detected from the CPU,
+ /// since the last call to this method.
+ /// </remarks>
+ /// <param name="address">Start address of the range to synchronize</param>
+ /// <param name="size">Size in bytes of the range to synchronize</param>
+ public void ForceSynchronizeMemory(ulong address, ulong size)
+ {
+ if (_useGranular)
+ {
+ _memoryTrackingGranular.QueryModified(address, size, _modifiedDelegate);
+ }
+ else
+ {
+ if (_memoryTracking.DirtyOrVolatile())
+ {
+ _memoryTracking.Reprotect();
+
+ if (_modifiedRanges != null)
+ {
+ _modifiedRanges.ExcludeModifiedRegions(Address, Size, _loadDelegate);
+ }
+ else
+ {
+ _context.Renderer.SetBufferData(Handle, 0, _context.PhysicalMemory.GetSpan(Address, (int)Size));
+ }
+ }
+ }
+ }
+
+ /// <summary>
/// Ensure that the modified range list exists.
/// </summary>
private void EnsureRangeList()
@@ -317,6 +365,29 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
+ /// Force a region of the buffer to be dirty. Avoids reprotection and nullifies sequence number check.
+ /// </summary>
+ /// <param name="mAddress">Start address of the modified region</param>
+ /// <param name="mSize">Size of the region to force dirty</param>
+ public void ForceDirty(ulong mAddress, ulong mSize)
+ {
+ if (_modifiedRanges != null)
+ {
+ _modifiedRanges.Clear(mAddress, mSize);
+ }
+
+ if (_useGranular)
+ {
+ _memoryTrackingGranular.ForceDirty(mAddress, mSize);
+ }
+ else
+ {
+ _memoryTracking.ForceDirty();
+ _sequenceNumber--;
+ }
+ }
+
+ /// <summary>
/// Performs copy of all the buffer data from one buffer to another.
/// </summary>
/// <param name="destination">The destination buffer to copy the data into</param>
@@ -385,6 +456,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void Unmapped(ulong address, ulong size)
{
_modifiedRanges?.Clear(address, size);
+
+ UnmappedSequence++;
}
/// <summary>
@@ -398,6 +471,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
_memoryTracking?.Dispose();
_context.Renderer.DeleteBuffer(Handle);
+
+ UnmappedSequence++;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs b/Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs
new file mode 100644
index 00000000..fa38b54e
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs
@@ -0,0 +1,43 @@
+namespace Ryujinx.Graphics.Gpu.Memory
+{
+ /// <summary>
+ /// A cached entry for easily locating a buffer that is used often internally.
+ /// </summary>
+ class BufferCacheEntry
+ {
+ /// <summary>
+ /// The CPU VA of the buffer destination.
+ /// </summary>
+ public ulong Address;
+
+ /// <summary>
+ /// The end GPU VA of the associated buffer, used to check if new data can fit.
+ /// </summary>
+ public ulong EndGpuAddress;
+
+ /// <summary>
+ /// The buffer associated with this cache entry.
+ /// </summary>
+ public Buffer Buffer;
+
+ /// <summary>
+ /// The UnmappedSequence of the buffer at the time of creation.
+ /// If this differs from the value currently in the buffer, then this cache entry is outdated.
+ /// </summary>
+ public int UnmappedSequence;
+
+ /// <summary>
+ /// Create a new cache entry.
+ /// </summary>
+ /// <param name="address">The CPU VA of the buffer destination</param>
+ /// <param name="gpuVa">The GPU VA of the buffer destination</param>
+ /// <param name="buffer">The buffer object containing the target buffer</param>
+ public BufferCacheEntry(ulong address, ulong gpuVa, Buffer buffer)
+ {
+ Address = address;
+ EndGpuAddress = gpuVa + (buffer.EndAddress - address);
+ Buffer = buffer;
+ UnmappedSequence = buffer.UnmappedSequence;
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
index b2cd1ced..4a794b19 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
@@ -114,6 +114,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
private bool _rebind;
+ private Dictionary<ulong, BufferCacheEntry> _dirtyCache;
+
/// <summary>
/// Creates a new instance of the buffer manager.
/// </summary>
@@ -143,6 +145,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
_bufferTextures = new List<BufferTextureBinding>();
+
+ _dirtyCache = new Dictionary<ulong, BufferCacheEntry>();
}
/// <summary>
@@ -467,6 +471,29 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
+ /// Performs address translation of the GPU virtual address, and attempts to force
+ /// the buffer in the region as dirty.
+ /// The buffer lookup for this function is cached in a dictionary for quick access, which
+ /// accelerates common UBO updates.
+ /// </summary>
+ /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
+ /// <param name="size">Size in bytes of the buffer</param>
+ public void ForceDirty(ulong gpuVa, ulong size)
+ {
+ BufferCacheEntry result;
+
+ if (!_dirtyCache.TryGetValue(gpuVa, out result) || result.EndGpuAddress < gpuVa + size || result.UnmappedSequence != result.Buffer.UnmappedSequence)
+ {
+ ulong address = TranslateAndCreateBuffer(gpuVa, size);
+ result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
+
+ _dirtyCache[gpuVa] = result;
+ }
+
+ result.Buffer.ForceDirty(result.Address, size);
+ }
+
+ /// <summary>
/// Creates a new buffer for the specified range, if needed.
/// If a buffer where this range can be fully contained already exists,
/// then the creation of a new buffer is not necessary.
@@ -520,7 +547,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
int dstOffset = (int)(buffer.Address - newBuffer.Address);
- buffer.SynchronizeMemory(buffer.Address, buffer.Size);
+ buffer.ForceSynchronizeMemory(buffer.Address, buffer.Size);
buffer.CopyTo(newBuffer, dstOffset);
newBuffer.InheritModifiedRanges(buffer);
diff --git a/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs b/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs
index 92099b6a..8a9c6767 100644
--- a/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs
@@ -56,5 +56,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
regionHandle.Reprotect(asDirty);
}
}
+
+ public void ForceDirty()
+ {
+ foreach (var regionHandle in _cpuRegionHandles)
+ {
+ regionHandle.ForceDirty();
+ }
+ }
}
}
diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
index 8b2401c7..3d2af532 100644
--- a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
@@ -12,19 +12,24 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Represents physical memory, accessible from the GPU.
/// This is actually working CPU virtual addresses, of memory mapped on the application process.
/// </summary>
- class PhysicalMemory
+ class PhysicalMemory : IDisposable
{
public const int PageSize = 0x1000;
- private readonly Cpu.MemoryManager _cpuMemory;
+ private IVirtualMemoryManagerTracked _cpuMemory;
/// <summary>
/// Creates a new instance of the physical memory.
/// </summary>
/// <param name="cpuMemory">CPU memory manager of the application process</param>
- public PhysicalMemory(Cpu.MemoryManager cpuMemory)
+ public PhysicalMemory(IVirtualMemoryManagerTracked cpuMemory)
{
_cpuMemory = cpuMemory;
+
+ if (_cpuMemory is IRefCounted rc)
+ {
+ rc.IncrementReferenceCount();
+ }
}
/// <summary>
@@ -213,5 +218,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
return _cpuMemory.BeginSmartGranularTracking(address, size, granularity);
}
+
+ /// <summary>
+ /// Release our reference to the CPU memory manager.
+ /// </summary>
+ public void Dispose()
+ {
+ if (_cpuMemory is IRefCounted rc)
+ {
+ rc.DecrementReferenceCount();
+
+ _cpuMemory = null;
+ }
+ }
}
} \ No newline at end of file