BufferMap.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Runtime.CompilerServices; 4 using System.Threading; 5 6 namespace Ryujinx.Graphics.GAL.Multithreading 7 { 8 /// <summary> 9 /// Buffer handles given to the client are not the same as those provided by the backend, 10 /// as their handle is created at a later point on the queue. 11 /// The handle returned is a unique identifier that will map to the real buffer when it is available. 12 /// Note that any uses within the queue should be safe, but outside you must use MapBufferBlocking. 13 /// </summary> 14 class BufferMap 15 { 16 private ulong _bufferHandle = 0; 17 18 private readonly Dictionary<BufferHandle, BufferHandle> _bufferMap = new(); 19 private readonly HashSet<BufferHandle> _inFlight = new(); 20 private readonly AutoResetEvent _inFlightChanged = new(false); 21 22 internal BufferHandle CreateBufferHandle() 23 { 24 ulong handle64 = Interlocked.Increment(ref _bufferHandle); 25 26 BufferHandle threadedHandle = Unsafe.As<ulong, BufferHandle>(ref handle64); 27 28 lock (_inFlight) 29 { 30 _inFlight.Add(threadedHandle); 31 } 32 33 return threadedHandle; 34 } 35 36 internal void AssignBuffer(BufferHandle threadedHandle, BufferHandle realHandle) 37 { 38 lock (_bufferMap) 39 { 40 _bufferMap[threadedHandle] = realHandle; 41 } 42 43 lock (_inFlight) 44 { 45 _inFlight.Remove(threadedHandle); 46 } 47 48 _inFlightChanged.Set(); 49 } 50 51 internal void UnassignBuffer(BufferHandle threadedHandle) 52 { 53 lock (_bufferMap) 54 { 55 _bufferMap.Remove(threadedHandle); 56 } 57 } 58 59 internal BufferHandle MapBuffer(BufferHandle handle) 60 { 61 // Maps a threaded buffer to a backend one. 62 // Threaded buffers are returned on creation as the buffer 63 // isn't actually created until the queue runs the command. 64 65 lock (_bufferMap) 66 { 67 if (!_bufferMap.TryGetValue(handle, out BufferHandle result)) 68 { 69 result = BufferHandle.Null; 70 } 71 72 return result; 73 } 74 } 75 76 internal BufferHandle MapBufferBlocking(BufferHandle handle) 77 { 78 // Blocks until the handle is available. 79 80 81 lock (_bufferMap) 82 { 83 if (_bufferMap.TryGetValue(handle, out BufferHandle result)) 84 { 85 return result; 86 } 87 } 88 89 bool signal = false; 90 91 while (true) 92 { 93 lock (_inFlight) 94 { 95 if (!_inFlight.Contains(handle)) 96 { 97 break; 98 } 99 } 100 101 _inFlightChanged.WaitOne(); 102 signal = true; 103 } 104 105 if (signal) 106 { 107 // Signal other threads which might still be waiting. 108 _inFlightChanged.Set(); 109 } 110 111 return MapBuffer(handle); 112 } 113 114 internal BufferRange MapBufferRange(BufferRange range) 115 { 116 return new BufferRange(MapBuffer(range.Handle), range.Offset, range.Size, range.Write); 117 } 118 119 internal Span<BufferRange> MapBufferRanges(Span<BufferRange> ranges) 120 { 121 // Rewrite the buffer ranges to point to the mapped handles. 122 123 lock (_bufferMap) 124 { 125 for (int i = 0; i < ranges.Length; i++) 126 { 127 ref BufferRange range = ref ranges[i]; 128 129 if (!_bufferMap.TryGetValue(range.Handle, out BufferHandle result)) 130 { 131 result = BufferHandle.Null; 132 } 133 134 range = new BufferRange(result, range.Offset, range.Size, range.Write); 135 } 136 } 137 138 return ranges; 139 } 140 141 internal Span<BufferAssignment> MapBufferRanges(Span<BufferAssignment> ranges) 142 { 143 // Rewrite the buffer ranges to point to the mapped handles. 144 145 lock (_bufferMap) 146 { 147 for (int i = 0; i < ranges.Length; i++) 148 { 149 ref BufferAssignment assignment = ref ranges[i]; 150 BufferRange range = assignment.Range; 151 152 if (!_bufferMap.TryGetValue(range.Handle, out BufferHandle result)) 153 { 154 result = BufferHandle.Null; 155 } 156 157 assignment = new BufferAssignment(ranges[i].Binding, new BufferRange(result, range.Offset, range.Size, range.Write)); 158 } 159 } 160 161 return ranges; 162 } 163 164 internal Span<VertexBufferDescriptor> MapBufferRanges(Span<VertexBufferDescriptor> ranges) 165 { 166 // Rewrite the buffer ranges to point to the mapped handles. 167 168 lock (_bufferMap) 169 { 170 for (int i = 0; i < ranges.Length; i++) 171 { 172 BufferRange range = ranges[i].Buffer; 173 174 if (!_bufferMap.TryGetValue(range.Handle, out BufferHandle result)) 175 { 176 result = BufferHandle.Null; 177 } 178 179 range = new BufferRange(result, range.Offset, range.Size, range.Write); 180 181 ranges[i] = new VertexBufferDescriptor(range, ranges[i].Stride, ranges[i].Divisor); 182 } 183 } 184 185 return ranges; 186 } 187 } 188 }