/ src / Ryujinx.Graphics.Vulkan / BufferMirrorRangeList.cs
BufferMirrorRangeList.cs
  1  using System;
  2  using System.Collections.Generic;
  3  
  4  namespace Ryujinx.Graphics.Vulkan
  5  {
  6      /// <summary>
  7      /// A structure tracking pending upload ranges for buffers.
  8      /// Where a range is present, pending data exists that can either be used to build mirrors
  9      /// or upload directly to the buffer.
 10      /// </summary>
 11      struct BufferMirrorRangeList
 12      {
 13          internal readonly struct Range
 14          {
 15              public int Offset { get; }
 16              public int Size { get; }
 17  
 18              public int End => Offset + Size;
 19  
 20              public Range(int offset, int size)
 21              {
 22                  Offset = offset;
 23                  Size = size;
 24              }
 25  
 26              public bool OverlapsWith(int offset, int size)
 27              {
 28                  return Offset < offset + size && offset < Offset + Size;
 29              }
 30          }
 31  
 32          private List<Range> _ranges;
 33  
 34          public readonly IEnumerable<Range> All()
 35          {
 36              return _ranges;
 37          }
 38  
 39          public readonly bool Remove(int offset, int size)
 40          {
 41              var list = _ranges;
 42              bool removedAny = false;
 43              if (list != null)
 44              {
 45                  int overlapIndex = BinarySearch(list, offset, size);
 46  
 47                  if (overlapIndex >= 0)
 48                  {
 49                      // Overlaps with a range. Search back to find the first one it doesn't overlap with.
 50  
 51                      while (overlapIndex > 0 && list[overlapIndex - 1].OverlapsWith(offset, size))
 52                      {
 53                          overlapIndex--;
 54                      }
 55  
 56                      int endOffset = offset + size;
 57                      int startIndex = overlapIndex;
 58  
 59                      var currentOverlap = list[overlapIndex];
 60  
 61                      // Orphan the start of the overlap.
 62                      if (currentOverlap.Offset < offset)
 63                      {
 64                          list[overlapIndex] = new Range(currentOverlap.Offset, offset - currentOverlap.Offset);
 65                          currentOverlap = new Range(offset, currentOverlap.End - offset);
 66                          list.Insert(++overlapIndex, currentOverlap);
 67                          startIndex++;
 68  
 69                          removedAny = true;
 70                      }
 71  
 72                      // Remove any middle overlaps.
 73                      while (currentOverlap.Offset < endOffset)
 74                      {
 75                          if (currentOverlap.End > endOffset)
 76                          {
 77                              // Update the end overlap instead of removing it, if it spans beyond the removed range.
 78                              list[overlapIndex] = new Range(endOffset, currentOverlap.End - endOffset);
 79  
 80                              removedAny = true;
 81                              break;
 82                          }
 83  
 84                          if (++overlapIndex >= list.Count)
 85                          {
 86                              break;
 87                          }
 88  
 89                          currentOverlap = list[overlapIndex];
 90                      }
 91  
 92                      int count = overlapIndex - startIndex;
 93  
 94                      list.RemoveRange(startIndex, count);
 95  
 96                      removedAny |= count > 0;
 97                  }
 98              }
 99  
100              return removedAny;
101          }
102  
103          public void Add(int offset, int size)
104          {
105              var list = _ranges;
106              if (list != null)
107              {
108                  int overlapIndex = BinarySearch(list, offset, size);
109                  if (overlapIndex >= 0)
110                  {
111                      while (overlapIndex > 0 && list[overlapIndex - 1].OverlapsWith(offset, size))
112                      {
113                          overlapIndex--;
114                      }
115  
116                      int endOffset = offset + size;
117                      int startIndex = overlapIndex;
118  
119                      while (overlapIndex < list.Count && list[overlapIndex].OverlapsWith(offset, size))
120                      {
121                          var currentOverlap = list[overlapIndex];
122                          var currentOverlapEndOffset = currentOverlap.Offset + currentOverlap.Size;
123  
124                          if (offset > currentOverlap.Offset)
125                          {
126                              offset = currentOverlap.Offset;
127                          }
128  
129                          if (endOffset < currentOverlapEndOffset)
130                          {
131                              endOffset = currentOverlapEndOffset;
132                          }
133  
134                          overlapIndex++;
135                          size = endOffset - offset;
136                      }
137  
138                      int count = overlapIndex - startIndex;
139  
140                      list.RemoveRange(startIndex, count);
141  
142                      overlapIndex = startIndex;
143                  }
144                  else
145                  {
146                      overlapIndex = ~overlapIndex;
147                  }
148  
149                  list.Insert(overlapIndex, new Range(offset, size));
150              }
151              else
152              {
153                  _ranges = new List<Range>
154                  {
155                      new Range(offset, size)
156                  };
157              }
158          }
159  
160          public readonly bool OverlapsWith(int offset, int size)
161          {
162              var list = _ranges;
163              if (list == null)
164              {
165                  return false;
166              }
167  
168              return BinarySearch(list, offset, size) >= 0;
169          }
170  
171          public readonly List<Range> FindOverlaps(int offset, int size)
172          {
173              var list = _ranges;
174              if (list == null)
175              {
176                  return null;
177              }
178  
179              List<Range> result = null;
180  
181              int index = BinarySearch(list, offset, size);
182  
183              if (index >= 0)
184              {
185                  while (index > 0 && list[index - 1].OverlapsWith(offset, size))
186                  {
187                      index--;
188                  }
189  
190                  do
191                  {
192                      (result ??= new List<Range>()).Add(list[index++]);
193                  }
194                  while (index < list.Count && list[index].OverlapsWith(offset, size));
195              }
196  
197              return result;
198          }
199  
200          private static int BinarySearch(List<Range> list, int offset, int size)
201          {
202              int left = 0;
203              int right = list.Count - 1;
204  
205              while (left <= right)
206              {
207                  int range = right - left;
208  
209                  int middle = left + (range >> 1);
210  
211                  var item = list[middle];
212  
213                  if (item.OverlapsWith(offset, size))
214                  {
215                      return middle;
216                  }
217  
218                  if (offset < item.Offset)
219                  {
220                      right = middle - 1;
221                  }
222                  else
223                  {
224                      left = middle + 1;
225                  }
226              }
227  
228              return ~left;
229          }
230  
231          public readonly void FillData(Span<byte> baseData, Span<byte> modData, int offset, Span<byte> result)
232          {
233              int size = baseData.Length;
234              int endOffset = offset + size;
235  
236              var list = _ranges;
237              if (list == null)
238              {
239                  baseData.CopyTo(result);
240              }
241  
242              int srcOffset = offset;
243              int dstOffset = 0;
244              bool activeRange = false;
245  
246              for (int i = 0; i < list.Count; i++)
247              {
248                  var range = list[i];
249  
250                  int rangeEnd = range.Offset + range.Size;
251  
252                  if (activeRange)
253                  {
254                      if (range.Offset >= endOffset)
255                      {
256                          break;
257                      }
258                  }
259                  else
260                  {
261                      if (rangeEnd <= offset)
262                      {
263                          continue;
264                      }
265  
266                      activeRange = true;
267                  }
268  
269                  int baseSize = range.Offset - srcOffset;
270  
271                  if (baseSize > 0)
272                  {
273                      baseData.Slice(dstOffset, baseSize).CopyTo(result.Slice(dstOffset, baseSize));
274                      srcOffset += baseSize;
275                      dstOffset += baseSize;
276                  }
277  
278                  int modSize = Math.Min(rangeEnd - srcOffset, endOffset - srcOffset);
279                  if (modSize != 0)
280                  {
281                      modData.Slice(dstOffset, modSize).CopyTo(result.Slice(dstOffset, modSize));
282                      srcOffset += modSize;
283                      dstOffset += modSize;
284                  }
285              }
286  
287              int baseSizeEnd = endOffset - srcOffset;
288  
289              if (baseSizeEnd > 0)
290              {
291                  baseData.Slice(dstOffset, baseSizeEnd).CopyTo(result.Slice(dstOffset, baseSizeEnd));
292              }
293          }
294  
295          public readonly int Count()
296          {
297              return _ranges?.Count ?? 0;
298          }
299  
300          public void Clear()
301          {
302              _ranges = null;
303          }
304      }
305  }