Merge branch 'giga' of ssh://ssh.github.com:443/Nitload-NSI/CCD_Github into local

merge newest code to main branch
This commit is contained in:
Nitload
2025-11-08 21:46:17 +08:00
21 changed files with 1049 additions and 999 deletions
@@ -29,6 +29,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
namespace StgSharp.Collections
{
@@ -45,6 +46,8 @@ namespace StgSharp.Collections
_index = -1;
}
public T? First => Volatile.Read(ref _index) > 0 ? _values[0] : default;
public bool IsEmpty => _index == -1;
public int Count => _index + 1;
@@ -0,0 +1,287 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ConcurrentCapacityFixedStack"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
namespace StgSharp.Collections
{
/// <summary>
/// Lock-free, fixed-capacity stack implemented on top of an array using atomic index
/// operations. - No linked list; only array slots - Uses CAS on a shared top index to
/// reserve/release slots - Per-slot state flag to safely publish/consume values under races
/// </summary>
public class ConcurrentCapacityFixedStack<T> : IEnumerable<T>
{
private readonly int[] _states; // 0 = empty, 1 = full
private readonly T[] _values;
private readonly int _capacity;
// Number of full slots (observable count)
private int _count;
// Next push position (0.._capacity). Only modified via CAS.
private int _top;
public ConcurrentCapacityFixedStack(int capacity)
{
if (capacity <= 0) {
throw new ArgumentOutOfRangeException(nameof(capacity));
}
_capacity = capacity;
_values = new T[capacity];
_states = new int[capacity];
_top = 0;
_count = 0;
}
public bool IsEmpty => Count == 0;
public int Capacity => _capacity;
public int Count => Volatile.Read(ref _count);
public void Clear()
{
// Pop all elements to return slots to empty state
while (TryPop(out _)) { }
}
public IEnumerator<T> GetEnumerator()
{
T[] snap = ToArraySnapshot();
for (int i = 0; i < snap.Length; i++) {
yield return snap[i];
}
}
public T Peek()
{
if (TryPeek(out T? v)) {
return v;
}
throw new InvalidOperationException("Stack is empty.");
}
public T Pop()
{
if (TryPop(out T value)) {
return value;
}
throw new InvalidOperationException("Stack is empty.");
}
public T[] PopRange(int count)
{
if (count <= 0) {
return [];
}
T[] result = new T[count];
int i = 0;
for (; i < count; i++)
{
if (!TryPop(out result[i]))
{
break;
}
}
if (i == count) {
return result;
}
if (i == 0) {
return [];
}
T[] trimmed = new T[i];
Array.Copy(result, trimmed, i);
return trimmed;
}
public void Push(T item)
{
SpinWait sw = default;
while (true)
{
int oldTop = Volatile.Read(ref _top);
if (oldTop >= _capacity) {
throw new InvalidOperationException("Stack is full.");
}
if (Interlocked.CompareExchange(ref _top, oldTop + 1, oldTop) != oldTop)
{
sw.SpinOnce();
continue; // failed to reserve slot
}
// We exclusively own slot oldTop. Publish value then state.
_values[oldTop] = item;
Volatile.Write(ref _states[oldTop], 1); // publish
_ = Interlocked.Increment(ref _count);
return;
}
}
public void PushRange(ReadOnlySpan<T> span)
{
for (int i = 0; i < span.Length; i++) {
Push(span[i]);
}
}
/// <summary>
/// Best-effort snapshot for enumeration; not strongly consistent under concurrent
/// mutations.
/// </summary>
public T[] ToArraySnapshot()
{
int expected = Math.Min(Volatile.Read(ref _count), _capacity);
if (expected == 0) {
return Array.Empty<T>();
}
T[] snap = new T[expected];
int copied = 0;
int idx = Volatile.Read(ref _top) - 1;
while (idx >= 0 && copied < expected)
{
if (Volatile.Read(ref _states[idx]) == 1) {
snap[copied++] = _values[idx];
}
idx--;
}
if (copied == snap.Length) {
return snap;
}
T[] trimmed = new T[copied];
Array.Copy(snap, trimmed, copied);
return trimmed;
}
public bool TryPeek(out T value)
{
value = default!;
SpinWait sw = default;
while (true)
{
int top = Volatile.Read(ref _top);
if (top <= 0) {
return false;
}
int idx = top - 1;
// If not yet published or racing, retry/spin
if (Volatile.Read(ref _states[idx]) == 0)
{
if (Volatile.Read(ref _top) != top)
{
continue; // head moved, retry
}
sw.SpinOnce();
continue;
}
T v = _values[idx];
// Validate consistency
if (Volatile.Read(ref _top) == top && Volatile.Read(ref _states[idx]) == 1)
{
value = v;
return true;
}
}
}
public bool TryPop(out T value)
{
value = default!;
SpinWait sw = default;
while (true)
{
int oldTop = Volatile.Read(ref _top);
if (oldTop <= 0)
{
return false; // empty
}
int newTop = oldTop - 1; // index to consume
if (Interlocked.CompareExchange(ref _top, newTop, oldTop) != oldTop)
{
sw.SpinOnce();
continue; // failed to reserve element
}
// If producer hasn't published yet, roll back our reservation and retry.
if (Volatile.Read(ref _states[newTop]) == 0)
{
// attempt to restore top; ok if it fails due to concurrent operations
_ = Interlocked.CompareExchange(ref _top, newTop + 1, newTop);
sw.SpinOnce();
continue;
}
value = _values[newTop];
_values[newTop] = default!; // avoid ref retention
Volatile.Write(ref _states[newTop], 0); // mark empty
_ = Interlocked.Decrement(ref _count);
return true;
}
}
public int TryPopRange(Span<T> destination)
{
if (destination.IsEmpty) {
return 0;
}
int i = 0;
for (; i < destination.Length; i++)
{
if (!TryPop(out destination[i]))
{
break;
}
}
return i;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
@@ -47,7 +47,7 @@ namespace StgSharp.HighPerformance.Memory
return new BufferStack<T>(4); // Default small capacity
}
BufferStack<T> stack = new BufferStack<T>(span.Length);
BufferStack<T> stack = new(span.Length);
// Use internal method for efficient bulk initialization
@@ -65,7 +65,7 @@ namespace StgSharp.HighPerformance.Memory
return new ConcurrentBufferStack<T>(4); // Default small capacity
}
ConcurrentBufferStack<T> stack = new ConcurrentBufferStack<T>(span.Length);
ConcurrentBufferStack<T> stack = new(span.Length);
// Use internal method for efficient bulk initialization
@@ -82,7 +82,7 @@ namespace StgSharp.HighPerformance.Memory
private byte* _buffer;
private readonly BufferExpansionLock _lock = new();
private ulong _count;
private long _count;
public ConcurrentBufferStack()
{
@@ -101,17 +101,25 @@ namespace StgSharp.HighPerformance.Memory
public ulong Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Volatile.Read(ref _count);
get => (ulong)Volatile.Read(ref _count);
}
public void Clear()
{
Header* header = (Header*)_buffer;
_lock.EnterExpansionProcess();
try
{
header->Top = 0;
Volatile.Write(ref _count, 0);
_lock.EnterBufferCopy(); // exclusive while mutating Top
try
{
Header* header = (Header*)_buffer;
header->Top = 0;
Volatile.Write(ref _count, 0);
}
finally
{
_lock.ExitBufferCopy();
}
}
finally
{
@@ -127,29 +135,29 @@ namespace StgSharp.HighPerformance.Memory
public T Pop()
{
Header* header = (Header*)_buffer;
while (true)
{
nuint oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0) {
throw new InvalidOperationException("Stack underflow");
}
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
_lock.EnterBufferRead(); // prevent buffer being reallocated while reading/modifying Top
try
{
_lock.EnterBufferRead();
try
Header* header = (Header*)_buffer;
int oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0) {
throw new InvalidOperationException("Stack underflow");
}
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{
T* dataStart = (T*)(_buffer + sizeof(Header));
_ = Interlocked.Decrement(ref _count);
return dataStart[oldTop - 1];
}
finally
{
_lock.ExitBufferRead();
}
}
Thread.Sleep(0); // Yield to avoid busy waiting
finally
{
_lock.ExitBufferRead();
}
Thread.Sleep(0);
}
}
@@ -157,14 +165,16 @@ namespace StgSharp.HighPerformance.Memory
{
while (true)
{
// Ensure capacity, expanding if needed
_lock.EnterMetaDataRead();
try
{
Header* header = (Header*)_buffer;
nuint oldTop = Volatile.Read(ref header->Top);
nuint cap = Volatile.Read(ref header->Capacity);
if (oldTop >= cap)
int topSnapshot = Volatile.Read(ref header->Top);
int cap = Volatile.Read(ref header->Capacity);
if (topSnapshot >= cap)
{
// Need expansion
_lock.ExitMetaDataRead();
_lock.EnterExpansionProcess();
try
@@ -172,8 +182,8 @@ namespace StgSharp.HighPerformance.Memory
header = (Header*)_buffer;
if (header->Top >= header->Capacity)
{
nuint newCap = header->Capacity * 2;
nuint newSize = (nuint)sizeof(Header) + (newCap * (nuint)sizeof(T));
int newCap = header->Capacity * 2;
nuint newSize = (nuint)sizeof(Header) + ((nuint)newCap * (nuint)sizeof(T));
_lock.EnterBufferCopy();
try
@@ -193,22 +203,6 @@ namespace StgSharp.HighPerformance.Memory
}
continue;
}
if (Interlocked.CompareExchange(ref header->Top, oldTop + 1, oldTop) == oldTop)
{
_lock.EnterBufferRead();
try
{
T* dataStart = (T*)(_buffer + sizeof(Header));
dataStart[oldTop] = item;
_ = Interlocked.Increment(ref _count);
return;
}
finally
{
_lock.ExitBufferRead();
}
}
}
finally
{
@@ -216,38 +210,60 @@ namespace StgSharp.HighPerformance.Memory
_lock.ExitMetaDataRead();
}
}
Thread.Sleep(0); // Yield to avoid busy waiting
// Reserve and write under exclusive buffer lock to avoid consumers reading unwritten data
_lock.EnterBufferCopy();
try
{
Header* header = (Header*)_buffer;
int idx = header->Top; // reserve next slot
if (idx >= header->Capacity)
{
// lost race against expander or other producer; retry
continue;
}
T* dataStart = (T*)(_buffer + sizeof(Header));
dataStart[idx] = item; // write first
header->Top = idx + 1; // then publish by increasing top
_ = Interlocked.Increment(ref _count);
return;
}
finally
{
_lock.ExitBufferCopy();
}
}
}
public bool TryPop(out T value)
{
Header* header = (Header*)_buffer;
while (true)
{
nuint oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0)
_lock.EnterBufferRead();
try
{
value = default;
return false;
}
Header* header = (Header*)_buffer;
int oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0)
{
value = default;
return false;
}
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{
_lock.EnterBufferRead();
try
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{
T* dataStart = (T*)(_buffer + sizeof(Header));
value = dataStart[oldTop - 1];
_ = Interlocked.Decrement(ref _count);
return true;
}
finally
{
_lock.ExitBufferRead();
}
return true;
}
Thread.Sleep(0); // Yield to avoid busy waiting
finally
{
_lock.ExitBufferRead();
}
Thread.Sleep(0);
}
}
@@ -264,28 +280,28 @@ namespace StgSharp.HighPerformance.Memory
fixed (T* sourcePtr = span) {
Buffer.MemoryCopy(sourcePtr, dataStart,
header->Capacity * (nuint)sizeof(T),
(nuint)header->Capacity * (nuint)sizeof(T),
(nuint)span.Length * (nuint)sizeof(T));
}
header->Top = (nuint)span.Length;
Volatile.Write(ref _count, (ulong)span.Length);
header->Top = span.Length;
Volatile.Write(ref _count, span.Length);
}
private void AllocateBuffer(int capacity)
{
nuint size = (nuint)sizeof(Header) + ((nuint)capacity * (nuint)sizeof(T));
nuint size = (nuint)sizeof(Header) + (nuint)capacity * (nuint)sizeof(T);
_buffer = (byte*)NativeMemory.Alloc(size);
Header* header = (Header*)_buffer;
header->Top = 0;
header->Capacity = (nuint)capacity;
header->Capacity = capacity;
}
private struct Header
{
public nuint Capacity; // max capacity of the stack (number of elements)
public nuint Top; // next available index in the stack
public int Capacity; // max capacity of the stack (number of elements)
public int Top; // next available index in the stack
}
+1 -1
View File
@@ -38,7 +38,7 @@ namespace StgSharp.Internal
internal static partial class GraphicFramework
{
internal static uint[] GLtype = [];
internal static uint[] GLtype = new uint[16];
static GraphicFramework()
{
@@ -1,35 +0,0 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="MatInterface"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
namespace StgSharp.Internal.Intrinsic
{
public interface IMat { }
}
+58 -52
View File
@@ -27,14 +27,17 @@
// -----------------------------------------------------------------------
using StgSharp.Mathematics.Numeric;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Graphic
{
[StructLayout(LayoutKind.Explicit, Size = 2 * 16 * sizeof(float) + sizeof(bool), Pack = 16)]
public unsafe struct Matrix44 : IEquatable<Matrix44>, IMatrix<float>
[CollectionBuilder(builderType:typeof(Matrix44), methodName: nameof(Matrix44.FromSpan))]
[StructLayout(LayoutKind.Explicit, Size = (2 * 16 * sizeof(float)) + sizeof(bool), Pack = 16)]
public unsafe struct Matrix44 : IEquatable<Matrix44>, IMatrix<float>, IEnumerable<Vec4>
{
[FieldOffset(2 * 64 * sizeof(float))] internal bool isTransposed;
@@ -160,12 +163,6 @@ namespace StgSharp.Mathematics.Graphic
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ColumnSet4, float>(ref mat), 16);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe float Det()
{
return 0;
}
public override bool Equals(object? obj)
{
return obj is Matrix44 x && Equals(x);
@@ -176,11 +173,24 @@ namespace StgSharp.Mathematics.Graphic
return mat.Equals(other.mat);
}
public static Matrix44 FromSpan(ReadOnlySpan<Vec4> rowSpan)
{
Matrix44 mat = new Matrix44();
mat.transpose.colum0 = rowSpan[0].vec;
mat.transpose.colum1 = rowSpan[1].vec;
mat.transpose.colum2 = rowSpan[2].vec;
mat.transpose.colum3 = rowSpan[3].vec;
NativeIntrinsic.Intrinsic.f32_transpose(&mat.transpose, &mat.mat);
mat.isTransposed = false;
return mat;
}
public override int GetHashCode()
{
return HashCode.Combine(
mat.colum0.GetHashCode(), mat.colum1.GetHashCode(), mat.colum2.GetHashCode(),
mat.colum3.GetHashCode());
return HashCode.Combine(mat.colum0.GetHashCode(), mat.colum1.GetHashCode(), mat.colum2.GetHashCode(),
mat.colum3.GetHashCode());
}
public override string ToString()
@@ -194,11 +204,8 @@ namespace StgSharp.Mathematics.Graphic
{
if (!isTransposed)
{
fixed (ColumnSet4* source = &mat, target = &transpose)
{
fixed (ColumnSet4* source = &mat, target = &transpose) {
NativeIntrinsic.Intrinsic.f32_transpose(source, target);
// InternalIO.Intrinsic.transpose44(source, target);
}
isTransposed = true;
}
@@ -207,13 +214,9 @@ namespace StgSharp.Mathematics.Graphic
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Matrix44 operator -(Matrix44 left, Matrix44 right)
{
return new Matrix44
{
column0 = right.column0 - left.column0,
column1 = right.column1 - left.column1,
column2 = right.column2 - left.column2,
column3 = right.column3 - left.column3
};
Matrix44 ret = new();
NativeIntrinsic.Intrinsic.f32_sub(&left, &right, &ret);
return ret;
}
public static bool operator !=(Matrix44 left, Matrix44 right)
@@ -224,47 +227,37 @@ namespace StgSharp.Mathematics.Graphic
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Matrix44 operator *(Matrix44 mat, float value)
{
return new Matrix44(
mat.mat.colum0 * value, mat.mat.colum1 * value, mat.mat.colum2 * value,
mat.mat.colum3 * value);
return new Matrix44(mat.mat.colum0 * value, mat.mat.colum1 * value, mat.mat.colum2 * value,
mat.mat.colum3 * value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix44 operator *(Matrix44 left, Matrix44 right)
{
left.InternalTranspose();
return new Matrix44(
Vector4.Dot(left.transpose.colum0, right.mat.colum0),
Vector4.Dot(left.transpose.colum0, right.mat.colum1),
Vector4.Dot(left.transpose.colum0, right.mat.colum2),
Vector4.Dot(left.transpose.colum0, right.mat.colum3),
Vector4.Dot(left.transpose.colum1, right.mat.colum0),
Vector4.Dot(left.transpose.colum1, right.mat.colum1),
Vector4.Dot(left.transpose.colum1, right.mat.colum2),
Vector4.Dot(left.transpose.colum1, right.mat.colum3),
Vector4.Dot(left.transpose.colum2, right.mat.colum0),
Vector4.Dot(left.transpose.colum2, right.mat.colum1),
Vector4.Dot(left.transpose.colum2, right.mat.colum2),
Vector4.Dot(left.transpose.colum2, right.mat.colum3),
Vector4.Dot(left.transpose.colum3, right.mat.colum0),
Vector4.Dot(left.transpose.colum3, right.mat.colum1),
Vector4.Dot(left.transpose.colum3, right.mat.colum2),
Vector4.Dot(left.transpose.colum3, right.mat.colum3));
return new Matrix44(Vector4.Dot(left.transpose.colum0, right.mat.colum0),
Vector4.Dot(left.transpose.colum0, right.mat.colum1),
Vector4.Dot(left.transpose.colum0, right.mat.colum2),
Vector4.Dot(left.transpose.colum0, right.mat.colum3),
Vector4.Dot(left.transpose.colum1, right.mat.colum0),
Vector4.Dot(left.transpose.colum1, right.mat.colum1),
Vector4.Dot(left.transpose.colum1, right.mat.colum2),
Vector4.Dot(left.transpose.colum1, right.mat.colum3),
Vector4.Dot(left.transpose.colum2, right.mat.colum0),
Vector4.Dot(left.transpose.colum2, right.mat.colum1),
Vector4.Dot(left.transpose.colum2, right.mat.colum2),
Vector4.Dot(left.transpose.colum2, right.mat.colum3),
Vector4.Dot(left.transpose.colum3, right.mat.colum0),
Vector4.Dot(left.transpose.colum3, right.mat.colum1),
Vector4.Dot(left.transpose.colum3, right.mat.colum2),
Vector4.Dot(left.transpose.colum3, right.mat.colum3));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Matrix44 operator +(Matrix44 left, Matrix44 right)
{
Matrix44 ret = new()
{
column0 = left.column0 + right.column0,
column1 = left.column1 + right.column1,
column2 = left.column2 + right.column2,
column3 = left.column3 + right.column3
};
Matrix44 ret = new();
NativeIntrinsic.Intrinsic.f32_add(&left, &right, &ret);
return ret;
}
@@ -273,5 +266,18 @@ namespace StgSharp.Mathematics.Graphic
return left.Equals(right);
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator<Vec4> IEnumerable<Vec4>.GetEnumerator()
{
ref Vec4 col0 = ref column0;
for (int i = 0; i < 4; i++) {
yield return Unsafe.Add(ref col0, i);
}
}
}
}
+26 -6
View File
@@ -36,8 +36,9 @@ using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Graphic
{
[CollectionBuilder(builderType:typeof(Vec4), methodName: nameof(Vec4.FromSpan))]
[StructLayout(LayoutKind.Explicit, Size = 16, Pack = 16)]
public struct Vec4 : IEquatable<Vec4>, IUnmanagedVector<Vec4>
public unsafe struct Vec4 : IEquatable<Vec4>, IUnmanagedVector<Vec4, float>
{
[FieldOffset(0)] internal unsafe fixed float num[4];
@@ -127,11 +128,33 @@ namespace StgSharp.Mathematics.Graphic
return left + right;
}
public ReadOnlySpan<float> AsSpan()
{
return MemoryMarshal.CreateReadOnlySpan(ref reg.Member<float>(0), 4);
}
public override bool Equals(object obj)
{
return obj is Vec4 v && v == this;
}
public static Vec4 FromSpan(ReadOnlySpan<float> span)
{
return new Vec4
{
X = span[0],
Y = span[1],
Z = span[2],
W = span[3]
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<float>.Enumerator GetEnumerator()
{
return AsSpan().GetEnumerator();
}
public override int GetHashCode()
{
return vec.GetHashCode();
@@ -163,11 +186,8 @@ namespace StgSharp.Mathematics.Graphic
public static Vec4 operator *(Matrix44 mat, Vec4 vec)
{
mat.InternalTranspose();
return new Vec4(
Vector4.Dot(mat.transpose.colum0, vec.vec),
Vector4.Dot(mat.transpose.colum1, vec.vec),
Vector4.Dot(mat.transpose.colum2, vec.vec),
Vector4.Dot(mat.transpose.colum3, vec.vec));
return new Vec4(Vector4.Dot(mat.transpose.colum0, vec.vec), Vector4.Dot(mat.transpose.colum1, vec.vec),
Vector4.Dot(mat.transpose.colum2, vec.vec), Vector4.Dot(mat.transpose.colum3, vec.vec));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -30,6 +30,7 @@ using StgSharp.Graphics;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
namespace StgSharp.Mathematics.Numeric
@@ -42,7 +42,7 @@ namespace StgSharp.Threading
public class BufferExpansionLock : IDisposable
{
private static readonly ThreadLocal<int> Count = new();
private static readonly ThreadLocal<int> Count = new(() => 0);
private readonly ReaderWriterLockSlim _copyLock;
private readonly ReaderWriterLockSlim _expansionLock;
@@ -74,13 +74,11 @@ namespace StgSharp.Threading
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterBufferRead()
{
if (++Count.Value == 1)
{
int c = Count.Value;
if (c == 0) {
_copyLock.EnterReadLock();
} else
{
Count.Value++;
}
Count.Value = c + 1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -104,12 +102,10 @@ namespace StgSharp.Threading
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitBufferRead()
{
if (--Count.Value == 0)
{
int c = Count.Value - 1;
Count.Value = c;
if (c == 0) {
_copyLock.ExitReadLock();
} else
{
Count.Value--;
}
}
@@ -1,457 +0,0 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SamplingAdaptiveBackoff"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
/// <summary>
/// Optimized sampling-based adaptive backoff strategy that re-samples and evaluates the current
/// contention situation and adjusts the strategy after a certain number of fallbacks
/// </summary>
public struct SamplingAdaptiveBackoff
{
/*
private const int CONFIDENCE_THRESHOLD = 3; // Requires N consecutive sampling confirmations to switch strategies
// Configuration constants
private const int FALLBACK_SAMPLING_THRESHOLD = 64; // Triggers re-sampling every 64 fallbacks
private const int INITIAL_SPINWAIT_THRESHOLD = 8; // Cheap spinning for the first few SpinWait attempts
private const int MAX_SAMPLING_WINDOW_MS = 1000; // Maximum sampling window time
private const int MIN_SAMPLING_WINDOW_MS = 100; // Minimum sampling window time
private const int PERFORMANCE_HISTORY_MASK = 7; // Use 3-bit mask for 8 entries (power of 2)
private const double SIGNIFICANT_IMPROVEMENT = 0.15; // 15% improvement is considered significant
private BackoffMethod _candidateMethod;
private BackoffMethod _currentMethod;
private byte _confidenceCounter;
private byte _consecutiveFailures;
// Packed performance data to reduce memory footprint
private PackedMethodPerformance _packedPerformances; // Bit-packed performance data for all 3 methods
private SamplingWindow _currentSample;
// Instance state
private SpinWait _spinWait;
private uint _rng;
/// <summary>
/// Gets the current method
/// </summary>
public readonly BackoffMethod CurrentMethod => _currentMethod;
/// <summary>
/// Creates a new optimized sampling adaptive backoff strategy instance
/// </summary>
public static SamplingAdaptiveBackoff Create()
{
// Choose initial strategy based on system characteristics
BackoffMethod initialMethod = DetermineInitialMethod();
SamplingAdaptiveBackoff instance = new()
{
_spinWait = default,
_currentMethod = initialMethod,
_consecutiveFailures = 0,
_confidenceCounter = 0,
_candidateMethod = initialMethod,
_rng = GenerateInitialSeed(),
_packedPerformances = PackedMethodPerformance.Initialize()
};
// Start the first sampling window
instance._currentSample.Reset(initialMethod);
return instance;
}
/// <summary>
/// Gets the current sampling status (for debugging)
/// </summary>
public readonly (int attempts, int successes, int fallbacks, double successRate, double fallbackRate) GetCurrentSampleStats()
{
return (_currentSample.TotalAttempts,
_currentSample.SuccessfulAttempts,
_currentSample.FallbackCount,
_currentSample.SuccessRate,
_currentSample.FallbackRate);
}
/// <summary>
/// Gets the historical performance of the specified method (for monitoring)
/// </summary>
public readonly (double successRate, double fallbackRate, double overallScore) GetMethodPerformance(
BackoffMethod method)
{
var (successRate, fallbackRate, score) = _packedPerformances.GetPerformance(method);
return (successRate, fallbackRate, score);
}
/// <summary>
/// Called when attempting CAS (must be called regardless of success or failure)
/// </summary>
/// <param name="success">
/// Whether this CAS attempt was successful
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnCASAttempt(bool success)
{
_currentSample.TotalAttempts++;
if (success)
{
_currentSample.SuccessfulAttempts++;
_consecutiveFailures = 0;
_spinWait = default; // Reset SpinWait after success
} else
{
_consecutiveFailures++;
}
}
/// <summary>
/// Called when CAS fails and fallback needs to be executed, performs backoff and may trigger
/// re-sampling
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnFallbackAndBackoff()
{
_currentSample.FallbackCount++;
// Check if re-sampling threshold is reached using bitwise operations for faster comparison
if ((_currentSample.FallbackCount & ~(FALLBACK_SAMPLING_THRESHOLD - 1)) != 0 ||
_currentSample.ElapsedTicks > MAX_SAMPLING_WINDOW_MS)
{
if (_currentSample.ElapsedTicks >= MIN_SAMPLING_WINDOW_MS) {
EvaluateCurrentSample();
}
}
// Perform backoff using jump table approach for better branch prediction
PerformBackoffOptimized();
}
/// <summary>
/// Determines initial method based on system characteristics
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static BackoffMethod DetermineInitialMethod()
{
int logicalCores = Environment.ProcessorCount;
return logicalCores <= 16 ? BackoffMethod.SpinWait : BackoffMethod.Sleep0;
}
/// <summary>
/// Determines the method to test in the next sampling window using optimized random selection
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private BackoffMethod DetermineNextTestMethod()
{
// Use bitwise operations for 80%/20% probability split
return (NextRandom() & 0xF) < 13 ? _candidateMethod : (BackoffMethod)(NextRandom() & 3);
}
/// <summary>
/// Evaluates the current sampling window and may adjust the strategy
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)]
private void EvaluateCurrentSample()
{
// Update performance record for the current method using packed data
_packedPerformances.UpdatePerformance(
_currentSample.TestedMethod, _currentSample.SuccessRate, _currentSample.FallbackRate,
_currentSample.ElapsedTicks / Math.Max(1, _currentSample.TotalAttempts));
// Find the currently best performing method
BackoffMethod bestMethod = _packedPerformances.FindBestMethod();
// Use branchless logic for confidence tracking
bool methodChanged = bestMethod != _currentMethod;
bool candidateMatches = bestMethod == _candidateMethod;
_confidenceCounter = methodChanged ? (candidateMatches ? (byte)(_confidenceCounter + 1) : (byte)1) : (byte)0;
if (methodChanged && candidateMatches && _confidenceCounter >= CONFIDENCE_THRESHOLD)
{
SwitchToMethod(bestMethod);
} else if (methodChanged && !candidateMatches)
{
_candidateMethod = bestMethod;
} else if (!methodChanged) {
_candidateMethod = _currentMethod;
}
// Start new sampling window
BackoffMethod nextTestMethod = DetermineNextTestMethod();
_currentSample.Reset(nextTestMethod);
if (nextTestMethod != _currentMethod)
{
_currentMethod = nextTestMethod;
_spinWait = default;
}
}
/// <summary>
/// Generates initial seed for RNG
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint GenerateInitialSeed()
{
return (uint)(Environment.TickCount64 ^ (Thread.CurrentThread.ManagedThreadId * 0x9E3779B9U));
}
/// <summary>
/// Fast pseudo-random number generator using XorShift32
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private uint NextRandom()
{
// XorShift32 - faster than LCG with better distribution
_rng ^= _rng << 13;
_rng ^= _rng >> 17;
_rng ^= _rng << 5;
return _rng;
}
/// <summary>
/// Optimized backoff operation using jump table approach
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void PerformBackoffOptimized()
{
// Use computed goto pattern for better branch prediction
switch (_currentMethod)
{
case BackoffMethod.SpinWait:
PerformSpinWaitBackoff();
break;
case BackoffMethod.Yield:
_ = Thread.Yield();
break;
case BackoffMethod.Sleep0:
Thread.Sleep(0);
break;
default:
break;
}
}
/// <summary>
/// Specialized SpinWait backoff with optimized logic
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void PerformSpinWaitBackoff()
{
if (_consecutiveFailures > INITIAL_SPINWAIT_THRESHOLD && _spinWait.NextSpinWillYield)
{
// Optimized random yield count using bitwise operations
int yields = (int)(NextRandom() & 7) + 1;
for (int i = 0; i < yields; i++) {
_ = Thread.Yield();
}
} else
{
_spinWait.SpinOnce(sleep1Threshold:-1);
}
}
/// <summary>
/// Switches to a new method with optimized state reset
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SwitchToMethod(BackoffMethod method)
{
_currentMethod = method;
_candidateMethod = method;
_confidenceCounter = 0;
_spinWait = default;
}
public enum BackoffMethod : byte
{
SpinWait = 0,
Yield = 1,
Sleep0 = 2
}
// Optimized sampling window state with better packing
private struct SamplingWindow
{
public BackoffMethod TestedMethod; // Current method being tested
public int FallbackCount; // Number of fallbacks
public int SuccessfulAttempts; // Number of successful attempts
public int TotalAttempts; // Total number of attempts
public long StartTicks; // Sampling window start time
public readonly double SuccessRate
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => TotalAttempts == 0 ? 0.0 : (double)SuccessfulAttempts / TotalAttempts;
}
public readonly double FallbackRate
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => TotalAttempts == 0 ? 0.0 : (double)FallbackCount / TotalAttempts;
}
public readonly long ElapsedTicks
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Environment.TickCount64 - StartTicks;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset(BackoffMethod method)
{
StartTicks = Environment.TickCount64;
TotalAttempts = 0;
SuccessfulAttempts = 0;
FallbackCount = 0;
TestedMethod = method;
}
}
// Bit-packed performance data structure for memory efficiency
private struct PackedMethodPerformance
{
// Pack all 3 methods' performance into compact format
private ulong _packed0; // Method 0 (SpinWait) data
private ulong _packed1; // Method 1 (Yield) data
private ulong _packed2; // Method 2 (Sleep0) data
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly BackoffMethod FindBestMethod()
{
(double _, double _, double score0) = UnpackPerformanceData(_packed0);
(double _, double _, double score1) = UnpackPerformanceData(_packed1);
(double _, double _, double score2) = UnpackPerformanceData(_packed2);
// Use branchless comparison for best performance
BackoffMethod best = score1 > score0 + SIGNIFICANT_IMPROVEMENT ?
BackoffMethod.Yield :
BackoffMethod.SpinWait;
best = score2 > GetScore(best) + SIGNIFICANT_IMPROVEMENT ? BackoffMethod.Sleep0 : best;
return best;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly (double successRate, double fallbackRate, double overallScore) GetPerformance(
BackoffMethod method)
{
ulong packed = method switch
{
BackoffMethod.SpinWait => _packed0,
BackoffMethod.Yield => _packed1,
BackoffMethod.Sleep0 => _packed2,
_ => _packed0
};
return UnpackPerformanceData(packed);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PackedMethodPerformance Initialize()
{
return new PackedMethodPerformance
{
_packed0 = PackPerformanceData(0.5, 0.5, 0),
_packed1 = PackPerformanceData(0.5, 0.5, 0),
_packed2 = PackPerformanceData(0.5, 0.5, 0)
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void UpdatePerformance(BackoffMethod method, double successRate, double fallbackRate, long avgLatency)
{
ulong packed = PackPerformanceData(successRate, fallbackRate, avgLatency);
switch (method)
{
case BackoffMethod.SpinWait:
_packed0 = packed;
break;
case BackoffMethod.Yield:
_packed1 = packed;
break;
case BackoffMethod.Sleep0:
_packed2 = packed;
break;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private readonly double GetScore(BackoffMethod method)
{
return method switch
{
BackoffMethod.SpinWait => UnpackPerformanceData(_packed0).overallScore,
BackoffMethod.Yield => UnpackPerformanceData(_packed1).overallScore,
BackoffMethod.Sleep0 => UnpackPerformanceData(_packed2).overallScore,
_ => 0.0
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong PackPerformanceData(double successRate, double fallbackRate, long avgLatency)
{
// Pack data into 64-bit value: 20 bits success rate, 20 bits fallback rate, 24 bits latency
uint success = (uint)(successRate * 1048575); // 20 bits (2^20 - 1)
uint fallback = (uint)(fallbackRate * 1048575); // 20 bits
uint latency = (uint)Math.Min(avgLatency, 16777215); // 24 bits (2^24 - 1)
return ((ulong)success << 44) | ((ulong)fallback << 24) | latency;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static (double successRate, double fallbackRate, double overallScore) UnpackPerformanceData(
ulong packed)
{
double successRate = ((packed >> 44) & 0xFFFFF) / 1048575.0;
double fallbackRate = ((packed >> 24) & 0xFFFFF) / 1048575.0;
long avgLatency = (long)(packed & 0xFFFFFF);
// Calculate overall score with optimized formula
double baseScore = successRate * 0.6 + (1.0 - fallbackRate) * 0.4;
double latencyPenalty = avgLatency > 0 ? Math.Min(0.1, avgLatency / 10000.0) : 0;
double overallScore = baseScore - latencyPenalty;
return (successRate, fallbackRate, overallScore);
}
}
/**/
}