aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs')
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs96
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);
+ }
+ }
+}