using System; using System.Collections.Generic; namespace Ryujinx.Graphics.Vulkan { interface ICacheKey : IDisposable { bool KeyEqual(ICacheKey other); } struct I8ToI16CacheKey : ICacheKey { // Used to notify the pipeline that bindings have invalidated on dispose. private readonly VulkanRenderer _gd; private Auto _buffer; public I8ToI16CacheKey(VulkanRenderer gd) { _gd = gd; _buffer = null; } public bool KeyEqual(ICacheKey other) { return other is I8ToI16CacheKey; } public void SetBuffer(Auto buffer) { _buffer = buffer; } public void Dispose() { _gd.PipelineInternal.DirtyIndexBuffer(_buffer); } } struct AlignedVertexBufferCacheKey : ICacheKey { private readonly int _stride; private readonly int _alignment; // Used to notify the pipeline that bindings have invalidated on dispose. private readonly VulkanRenderer _gd; private Auto _buffer; public AlignedVertexBufferCacheKey(VulkanRenderer gd, int stride, int alignment) { _gd = gd; _stride = stride; _alignment = alignment; _buffer = null; } public bool KeyEqual(ICacheKey other) { return other is AlignedVertexBufferCacheKey entry && entry._stride == _stride && entry._alignment == _alignment; } public void SetBuffer(Auto buffer) { _buffer = buffer; } public void Dispose() { _gd.PipelineInternal.DirtyVertexBuffer(_buffer); } } struct TopologyConversionCacheKey : ICacheKey { private IndexBufferPattern _pattern; private int _indexSize; // Used to notify the pipeline that bindings have invalidated on dispose. private readonly VulkanRenderer _gd; private Auto _buffer; public TopologyConversionCacheKey(VulkanRenderer gd, IndexBufferPattern pattern, int indexSize) { _gd = gd; _pattern = pattern; _indexSize = indexSize; _buffer = null; } public bool KeyEqual(ICacheKey other) { return other is TopologyConversionCacheKey entry && entry._pattern == _pattern && entry._indexSize == _indexSize; } public void SetBuffer(Auto buffer) { _buffer = buffer; } public void Dispose() { _gd.PipelineInternal.DirtyIndexBuffer(_buffer); } } struct CacheByRange where T : IDisposable { private struct Entry { public ICacheKey Key; public T Value; public Entry(ICacheKey key, T value) { Key = key; Value = value; } } private Dictionary> _ranges; public void Add(int offset, int size, ICacheKey key, T value) { List entries = GetEntries(offset, size); entries.Add(new Entry(key, value)); } public bool TryGetValue(int offset, int size, ICacheKey key, out T value) { List entries = GetEntries(offset, size); foreach (Entry entry in entries) { if (entry.Key.KeyEqual(key)) { value = entry.Value; return true; } } value = default; return false; } public void Clear() { if (_ranges != null) { foreach (List entries in _ranges.Values) { foreach (Entry entry in entries) { entry.Key.Dispose(); entry.Value.Dispose(); } } _ranges.Clear(); _ranges = null; } } public void ClearRange(int offset, int size) { if (_ranges != null && _ranges.Count > 0) { int end = offset + size; List toRemove = null; foreach (KeyValuePair> range in _ranges) { (int rOffset, int rSize) = UnpackRange(range.Key); int rEnd = rOffset + rSize; if (rEnd > offset && rOffset < end) { List entries = range.Value; foreach (Entry entry in entries) { entry.Key.Dispose(); entry.Value.Dispose(); } (toRemove ??= new List()).Add(range.Key); } } if (toRemove != null) { foreach (ulong range in toRemove) { _ranges.Remove(range); } } } } private List GetEntries(int offset, int size) { if (_ranges == null) { _ranges = new Dictionary>(); } ulong key = PackRange(offset, size); List value; if (!_ranges.TryGetValue(key, out value)) { value = new List(); _ranges.Add(key, value); } return value; } private static ulong PackRange(int offset, int size) { return (uint)offset | ((ulong)size << 32); } private static (int offset, int size) UnpackRange(ulong range) { return ((int)range, (int)(range >> 32)); } public void Dispose() { Clear(); } } }