Added WaitAll and WaitAny to JobScheduler;
Added generation support to UnsafeSparseSet;
This commit is contained in:
@@ -1,47 +0,0 @@
|
||||
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
public readonly struct CollectionHandle
|
||||
{
|
||||
public readonly int id;
|
||||
public readonly int generation;
|
||||
|
||||
public static CollectionHandle Invalid => new(-1, -1);
|
||||
|
||||
public bool IsValid => this != Invalid;
|
||||
|
||||
internal CollectionHandle(int id, int generation)
|
||||
{
|
||||
this.id = id;
|
||||
this.generation = generation;
|
||||
}
|
||||
|
||||
public bool Equals(CollectionHandle other)
|
||||
{
|
||||
return id == other.id && generation == other.generation;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is CollectionHandle handle && Equals(handle);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(id, generation);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return IsValid ? $"CollectionHandle({id}, {generation})" : "CollectionHandle(Invalid)";
|
||||
}
|
||||
|
||||
public static bool operator ==(CollectionHandle left, CollectionHandle right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(CollectionHandle left, CollectionHandle right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
@@ -76,10 +76,12 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
#if DISABLE_COLLECTION_CHECKS
|
||||
if (index < 0 || index >= _count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
|
||||
}
|
||||
#endif
|
||||
|
||||
return ref UnsafeUtilities.ReadArrayElementRef<T>(_buffer, index);
|
||||
}
|
||||
@@ -90,10 +92,12 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
#if DISABLE_COLLECTION_CHECKS
|
||||
if (index >= _count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
|
||||
}
|
||||
#endif
|
||||
|
||||
return ref UnsafeUtilities.ReadArrayElementRef<T>(_buffer, index);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,12 @@ namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||
public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
where T : unmanaged
|
||||
{
|
||||
private struct SlotEntry
|
||||
{
|
||||
public T value;
|
||||
public int generation;
|
||||
}
|
||||
|
||||
public struct Enumerator : IEnumerator<T>
|
||||
{
|
||||
private UnsafeSparseSet<T>* _collection;
|
||||
@@ -34,10 +40,14 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index++;
|
||||
if (_index < _collection->_count)
|
||||
if (_index < _collection->_sparse.Count)
|
||||
{
|
||||
_value = UnsafeUtilities.ReadArrayElement<T>(_collection->_dense.GetUnsafePtr(), _index);
|
||||
return true;
|
||||
var index = _collection->_sparse[_index];
|
||||
if (index >= 0 && index < _collection->_count)
|
||||
{
|
||||
_value = _collection->_dense[index].value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_value = default;
|
||||
@@ -66,7 +76,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
}
|
||||
}
|
||||
|
||||
private UnsafeArray<T> _dense;
|
||||
private UnsafeArray<SlotEntry> _dense;
|
||||
private UnsafeArray<int> _sparse;
|
||||
private UnsafeArray<int> _reverse; // Maps dense index to sparse index
|
||||
private UnsafeArray<int> _freeList; // Stack of available sparse indices
|
||||
@@ -103,7 +113,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be greater than zero.");
|
||||
}
|
||||
|
||||
_dense = new UnsafeArray<T>(capacity, ref handle, allocationOption);
|
||||
_dense = new UnsafeArray<SlotEntry>(capacity, ref handle, allocationOption);
|
||||
_sparse = new UnsafeArray<int>(capacity, ref handle, allocationOption);
|
||||
_reverse = new UnsafeArray<int>(capacity, ref handle, allocationOption);
|
||||
_freeList = new UnsafeArray<int>(capacity, ref handle, allocationOption);
|
||||
@@ -130,8 +140,9 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
/// Adds a value to the sparse set and returns a unique sparse index for the value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to add to the sparse set.</param>
|
||||
/// <param name="generation">Outputs the generation number associated with the added value.</param>
|
||||
/// <returns>A unique sparse index that can be used to reference this value.</returns>
|
||||
public int Add(T value)
|
||||
public int Add(T value, out int generation)
|
||||
{
|
||||
int sparseIndex;
|
||||
|
||||
@@ -162,68 +173,27 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
}
|
||||
|
||||
// Add the value to the dense array and update mappings
|
||||
_dense[_count] = value;
|
||||
ref var entry = ref _dense[_count];
|
||||
entry.value = value;
|
||||
|
||||
_sparse[sparseIndex] = _count;
|
||||
_reverse[_count] = sparseIndex;
|
||||
_count++;
|
||||
|
||||
generation = entry.generation;
|
||||
|
||||
return sparseIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a value to the sparse set at the specified sparse index.
|
||||
/// This method is provided for compatibility when you need to specify the exact sparse index.
|
||||
/// </summary>
|
||||
/// <param name="sparseIndex">The index in the sparse array where the value should be mapped.</param>
|
||||
/// <param name="value">The value to add to the sparse set.</param>
|
||||
/// <returns>True if the value was added, false if the sparse index is already occupied.</returns>
|
||||
public bool AddAt(int sparseIndex, T value)
|
||||
{
|
||||
if (sparseIndex < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(sparseIndex), "Sparse index must be non-negative.");
|
||||
}
|
||||
|
||||
if (sparseIndex >= _sparse.Count)
|
||||
{
|
||||
ResizeSparse(sparseIndex + 1);
|
||||
}
|
||||
|
||||
if (Contains(sparseIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_count >= _dense.Count)
|
||||
{
|
||||
var newCapacity = _dense.Count + Math.Max(1, _dense.Count / 2);
|
||||
_dense.Resize(newCapacity);
|
||||
_reverse.Resize(newCapacity);
|
||||
}
|
||||
|
||||
// Add the value to the dense array and update mappings
|
||||
_dense[_count] = value;
|
||||
_sparse[sparseIndex] = _count;
|
||||
_reverse[_count] = sparseIndex; // Store reverse mapping
|
||||
_count++;
|
||||
|
||||
// Update _nextId if we're using a higher ID
|
||||
if (sparseIndex >= _nextId)
|
||||
{
|
||||
_nextId = sparseIndex + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the value at the specified sparse index.
|
||||
/// </summary>
|
||||
/// <param name="sparseIndex">The sparse index of the value to remove.</param>
|
||||
/// <param name="generation">The generation number associated with the sparse index to validate.</param>
|
||||
/// <returns>True if the value was removed, false if the sparse index was not found.</returns>
|
||||
public bool Remove(int sparseIndex)
|
||||
public bool Remove(int sparseIndex, int generation)
|
||||
{
|
||||
if (!Contains(sparseIndex))
|
||||
if (!Contains(sparseIndex, generation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -246,12 +216,14 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
|
||||
// Mark the sparse index as unused and add to free list
|
||||
_sparse[sparseIndex] = -1;
|
||||
_dense[lastIndex].generation++; // Increment generation to invalidate old references
|
||||
|
||||
// Add the freed sparse index to the free list for reuse
|
||||
if (_freeCount >= _freeList.Count)
|
||||
{
|
||||
_freeList.Resize(_freeList.Count + Math.Max(1, _freeList.Count / 2));
|
||||
}
|
||||
|
||||
_freeList[_freeCount] = sparseIndex;
|
||||
_freeCount++;
|
||||
|
||||
@@ -264,9 +236,10 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
/// Checks if the sparse set contains a value at the specified sparse index.
|
||||
/// </summary>
|
||||
/// <param name="sparseIndex">The sparse index to check.</param>
|
||||
/// <param name="generation">The generation number to validate against the stored generation.</param>
|
||||
/// <returns>True if the sparse index is valid and contains a value, false otherwise.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool Contains(int sparseIndex)
|
||||
public readonly bool Contains(int sparseIndex, int generation)
|
||||
{
|
||||
if (sparseIndex < 0 || sparseIndex >= _sparse.Count)
|
||||
{
|
||||
@@ -274,21 +247,24 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
}
|
||||
|
||||
var denseIndex = _sparse[sparseIndex];
|
||||
return denseIndex >= 0 && denseIndex < _count;
|
||||
return denseIndex >= 0 && denseIndex < _count && _dense[denseIndex].generation == generation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value at the specified sparse index.
|
||||
/// Gets the value at the specified sparse index and generation.
|
||||
/// </summary>
|
||||
/// <param name="sparseIndex">The sparse index to retrieve the value from.</param>
|
||||
/// <param name="generation">The generation number to validate against the stored generation.</param>
|
||||
/// <param name="value">When this method returns, contains the value at the specified sparse index, if found.</param>
|
||||
/// <returns>True if the sparse index contains a value, false otherwise.</returns>
|
||||
public readonly bool TryGetValue(int sparseIndex, out T value)
|
||||
public readonly bool TryGetValue(int sparseIndex, int generation, out T value)
|
||||
{
|
||||
if (Contains(sparseIndex))
|
||||
if (Contains(sparseIndex, generation))
|
||||
{
|
||||
var denseIndex = _sparse[sparseIndex];
|
||||
value = _dense[denseIndex];
|
||||
ref var entry = ref _dense[denseIndex];
|
||||
|
||||
value = entry.value;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -297,40 +273,71 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value at the specified sparse index.
|
||||
/// Gets the value at the specified sparse index and generation.
|
||||
/// </summary>
|
||||
/// <param name="sparseIndex">The sparse index to retrieve the value from.</param>
|
||||
/// <param name="generation">The generation number to validate against the stored generation.</param>
|
||||
/// <returns>The value at the specified sparse index.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the sparse index is not found.</exception>
|
||||
public readonly T GetValue(int sparseIndex)
|
||||
public readonly T GetValue(int sparseIndex, int generation)
|
||||
{
|
||||
if (!Contains(sparseIndex))
|
||||
if (!Contains(sparseIndex, generation))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(sparseIndex), "Sparse index not found in the set.");
|
||||
throw new ArgumentOutOfRangeException(nameof(sparseIndex), "Sparse index and feneration not found in the set.");
|
||||
}
|
||||
|
||||
var denseIndex = _sparse[sparseIndex];
|
||||
return _dense[denseIndex];
|
||||
ref var entry = ref _dense[denseIndex];
|
||||
|
||||
return entry.value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets reference of the value at the specified sparse index and generation.
|
||||
/// </summary>
|
||||
/// <param name="sparseIndex">The sparse index to retrieve the value from.</param>
|
||||
/// <param name="generation">The generation number to validate against the stored generation.</param>
|
||||
/// <param name="exist">Outputs whether the sparse index exists in the set.</param>
|
||||
/// <returns>Reference of the value at the specified sparse index.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the sparse index is not found.</exception>
|
||||
public readonly ref T GetValueReference(int sparseIndex, int generation, out bool exist)
|
||||
{
|
||||
if (!Contains(sparseIndex, generation))
|
||||
{
|
||||
exist = false;
|
||||
return ref Unsafe.NullRef<T>();
|
||||
}
|
||||
|
||||
var denseIndex = _sparse[sparseIndex];
|
||||
ref var entry = ref _dense[denseIndex];
|
||||
|
||||
exist = true;
|
||||
|
||||
return ref entry.value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the value at the specified sparse index.
|
||||
/// </summary>
|
||||
/// <param name="sparseIndex">The sparse index of the value to update.</param>
|
||||
/// <param name="generation">The generation number to validate against the stored generation.</param>
|
||||
/// <param name="value">The new value.</param>
|
||||
/// <returns>True if the value was updated, false if the sparse index was not found.</returns>
|
||||
public bool SetValue(int sparseIndex, T value)
|
||||
public bool SetValue(int sparseIndex, int generation, T value)
|
||||
{
|
||||
if (!Contains(sparseIndex))
|
||||
if (!Contains(sparseIndex, generation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var denseIndex = _sparse[sparseIndex];
|
||||
_dense[denseIndex] = value;
|
||||
ref var entry = ref _dense[denseIndex];
|
||||
entry.value = value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ResizeSparse(int newSize)
|
||||
{
|
||||
var oldSize = _sparse.Count;
|
||||
|
||||
Reference in New Issue
Block a user