diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs')
-rw-r--r-- | src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs new file mode 100644 index 00000000..0632add6 --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu.Shader.HashTable +{ + /// <summary> + /// Smart data accessor that can cache data and hashes to avoid reading and re-hashing the same memory regions. + /// </summary> + ref struct SmartDataAccessor + { + private readonly IDataAccessor _dataAccessor; + private ReadOnlySpan<byte> _data; + private readonly SortedList<int, HashState> _cachedHashes; + + /// <summary> + /// Creates a new smart data accessor. + /// </summary> + /// <param name="dataAccessor">Data accessor</param> + public SmartDataAccessor(IDataAccessor dataAccessor) + { + _dataAccessor = dataAccessor; + _data = ReadOnlySpan<byte>.Empty; + _cachedHashes = new SortedList<int, HashState>(); + } + + /// <summary> + /// Get a spans of a given size. + /// </summary> + /// <remarks> + /// The actual length of the span returned depends on the <see cref="IDataAccessor"/> + /// and might be less than requested. + /// </remarks> + /// <param name="length">Size in bytes</param> + /// <returns>Span with the requested size</returns> + public ReadOnlySpan<byte> GetSpan(int length) + { + if (_data.Length < length) + { + _data = _dataAccessor.GetSpan(0, length); + } + else if (_data.Length > length) + { + return _data.Slice(0, length); + } + + return _data; + } + + /// <summary> + /// Gets a span of the requested size, and a hash of its data. + /// </summary> + /// <param name="length">Length of the span</param> + /// <param name="hash">Hash of the span data</param> + /// <returns>Span of data</returns> + public ReadOnlySpan<byte> GetSpanAndHash(int length, out uint hash) + { + ReadOnlySpan<byte> data = GetSpan(length); + hash = data.Length == length ? CalcHashCached(data) : 0; + return data; + } + + /// <summary> + /// Calculates the hash for a requested span. + /// This will try to use a cached hash if the data was already accessed before, to avoid re-hashing. + /// </summary> + /// <param name="data">Data to be hashed</param> + /// <returns>Hash of the data</returns> + private uint CalcHashCached(ReadOnlySpan<byte> data) + { + HashState state = default; + bool found = false; + + for (int i = _cachedHashes.Count - 1; i >= 0; i--) + { + int cachedHashSize = _cachedHashes.Keys[i]; + + if (cachedHashSize < data.Length) + { + state = _cachedHashes.Values[i]; + found = true; + break; + } + } + + if (!found) + { + state = new HashState(); + state.Initialize(); + } + + state.Continue(data); + _cachedHashes[data.Length & ~7] = state; + return state.Finalize(data); + } + } +} |