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 }