diff options
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Memory')
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 83 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs | 43 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/BufferManager.cs | 29 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs | 8 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs | 24 |
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 |
