diff options
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Memory/BufferManager.cs')
-rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/BufferManager.cs | 438 |
1 files changed, 49 insertions, 389 deletions
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 20fa1f3a..e43cb3b3 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -1,9 +1,7 @@ -using Ryujinx.Common; +using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Image; -using Ryujinx.Graphics.Gpu.State; using Ryujinx.Graphics.Shader; -using Ryujinx.Memory.Range; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -14,26 +12,16 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <summary> /// Buffer manager. /// </summary> - class BufferManager + class BufferManager : IDisposable { private const int StackToHeapThreshold = 16; - private const int OverlapsBufferInitialCapacity = 10; - private const int OverlapsBufferMaxCapacity = 10000; - - private const ulong BufferAlignmentSize = 0x1000; - private const ulong BufferAlignmentMask = BufferAlignmentSize - 1; - - private GpuContext _context; - - private RangeList<Buffer> _buffers; - - private Buffer[] _bufferOverlaps; + private readonly GpuContext _context; private IndexBuffer _indexBuffer; - private VertexBuffer[] _vertexBuffers; - private BufferBounds[] _transformFeedbackBuffers; - private List<BufferTextureBinding> _bufferTextures; + private readonly VertexBuffer[] _vertexBuffers; + private readonly BufferBounds[] _transformFeedbackBuffers; + private readonly List<BufferTextureBinding> _bufferTextures; /// <summary> /// Holds shader stage buffer state and binding information. @@ -94,10 +82,10 @@ namespace Ryujinx.Graphics.Gpu.Memory } } - private BuffersPerStage _cpStorageBuffers; - private BuffersPerStage _cpUniformBuffers; - private BuffersPerStage[] _gpStorageBuffers; - private BuffersPerStage[] _gpUniformBuffers; + private readonly BuffersPerStage _cpStorageBuffers; + private readonly BuffersPerStage _cpUniformBuffers; + private readonly BuffersPerStage[] _gpStorageBuffers; + private readonly BuffersPerStage[] _gpUniformBuffers; private int _cpStorageBufferBindings; private int _cpUniformBufferBindings; @@ -114,20 +102,14 @@ namespace Ryujinx.Graphics.Gpu.Memory private bool _rebind; - private Dictionary<ulong, BufferCacheEntry> _dirtyCache; - /// <summary> /// Creates a new instance of the buffer manager. /// </summary> - /// <param name="context">The GPU context that the buffer manager belongs to</param> + /// <param name="context">GPU context that the buffer manager belongs to</param> public BufferManager(GpuContext context) { _context = context; - _buffers = new RangeList<Buffer>(); - - _bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity]; - _vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers]; _transformFeedbackBuffers = new BufferBounds[Constants.TotalTransformFeedbackBuffers]; @@ -146,9 +128,10 @@ namespace Ryujinx.Graphics.Gpu.Memory _bufferTextures = new List<BufferTextureBinding>(); - _dirtyCache = new Dictionary<ulong, BufferCacheEntry>(); + context.Methods.BufferCache.NotifyBuffersModified += Rebind; } + /// <summary> /// Sets the memory range with the index buffer data, to be used for subsequent draw calls. /// </summary> @@ -157,11 +140,11 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <param name="type">Type of each index buffer element</param> public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type) { - ulong address = TranslateAndCreateBuffer(gpuVa, size); + ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size); _indexBuffer.Address = address; - _indexBuffer.Size = size; - _indexBuffer.Type = type; + _indexBuffer.Size = size; + _indexBuffer.Type = type; _indexBufferDirty = true; } @@ -188,11 +171,11 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param> public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor) { - ulong address = TranslateAndCreateBuffer(gpuVa, size); + ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size); _vertexBuffers[index].Address = address; - _vertexBuffers[index].Size = size; - _vertexBuffers[index].Stride = stride; + _vertexBuffers[index].Size = size; + _vertexBuffers[index].Stride = stride; _vertexBuffers[index].Divisor = divisor; _vertexBuffersDirty = true; @@ -216,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <param name="size">Size in bytes of the transform feedback buffer</param> public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size) { - ulong address = TranslateAndCreateBuffer(gpuVa, size); + ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size); _transformFeedbackBuffers[index] = new BufferBounds(address, size); _transformFeedbackBuffersDirty = true; @@ -236,7 +219,7 @@ namespace Ryujinx.Graphics.Gpu.Memory gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment); - ulong address = TranslateAndCreateBuffer(gpuVa, size); + ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size); _cpStorageBuffers.SetBounds(index, address, size, flags); } @@ -256,10 +239,10 @@ namespace Ryujinx.Graphics.Gpu.Memory gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment); - ulong address = TranslateAndCreateBuffer(gpuVa, size); + ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size); if (_gpStorageBuffers[stage].Buffers[index].Address != address || - _gpStorageBuffers[stage].Buffers[index].Size != size) + _gpStorageBuffers[stage].Buffers[index].Size != size) { _gpStorageBuffersDirty = true; } @@ -276,7 +259,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <param name="size">Size in bytes of the storage buffer</param> public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size) { - ulong address = TranslateAndCreateBuffer(gpuVa, size); + ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size); _cpUniformBuffers.SetBounds(index, address, size); } @@ -291,7 +274,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <param name="size">Size in bytes of the storage buffer</param> public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size) { - ulong address = TranslateAndCreateBuffer(gpuVa, size); + ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size); _gpUniformBuffers[stage].SetBounds(index, address, size); _gpUniformBuffersDirty = true; @@ -397,191 +380,6 @@ namespace Ryujinx.Graphics.Gpu.Memory return mask; } - /// <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) - { - Buffer[] overlaps = new Buffer[10]; - int overlapCount; - - ulong address = _context.MemoryManager.Translate(e.Address); - ulong size = e.Size; - - lock (_buffers) - { - overlapCount = _buffers.FindOverlaps(address, size, ref overlaps); - } - - for (int i = 0; i < overlapCount; i++) - { - overlaps[i].Unmapped(address, size); - } - } - - /// <summary> - /// Performs address translation of the GPU virtual address, and creates a - /// new buffer, if needed, for the specified range. - /// </summary> - /// <param name="gpuVa">Start GPU virtual address of the buffer</param> - /// <param name="size">Size in bytes of the buffer</param> - /// <returns>CPU virtual address of the buffer, after address translation</returns> - private ulong TranslateAndCreateBuffer(ulong gpuVa, ulong size) - { - if (gpuVa == 0) - { - return 0; - } - - ulong address = _context.MemoryManager.Translate(gpuVa); - - if (address == MemoryManager.PteUnmapped) - { - return 0; - } - - CreateBuffer(address, size); - - return address; - } - - /// <summary> - /// Creates a new buffer for the specified range, if it does not yet exist. - /// This can be used to ensure the existance of a buffer. - /// </summary> - /// <param name="address">Address of the buffer in memory</param> - /// <param name="size">Size of the buffer in bytes</param> - public void CreateBuffer(ulong address, ulong size) - { - ulong endAddress = address + size; - - ulong alignedAddress = address & ~BufferAlignmentMask; - - ulong alignedEndAddress = (endAddress + BufferAlignmentMask) & ~BufferAlignmentMask; - - // The buffer must have the size of at least one page. - if (alignedEndAddress == alignedAddress) - { - alignedEndAddress += BufferAlignmentSize; - } - - CreateBufferAligned(alignedAddress, alignedEndAddress - alignedAddress); - } - - /// <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. - /// </summary> - /// <param name="address">Address of the buffer in guest memory</param> - /// <param name="size">Size in bytes of the buffer</param> - private void CreateBufferAligned(ulong address, ulong size) - { - int overlapsCount; - - lock (_buffers) - { - overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps); - } - - if (overlapsCount != 0) - { - // The buffer already exists. We can just return the existing buffer - // if the buffer we need is fully contained inside the overlapping buffer. - // Otherwise, we must delete the overlapping buffers and create a bigger buffer - // that fits all the data we need. We also need to copy the contents from the - // old buffer(s) to the new buffer. - ulong endAddress = address + size; - - if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress) - { - for (int index = 0; index < overlapsCount; index++) - { - Buffer buffer = _bufferOverlaps[index]; - - address = Math.Min(address, buffer.Address); - endAddress = Math.Max(endAddress, buffer.EndAddress); - - lock (_buffers) - { - _buffers.Remove(buffer); - } - } - - Buffer newBuffer = new Buffer(_context, address, endAddress - address, _bufferOverlaps.Take(overlapsCount)); - - lock (_buffers) - { - _buffers.Add(newBuffer); - } - - for (int index = 0; index < overlapsCount; index++) - { - Buffer buffer = _bufferOverlaps[index]; - - int dstOffset = (int)(buffer.Address - newBuffer.Address); - - buffer.CopyTo(newBuffer, dstOffset); - newBuffer.InheritModifiedRanges(buffer); - - buffer.DisposeData(); - } - - newBuffer.SynchronizeMemory(address, endAddress - address); - - // Existing buffers were modified, we need to rebind everything. - _rebind = true; - } - } - else - { - // No overlap, just create a new buffer. - Buffer buffer = new Buffer(_context, address, size); - - lock (_buffers) - { - _buffers.Add(buffer); - } - } - - ShrinkOverlapsBufferIfNeeded(); - } - - /// <summary> - /// Resizes the temporary buffer used for range list intersection results, if it has grown too much. - /// </summary> - private void ShrinkOverlapsBufferIfNeeded() - { - if (_bufferOverlaps.Length > OverlapsBufferMaxCapacity) - { - Array.Resize(ref _bufferOverlaps, OverlapsBufferMaxCapacity); - } - } /// <summary> /// Gets the address of the compute uniform buffer currently bound at the given index. @@ -624,7 +422,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { // The storage buffer size is not reliable (it might be lower than the actual size), // so we bind the entire buffer to allow otherwise out of range accesses to work. - sRanges[bindingInfo.Binding] = GetBufferRangeTillEnd( + sRanges[bindingInfo.Binding] = _context.Methods.BufferCache.GetBufferRangeTillEnd( bounds.Address, bounds.Size, bounds.Flags.HasFlag(BufferUsageFlags.Write)); @@ -645,7 +443,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (bounds.Address != 0) { - uRanges[bindingInfo.Binding] = GetBufferRange(bounds.Address, bounds.Size); + uRanges[bindingInfo.Binding] = _context.Methods.BufferCache.GetBufferRange(bounds.Address, bounds.Size); } } @@ -654,7 +452,7 @@ namespace Ryujinx.Graphics.Gpu.Memory CommitBufferTextureBindings(); // Force rebind after doing compute work. - _rebind = true; + Rebind(); } /// <summary> @@ -666,7 +464,9 @@ namespace Ryujinx.Graphics.Gpu.Memory { foreach (var binding in _bufferTextures) { - binding.Texture.SetStorage(GetBufferRange(binding.Address, binding.Size, binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore))); + var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); + var range = _context.Methods.BufferCache.GetBufferRange(binding.Address, binding.Size, isStore); + binding.Texture.SetStorage(range); // The texture must be rebound to use the new storage if it was updated. @@ -696,14 +496,14 @@ namespace Ryujinx.Graphics.Gpu.Memory if (_indexBuffer.Address != 0) { - BufferRange buffer = GetBufferRange(_indexBuffer.Address, _indexBuffer.Size); + BufferRange buffer = _context.Methods.BufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size); _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type); } } else if (_indexBuffer.Address != 0) { - SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size); + _context.Methods.BufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size); } uint vbEnableMask = _vertexBuffersEnableMask; @@ -723,7 +523,7 @@ namespace Ryujinx.Graphics.Gpu.Memory continue; } - BufferRange buffer = GetBufferRange(vb.Address, vb.Size); + BufferRange buffer = _context.Methods.BufferCache.GetBufferRange(vb.Address, vb.Size); vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor); } @@ -741,7 +541,7 @@ namespace Ryujinx.Graphics.Gpu.Memory continue; } - SynchronizeBufferRange(vb.Address, vb.Size); + _context.Methods.BufferCache.SynchronizeBufferRange(vb.Address, vb.Size); } } @@ -761,7 +561,7 @@ namespace Ryujinx.Graphics.Gpu.Memory continue; } - tfbs[index] = GetBufferRange(tfb.Address, tfb.Size); + tfbs[index] = _context.Methods.BufferCache.GetBufferRange(tfb.Address, tfb.Size); } _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs); @@ -777,7 +577,7 @@ namespace Ryujinx.Graphics.Gpu.Memory continue; } - SynchronizeBufferRange(tfb.Address, tfb.Size); + _context.Methods.BufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size); } } @@ -831,9 +631,10 @@ namespace Ryujinx.Graphics.Gpu.Memory if (bounds.Address != 0) { + var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); ranges[bindingInfo.Binding] = isStorage - ? GetBufferRangeTillEnd(bounds.Address, bounds.Size, bounds.Flags.HasFlag(BufferUsageFlags.Write)) - : GetBufferRange(bounds.Address, bounds.Size, bounds.Flags.HasFlag(BufferUsageFlags.Write)); + ? _context.Methods.BufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite) + : _context.Methods.BufferCache.GetBufferRange(bounds.Address, bounds.Size, isWrite); } } } @@ -869,7 +670,7 @@ namespace Ryujinx.Graphics.Gpu.Memory continue; } - SynchronizeBufferRange(bounds.Address, bounds.Size); + _context.Methods.BufferCache.SynchronizeBufferRange(bounds.Address, bounds.Size); } } } @@ -885,167 +686,26 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <param name="isImage">Whether the binding is for an image or a sampler</param> public void SetBufferTextureStorage(ITexture texture, ulong address, ulong size, TextureBindingInfo bindingInfo, Format format, bool isImage) { - CreateBuffer(address, size); + _context.Methods.BufferCache.CreateBuffer(address, size); _bufferTextures.Add(new BufferTextureBinding(texture, address, size, bindingInfo, format, isImage)); } /// <summary> - /// Copy a buffer data from a given address to another. - /// </summary> - /// <remarks> - /// This does a GPU side copy. - /// </remarks> - /// <param name="srcVa">GPU virtual address of the copy source</param> - /// <param name="dstVa">GPU virtual address of the copy destination</param> - /// <param name="size">Size in bytes of the copy</param> - public void CopyBuffer(GpuVa srcVa, GpuVa dstVa, ulong size) - { - ulong srcAddress = TranslateAndCreateBuffer(srcVa.Pack(), size); - ulong dstAddress = TranslateAndCreateBuffer(dstVa.Pack(), size); - - Buffer srcBuffer = GetBuffer(srcAddress, size); - Buffer dstBuffer = GetBuffer(dstAddress, size); - - int srcOffset = (int)(srcAddress - srcBuffer.Address); - int dstOffset = (int)(dstAddress - dstBuffer.Address); - - _context.Renderer.Pipeline.CopyBuffer( - srcBuffer.Handle, - dstBuffer.Handle, - srcOffset, - dstOffset, - (int)size); - - if (srcBuffer.IsModified(srcAddress, size)) - { - dstBuffer.SignalModified(dstAddress, size); - } - else - { - // Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU. - - dstBuffer.ClearModified(dstAddress, size); - _context.PhysicalMemory.WriteUntracked(dstAddress, _context.PhysicalMemory.GetSpan(srcAddress, (int)size)); - } - } - - /// <summary> - /// Clears a buffer at a given address with the specified value. - /// </summary> - /// <remarks> - /// Both the address and size must be aligned to 4 bytes. - /// </remarks> - /// <param name="gpuVa">GPU virtual address of the region to clear</param> - /// <param name="size">Number of bytes to clear</param> - /// <param name="value">Value to be written into the buffer</param> - public void ClearBuffer(GpuVa gpuVa, ulong size, uint value) - { - ulong address = TranslateAndCreateBuffer(gpuVa.Pack(), size); - - Buffer buffer = GetBuffer(address, size); - - int offset = (int)(address - buffer.Address); - - _context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value); - - buffer.SignalModified(address, size); - } - - /// <summary> - /// Gets a buffer sub-range starting at a given memory address. - /// </summary> - /// <param name="address">Start address of the memory range</param> - /// <param name="size">Size in bytes of the memory range</param> - /// <param name="write">Whether the buffer will be written to by this use</param> - /// <returns>The buffer sub-range starting at the given memory address</returns> - private BufferRange GetBufferRangeTillEnd(ulong address, ulong size, bool write = false) - { - return GetBuffer(address, size, write).GetRange(address); - } - - /// <summary> - /// Gets a buffer sub-range for a given memory range. - /// </summary> - /// <param name="address">Start address of the memory range</param> - /// <param name="size">Size in bytes of the memory range</param> - /// <param name="write">Whether the buffer will be written to by this use</param> - /// <returns>The buffer sub-range for the given range</returns> - private BufferRange GetBufferRange(ulong address, ulong size, bool write = false) - { - return GetBuffer(address, size, write).GetRange(address, size); - } - - /// <summary> - /// Gets a buffer for a given memory range. - /// A buffer overlapping with the specified range is assumed to already exist on the cache. - /// </summary> - /// <param name="address">Start address of the memory range</param> - /// <param name="size">Size in bytes of the memory range</param> - /// <param name="write">Whether the buffer will be written to by this use</param> - /// <returns>The buffer where the range is fully contained</returns> - private Buffer GetBuffer(ulong address, ulong size, bool write = false) - { - Buffer buffer; - - if (size != 0) - { - lock (_buffers) - { - buffer = _buffers.FindFirstOverlap(address, size); - } - - buffer.SynchronizeMemory(address, size); - - if (write) - { - buffer.SignalModified(address, size); - } - } - else - { - lock (_buffers) - { - buffer = _buffers.FindFirstOverlap(address, 1); - } - } - - return buffer; - } - - /// <summary> - /// Performs guest to host memory synchronization of a given memory range. + /// Force all bound textures and images to be rebound the next time CommitBindings is called. /// </summary> - /// <param name="address">Start address of the memory range</param> - /// <param name="size">Size in bytes of the memory range</param> - private void SynchronizeBufferRange(ulong address, ulong size) + public void Rebind() { - if (size != 0) - { - Buffer buffer; - - lock (_buffers) - { - buffer = _buffers.FindFirstOverlap(address, size); - } - - buffer.SynchronizeMemory(address, size); - } + _rebind = true; } /// <summary> - /// Disposes all buffers in the cache. - /// It's an error to use the buffer manager after disposal. + /// Disposes the buffer manager. + /// It is an error to use the buffer manager after disposal. /// </summary> public void Dispose() { - lock (_buffers) - { - foreach (Buffer buffer in _buffers) - { - buffer.Dispose(); - } - } + _context.Methods.BufferCache.NotifyBuffersModified -= Rebind; } } -}
\ No newline at end of file +} |