MultiRangeBuffer.cs
1 using Ryujinx.Graphics.GAL; 2 using Ryujinx.Memory.Range; 3 using System; 4 using System.Collections.Generic; 5 6 namespace Ryujinx.Graphics.Gpu.Memory 7 { 8 /// <summary> 9 /// Buffer, used to store vertex and index data, uniform and storage buffers, and others. 10 /// </summary> 11 class MultiRangeBuffer : IMultiRangeItem, IDisposable 12 { 13 private readonly GpuContext _context; 14 15 /// <summary> 16 /// Host buffer handle. 17 /// </summary> 18 public BufferHandle Handle { get; } 19 20 /// <summary> 21 /// Range of memory where the data is located. 22 /// </summary> 23 public MultiRange Range { get; } 24 25 /// <summary> 26 /// Ever increasing counter value indicating when the buffer was modified relative to other buffers. 27 /// </summary> 28 public int ModificationSequenceNumber { get; private set; } 29 30 /// <summary> 31 /// Physical buffer dependency entry. 32 /// </summary> 33 private readonly struct PhysicalDependency 34 { 35 /// <summary> 36 /// Physical buffer. 37 /// </summary> 38 public readonly Buffer PhysicalBuffer; 39 40 /// <summary> 41 /// Offset of the range on the physical buffer. 42 /// </summary> 43 public readonly ulong PhysicalOffset; 44 45 /// <summary> 46 /// Offset of the range on the virtual buffer. 47 /// </summary> 48 public readonly ulong VirtualOffset; 49 50 /// <summary> 51 /// Size of the range. 52 /// </summary> 53 public readonly ulong Size; 54 55 /// <summary> 56 /// Creates a new physical dependency. 57 /// </summary> 58 /// <param name="physicalBuffer">Physical buffer</param> 59 /// <param name="physicalOffset">Offset of the range on the physical buffer</param> 60 /// <param name="virtualOffset">Offset of the range on the virtual buffer</param> 61 /// <param name="size">Size of the range</param> 62 public PhysicalDependency(Buffer physicalBuffer, ulong physicalOffset, ulong virtualOffset, ulong size) 63 { 64 PhysicalBuffer = physicalBuffer; 65 PhysicalOffset = physicalOffset; 66 VirtualOffset = virtualOffset; 67 Size = size; 68 } 69 } 70 71 private List<PhysicalDependency> _dependencies; 72 private BufferModifiedRangeList _modifiedRanges = null; 73 74 /// <summary> 75 /// Creates a new instance of the buffer. 76 /// </summary> 77 /// <param name="context">GPU context that the buffer belongs to</param> 78 /// <param name="range">Range of memory where the data is mapped</param> 79 public MultiRangeBuffer(GpuContext context, MultiRange range) 80 { 81 _context = context; 82 Range = range; 83 Handle = context.Renderer.CreateBuffer((int)range.GetSize()); 84 } 85 86 /// <summary> 87 /// Creates a new instance of the buffer. 88 /// </summary> 89 /// <param name="context">GPU context that the buffer belongs to</param> 90 /// <param name="range">Range of memory where the data is mapped</param> 91 /// <param name="storages">Backing memory for the buffer</param> 92 public MultiRangeBuffer(GpuContext context, MultiRange range, ReadOnlySpan<BufferRange> storages) 93 { 94 _context = context; 95 Range = range; 96 Handle = context.Renderer.CreateBufferSparse(storages); 97 } 98 99 /// <summary> 100 /// Gets a sub-range from the buffer. 101 /// </summary> 102 /// <remarks> 103 /// This can be used to bind and use sub-ranges of the buffer on the host API. 104 /// </remarks> 105 /// <param name="range">Range of memory where the data is mapped</param> 106 /// <returns>The buffer sub-range</returns> 107 public BufferRange GetRange(MultiRange range) 108 { 109 int offset = Range.FindOffset(range); 110 111 return new BufferRange(Handle, offset, (int)range.GetSize()); 112 } 113 114 /// <summary> 115 /// Removes all physical buffer dependencies. 116 /// </summary> 117 public void ClearPhysicalDependencies() 118 { 119 _dependencies?.Clear(); 120 } 121 122 /// <summary> 123 /// Adds a physical buffer dependency. 124 /// </summary> 125 /// <param name="buffer">Physical buffer to be added</param> 126 /// <param name="rangeAddress">Address inside the physical buffer where the virtual buffer range is located</param> 127 /// <param name="dstOffset">Offset inside the virtual buffer where the physical range is located</param> 128 /// <param name="rangeSize">Size of the range in bytes</param> 129 public void AddPhysicalDependency(Buffer buffer, ulong rangeAddress, ulong dstOffset, ulong rangeSize) 130 { 131 (_dependencies ??= new()).Add(new(buffer, rangeAddress - buffer.Address, dstOffset, rangeSize)); 132 buffer.AddVirtualDependency(this); 133 } 134 135 /// <summary> 136 /// Tries to get the physical range corresponding to the given physical buffer. 137 /// </summary> 138 /// <param name="buffer">Physical buffer</param> 139 /// <param name="minimumVirtOffset">Minimum virtual offset that a range match can have</param> 140 /// <param name="physicalOffset">Physical offset of the match</param> 141 /// <param name="virtualOffset">Virtual offset of the match, always greater than or equal <paramref name="minimumVirtOffset"/></param> 142 /// <param name="size">Size of the range match</param> 143 /// <returns>True if a match was found for the given parameters, false otherwise</returns> 144 public bool TryGetPhysicalOffset(Buffer buffer, ulong minimumVirtOffset, out ulong physicalOffset, out ulong virtualOffset, out ulong size) 145 { 146 physicalOffset = 0; 147 virtualOffset = 0; 148 size = 0; 149 150 if (_dependencies != null) 151 { 152 foreach (var dependency in _dependencies) 153 { 154 if (dependency.PhysicalBuffer == buffer && dependency.VirtualOffset >= minimumVirtOffset) 155 { 156 physicalOffset = dependency.PhysicalOffset; 157 virtualOffset = dependency.VirtualOffset; 158 size = dependency.Size; 159 160 return true; 161 } 162 } 163 } 164 165 return false; 166 } 167 168 /// <summary> 169 /// Adds a modified virtual memory range. 170 /// </summary> 171 /// <remarks> 172 /// This is only required when the host does not support sparse buffers, otherwise only physical buffers need to track modification. 173 /// </remarks> 174 /// <param name="range">Modified range</param> 175 /// <param name="modifiedSequenceNumber">ModificationSequenceNumber</param> 176 public void AddModifiedRegion(MultiRange range, int modifiedSequenceNumber) 177 { 178 _modifiedRanges ??= new(_context, null, null); 179 180 for (int i = 0; i < range.Count; i++) 181 { 182 MemoryRange subRange = range.GetSubRange(i); 183 184 _modifiedRanges.SignalModified(subRange.Address, subRange.Size); 185 } 186 187 ModificationSequenceNumber = modifiedSequenceNumber; 188 } 189 190 /// <summary> 191 /// Calls the specified <paramref name="rangeAction"/> for all modified ranges that overlaps with <paramref name="buffer"/>. 192 /// </summary> 193 /// <param name="buffer">Buffer to have its range checked</param> 194 /// <param name="rangeAction">Action to perform for modified ranges</param> 195 public void ConsumeModifiedRegion(Buffer buffer, Action<ulong, ulong> rangeAction) 196 { 197 ConsumeModifiedRegion(buffer.Address, buffer.Size, rangeAction); 198 } 199 200 /// <summary> 201 /// Calls the specified <paramref name="rangeAction"/> for all modified ranges that overlaps with <paramref name="address"/> and <paramref name="size"/>. 202 /// </summary> 203 /// <param name="address">Address of the region to consume</param> 204 /// <param name="size">Size of the region to consume</param> 205 /// <param name="rangeAction">Action to perform for modified ranges</param> 206 public void ConsumeModifiedRegion(ulong address, ulong size, Action<ulong, ulong> rangeAction) 207 { 208 if (_modifiedRanges != null) 209 { 210 _modifiedRanges.GetRanges(address, size, rangeAction); 211 _modifiedRanges.Clear(address, size); 212 } 213 } 214 215 /// <summary> 216 /// Gets data from the specified region of the buffer, and places it on <paramref name="output"/>. 217 /// </summary> 218 /// <param name="output">Span to put the data into</param> 219 /// <param name="offset">Offset of the buffer to get the data from</param> 220 /// <param name="size">Size of the data in bytes</param> 221 public void GetData(Span<byte> output, int offset, int size) 222 { 223 using PinnedSpan<byte> data = _context.Renderer.GetBufferData(Handle, offset, size); 224 data.Get().CopyTo(output); 225 } 226 227 /// <summary> 228 /// Disposes the host buffer. 229 /// </summary> 230 public void Dispose() 231 { 232 if (_dependencies != null) 233 { 234 foreach (var dependency in _dependencies) 235 { 236 dependency.PhysicalBuffer.RemoveVirtualDependency(this); 237 } 238 239 _dependencies = null; 240 } 241 242 _context.Renderer.DeleteBuffer(Handle); 243 } 244 } 245 }