diff options
Diffstat (limited to 'Ryujinx.Graphics.Gpu')
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/Compute.cs | 2 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs | 2 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs | 2 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs | 56 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/Methods.cs | 2 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/GpuContext.cs | 4 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Image/Pool.cs | 2 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 3 | ||||
| -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 |
13 files changed, 241 insertions, 19 deletions
diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute.cs b/Ryujinx.Graphics.Gpu/Engine/Compute.cs index bcff5953..be317a7f 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute.cs @@ -16,6 +16,8 @@ namespace Ryujinx.Graphics.Gpu.Engine /// <param name="argument">Method call argument</param> public void Dispatch(GpuState state, int argument) { + FlushUboDirty(); + uint qmdAddress = (uint)state.Get<int>(MethodOffset.DispatchParamsAddress); var qmd = _context.MemoryManager.Read<ComputeQmd>((ulong)qmdAddress << 8); diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs index dd16cb2d..a1cf86ec 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs @@ -69,6 +69,8 @@ namespace Ryujinx.Graphics.Gpu.Engine return; } + FlushUboDirty(); + if (copy2D) { // Buffer to texture copy. diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs index 3fee1fcf..16fb31d6 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs @@ -66,6 +66,8 @@ namespace Ryujinx.Graphics.Gpu.Engine int index = (argument >> 4) & 0x1f; + FlushUboDirty(); + if (enable) { var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState); diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs index 61772327..3e1dd151 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs @@ -1,3 +1,4 @@ +using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.State; using System; using System.Runtime.InteropServices; @@ -6,6 +7,25 @@ namespace Ryujinx.Graphics.Gpu.Engine { partial class Methods { + // State associated with direct uniform buffer updates. + // This state is used to attempt to batch together consecutive updates. + private ulong _ubBeginCpuAddress = 0; + private ulong _ubFollowUpAddress = 0; + private ulong _ubByteCount = 0; + + /// <summary> + /// Flushes any queued ubo updates. + /// </summary> + private void FlushUboDirty() + { + if (_ubFollowUpAddress != 0) + { + BufferManager.ForceDirty(_ubFollowUpAddress - _ubByteCount, _ubByteCount); + + _ubFollowUpAddress = 0; + } + } + /// <summary> /// Updates the uniform buffer data with inline data. /// </summary> @@ -15,11 +35,22 @@ namespace Ryujinx.Graphics.Gpu.Engine { var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState); - _context.MemoryManager.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, argument); + ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset; - state.SetUniformBufferOffset(uniformBuffer.Offset + 4); + if (_ubFollowUpAddress != address) + { + FlushUboDirty(); + + _ubByteCount = 0; + _ubBeginCpuAddress = _context.MemoryManager.Translate(address); + } - _context.AdvanceSequence(); + _context.PhysicalMemory.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref argument, 1))); + + _ubFollowUpAddress = address + 4; + _ubByteCount += 4; + + state.SetUniformBufferOffset(uniformBuffer.Offset + 4); } /// <summary> @@ -31,11 +62,24 @@ namespace Ryujinx.Graphics.Gpu.Engine { var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState); - _context.MemoryManager.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, MemoryMarshal.Cast<int, byte>(data)); + ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset; - state.SetUniformBufferOffset(uniformBuffer.Offset + data.Length * 4); + ulong size = (ulong)data.Length * 4; + + if (_ubFollowUpAddress != address) + { + FlushUboDirty(); - _context.AdvanceSequence(); + _ubByteCount = 0; + _ubBeginCpuAddress = _context.MemoryManager.Translate(address); + } + + _context.PhysicalMemory.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, MemoryMarshal.Cast<int, byte>(data)); + + _ubFollowUpAddress = address + size; + _ubByteCount += size; + + state.SetUniformBufferOffset(uniformBuffer.Offset + data.Length * 4); } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index ae9bdb0d..431ea449 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -130,6 +130,8 @@ namespace Ryujinx.Graphics.Gpu.Engine _prevTfEnable = false; } + FlushUboDirty(); + // Shaders must be the first one to be updated if modified, because // some of the other state depends on information from the currently // bound shaders. diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs index f131ecc3..a9386ce5 100644 --- a/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -137,7 +137,7 @@ namespace Ryujinx.Graphics.Gpu /// This is required for any GPU memory access. /// </summary> /// <param name="cpuMemory">CPU memory manager</param> - public void SetVmm(Cpu.MemoryManager cpuMemory) + public void SetVmm(Cpu.IVirtualMemoryManagerTracked cpuMemory) { PhysicalMemory = new PhysicalMemory(cpuMemory); } @@ -187,6 +187,8 @@ namespace Ryujinx.Graphics.Gpu Renderer.Dispose(); GPFifo.Dispose(); HostInitalized.Dispose(); + + PhysicalMemory.Dispose(); } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/Ryujinx.Graphics.Gpu/Image/Pool.cs index 855f6344..0b4c2993 100644 --- a/Ryujinx.Graphics.Gpu/Image/Pool.cs +++ b/Ryujinx.Graphics.Gpu/Image/Pool.cs @@ -1,6 +1,4 @@ -using Ryujinx.Common; using Ryujinx.Cpu.Tracking; -using Ryujinx.Graphics.Gpu.Memory; using System; namespace Ryujinx.Graphics.Gpu.Image diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 52129d64..30ca59d4 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -1,5 +1,4 @@ -using Ryujinx.Common; -using Ryujinx.Cpu.Tracking; +using Ryujinx.Cpu.Tracking; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Texture; using Ryujinx.Memory.Range; 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 |
