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  }