/ src / Ryujinx.Graphics.Vulkan / CacheByRange.cs
CacheByRange.cs
  1  using System;
  2  using System.Collections.Generic;
  3  
  4  namespace Ryujinx.Graphics.Vulkan
  5  {
  6      interface ICacheKey : IDisposable
  7      {
  8          bool KeyEqual(ICacheKey other);
  9      }
 10  
 11      struct I8ToI16CacheKey : ICacheKey
 12      {
 13          // Used to notify the pipeline that bindings have invalidated on dispose.
 14          private readonly VulkanRenderer _gd;
 15          private Auto<DisposableBuffer> _buffer;
 16  
 17          public I8ToI16CacheKey(VulkanRenderer gd)
 18          {
 19              _gd = gd;
 20              _buffer = null;
 21          }
 22  
 23          public readonly bool KeyEqual(ICacheKey other)
 24          {
 25              return other is I8ToI16CacheKey;
 26          }
 27  
 28          public void SetBuffer(Auto<DisposableBuffer> buffer)
 29          {
 30              _buffer = buffer;
 31          }
 32  
 33          public readonly void Dispose()
 34          {
 35              _gd.PipelineInternal.DirtyIndexBuffer(_buffer);
 36          }
 37      }
 38  
 39      struct AlignedVertexBufferCacheKey : ICacheKey
 40      {
 41          private readonly int _stride;
 42          private readonly int _alignment;
 43  
 44          // Used to notify the pipeline that bindings have invalidated on dispose.
 45          private readonly VulkanRenderer _gd;
 46          private Auto<DisposableBuffer> _buffer;
 47  
 48          public AlignedVertexBufferCacheKey(VulkanRenderer gd, int stride, int alignment)
 49          {
 50              _gd = gd;
 51              _stride = stride;
 52              _alignment = alignment;
 53              _buffer = null;
 54          }
 55  
 56          public readonly bool KeyEqual(ICacheKey other)
 57          {
 58              return other is AlignedVertexBufferCacheKey entry &&
 59                  entry._stride == _stride &&
 60                  entry._alignment == _alignment;
 61          }
 62  
 63          public void SetBuffer(Auto<DisposableBuffer> buffer)
 64          {
 65              _buffer = buffer;
 66          }
 67  
 68          public readonly void Dispose()
 69          {
 70              _gd.PipelineInternal.DirtyVertexBuffer(_buffer);
 71          }
 72      }
 73  
 74      struct TopologyConversionCacheKey : ICacheKey
 75      {
 76          private readonly IndexBufferPattern _pattern;
 77          private readonly int _indexSize;
 78  
 79          // Used to notify the pipeline that bindings have invalidated on dispose.
 80          private readonly VulkanRenderer _gd;
 81          private Auto<DisposableBuffer> _buffer;
 82  
 83          public TopologyConversionCacheKey(VulkanRenderer gd, IndexBufferPattern pattern, int indexSize)
 84          {
 85              _gd = gd;
 86              _pattern = pattern;
 87              _indexSize = indexSize;
 88              _buffer = null;
 89          }
 90  
 91          public readonly bool KeyEqual(ICacheKey other)
 92          {
 93              return other is TopologyConversionCacheKey entry &&
 94                  entry._pattern == _pattern &&
 95                  entry._indexSize == _indexSize;
 96          }
 97  
 98          public void SetBuffer(Auto<DisposableBuffer> buffer)
 99          {
100              _buffer = buffer;
101          }
102  
103          public readonly void Dispose()
104          {
105              _gd.PipelineInternal.DirtyIndexBuffer(_buffer);
106          }
107      }
108  
109      readonly struct TopologyConversionIndirectCacheKey : ICacheKey
110      {
111          private readonly TopologyConversionCacheKey _baseKey;
112          private readonly BufferHolder _indirectDataBuffer;
113          private readonly int _indirectDataOffset;
114          private readonly int _indirectDataSize;
115  
116          public TopologyConversionIndirectCacheKey(
117              VulkanRenderer gd,
118              IndexBufferPattern pattern,
119              int indexSize,
120              BufferHolder indirectDataBuffer,
121              int indirectDataOffset,
122              int indirectDataSize)
123          {
124              _baseKey = new TopologyConversionCacheKey(gd, pattern, indexSize);
125              _indirectDataBuffer = indirectDataBuffer;
126              _indirectDataOffset = indirectDataOffset;
127              _indirectDataSize = indirectDataSize;
128          }
129  
130          public bool KeyEqual(ICacheKey other)
131          {
132              return other is TopologyConversionIndirectCacheKey entry &&
133                  entry._baseKey.KeyEqual(_baseKey) &&
134                  entry._indirectDataBuffer == _indirectDataBuffer &&
135                  entry._indirectDataOffset == _indirectDataOffset &&
136                  entry._indirectDataSize == _indirectDataSize;
137          }
138  
139          public void SetBuffer(Auto<DisposableBuffer> buffer)
140          {
141              _baseKey.SetBuffer(buffer);
142          }
143  
144          public void Dispose()
145          {
146              _baseKey.Dispose();
147          }
148      }
149  
150      readonly struct IndirectDataCacheKey : ICacheKey
151      {
152          private readonly IndexBufferPattern _pattern;
153  
154          public IndirectDataCacheKey(IndexBufferPattern pattern)
155          {
156              _pattern = pattern;
157          }
158  
159          public bool KeyEqual(ICacheKey other)
160          {
161              return other is IndirectDataCacheKey entry && entry._pattern == _pattern;
162          }
163  
164          public void Dispose()
165          {
166          }
167      }
168  
169      struct DrawCountCacheKey : ICacheKey
170      {
171          public readonly bool KeyEqual(ICacheKey other)
172          {
173              return other is DrawCountCacheKey;
174          }
175  
176          public readonly void Dispose()
177          {
178          }
179      }
180  
181      readonly struct Dependency
182      {
183          private readonly BufferHolder _buffer;
184          private readonly int _offset;
185          private readonly int _size;
186          private readonly ICacheKey _key;
187  
188          public Dependency(BufferHolder buffer, int offset, int size, ICacheKey key)
189          {
190              _buffer = buffer;
191              _offset = offset;
192              _size = size;
193              _key = key;
194          }
195  
196          public void RemoveFromOwner()
197          {
198              _buffer.RemoveCachedConvertedBuffer(_offset, _size, _key);
199          }
200      }
201  
202      struct CacheByRange<T> where T : IDisposable
203      {
204          private struct Entry
205          {
206              public ICacheKey Key;
207              public T Value;
208              public List<Dependency> DependencyList;
209  
210              public Entry(ICacheKey key, T value)
211              {
212                  Key = key;
213                  Value = value;
214                  DependencyList = null;
215              }
216  
217              public readonly void InvalidateDependencies()
218              {
219                  if (DependencyList != null)
220                  {
221                      foreach (Dependency dependency in DependencyList)
222                      {
223                          dependency.RemoveFromOwner();
224                      }
225  
226                      DependencyList.Clear();
227                  }
228              }
229          }
230  
231          private Dictionary<ulong, List<Entry>> _ranges;
232  
233          public void Add(int offset, int size, ICacheKey key, T value)
234          {
235              List<Entry> entries = GetEntries(offset, size);
236  
237              entries.Add(new Entry(key, value));
238          }
239  
240          public void AddDependency(int offset, int size, ICacheKey key, Dependency dependency)
241          {
242              List<Entry> entries = GetEntries(offset, size);
243  
244              for (int i = 0; i < entries.Count; i++)
245              {
246                  Entry entry = entries[i];
247  
248                  if (entry.Key.KeyEqual(key))
249                  {
250                      if (entry.DependencyList == null)
251                      {
252                          entry.DependencyList = new List<Dependency>();
253                          entries[i] = entry;
254                      }
255  
256                      entry.DependencyList.Add(dependency);
257  
258                      break;
259                  }
260              }
261          }
262  
263          public void Remove(int offset, int size, ICacheKey key)
264          {
265              List<Entry> entries = GetEntries(offset, size);
266  
267              for (int i = 0; i < entries.Count; i++)
268              {
269                  Entry entry = entries[i];
270  
271                  if (entry.Key.KeyEqual(key))
272                  {
273                      entries.RemoveAt(i--);
274  
275                      DestroyEntry(entry);
276                  }
277              }
278  
279              if (entries.Count == 0)
280              {
281                  _ranges.Remove(PackRange(offset, size));
282              }
283          }
284  
285          public bool TryGetValue(int offset, int size, ICacheKey key, out T value)
286          {
287              List<Entry> entries = GetEntries(offset, size);
288  
289              foreach (Entry entry in entries)
290              {
291                  if (entry.Key.KeyEqual(key))
292                  {
293                      value = entry.Value;
294  
295                      return true;
296                  }
297              }
298  
299              value = default;
300              return false;
301          }
302  
303          public void Clear()
304          {
305              if (_ranges != null)
306              {
307                  foreach (List<Entry> entries in _ranges.Values)
308                  {
309                      foreach (Entry entry in entries)
310                      {
311                          DestroyEntry(entry);
312                      }
313                  }
314  
315                  _ranges.Clear();
316                  _ranges = null;
317              }
318          }
319  
320          public readonly void ClearRange(int offset, int size)
321          {
322              if (_ranges != null && _ranges.Count > 0)
323              {
324                  int end = offset + size;
325  
326                  List<ulong> toRemove = null;
327  
328                  foreach (KeyValuePair<ulong, List<Entry>> range in _ranges)
329                  {
330                      (int rOffset, int rSize) = UnpackRange(range.Key);
331  
332                      int rEnd = rOffset + rSize;
333  
334                      if (rEnd > offset && rOffset < end)
335                      {
336                          List<Entry> entries = range.Value;
337  
338                          foreach (Entry entry in entries)
339                          {
340                              DestroyEntry(entry);
341                          }
342  
343                          (toRemove ??= new List<ulong>()).Add(range.Key);
344                      }
345                  }
346  
347                  if (toRemove != null)
348                  {
349                      foreach (ulong range in toRemove)
350                      {
351                          _ranges.Remove(range);
352                      }
353                  }
354              }
355          }
356  
357          private List<Entry> GetEntries(int offset, int size)
358          {
359              _ranges ??= new Dictionary<ulong, List<Entry>>();
360  
361              ulong key = PackRange(offset, size);
362  
363              if (!_ranges.TryGetValue(key, out List<Entry> value))
364              {
365                  value = new List<Entry>();
366                  _ranges.Add(key, value);
367              }
368  
369              return value;
370          }
371  
372          private static void DestroyEntry(Entry entry)
373          {
374              entry.Key.Dispose();
375              entry.Value?.Dispose();
376              entry.InvalidateDependencies();
377          }
378  
379          private static ulong PackRange(int offset, int size)
380          {
381              return (uint)offset | ((ulong)size << 32);
382          }
383  
384          private static (int offset, int size) UnpackRange(ulong range)
385          {
386              return ((int)range, (int)(range >> 32));
387          }
388  
389          public void Dispose()
390          {
391              Clear();
392          }
393      }
394  }