diff --git a/StgSharp.Common/Collections/NotEnumerableList.cs b/StgSharp.Common/Collections/NotEnumerableList.cs
index 43b7732a..31db3c5d 100644
--- a/StgSharp.Common/Collections/NotEnumerableList.cs
+++ b/StgSharp.Common/Collections/NotEnumerableList.cs
@@ -26,6 +26,7 @@
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
+using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace StgSharp.Collections
@@ -33,9 +34,13 @@ namespace StgSharp.Collections
public static class LinearBagBuilder
{
+ ///
+ /// Build a from a read-only span. Capacity will exactly fit
+ /// the input.
+ ///
public static LinearBag FromSpan(ReadOnlySpan values)
{
- if (values.IsEmpty || values.Length == 0) {
+ if (values.IsEmpty) {
return new LinearBag(4);
}
LinearBag bag = new(values.Length);
@@ -47,12 +52,24 @@ namespace StgSharp.Collections
}
+ ///
+ /// A very lightweight contiguous storage structure: unordered, not enumerable by design.
+ /// Provides O(1) Add and O(1) RemoveAt (by swapping with the tail). Intended for high-
+ /// performance scenarios where element ordering does not matter. NOT thread-safe.
+ ///
+ ///
+ /// Element type.
+ ///
[CollectionBuilder(typeof(LinearBagBuilder), nameof(LinearBagBuilder.FromSpan))]
public class LinearBag(int capacity = 4)
{
- private T[] _items = new T[capacity];
+ // Invariant: valid elements occupy indexes [0, Count-1]. Count is the number of live elements.
+ private T[] _items = new T[Math.Max(0, capacity)];
+ ///
+ /// Access element by index (0..Count-1). Throws if out of range.
+ ///
public T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -68,10 +85,19 @@ namespace StgSharp.Collections
}
}
+ ///
+ /// Total allocated slots.
+ ///
public int Capacity => _items.Length;
+ ///
+ /// Number of currently stored elements (valid index range 0..Count-1).
+ ///
public int Count { get; private set; }
+ ///
+ /// Adds an item (unordered). Expands internal array if needed.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(T item)
{
@@ -79,15 +105,19 @@ namespace StgSharp.Collections
EnsureCapacity(Count + 1);
}
- _items[Count++] = item;
+ _items[Count] = item;
+ Count++;
}
+ ///
+ /// Returns a span over the live portion of the bag (0..Count-1).
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span AsSpan()
- {
- return new Span(_items, 0, Count);
- }
+ public Span AsSpan() => new Span(_items, 0, Count);
+ ///
+ /// Clears all elements and zeroes out references to avoid retention. Count becomes 0.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
@@ -95,18 +125,22 @@ namespace StgSharp.Collections
Count = 0;
}
+ ///
+ /// Fast clear: only resets Count (leaves underlying array unchanged). References are NOT
+ /// released. Use only when you are certain old references can remain.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void FastClear()
- {
- // 只重置size,不清理数组内容
- Count = 0;
- }
+ public void FastClear() => Count = 0;
+ ///
+ /// Removes the first occurrence of value (unordered). Does nothing if not found.
+ ///
public void Remove(T value)
{
+ EqualityComparer comparer = EqualityComparer.Default;
for (int i = 0; i < Count; i++)
{
- if (_items[i]?.Equals(value) ?? false)
+ if (comparer.Equals(_items[i], value))
{
RemoveAt(i);
return;
@@ -114,6 +148,10 @@ namespace StgSharp.Collections
}
}
+ ///
+ /// Removes element at index by swapping the last element into its slot (unless already
+ /// last). Preserves O(1) removal at cost of ordering.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(int index)
{
@@ -122,27 +160,30 @@ namespace StgSharp.Collections
}
int last = Count - 1;
- if (index != last)
- {
- _items[index] = _items[last]; // swap back from tail
+ if (index != last) {
+ _items[index] = _items[last];
}
- _items[last] = default!; // clear last slot to avoid ref retention
+ _items[last] = default!; // release reference / reset value
Count = last;
}
+ ///
+ /// Ensures capacity is at least . Doubles size when growing.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureCapacity(int min)
{
- if (_items.Length < min)
- {
- int newCapacity = _items.Length == 0 ? 4 : _items.Length * 2;
- if (newCapacity < min) {
- newCapacity = min;
- }
-
- Array.Resize(ref _items, newCapacity);
+ if (_items.Length >= min) {
+ return;
}
+
+ int newCapacity = _items.Length == 0 ? 4 : _items.Length * 2;
+ if (newCapacity < min) {
+ newCapacity = min;
+ }
+
+ Array.Resize(ref _items, newCapacity);
}
}
diff --git a/StgSharp.Common/Controlling/Controls/MarkingLable.cs b/StgSharp.Common/Controlling/Controls/MarkingLable.cs
index 04ca7641..554d0b08 100644
--- a/StgSharp.Common/Controlling/Controls/MarkingLable.cs
+++ b/StgSharp.Common/Controlling/Controls/MarkingLable.cs
@@ -45,14 +45,11 @@ namespace StgSharp.Controls
private PlainGeometryMesh marker;
- private TimeFunction _movement;
-
private Vec2 _currentPosition, _movementVec;
- public MarkingLabel(PlainGeometryMesh marker, TimeFunction movement)
+ public MarkingLabel(PlainGeometryMesh marker)
{
this.marker = marker;
- _movement = movement;
}
public bool IsEntity => true;
@@ -74,9 +71,7 @@ namespace StgSharp.Controls
public void Move(Vec2 movement)
{
- if (!_movement.IsComplete) {
- Position = _currentPosition + (_movement.Calculate() * _movementVec);
- }
+ throw new NotImplementedException();
}
public void MoveTo(Vec2 target)
diff --git a/StgSharp.Common/Environment/Platform.cs b/StgSharp.Common/Environment/Platform.cs
index 04fba8af..e62b875d 100644
--- a/StgSharp.Common/Environment/Platform.cs
+++ b/StgSharp.Common/Environment/Platform.cs
@@ -186,7 +186,7 @@ namespace StgSharp
internal static unsafe void InternalInitialize()
{
- mainTimeProvider = new StgSharpTime();
+ // Initialize time provider and start providing time
MainTimeProvider.StartProvidingTime();
GraphicFramework.LoadGlfw();
if (GraphicFramework.glfwInit() == 0) {
diff --git a/StgSharp.Common/Graphics/RenderStream.cs b/StgSharp.Common/Graphics/RenderStream.cs
index 2ab3fa1c..a63585f1 100644
--- a/StgSharp.Common/Graphics/RenderStream.cs
+++ b/StgSharp.Common/Graphics/RenderStream.cs
@@ -120,7 +120,7 @@ namespace StgSharp.Graphics
protected float TimePassed
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _timeProvider.CurrentSecond;
+ get => (float)_timeProvider.CurrentSecond;
}
protected TimeSpanProvider TimeProvider
@@ -147,8 +147,8 @@ namespace StgSharp.Graphics
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public abstract void SetSizeLimit(int minWidth, int minHeight, int maxWidth, int maxHeight);
- public static TTarget ShareContextFrom([NotNull]TSource source) where TSource: RenderStream
- where TTarget: RenderStream, new()
+ public static TTarget ShareContextFrom([NotNull]TSource source) where TSource : RenderStream
+ where TTarget : RenderStream, new()
{
TTarget target = new TTarget();
target.Initialize(source.BindedViewPortContext, source._timeProvider);
diff --git a/StgSharp.Common/Internal/Graphic/glfw.cs b/StgSharp.Common/Internal/Graphic/glfw.cs
index 1744e077..f1133880 100644
--- a/StgSharp.Common/Internal/Graphic/glfw.cs
+++ b/StgSharp.Common/Internal/Graphic/glfw.cs
@@ -230,9 +230,18 @@ namespace StgSharp.Internal
IntPtr monitor,
IntPtr share)
{
+ nint handle;
fixed (byte* titlePtr = title) {
- return _glfw.glfwCreateWindow(width, height, titlePtr, monitor, share);
+ handle = _glfw.glfwCreateWindow(width, height, titlePtr, monitor, share);
}
+ if (handle == 0)
+ {
+ byte* desc;
+ int code = _glfw.glfwGetError(&desc);
+ string msg = $"{code}: {Marshal.PtrToStringUTF8((nint)desc)}";
+ throw new InvalidOperationException(msg);
+ }
+ return handle;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -813,7 +822,9 @@ namespace StgSharp.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe IntPtr glfwGetProcAddress(ReadOnlySpan procName)
{
- return glfwGetProcAddress(procName);
+ fixed (byte* sPtr = procName) {
+ return _glfw.glfwGetProcAddress(sPtr);
+ }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/StgSharp.Common/Internal/Graphic/glfw.function.cs b/StgSharp.Common/Internal/Graphic/glfw.function.cs
index 990aa1e5..4dc85105 100644
--- a/StgSharp.Common/Internal/Graphic/glfw.function.cs
+++ b/StgSharp.Common/Internal/Graphic/glfw.function.cs
@@ -1,6 +1,6 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
-// file="glfwFunction"
+// file="glfw.function"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
diff --git a/StgSharp.Common/MVVM/ViewModel/ShowViewPort.cs b/StgSharp.Common/MVVM/ViewModel/ShowViewPort.cs
index f9d10b6d..dd63559b 100644
--- a/StgSharp.Common/MVVM/ViewModel/ShowViewPort.cs
+++ b/StgSharp.Common/MVVM/ViewModel/ShowViewPort.cs
@@ -57,9 +57,8 @@ namespace StgSharp.MVVM.ViewModel
{
CustomizeInitialize();
UseViewInNextFrame(viewEntryName);
- TimeSpanAwaitingToken fpsCountingToken = fpsCountProvider.ParticipantSpanAwaiting(1);
- fpsCountingToken.TimeSpanRefreshed += CountFps;
- fpsCountingToken.MissTimeSpanRefreshed += CountFps;
+ fpsCountProvider.TimeSpanRefreshed += CountFps;
+ fpsCountProvider.MissTimeSpanRefreshed += CountFps;
if (_nextView != _currentView) {
_ = Interlocked.Exchange(ref _currentView, _nextView);
}
@@ -78,24 +77,19 @@ namespace StgSharp.MVVM.ViewModel
});
renderThread.Start();
/**/
- using (TimeSpanAwaitingToken eventAwaitingToken = frameTimeProvider.ParticipantSpanAwaiting(
- 1))
+ while (_closed)
{
- while (_closed)
- {
- if (_nextView != _currentView) {
- _ = Interlocked.Exchange(ref _currentView, _nextView);
- }
- GraphicFramework.glfwPollEvents();
- eventAwaitingToken.WaitNextSpan();
- _closed = !ShouldClose;
+ if (_nextView != _currentView) {
+ _ = Interlocked.Exchange(ref _currentView, _nextView);
}
+ GraphicFramework.glfwPollEvents();
+ frameTimeProvider.WaitNextSpan();
+ _closed = !ShouldClose;
}
/**/
renderThread.Join();
- fpsCountingToken.Dispose();
- fpsCountProvider.StopSpanProviding();
- frameTimeProvider.StopSpanProviding();
+ fpsCountProvider.Dispose();
+ frameTimeProvider.Dispose();
}
protected void UseViewInNextFrame(string name)
@@ -124,19 +118,15 @@ namespace StgSharp.MVVM.ViewModel
private void ProcessFrameWithoutEvent()
{
- using (TimeSpanAwaitingToken renderAwaitingToken = frameTimeProvider.ParticipantSpanAwaiting(
- 1))
+ frameTimeProvider.TimeSpanRefreshed += (_, _) => { };
+ while (_closed)
{
- renderAwaitingToken.TimeSpanRefreshed += (_, _) => { };
- while (_closed)
- {
- foreach (ViewPort context in CurrentView.Render) {
- context.FlushSize();
- }
- CurrentView.Use();
- renderAwaitingToken.WaitNextSpan();
- Interlocked.Add(ref _frameCount, 1);
+ foreach (ViewPort context in CurrentView.Render) {
+ context.FlushSize();
}
+ CurrentView.Use();
+ frameTimeProvider.WaitNextSpan();
+ Interlocked.Add(ref _frameCount, 1);
}
}
diff --git a/StgSharp.Common/MVVM/ViewModel/ViewModel.cs b/StgSharp.Common/MVVM/ViewModel/ViewModel.cs
index a31a3faa..6a95d121 100644
--- a/StgSharp.Common/MVVM/ViewModel/ViewModel.cs
+++ b/StgSharp.Common/MVVM/ViewModel/ViewModel.cs
@@ -25,6 +25,7 @@
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
+using StgSharp.Common.Timing;
using StgSharp.Controlling.UsrActivity;
using StgSharp.Graphics;
using StgSharp.Graphics.OpenGL;
@@ -65,24 +66,23 @@ namespace StgSharp.MVVM.ViewModel
{
viewPortDisplay = new ViewPort();
allView = [];
- fpsCountProvider = new TimeSpanProvider(1.0, TimeSpanProvider.DefaultProvider);
+ fpsCountProvider = new TimeSpanProvider(TimeSequence.CreateLinear(double.MaxValue, 1.0 / frameSpeed), TimeSpanProvider.DefaultProvider);
_frameSpeed = 0;
_frameCount = 0;
long frameLength = (FrameSpeed > 0) ? (1000L * 1000L / FrameSpeed) : 0;
- frameTimeProvider = new TimeSpanProvider(frameLength, TimeSpanProvider.DefaultProvider);
+ frameTimeProvider = new TimeSpanProvider(TimeSequence.CreateLinear(double.MaxValue, 1.0 / frameSpeed), TimeSpanProvider.DefaultProvider);
SetFrameSizeCallback(DefaultFrameResizeCallback);
}
- protected ViewModelBase(float frameLength)
+ protected ViewModelBase(double frameLength)
{
viewPortDisplay = new ViewPort();
allView = [];
- fpsCountProvider = new TimeSpanProvider(1.0, TimeSpanProvider.DefaultProvider);
+ fpsCountProvider = new TimeSpanProvider(TimeSequence.CreateLinear(double.MaxValue, frameLength), TimeSpanProvider.DefaultProvider);
_frameSpeed = 0;
_frameCount = 0;
long _frameLength = (FrameSpeed > 0) ? ((long)(1000 * frameLength)) : 0;
- frameTimeProvider = new TimeSpanProvider(_frameLength,
- TimeSpanProvider.DefaultProvider);
+ frameTimeProvider = new TimeSpanProvider(TimeSequence.CreateLinear(double.MaxValue, frameLength), TimeSpanProvider.DefaultProvider);
SetFrameSizeCallback(DefaultFrameResizeCallback);
}
@@ -90,24 +90,23 @@ namespace StgSharp.MVVM.ViewModel
{
viewPortDisplay = vBinding;
allView = [];
- fpsCountProvider = new TimeSpanProvider(1.0, TimeSpanProvider.DefaultProvider);
+ fpsCountProvider = new TimeSpanProvider(TimeSequence.CreateLinear(double.MaxValue, 1.0), TimeSpanProvider.DefaultProvider);
_frameSpeed = 0;
_frameCount = 0;
long frameLength = (frameSpeed > 0) ? (1000L * 1000L / frameSpeed) : 0;
- frameTimeProvider = new TimeSpanProvider(frameLength, TimeSpanProvider.DefaultProvider);
+ frameTimeProvider = new TimeSpanProvider(TimeSequence.CreateLinear(double.MaxValue, 1.0 / frameSpeed), TimeSpanProvider.DefaultProvider);
SetFrameSizeCallback(DefaultFrameResizeCallback);
}
- public ViewModelBase(ViewPort vBinding, float frameLength)
+ public ViewModelBase(ViewPort vBinding, double frameLength)
{
viewPortDisplay = vBinding;
allView = [];
- fpsCountProvider = new TimeSpanProvider(1.0, TimeSpanProvider.DefaultProvider);
+ fpsCountProvider = new TimeSpanProvider(TimeSequence.CreateLinear(double.MaxValue, 1.0), TimeSpanProvider.DefaultProvider);
_frameSpeed = 0;
_frameCount = 0;
- long _frameLength = (frameLength > 0) ? ((long)(1000 * frameLength)) : 0;
- frameTimeProvider = new TimeSpanProvider(_frameLength,
- TimeSpanProvider.DefaultProvider);
+ long _frameLength = (FrameSpeed > 0) ? ((long)(1000 * frameLength)) : 0;
+ frameTimeProvider = new TimeSpanProvider(TimeSequence.CreateLinear(double.MaxValue, frameLength), TimeSpanProvider.DefaultProvider);
SetFrameSizeCallback(DefaultFrameResizeCallback);
}
@@ -193,9 +192,8 @@ namespace StgSharp.MVVM.ViewModel
public void SetSizeLimit(Vec2 minSize, Vec2 maxSize)
{
ThreadHelper.MainThreadAssert();
- GraphicFramework.glfwSetWindowSizeLimits(
- this.viewPortDisplay.ViewPortHandle, (int)minSize.X, (int)minSize.Y, (int)maxSize.X,
- (int)maxSize.Y);
+ GraphicFramework.glfwSetWindowSizeLimits(this.viewPortDisplay.ViewPortHandle, (int)minSize.X,
+ (int)minSize.Y, (int)maxSize.X, (int)maxSize.Y);
}
///
@@ -217,8 +215,8 @@ namespace StgSharp.MVVM.ViewModel
public void SetSizeLimit(int minWidth, int minHeight, int maxWidth, int maxHeight)
{
ThreadHelper.MainThreadAssert();
- GraphicFramework.glfwSetWindowSizeLimits(
- this.viewPortDisplay.ViewPortHandle, minWidth, minHeight, maxWidth, maxHeight);
+ GraphicFramework.glfwSetWindowSizeLimits(this.viewPortDisplay.ViewPortHandle, minWidth, minHeight, maxWidth,
+ maxHeight);
}
public abstract bool TryGetObjectReference(string name, out DataBindingEntry entry);
diff --git a/StgSharp.Common/StgSharp.Common.csproj b/StgSharp.Common/StgSharp.Common.csproj
index ad5fdb7d..2d3616b8 100644
--- a/StgSharp.Common/StgSharp.Common.csproj
+++ b/StgSharp.Common/StgSharp.Common.csproj
@@ -84,6 +84,10 @@
+
+
+
+
diff --git a/StgSharp.Common/StgSharp.Common.deps.json b/StgSharp.Common/StgSharp.Common.deps.json
new file mode 100644
index 00000000..2f525abd
--- /dev/null
+++ b/StgSharp.Common/StgSharp.Common.deps.json
@@ -0,0 +1,23 @@
+{
+ "runtimeTarget": {
+ "name": ".NETCoreApp,Version=v8.0",
+ "signature": ""
+ },
+ "compilationOptions": {},
+ "targets": {
+ ".NETCoreApp,Version=v8.0": {
+ "StgSharp.Common/1.0.0": {
+ "runtime": {
+ "StgSharp.Common.dll": {}
+ }
+ }
+ }
+ },
+ "libraries": {
+ "StgSharp.Common/1.0.0": {
+ "type": "project",
+ "serviceable": false,
+ "sha512": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/StgSharp.Common/StgSharp.Common.dll b/StgSharp.Common/StgSharp.Common.dll
new file mode 100644
index 00000000..9eaa85f7
Binary files /dev/null and b/StgSharp.Common/StgSharp.Common.dll differ
diff --git a/StgSharp.Common/StgSharp.Common.pdb b/StgSharp.Common/StgSharp.Common.pdb
new file mode 100644
index 00000000..48f283fe
Binary files /dev/null and b/StgSharp.Common/StgSharp.Common.pdb differ
diff --git a/StgSharp.Common/StgSharp.Common.xml b/StgSharp.Common/StgSharp.Common.xml
new file mode 100644
index 00000000..82744fdb
--- /dev/null
+++ b/StgSharp.Common/StgSharp.Common.xml
@@ -0,0 +1,1249 @@
+
+
+
+ StgSharp.Common
+
+
+
+
+ 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
+
+
+
+
+ Best-effort snapshot for enumeration; not strongly consistent under concurrent
+ mutations.
+
+
+
+
+ Determines whether the contains the
+ specified key.
+
+
+ The value to locate in the .
+
+
+ true if the contains an element with
+ the specified key; otherwise, false.
+
+
+
+
+ Determines whether the contains the
+ specified key.
+
+
+ The value to locate in the .
+
+
+ true if the contains an element with
+ the specified key; otherwise, false.
+
+
+
+
+ Cache hash result for obj with counts.
+
+
+
+
+
+
+
+
+
+
+ Part of a collection. This contains a direct reference to _origin collection, so all
+ change to _origin collection will be reflected in this object.
+
+
+
+
+
+
+
+ Initializes a new instance of the RandomAccessStack class that is empty and has the
+ default initial capacity.
+
+
+
+
+ Initializes a new instance of the RandomAccessStack class that is empty and has the
+ specified initial capacity.
+
+
+
+
+ Gets or sets the element at the specified index in the stack (0 = bottom, Count-1 =
+ top).
+
+
+
+
+ Gets the number of elements contained in the stack.
+
+
+
+
+ Removes all objects from the stack.
+
+
+
+
+ Returns an enumerator that iterates through the stack from top to bottom. Throws if
+ the stack is modified during iteration.
+
+
+
+
+ Returns the object at the top of the stack without removing it.
+
+
+
+
+ Removes and returns the object at the top of the stack.
+
+
+
+
+ Adds an item to the top of the stack.
+
+
+
+
+ CustomizeInit an instance of OpenGL program, This method should be called before any
+ other World api.
+
+
+
+
+ Version of current World platform. Main version represent in hundred's digit, and sub
+ version represent in ten's digit.
+
+
+
+
+ Write a log message to log file, and mark the _level of this message.
+
+
+ A string contains information of how current evironment runs.
+
+
+ The severity of current message.
+
+
+
+
+ A class attempt to reduce cost of boxing, though still a bit slower than direct access to
+ _origin value type instance.
+
+
+ Type of instance to be boxed.
+
+
+
+
+ A class contains a sets of methods to serialize several types
+
+
+
+
+ An independent storage area, used as the only folder type in root folder. Contains several
+ files reference as virtual disc. Data is actually stored in s in certain RAID mode.
+
+
+
+
+ 当几何体调用了过多的CalcVec方法时,产生UnusedVertexException异常
+
+
+
+
+ Plain geometry only contains straight sides.
+
+
+
+
+ Calculate movement to center
+
+
+ CurrentPipeline time tick
+
+
+
+
+
+
+
+ A square
+
+
+
+
+ Get all uniform related to this .
+
+
+ Shader program requires this camera.
+
+
+ ContextName of all related uniforms. If you fallow form from
+
+
+
+
+
+
+
+ A two dimension vector defined by two elements. Vec2 in World are default used as colum
+ vector.
+
+
+
+
+ Orthogonalize two s, let their length be 1 and perpendicular to
+ each other.
+
+
+
+
+
+
+
+
+
+
+ Length of the matrix in columns.
+
+
+
+
+ Length of the matrix in rows.
+
+
+
+
+ Reurns the value of ArcSin(x)
+
+
+ The sine value of the angle
+
+
+
+
+
+
+
+
+
+
+ Get the value of E ^ x;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Returns the value of ln(x).
+
+
+
+
+ calculate the value of x^y
+
+
+
+
+
+
+
+
+ Returns the value of x^y
+
+
+
+
+ returns a random float number between 0 and 1.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Generates a random seed in int value.
+
+
+
+
+
+
+
+ Interface of all kinds OpenGL BufferHandle objects
+
+
+
+
+ Get the only handle to one of the Object instance
+
+
+
+
+
+
+
+
+
+
+ Bind a BufferHandle instance to OpenGL
+
+
+ Index of handle of the object in this instance to be bind
+
+
+
+
+
+
+
+ Function handler to load an opengl function by searching its _label.
+
+
+ the _label of the Opengl function
+
+
+ An Intptr value representing the pointer to the function
+
+
+
+
+ Hash code of time pixels updated last time.
+
+
+
+
+ A collection of a sets of OpenGL textures. The collection can automatically select one of
+ the texture with minimum costs to upgrade pixels to BufferHandle. Usually is used for loading, but not for
+ target of .
+
+
+
+
+ A collection of handle to Element BufferHandle Object in OpenglFunc. Each value indexed from instance of this class this is the only handle to
+ one Element BufferHandle Object.
+
+
+
+
+ Bind the frame BufferHandle object with _label frame BufferHandle to the frame
+ BufferHandle target specified by target. will
+ be set as default target.
+
+
+ Index of object to be binded in handle set
+
+
+
+
+ Bind the frame BufferHandle object with _label frame BufferHandle to the frame
+ BufferHandle target specified by target. Target of this frame BufferHandle should be
+ set manually.
+
+
+ The frame BufferHandle target of the binding operation.
+
+
+ Index of object to be binded in handle set
+
+
+
+
+ Load an to certain texture.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Set _data to current vertex BufferHandle object
+
+
+ Type of bufferData
+
+
+ Index to find certain VBO in this instance
+
+
+ Data to write in
+
+
+ How OpenGL use these _data, defined by
+
+
+
+
+ Set _data to current vertex BufferHandle object
+
+
+ Type of bufferData
+
+
+ Index to find certain VBO in this instance
+
+
+ Data to write in
+
+
+ How OpenGL use these _data, defined by
+
+
+
+
+ Set _data to current vertex BufferHandle object
+
+
+ Type of bufferData
+
+
+ Index to find certain VBO in this instance
+
+
+ A of _data to write in
+
+
+ How OpenGL use these _data, defined by
+
+
+
+
+ Set data to current vertex BufferHandle object
+
+
+ Type of bufferData
+
+
+ Index to find certain VBO in this instance
+
+
+ A of data to write in
+
+
+ How OpenGL use these data, defined by
+
+
+
+
+ Set _data to current vertex BufferHandle object
+
+
+ Type of bufferData
+
+
+ Index to find certain VBO in this instance
+
+
+ A array of _data to write in
+
+
+ How OpenGL use these _data, defined by
+
+
+
+
+ Get the function pointer of given OpenGL api _label
+
+
+ ContextName of the OpenGL api
+
+
+ The funtion pointer of the api in stream of
+
+
+
+
+ Load all supported OpenGL APIs and bind them to given .
+
+
+ The handle to current OpenGL viewPortDisplay
+
+
+
+
+
+
+ Create a set of .
+
+
+ Amount of EBO to be created.
+
+
+
+
+
+
+
+ Create a sets of
+
+
+ Amount of textures to be created.
+
+
+
+
+
+
+
+ Create a set of .
+
+
+ Amount of VAO to be created.
+
+
+
+
+
+
+
+ Create a set of .
+
+
+ Amount of Object to be created.
+
+
+
+
+
+
+
+ Initialize an opengl context and load all opengl functions.
+
+
+
+
+ Initialize an OpenGl viewPortDisplay if current form has not been binded to any
+ viewPortDisplay. and set the viewPortDisplay binded to current form as current OpenGL
+ viewPortDisplay.
+
+
+ Something goes wrong and World fails in creating a new OpenGL viewPortDisplay.
+
+
+
+
+
+
+
+
+
+
+ Create an instance of program
+
+
+ New instance of instance.
+
+
+
+
+ OpenGL numeric type. An IEEE-754 floating-point value, clamped to the range [0,1].
+
+
+
+
+ OpenGL numeric type. An IEEE-754 double precision floating-point value, clamped to the
+ range [0,1].
+
+
+
+
+ Attach one shader code to a shader program.
+
+
+ Shader program to attach
+
+
+ Index of shader code in current shader code set.
+
+
+
+
+ CustomizeInit a program with no shader attached.
+
+
+
+
+ Get a uniform form current shader, the value type of the uniform should be provided.
+
+
+ ContextName of the Uniform in shader code
+
+
+
+
+
+
+
+ Get a uniform form current shader, the value type of the uniform should be provided.
+
+
+ ContextName of the Uniform in shader code
+
+
+
+
+
+
+
+ Get a uniform form current shader, the value type of the uniform should be provided.
+
+
+ ContextName of the Uniform in shader code
+
+
+
+
+
+
+
+ Get a uniform form current shader, the value type of the uniform should be provided.
+
+
+ ContextName of the Uniform in shader code
+
+
+
+
+
+
+
+ Link the shader to current viewPortDisplay.
+
+
+
+
+ Let the current viewPortDisplay use this shader.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Set the Size limit of current form
+
+
+ Minimum width of current form
+
+
+ Minimum height of current form
+
+
+ Maximum width of current form
+
+
+ Maximum height of current form
+
+
+
+
+ Get (TView is ) uniform from a shader.
+ This uniform will be used as projection matrix of this camera. Additionally, for
+ multi- shader rendering, each shader requires a uniform, so do not ignore return
+ value of this method.
+
+
+ Shader using this camera
+
+
+ ContextName of projection camera named in shader
+
+
+ (TView is ) representing projection
+ matrix in shader program.
+
+
+
+
+ Marking the start of current render operations, and set this renser context as
+ current.
+
+
+
+
+ Marking the end of current render operations, and release the binding of this renser
+ context to current, making it available for another thread as current.
+
+
+
+
+ Provide a lock for managing concurrent access to a buffer that can be expanded and use a
+ common two-pase-copy expanding mechanism.
+
+
+
+
+ An individual thread doing nothing, and its always returns false.
+
+
+
+
+ A single-consumer bell/signal primitive.
+
+
+ Producers can call multiple times to publish signals. The bell
+ keeps a pending count of not-yet-consumed signals. The consumer uses or to consume
+ signals. When a producer increments the pending count from 0 to 1, the
+ internal event is set to wake the consumer. Consuming via
+ or clears the entire pending count
+ (aggregate consumption). If new signals arrive while resetting, the event is set again to
+ avoid missed signals. Ownership: only the specified owner can call
+ the consumer APIs.
+
+
+
+
+ Initializes a new instance of .
+
+
+ The unique consumer identity; subsequent calls to Wait/TryWait must pass the same
+ reference.
+
+
+ Spin count passed to the underlying .
+
+
+ Thrown if is null.
+
+
+
+
+ Disposes the bell, sets the event to wake any waiting consumer, and releases
+ resources.
+
+
+
+
+ Publishes a signal by incrementing the not-yet-consumed count and returns the current
+ pending count.
+
+
+ If the pending count transitions from 0 to 1, the event is set to wake the consumer.
+
+
+ The current pending count including this call.
+
+
+
+
+ Attempts to consume any pending signals without blocking.
+
+
+ The consumer object; must be the same reference provided at construction.
+
+
+ true if a signal was available (pending or event set); otherwise false
+ .
+
+
+ Thrown if is not the owner.
+
+
+
+
+ Blocks until a signal is available, then consumes all pending signals.
+
+
+ The consumer object; must be the same reference provided at construction.
+
+
+ A token to cancel the wait.
+
+
+ Thrown if is not the owner.
+
+
+
+
+ A counting semaphore with adjustable maximum capacity at runtime. Implemented using one or
+ more ManualResetEventSlim (MRES) gates and atomic counters to avoid kernel semaphore
+ overhead while supporting high-frequency Release/Wait patterns. Notes: - This is a
+ counting primitive (not a barrier). Permits accumulate up to . -
+ Reducing does not revoke already issued permits; it only clamps
+ future releases. - Striped MRES can reduce contention in highly parallel waits.
+
+
+
+
+
+ Current number of available permits.
+
+
+
+
+ Logical maximum number of permits. Can be changed at runtime.
+
+
+
+
+ Release a single permit if under . Returns the previous
+ count.
+
+
+
+
+ Release multiple permits (capped by ). Returns the previous
+ count.
+
+
+
+
+ Try to take a permit without blocking. Returns true if acquired.
+
+
+
+
+ Block until a permit is available, then consume one.
+
+
+
+
+ Block until a permit is available or canceled, then consume one.
+
+
+
+
+ Abstraction for a wide SIMD register or equivalent packed numeric storage. Provides raw
+ reference access, broadcast initialization and element level access. Typical
+ implementations map to 128/256/512-bit hardware registers or software emulated blocks.
+
+
+
+
+ Gets a reference to the first element of the register reinterpreted as type .
+
+
+ Unmanaged numeric element type implementing .
+
+
+ Writable reference to element 0 (lowest lane / lowest bits).
+
+
+ Intended for treating the register as a contiguous block of elements. The returned reference
+ must remain valid for the lifetime of the register and be identical to with index 0. Caller must choose a type layout compatible with the
+ actual register representation.
+
+
+
+
+ Fills all elements (lanes) of the register with the provided scalar .
+
+
+ Unmanaged numeric element type implementing .
+
+
+ Scalar value to replicate to every element.
+
+
+ Should map to a single broadcast/shuffle instruction where possible. After completion all
+ lanes read back the same value.
+
+
+
+
+ Gets a writable reference to the element at the specified .
+
+
+ Unmanaged numeric element type implementing .
+
+
+ Zero-based lane index (range defined by implementation).
+
+
+ Writable reference to the lane.
+
+
+ Enables scalar lane edits without unpacking the entire register. Implementations may omit
+ bounds checks for performance. Caller is responsible for passing a valid index.
+
+
+
+
+ Buffer for SSE targeted matrix panel, size of a panel is 4x4 elements, and no smaller than
+ 16 bytes.
+
+
+ Type of matrix element.
+
+
+
+
+ Buffer for AVX targeted matrix panel, size of a panel is 4x4 elements, and no smaller than
+ 16 bytes.
+
+
+ Type of matrix element.
+
+
+
+
+ Buffer for AVX512 targeted matrix panel, size of a panel is 4x4 elements, and no smaller
+ than 16 bytes.
+
+
+ Type of matrix element.
+
+
+
+
+ Try to allocate a new 16MB block when all buckets are empty
+
+
+ Output handle to the allocated Entry
+
+
+ True if allocation succeeded
+
+
+
+
+ Helper method for allocation - converts BucketNode to Entry
+
+
+
+
+ BucketNode structure - specialized node for bucket linked lists Stored in main
+ allocation area (user-overwritable region)
+
+
+
+
+ Free allocated memory block and perform adjacent block merging
+
+
+ Handle to the allocated memory block
+
+
+
+
+ Determine segment index based on size and originLevel
+
+
+
+
+ Check if two Entries are adjacent in memory
+
+
+
+
+ Used in Free operations, efficiently remove from buckets and merge adjacent blocks.
+ Current Entry will serve as the final merged block. The Merge will be in lazy
+ strategy, any fail in acquiring spin lock will end a merging process.
+
+
+
+
+ Remove node from position linked list
+
+
+
+
+ Entry structure - streamlined version (only position and state information) Stored in
+ external SLAB
+
+
+
+
+ Creates a new SLAB allocator instance with the specified configuration.
+
+
+ Initial number of objects that can be allocated.
+
+
+ Memory layout strategy for the SLAB allocator.
+
+
+ Whether to enable concurrent access support.
+
+
+ A new SLAB allocator instance.
+
+
+ Thrown when an invalid layout is specified.
+
+
+ Thrown when requesting unsupported configurations.
+
+
+ Important Threading Considerations: While SLAB
+ /// allocators themselves may be designed for single-threaded use, allocated objects
+ are /// frequently shared across multiple threads in real-world applications. Due to
+ the /// special implementation of expandable concurrent sequential SLAB allocators,
+ they rely /// on internal expansion locks to ensure that memory buffer expansions
+ (which involve /// copying the entire buffer to a new location) do not occur while
+ other threads are /// reading from allocated regions.
+ Sequential Layout Limitations: ///
Sequential layout SLAB
+ allocators cannot provide a single-threaded /// version because:- They use contiguous memory /// layout that requires
+ buffer expansion when capacity is exceeded
+ ///
- Buffer expansion involves copying all ///
+ existing data to a new memory location
- ///
+ Without proper synchronization, concurrent access to allocated objects during ///
+ expansion would cause memory corruption
- The
+ /// expansion lock mechanism is essential even if allocation itself is single- ///
+ threaded
Chunked Layout:
+ ///
Chunked layout allocators can optionally provide single-threaded
+ /// versions since they don't require buffer expansion and copying. Each chunk is
+ /// allocated independently, avoiding the memory relocation issues present in
+ sequential /// layouts.
+
+
+
+
+ CustomizeInit this form and render it on screen, and respond with operation
+
+
+
+
+ Height of this form, represent in
+
+
+
+
+ Width of this form, represent in
+
+
+
+
+ Width of this form, represent in
+
+
+
+
+ Set the Size limit of current form
+
+
+ Minimum Size of current form. In form of a of (width,height)
+
+
+ Maximum Size of current form. In form of a of (width,height)
+
+
+
+
+ Set the Size limit of current form
+
+
+ Minimum width of current form
+
+
+ Minimum height of current form
+
+
+ Maximum width of current form
+
+
+ Maximum height of current form
+
+
+
+
+ ViewBase is the base type for all view types, providing context binding and control
+ element binding. A complete view should contain one complete , , and , which provide
+ layout descriptions of control elements, user responses, and rendering implementations in
+ the view. Data between instances of , , and is shared through .
+
+
+
+
+ Get the status of a certain key on the keyboard.
+
+
+
+
+
+ representing if a key is pressed or released.
+
+
+
+
+ ViewDesigner provides descriptions of various controls in the view, binds the
+ controls to specific user actions and behaviors, and describes the layout and
+ interactions of each control.
+
+
+
+
+
+
+
+ Reset pipeline has risk, this operation is a direct operation to port in current node, the
+ port on the other node cannot get the change.
+
+
+
+
+ Only the following occasions of calling are valid: 1. no bp is
+ running or the only bp is quitting. 2. calling from the main thread or a
+ thread the current bp is running on.
+
+
+
+
+ A sets of complex logic designed for running in multi threads environment. Warning: do not
+ run a instance in multi threads more than once.
+
+
+
+
+ TimeSpanProvider: uses a bound TimeSequence and TimeSourceProviderBase to publish span
+ ticks. Each OnSpanTick advances the sequence; when sequence ends provider unsubscribes.
+
+
+
+
+ True if there is at least one unconsumed span tick (PendingSpanCount > 0).
+
+
+
+
+ Number of span ticks not yet consumed by Wait ( > 0 means last tick not waited).
+
+
+
+
+
+
+
+ Tick passed since binded time source.
+
+
+ True if the span provider is working. False if time is over (the sequence ended).
+
+
+
+
+ Efficient adaptive backoff strategy that dynamically selects optimal backoff methods based on
+ system load and success rate
+
+
+
+
+ Gets the currently used backoff method (for debugging/monitoring)
+
+
+
+
+ Gets the globally considered optimal backoff method
+
+
+
+
+ Creates a new adaptive backoff strategy instance
+
+
+
+
+ Gets global statistics for the specified strategy (for monitoring)
+
+
+
+
+ Called after CAS failure, performs backoff and may switch strategies
+
+
+
+
+ Called after CAS success, resets state
+
+
+
+
+ Evaluates and updates the global optimal strategy
+
+
+
+
+ Fast pseudo-random number generator (LCG)
+
+
+
+
+ Performs the actual backoff operation
+
+
+
+
+ Checks whether global strategy evaluation should be performed
+
+
+
+
+ Determines whether strategy should be switched
+
+
+
+
+ Switches to the currently considered optimal strategy
+
+
+
+
diff --git a/StgSharp.Common/Threading/VariableCapacitySemaphore.cs b/StgSharp.Common/Threading/VariableCapacitySemaphore.cs
new file mode 100644
index 00000000..fc90f51e
--- /dev/null
+++ b/StgSharp.Common/Threading/VariableCapacitySemaphore.cs
@@ -0,0 +1,316 @@
+//-----------------------------------------------------------------------
+// -----------------------------------------------------------------------
+// file="VariableCapacitySemaphore"
+// Project: StgSharp.Common
+// AuthorGroup: Nitload
+// License: MIT
+// -----------------------------------------------------------------------
+using System;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+namespace StgSharp.Threading
+{
+ ///
+ /// A counting semaphore with adjustable maximum capacity at runtime. Implemented using one or
+ /// more ManualResetEventSlim (MRES) gates and atomic counters to avoid kernel semaphore
+ /// overhead while supporting high-frequency Release/Wait patterns. Notes: - This is a
+ /// counting primitive (not a barrier). Permits accumulate up to . -
+ /// Reducing does not revoke already issued permits; it only clamps
+ /// future releases. - Striped MRES can reduce contention in highly parallel waits.
+ ///
+ public sealed class VariableCapacitySemaphore : IDisposable
+ {
+
+ // MRES stripes (one gate per stripe). All stripes are set when transitioning 0 -> >0 and reset at >0 -> 0.
+ private readonly ManualResetEventSlim[] _events;
+ private volatile int _current;
+ private volatile int _max;
+ private readonly int _stripeMask; // for power-of-two stripe counts; otherwise -1
+
+ ///
+ /// Create a new variable-capacity semaphore.
+ ///
+ ///
+ /// Initial available permits (clamped to [0, maxPermits]).
+ ///
+ ///
+ /// Logical maximum permits (>= 1).
+ ///
+ ///
+ /// Optional stripe count for MRES (<= 0 to auto choose by CPU).
+ ///
+ public VariableCapacitySemaphore(int initialCount = 0, int maxPermits = 1, int stripes = 0)
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(maxPermits, 1);
+ ArgumentOutOfRangeException.ThrowIfNegative(initialCount);
+
+ _max = maxPermits;
+ _current = Math.Min(initialCount, maxPermits);
+
+ int stripeCount = stripes > 0 ? stripes : ClampToPow2(Math.Min(Environment.ProcessorCount, 8));
+ _events = new ManualResetEventSlim[stripeCount];
+ bool set = _current > 0;
+ for (int i = 0; i < stripeCount; i++) {
+ _events[i] = new ManualResetEventSlim(set, 0);
+ }
+
+ _stripeMask = IsPow2(stripeCount) ? (stripeCount - 1) : -1;
+ }
+
+ ///
+ /// Current number of available permits.
+ ///
+ public int CurrentCount => Volatile.Read(ref _current);
+
+ ///
+ /// Logical maximum number of permits. Can be changed at runtime.
+ ///
+ public int MaxPermits
+ {
+ get => Volatile.Read(ref _max);
+ set
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(value, 1);
+
+ Volatile.Write(ref _max, value);
+
+ // Clamp current to not exceed the new max (do not block; let Wait() consume down).
+ while (true)
+ {
+ int cur = Volatile.Read(ref _current);
+ if (cur <= value)
+ {
+ break;
+ }
+
+ if (Interlocked.CompareExchange(ref _current, value, cur) == cur)
+ {
+ break;
+ }
+ }
+
+ // Reflect availability to gates
+ if (Volatile.Read(ref _current) > 0)
+ {
+ SetAll();
+ } else
+ {
+ ResetAll();
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ foreach (ManualResetEventSlim e in _events) {
+ e.Dispose();
+ }
+ }
+
+ ///
+ /// Release a single permit if under . Returns the previous
+ /// count.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int Release()
+ {
+ while (true)
+ {
+ int cur = Volatile.Read(ref _current);
+ int max = Volatile.Read(ref _max);
+ if (cur >= max) {
+ return cur;
+ }
+
+ if (Interlocked.CompareExchange(ref _current, cur + 1, cur) == cur)
+ {
+ if (cur == 0) {
+ SetAll();
+ }
+
+ return cur;
+ }
+ }
+ }
+
+ ///
+ /// Release multiple permits (capped by ). Returns the previous
+ /// count.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int Release(int permits)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(permits);
+ while (true)
+ {
+ int cur = Volatile.Read(ref _current);
+ int max = Volatile.Read(ref _max);
+ if (cur >= max) {
+ return cur;
+ }
+
+ long target = (long)cur + permits;
+ if (target > max) {
+ target = max;
+ }
+
+ if (Interlocked.CompareExchange(ref _current, (int)target, cur) == cur)
+ {
+ if (cur == 0) {
+ SetAll();
+ }
+
+ return cur;
+ }
+ }
+ }
+
+ ///
+ /// Try to take a permit without blocking. Returns true if acquired.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryWait()
+ {
+ while (true)
+ {
+ int cur = Volatile.Read(ref _current);
+ if (cur == 0) {
+ return false;
+ }
+
+ if (Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
+ {
+ if (cur - 1 == 0) {
+ ResetAll();
+ }
+
+ return true;
+ }
+ }
+ }
+
+ ///
+ /// Block until a permit is available, then consume one.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Wait()
+ {
+ ManualResetEventSlim gate = GetStripeEvent();
+ SpinWait spinner = new SpinWait();
+ while (true)
+ {
+ int cur = Volatile.Read(ref _current);
+ if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
+ {
+ if (cur - 1 == 0) {
+ ResetAll();
+ }
+
+ return;
+ }
+ if (!spinner.NextSpinWillYield)
+ {
+ spinner.SpinOnce();
+ } else
+ {
+ gate.Wait();
+ }
+ }
+ }
+
+ ///
+ /// Block until a permit is available or canceled, then consume one.
+ ///
+ public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken = default)
+ {
+ ManualResetEventSlim gate = GetStripeEvent();
+ SpinWait spinner = new SpinWait();
+ int start = Environment.TickCount;
+ int remaining = millisecondsTimeout;
+ while (true)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ int cur = Volatile.Read(ref _current);
+ if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
+ {
+ if (cur - 1 == 0) {
+ ResetAll();
+ }
+
+ return true;
+ }
+ if (!spinner.NextSpinWillYield)
+ {
+ spinner.SpinOnce();
+ continue;
+ }
+ if (millisecondsTimeout < 0)
+ {
+ gate.Wait(cancellationToken);
+ continue;
+ }
+ if (remaining <= 0) {
+ return false;
+ }
+
+ if (!gate.Wait(remaining, cancellationToken)) {
+ return false;
+ }
+
+ remaining = millisecondsTimeout - (Environment.TickCount - start);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int ClampToPow2(int x)
+ {
+ if (x < 1) {
+ x = 1;
+ }
+
+ if (x > 8) {
+ x = 8;
+ }
+
+ int p = 1;
+ while ((p << 1) <= x) {
+ p <<= 1;
+ }
+
+ return p;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ManualResetEventSlim GetStripeEvent()
+ {
+ int id = Environment.CurrentManagedThreadId & 0x7fffffff;
+ if (_stripeMask != -1) {
+ return _events[id & _stripeMask];
+ }
+
+ int idx = id % _events.Length;
+ return _events[idx];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool IsPow2(int x) => (x & (x - 1)) == 0;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void ResetAll()
+ {
+ foreach (ManualResetEventSlim e in _events) {
+ e.Reset();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void SetAll()
+ {
+ foreach (ManualResetEventSlim e in _events) {
+ e.Set();
+ }
+ }
+
+ }
+}
diff --git a/StgSharp.Common/Timing/TimeProvider.cs b/StgSharp.Common/Timing/StgSharpTime.cs
similarity index 67%
rename from StgSharp.Common/Timing/TimeProvider.cs
rename to StgSharp.Common/Timing/StgSharpTime.cs
index fe4d687b..f08f06fc 100644
--- a/StgSharp.Common/Timing/TimeProvider.cs
+++ b/StgSharp.Common/Timing/StgSharpTime.cs
@@ -1,6 +1,6 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
-// file="TimeProvider"
+// file="StgSharpTime"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
@@ -27,12 +27,8 @@
// -----------------------------------------------------------------------
using StgSharp.Collections;
using StgSharp.Timing;
-
using System;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.Linq;
-using System.Runtime.CompilerServices;
using System.Threading;
namespace StgSharp
@@ -40,9 +36,7 @@ namespace StgSharp
public static partial class World
{
- internal static StgSharpTime mainTimeProvider = new StgSharpTime();
-
- internal static TimeSourceProviderBase MainTimeProvider => mainTimeProvider;
+ public static TimeSourceProviderBase MainTimeProvider => StgSharpTime.OnlyInstance;
}
@@ -50,40 +44,30 @@ namespace StgSharp
{
private static readonly LinearBag _subscribers = new();
- private static long accuracy;
private static readonly Stopwatch mainProvider = new();
private static Thread timeProvideThread;
- internal StgSharpTime() { }
+ private StgSharpTime() { }
- public static TimeSourceProviderBase OnlyInstance => World.MainTimeProvider;
+ // Use Stopwatch frequency as base tick frequency (ticks per second).
+ public override long Frequency => Stopwatch.Frequency;
+
+ public static StgSharpTime OnlyInstance { get; } = new StgSharpTime();
+
+ public override long GetCurrentTimeSpanTick() => mainProvider.ElapsedTicks;
public sealed override void StartProvidingTime()
{
if (mainProvider.IsRunning) {
return;
}
- mainProvider.Start();
- // speed test
- long totalMS, internalFrequency = Stopwatch.Frequency;
- for (int i = 0; i < 100; i++)
+ mainProvider.Start();
+ timeProvideThread = new Thread(ProvideTime)
{
- totalMS = mainProvider.ElapsedTicks / (internalFrequency / (1000L * 1000L));
- for (int t = 0; t < 10; t++)
- {
- if (totalMS < 1000) {
- _subscribers.Add(
- new TimeSpanProvider(100L, TimeSpanProvider.DefaultProvider));
- }
- }
- }
- mainProvider.Stop();
- _subscribers.Clear();
- totalMS = mainProvider.ElapsedTicks / (internalFrequency / (1000L * 1000L)) / 100L;
- accuracy = totalMS * 2 / 3;
- mainProvider.Reset();
- timeProvideThread = new Thread(new ThreadStart(ProvideTime));
+ IsBackground = true,
+ Name = "StgSharpTime"
+ };
timeProvideThread.Start();
}
@@ -113,29 +97,36 @@ namespace StgSharp
private void ProvideTime()
{
- long totalMS, internalFrequency = Stopwatch.Frequency;
+ long internalFrequency = Stopwatch.Frequency; // ticks per second
lock (mainProvider) {
mainProvider.Restart();
}
+
while (mainProvider.IsRunning)
{
- totalMS = mainProvider.ElapsedTicks / (internalFrequency / (1000L * 1000L));
+ long elapsedTicks;
+ lock (mainProvider) {
+ elapsedTicks = mainProvider.ElapsedTicks;
+ }
if (_subscribers.Count == 0)
{
+ Thread.SpinWait(50);
continue;
}
lock (SubscribeLock)
{
for (int i = 0; i < _subscribers.Count; i++)
{
- if (!_subscribers[i].CheckTime(totalMS))
+ if (!_subscribers[i].OnSpanTick(elapsedTicks))
{
_subscribers.RemoveAt(i);
- DefaultLog.InternalWriteLog(
- $"Time subscriber {_subscribers[i]} is to be removed.", LogType.Info);
+ i--; // adjust index after removal
}
}
}
+
+ // Optional: brief spin to avoid tight loop if frame lengths are large.
+ Thread.SpinWait(20);
}
}
diff --git a/StgSharp.Common/Timing/TimeFunction.cs b/StgSharp.Common/Timing/TimeFunction.cs
deleted file mode 100644
index b3dee2b8..00000000
--- a/StgSharp.Common/Timing/TimeFunction.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-//-----------------------------------------------------------------------
-// -----------------------------------------------------------------------
-// file="TimeFunction"
-// 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.Runtime.CompilerServices;
-using System.Text;
-
-namespace StgSharp.Timing
-{
- public class TimeFunction
- {
-
- private float _beginning, _ending;
- private TimeSpanProvider _timeProvider;
-
- internal TimeFunction() { }
-
- public TimeFunction(TimeSpanProvider timeSpanProvider, Func timeRelatedCalculation, float range)
- {
- _timeProvider = timeSpanProvider;
- TimeRelatedCalculation = timeRelatedCalculation;
- if (timeSpanProvider == null) {
- return;
- }
- _beginning = _timeProvider.CurrentSecond;
- _ending = _timeProvider.CurrentSecond + range;
- }
-
- public virtual bool IsComplete
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _timeProvider.CurrentSecond > _ending;
- }
-
- public Func TimeRelatedCalculation { get; set; }
-
- public static TimeFunction One
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => new TimeFunctionOne();
- }
-
- public TimeSpanProvider TimeSpanProvider
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _timeProvider;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- set => _timeProvider = value;
- }
-
- public virtual float Calculate()
- {
- if (_timeProvider.CurrentSecond < _ending) {
- return 0;
- }
- return TimeRelatedCalculation(_timeProvider.CurrentSecond - _beginning);
- }
-
- public static TimeFunction Proportional(TimeSpanProvider time, float begin, float range)
- {
- return new TimeFunction(time, (float t) => t + begin, range);
- }
-
- public virtual TimeFunction Renew((Func func, float range) funcPair)
- {
- return new TimeFunction(_timeProvider, funcPair.func, funcPair.range);
- }
-
- public virtual TimeFunction Renew(Func func, float range)
- {
- return new TimeFunction(_timeProvider, func, range);
- }
-
- public virtual void Reset()
- {
- float range = _ending - _beginning;
- _beginning = _timeProvider.CurrentSecond;
- _ending = _beginning + range;
- }
-
- public virtual TimeFunction Reverse()
- {
- float range = _ending - _beginning;
- return new TimeFunction(
- _timeProvider, p => TimeRelatedCalculation(range - p), range);
- }
-
- }
-
- public sealed class TimeFunctionOne : TimeFunction
- {
-
- internal TimeFunctionOne() : base() { }
-
- public override bool IsComplete => true;
-
- public override float Calculate()
- {
- return 1;
- }
-
- public override TimeFunction Renew((Func func, float range) funcPair)
- {
- throw new NotSupportedException();
- }
-
- public override TimeFunction Renew(Func func, float range)
- {
- throw new NotSupportedException();
- }
-
- #pragma warning disable CA1822
- public override void Reset()
- {
- }
- #pragma warning restore CA1822
- }
-}
diff --git a/StgSharp.Common/Timing/TimeSequence.cs b/StgSharp.Common/Timing/TimeSequence.cs
index 0e321d87..fba82b03 100644
--- a/StgSharp.Common/Timing/TimeSequence.cs
+++ b/StgSharp.Common/Timing/TimeSequence.cs
@@ -25,69 +25,193 @@
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
-using StgSharp.Mathematics;
+using StgSharp.Timing;
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading.Tasks;
namespace StgSharp.Common.Timing
{
public abstract class TimeSequence
{
- public ulong BeginningTick { get; protected set; }
+ public long BeginningTick { get; protected set; }
- public ulong EndingTick { get; protected set; }
+ public long EndingTick { get; protected set; }
- public ulong FrameLength { get; protected set; }
+ public long PreviousFrameTick { get; protected set; }
- public static TimeSequence CreateLinear(ulong timeout, ulong length)
- {
- return new LinearTimeSequence(timeout, timeout + length, length);
- }
+ public static TimeSequence CreateLinear(double totalSeconds, double frameSeconds) => new LinearTimeSequence(totalSeconds, frameSeconds);
- public abstract bool IsNextFrameReady(ulong currentTick);
+ public static TimeSequence CreateLogarithmic(
+ double initialSliceSeconds,
+ double totalSeconds,
+ double growthMultiplier,
+ double minSliceSeconds) => new LogTimeSequence(initialSliceSeconds, totalSeconds,
+ growthMultiplier, minSliceSeconds);
- }
+ public abstract bool EndsAt(long currentTick);
- internal sealed class LogTimeSequence : TimeSequence
- {
+ public abstract bool IsNextFrameReady(long currentTick);
- private float _rate ;
-
- public LogTimeSequence(ulong beginningTick, ulong endingTick, ulong tickPerDecade)
- {
- BeginningTick = beginningTick;
- EndingTick = endingTick;
- _rate = Scalar.Pow(10, 1 / tickPerDecade);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public override bool IsNextFrameReady(ulong currentTick)
- {
- return (ulong)Math.Log(currentTick - BeginningTick + 1) >= FrameLength;
- }
+ public abstract void StartFrom(TimeSourceProviderBase source);
}
internal sealed class LinearTimeSequence : TimeSequence
{
- public LinearTimeSequence(ulong beginningTick, ulong endingTick, ulong tickPerFrame)
+ private readonly double _frameSeconds;
+ private readonly double _totalSeconds;
+ private long _frameLengthTicks;
+
+ public LinearTimeSequence(double totalSeconds, double frameSeconds)
{
- BeginningTick = beginningTick;
- EndingTick = endingTick;
- FrameLength = tickPerFrame;
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds);
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds);
+ if (frameSeconds > totalSeconds) {
+ throw new ArgumentException("Frame length greater than total length.");
+ }
+
+ _totalSeconds = totalSeconds;
+ _frameSeconds = frameSeconds;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public override bool IsNextFrameReady(ulong currentTick)
+ public override bool EndsAt(long currentTick) => currentTick >= EndingTick;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override bool IsNextFrameReady(long currentTick)
{
- return currentTick - BeginningTick >= FrameLength;
+ long span = currentTick - PreviousFrameTick;
+ if (span >= _frameLengthTicks && currentTick < EndingTick)
+ {
+ PreviousFrameTick += _frameLengthTicks; // advance start of next frame
+ return true;
+ }
+ return false;
+ }
+
+ public override void StartFrom(TimeSourceProviderBase source)
+ {
+ long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
+ _frameLengthTicks = (long)Math.Max(1, _frameSeconds * f);
+ long totalTicks = (long)Math.Max(_frameLengthTicks, Math.Round(_totalSeconds * f, MidpointRounding.AwayFromZero));
+ BeginningTick = source.GetCurrentTimeSpanTick();
+ EndingTick = BeginningTick + totalTicks;
+ EndingTick = EndingTick < 0 ? long.MaxValue : EndingTick;
+ PreviousFrameTick = BeginningTick;
}
}
+
+ ///
+ /// Logarithmic time sequence with growth factor. Boundaries (cumulative times since start)
+ /// follow initial * multiplier^k, but each slice duration (difference between adjusted
+ /// boundaries) is at least minSliceSeconds. First slice uses the exact initial length even if
+ /// below minSliceSeconds, subsequent slices enforce minimum. Example: initial=2,
+ /// multiplier=2, total>=8, min=3 => boundaries: 2,5,8 (durations:2,3,3).
+ ///
+ internal sealed class LogTimeSequence : TimeSequence
+ {
+
+ private readonly double _initialSeconds;
+ private double _lastBoundarySecondsAdjusted; // adjusted cumulative seconds of last emitted boundary (0 at start)
+ private readonly double _minSliceSeconds;
+ private readonly double _multiplier;
+ private double _nextBoundarySecondsAdjusted; // adjusted cumulative seconds for next boundary
+ private readonly double _totalSeconds;
+
+ private int _sliceIndex; // index of next boundary (0 => first boundary at initialSeconds)
+ private long _freq;
+ private long _nextBoundaryTick; // absolute tick for next boundary
+ private long _totalTicks;
+
+ public LogTimeSequence(
+ double initialSliceSeconds,
+ double totalSeconds,
+ double growthMultiplier,
+ double minSliceSeconds)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(initialSliceSeconds);
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds);
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(growthMultiplier);
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(minSliceSeconds);
+ if (growthMultiplier < 1.0) {
+ throw new ArgumentException("growthMultiplier must be >= 1");
+ }
+
+ if (initialSliceSeconds > totalSeconds) {
+ throw new ArgumentException("Initial slice length greater than total length.");
+ }
+
+ _initialSeconds = initialSliceSeconds;
+ _totalSeconds = totalSeconds;
+ _multiplier = growthMultiplier;
+ _minSliceSeconds = minSliceSeconds;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override bool EndsAt(long currentTick) => currentTick >= EndingTick ||
+ _nextBoundarySecondsAdjusted >= _totalSeconds;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override bool IsNextFrameReady(long currentTick)
+ {
+ if (currentTick < _nextBoundaryTick || currentTick >= EndingTick) {
+ return false;
+ }
+
+ // Emit frame: update previous frame tick and prepare next boundary.
+ PreviousFrameTick = _nextBoundaryTick; // boundary just reached
+ _lastBoundarySecondsAdjusted = _nextBoundarySecondsAdjusted;
+ _sliceIndex++;
+
+ // Compute original (unadjusted) next cumulative boundary in seconds.
+ double originalNext = _initialSeconds * Math.Pow(_multiplier, _sliceIndex);
+
+ // Enforce total cap.
+ if (originalNext > _totalSeconds)
+ {
+ originalNext = _totalSeconds;
+ }
+
+ // Compute slice duration based on adjusted previous boundary.
+ double delta = originalNext - _lastBoundarySecondsAdjusted;
+ if (_sliceIndex > 0 && delta < _minSliceSeconds)
+ {
+ // Enforce minimum (except for first slice which uses initial value)
+ originalNext = _lastBoundarySecondsAdjusted + _minSliceSeconds;
+ if (originalNext > _totalSeconds) {
+ originalNext = _totalSeconds;
+ }
+ }
+ _nextBoundarySecondsAdjusted = originalNext;
+
+ if (_nextBoundarySecondsAdjusted >= _totalSeconds)
+ {
+ _nextBoundaryTick = EndingTick; // sequence will end after this boundary
+ } else
+ {
+ _nextBoundaryTick = BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted);
+ }
+ return true;
+ }
+
+ public override void StartFrom(TimeSourceProviderBase source)
+ {
+ _freq = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
+ BeginningTick = source.GetCurrentTimeSpanTick();
+ _totalTicks = SecondsToTicks(_totalSeconds);
+ EndingTick = BeginningTick + _totalTicks;
+ _sliceIndex = 0;
+ _lastBoundarySecondsAdjusted = 0.0;
+ _nextBoundarySecondsAdjusted = _initialSeconds; // first boundary uses exact initial length
+ _nextBoundaryTick = BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted);
+ PreviousFrameTick = BeginningTick; // start point
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private long SecondsToTicks(double seconds) => (long)Math.Max(1L, Math.Round(seconds * _freq, MidpointRounding.AwayFromZero));
+
+ }
}
diff --git a/StgSharp.Common/Timing/TimeSourceProvider.cs b/StgSharp.Common/Timing/TimeSourceProvider.cs
index 5d6f98d3..d07c2fdc 100644
--- a/StgSharp.Common/Timing/TimeSourceProvider.cs
+++ b/StgSharp.Common/Timing/TimeSourceProvider.cs
@@ -36,6 +36,8 @@ namespace StgSharp.Timing
public abstract class TimeSourceProviderBase
{
+ public abstract long Frequency { get; }
+
protected object SubscribeLock { get; } = new();
public void AddSubscriber(TimeSpanProvider subscriber)
@@ -45,6 +47,8 @@ namespace StgSharp.Timing
}
}
+ public abstract long GetCurrentTimeSpanTick();
+
public void RemoveSubscriber(TimeSpanProvider subscriber)
{
lock (SubscribeLock) {
diff --git a/StgSharp.Common/Timing/TimeSpanAwaitingToken.cs b/StgSharp.Common/Timing/TimeSpanAwaitingToken.cs
deleted file mode 100644
index 5fe50834..00000000
--- a/StgSharp.Common/Timing/TimeSpanAwaitingToken.cs
+++ /dev/null
@@ -1,147 +0,0 @@
-//-----------------------------------------------------------------------
-// -----------------------------------------------------------------------
-// file="TimeSpanAwaitingToken"
-// 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 StgSharp.HighPerformance.Memory;
-
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Diagnostics.Contracts;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace StgSharp.Timing
-{
- public sealed class TimeSpanAwaitingToken : IDisposable
- {
-
- private static readonly ConcurrentBufferStack s_unusedID = new(1);
-
- private static int s_maxID;
-
- private EventHandler? _refreshHandler,_missHandler;
- private int _size, _id;
-
- private readonly object _participantObject = new();
- private SemaphoreSlim _awaitingSemaphore;
-
- private TimeSpanProvider _provider;
-
- internal TimeSpanAwaitingToken(TimeSpanProvider provider)
- {
- if (!s_unusedID.TryPop(out int id)) {
- id = Interlocked.Increment(ref s_maxID);
- }
- TokenID = id;
- _provider = provider;
- _size = 1;
- _awaitingSemaphore = new SemaphoreSlim(0, 1);
- }
-
- internal TimeSpanAwaitingToken(TimeSpanProvider provider, int count)
- {
- if (!s_unusedID.TryPop(out int id)) {
- id = Interlocked.Increment(ref s_maxID);
- }
- _provider = provider;
- _size = count;
- _awaitingSemaphore = new SemaphoreSlim(0, _size);
- }
-
- public event EventHandler MissTimeSpanRefreshed
- {
- add => _missHandler += value;
- remove => _missHandler -= value;
- }
-
- public event EventHandler TimeSpanRefreshed
- {
- add => _refreshHandler += value;
- remove => _refreshHandler -= value;
- }
-
- public int TokenID
- {
- get => _id;
- private set => _id = value;
- }
-
- internal SemaphoreSlim AwaitingSemaphoreSlim
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _awaitingSemaphore;
- }
-
- public void Dispose()
- {
- _provider.QuitSpanAwaiting(this);
- s_unusedID.Push(TokenID);
- _awaitingSemaphore.Dispose();
- GC.SuppressFinalize(this);
- }
-
- public override int GetHashCode()
- {
- return _id;
- }
-
- public void Participant()
- {
- lock (_participantObject)
- {
- _size++;
- SemaphoreSlim newSemaphore = new(_awaitingSemaphore.CurrentCount, _size);
- Interlocked.Exchange(ref _awaitingSemaphore, newSemaphore).Dispose();
- }
- }
-
- public void WaitNextSpan()
- {
- _awaitingSemaphore.Wait();
- }
-
- internal void MissRefresh()
- {
- _missHandler?.Invoke(this, EventArgs.Empty);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void Refresh()
- {
- _refreshHandler?.Invoke(this, EventArgs.Empty);
- }
-
- ~TimeSpanAwaitingToken()
- {
- Dispose();
- }
-
- }
-}
diff --git a/StgSharp.Common/Timing/TimeSpanProvider.Awaiting.cs b/StgSharp.Common/Timing/TimeSpanProvider.Awaiting.cs
new file mode 100644
index 00000000..1763ddd1
--- /dev/null
+++ b/StgSharp.Common/Timing/TimeSpanProvider.Awaiting.cs
@@ -0,0 +1,198 @@
+//-----------------------------------------------------------------------
+// -----------------------------------------------------------------------
+// file="TimeSpanProvider.Awaiting"
+// 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 StgSharp.HighPerformance.Memory;
+
+using System;
+using System.Collections.Concurrent;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace StgSharp.Timing
+{
+ public partial class TimeSpanProvider
+ {
+
+ private static readonly ConcurrentBufferStack s_unusedID = new ConcurrentBufferStack(8);
+
+ private static int s_maxID;
+
+ private EventHandler? _refreshHandler, _missHandler;
+ private int _pendingSpans; // Number of unconsumed span ticks since last Wait.
+ private int _size, _id;
+
+ private readonly ManualResetEventSlim _event = new();
+ private readonly object _participantObject = new();
+
+ public event EventHandler MissTimeSpanRefreshed
+ {
+ add => _missHandler += value;
+ remove => _missHandler -= value;
+ }
+
+ public event EventHandler TimeSpanRefreshed
+ {
+ add => _refreshHandler += value;
+ remove => _refreshHandler -= value;
+ }
+
+ ///
+ /// True if there is at least one unconsumed span tick (PendingSpanCount > 0).
+ ///
+ public bool HasPendingSpan => PendingSpanCount > 0;
+
+ public int TokenID
+ {
+ get => _id;
+ private set => _id = value;
+ }
+
+ ///
+ /// Number of span ticks not yet consumed by Wait ( > 0 means last tick not waited).
+ ///
+ public int PendingSpanCount => Volatile.Read(ref _pendingSpans);
+
+ internal ManualResetEventSlim AwaitingEvent
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _event;
+ }
+
+ public void Dispose()
+ {
+ s_unusedID.Push(TokenID);
+ _event.Dispose();
+ GC.SuppressFinalize(this);
+ }
+
+ public override int GetHashCode() => _id;
+
+ public void Participant()
+ {
+ lock (_participantObject)
+ {
+ // Increase the maximum concurrent consumers for the next tick.
+ _size++;
+ }
+ }
+
+ public void WaitNextSpan()
+ {
+ _event.Wait();
+
+ // Consume one span slot (clamp at 0 so multiple subscribers won't make it negative).
+ while (true)
+ {
+ int cur = Volatile.Read(ref _pendingSpans);
+ if (cur == 0) {
+ return;
+ }
+
+ if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
+ {
+ if (cur - 1 == 0)
+ {
+ // Last consumer resets the gate for the next tick.
+ _event.Reset();
+ }
+ return;
+ }
+ }
+ }
+
+ public async Task WaitNextSpanAsync()
+ {
+ await Task.Run(() => _event.Wait()).ConfigureAwait(false);
+ while (true)
+ {
+ int cur = Volatile.Read(ref _pendingSpans);
+ if (cur == 0)
+ {
+ break;
+ }
+
+ if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
+ {
+ if (cur - 1 == 0) {
+ _event.Reset();
+ }
+
+ break;
+ }
+ }
+ }
+
+ internal void MissRefresh() => _missHandler?.Invoke(this, EventArgs.Empty); // Legacy compatibility.
+
+ ///
+ ///
+ ///
+ ///
+ /// Tick passed since binded time source.
+ ///
+ ///
+ /// True if the span provider is working. False if time is over (the sequence ended).
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal bool OnSpanTick(long currentTick)
+ {
+ if (_sequence.EndsAt(currentTick))
+ {
+ Dispose();
+ return false;
+ }
+ if (!_sequence.IsNextFrameReady(currentTick)) {
+ return true;
+ }
+ Console.WriteLine("Frame hit!");
+
+ // Fast path: if no pending consumers, initialize the counter to current capacity (_size).
+ if (Volatile.Read(ref _pendingSpans) == 0)
+ {
+ int toIssue = Math.Max(1, Volatile.Read(ref _size));
+ if (Interlocked.CompareExchange(ref _pendingSpans, toIssue, 0) == 0)
+ {
+ // Open the gate for all current waiters (up to `_size`).
+ _event.Set();
+ _refreshHandler?.Invoke(this, EventArgs.Empty);
+ }
+ } else
+ {
+ // A tick is already pending (or was just set by another thread): mark as miss.
+ _missHandler?.Invoke(this, EventArgs.Empty);
+ }
+ return true;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void Refresh() => _refreshHandler?.Invoke(this, EventArgs.Empty); // Legacy compatibility.
+
+ ~TimeSpanProvider() => Dispose();
+
+ }
+}
diff --git a/StgSharp.Common/Timing/TimeSpanProvider.cs b/StgSharp.Common/Timing/TimeSpanProvider.cs
index 947dc4c2..adc5cdc1 100644
--- a/StgSharp.Common/Timing/TimeSpanProvider.cs
+++ b/StgSharp.Common/Timing/TimeSpanProvider.cs
@@ -25,207 +25,53 @@
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
+using StgSharp.Common.Timing;
using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using System.Threading;
namespace StgSharp.Timing
{
///
- /// A microsecond-accuracy timer. It will refresh every a certain time span.
+ /// TimeSpanProvider: uses a bound TimeSequence and TimeSourceProviderBase to publish span
+ /// ticks. Each OnSpanTick advances the sequence; when sequence ends provider unsubscribes.
///
- public class TimeSpanProvider
+ public partial class TimeSpanProvider
{
private bool _subscribed;
-
- private readonly ConcurrentQueue _tokenToAdd = new();
- private readonly ConcurrentQueue _tokenToQuit;
- private Counter timeSpanCount = new(-1, 1, 1);
- private readonly HashSet _awaitingTokens;
- private long _maxSpanCount;
- private long _spanBegin;
- private readonly long _spanLength;
+ private int _emittedSpanCount; // legacy counter
+ private readonly TimeSequence _sequence;
private readonly TimeSourceProviderBase _timeSource;
- ///
- /// Create a new and define the length of time span.
- /// Length of a single time span is defined in microseconds.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public TimeSpanProvider(long spanMicroSeconds, TimeSourceProviderBase provider)
+ public TimeSpanProvider(TimeSequence sequence, TimeSourceProviderBase provider, int maxWaitCount = 1)
{
ArgumentNullException.ThrowIfNull(provider);
+ ArgumentNullException.ThrowIfNull(sequence);
_timeSource = provider;
- _spanLength = spanMicroSeconds;
- _maxSpanCount = int.MaxValue;
+ _sequence = sequence;
+ _sequence.StartFrom(provider);
provider.AddSubscriber(this);
_subscribed = true;
- _awaitingTokens = [];
- _tokenToQuit = new ConcurrentQueue();
- }
-
- ///
- /// Create a new and define the length of time span.
- /// Length of a single time span is defined in seconds.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public TimeSpanProvider(double spanSeconds, TimeSourceProviderBase provider)
- {
- ArgumentNullException.ThrowIfNull(provider);
- _timeSource = provider;
- _spanLength = (long)(spanSeconds * 1000L * 1000L);
- _spanBegin = -_spanLength;
- _maxSpanCount = long.MaxValue;
- provider.AddSubscriber(this);
- _subscribed = true;
- _awaitingTokens = [];
- _tokenToQuit = new ConcurrentQueue();
- }
-
- ///
- /// Create a new and define the length of time span.
- /// Length of a single time span is defined in microseconds.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public TimeSpanProvider(long spanLength, long maxSpan, TimeSourceProviderBase provider)
- {
- ArgumentNullException.ThrowIfNull(provider);
- if (maxSpan <= 0) {
- throw new ArgumentException("Sapn count cannot be smaller than zero");
+ if (!s_unusedID.TryPop(out int id)) {
+ id = Interlocked.Increment(ref s_maxID);
}
- _timeSource = provider;
- _spanLength = spanLength;
- _spanBegin = -_spanLength;
- provider.AddSubscriber(this);
- _subscribed = true;
- _awaitingTokens = new HashSet();
- _tokenToQuit = new ConcurrentQueue();
+ TokenID = id;
+ _size = maxWaitCount > 0 ? maxWaitCount : 1;
+ _event = new ManualResetEventSlim(initialState:false, spinCount:0);
+ _pendingSpans = 0;
}
- public double SpanLength
+ public double CurrentSecond
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _spanLength * 1.0 / (1000L * 1000L);
+ get => (double)_sequence.PreviousFrameTick / _timeSource.Frequency;
}
- public float CurrentSecond
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => timeSpanCount.Value * SpanLengthLowPrecession;
- }
+ public int CurrentSpan => _emittedSpanCount;
- public float SpanLengthLowPrecession
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _spanLength * 1.0f / (1000 * 1000);
- }
+ public static TimeSourceProviderBase DefaultProvider => World.MainTimeProvider;
- public int CurrentSpan
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => timeSpanCount.Value;
- }
-
- ///
- /// Time source provider provided in internal framework.
- ///
- public static TimeSourceProviderBase DefaultProvider => StgSharpTime.OnlyInstance;
-
- public TimeSpanAwaitingToken ParticipantSpanAwaiting(int count)
- {
- TimeSpanAwaitingToken token = new(this, count);
- _tokenToAdd.Enqueue(token);
- return token;
- }
-
- public void QuitSpanAwaiting(TimeSpanAwaitingToken token)
- {
- _tokenToQuit.Enqueue(token);
- }
-
- public void StopSpanProviding()
- {
- _ = Interlocked.Exchange(ref _maxSpanCount, 0);
- }
-
- public void SubscribeToTimeSource(TimeSourceProviderBase serviceHandler)
- {
- ArgumentNullException.ThrowIfNull(serviceHandler);
- if (_subscribed) {
- return;
- }
- _subscribed = true;
- serviceHandler.AddSubscriber(this);
- }
-
- public int ToInt32()
- {
- return timeSpanCount.Value;
- }
-
- internal bool CheckTime(long microseconds)
- {
- if (microseconds - _spanBegin < _spanLength) {
- return true;
- }
- foreach (TimeSpanAwaitingToken tokenAdd in _tokenToAdd) {
- _ = _awaitingTokens.Add(tokenAdd);
- }
- _tokenToAdd.Clear();
- foreach (TimeSpanAwaitingToken tokenQuit in _tokenToQuit)
- {
- _ = _awaitingTokens.Remove(tokenQuit);
- DefaultLog.InternalWriteLog(
- $"Awaiting token {tokenQuit!.TokenID} has been removed.", LogType.Info);
- }
- _tokenToQuit.Clear();
-
- _spanBegin = microseconds;
- timeSpanCount++;
-
- foreach (TimeSpanAwaitingToken item in _awaitingTokens)
- {
- if (item.AwaitingSemaphoreSlim.CurrentCount == 0)
- {
- _ = item.AwaitingSemaphoreSlim.Release();
- item.Refresh();
- } else
- {
- item.MissRefresh();
- }
- }
-
- return timeSpanCount < _maxSpanCount;
- }
-
- public static explicit operator int([NotNull]TimeSpanProvider provider)
- {
- return provider.ToInt32();
- }
+ public static explicit operator int(TimeSpanProvider provider) => provider?._emittedSpanCount ?? 0;
}
}
\ No newline at end of file
diff --git a/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs b/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs
index 5e50c3f1..37acd586 100644
--- a/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs
+++ b/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs
@@ -44,13 +44,28 @@ namespace StgSharp.Mathematics.Numeric
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe MatrixParallelHandle ScheduleTask(MatrixParallelTaskPackage* package)
- where T : unmanaged
+ where T : unmanaged,INumber
{
MatrixParallelTaskPackageNonGeneric* nonGeneric = (MatrixParallelTaskPackageNonGeneric*)package;
- return ScheduleTask(nonGeneric);
+ switch (nonGeneric->ComputeMode.OperationStyle)
+ {
+ case MatrixOperationStyle.PANEL_OP:
+ throw new NotImplementedException();
+ case MatrixOperationStyle.BUFFER_OP:
+ return ScheduleBufferTask(nonGeneric);
+ case MatrixOperationStyle.KERNEL_OP:
+ return ScheduleKernelTask(nonGeneric);
+ default:
+ throw new InvalidOperationException($"Unknown mode {nonGeneric->ComputeMode.OperationStyle}.");
+ }
}
- internal static unsafe MatrixParallelHandle ScheduleTask(MatrixParallelTaskPackageNonGeneric* package)
+ private static unsafe MatrixParallelHandle ScheduleBufferTask(MatrixParallelTaskPackageNonGeneric* package)
+ {
+ throw new NotImplementedException();
+ }
+
+ private static unsafe MatrixParallelHandle ScheduleKernelTask(MatrixParallelTaskPackageNonGeneric* package)
{
int primCount = package->PrimCount;
int secCount = package->SecCount;
diff --git a/StgSharp.Native/sn-head/glfw_function.h b/StgSharp.Native/sn-head/glfw_function.h
index ca17ae83..36f7fc98 100644
--- a/StgSharp.Native/sn-head/glfw_function.h
+++ b/StgSharp.Native/sn-head/glfw_function.h
@@ -20,7 +20,7 @@ typedef struct GLFWFunctionTable {
GLFWwindow *(*glfwGetCurrentContext)(void);
void (*glfwGetCursorPos)(GLFWwindow *window, double *xpos, double *ypos);
int (*glfwGetError)(const char **description);
- void (*glfwGetFramebufferSize)(GLFWwindow *window, int *width, int *height);
+ void (*glfwGetFrameBufferSize)(GLFWwindow *window, int *width, int *height);
const char *(*glfwGetGamepadName)(int jid);
int (*glfwGetGamepadState)(int jid, GLFWgamepadstate *state);
const GLFWgammaramp *(*glfwGetGammaRamp)(GLFWmonitor *monitor);
@@ -38,10 +38,10 @@ typedef struct GLFWFunctionTable {
const char *(*glfwGetMonitorName)(GLFWmonitor *monitor);
void (*glfwGetMonitorPhysicalSize)(GLFWmonitor *monitor, int *widthMM, int *heightMM);
void (*glfwGetMonitorPos)(GLFWmonitor *monitor, int *xpos, int *ypos);
+ GLFWmonitor **(*glfwGetMonitors)(int *count);
void *(*glfwGetMonitorUserPointer)(GLFWmonitor *monitor);
void (*glfwGetMonitorWorkarea)(GLFWmonitor *monitor, int *xpos, int *ypos, int *width,
int *height);
- GLFWmonitor **(*glfwGetMonitors)(int *count);
int (*glfwGetMouseButton)(GLFWwindow *window, int button);
GLFWmonitor *(*glfwGetPrimaryMonitor)(void);
GLFWglproc (*glfwGetProcAddress)(const char *procname);
@@ -66,7 +66,7 @@ typedef struct GLFWFunctionTable {
void (*glfwIconifyWindow)(GLFWwindow *window);
int (*glfwInit)(void);
void (*glfwInitHint)(int hint, int value);
- int (*glfwJoystickIsGamepad)(int jid);
+ int (*glfwJoystickIsGamePad)(int jid);
int (*glfwJoystickPresent)(int jid);
void (*glfwMakeContextCurrent)(GLFWwindow *window);
void (*glfwMaximizeWindow)(GLFWwindow *window);
@@ -75,13 +75,13 @@ typedef struct GLFWFunctionTable {
int (*glfwRawMouseMotionSupported)(void);
void (*glfwRequestWindowAttention)(GLFWwindow *window);
void (*glfwRestoreWindow)(GLFWwindow *window);
- void (*glfwSetClipboardString)(GLFWwindow *window, const char *str);
- void (*glfwSetCursor)(GLFWwindow *window, GLFWcursor *cursor);
- void (*glfwSetCursorPos)(GLFWwindow *window, double xpos, double ypos);
GLFWcharfun (*glfwSetCharCallback)(GLFWwindow *window, GLFWcharfun callback);
GLFWcharmodsfun (*glfwSetCharModsCallback)(GLFWwindow *window, GLFWcharmodsfun callback);
+ void (*glfwSetClipboardString)(GLFWwindow *window, const char *str);
+ void (*glfwSetCursor)(GLFWwindow *window, GLFWcursor *cursor);
GLFWcursorenterfun (*glfwSetCursorEnterCallback)(GLFWwindow *window,
GLFWcursorenterfun callback);
+ void (*glfwSetCursorPos)(GLFWwindow *window, double xpos, double ypos);
GLFWcursorposfun (*glfwSetCursorPosCallback)(GLFWwindow *window, GLFWcursorposfun callback);
GLFWdropfun (*glfwSetDropCallback)(GLFWwindow *window, GLFWdropfun callback);
GLFWerrorfun (*glfwSetErrorCallback)(GLFWerrorfun callback);
@@ -91,6 +91,7 @@ typedef struct GLFWFunctionTable {
void (*glfwSetGammaRamp)(GLFWmonitor *monitor, const GLFWgammaramp *ramp);
void (*glfwSetInputMode)(GLFWwindow *window, int mode, int value);
GLFWjoystickfun (*glfwSetJoystickCallback)(GLFWjoystickfun callback);
+ void (*glfwSetJoystickUserPointer)(int jid, void *pointer);
GLFWkeyfun (*glfwSetKeyCallback)(GLFWwindow *window, GLFWkeyfun callback);
GLFWmonitorfun (*glfwSetMonitorCallback)(GLFWmonitorfun callback);
void (*glfwSetMonitorUserPointer)(GLFWmonitor *monitor, void *pointer);
@@ -130,7 +131,7 @@ typedef struct GLFWFunctionTable {
void (*glfwSwapBuffers)(GLFWwindow *window);
void (*glfwSwapInterval)(int interval);
void (*glfwTerminate)(void);
- int (*glfwUpdateGamepadMappings)(const char *str);
+ int (*glfwUpdateGamePadMappings)(const char *str);
int (*glfwVulkanSupported)(void);
void (*glfwWaitEvents)(void);
void (*glfwWaitEventsTimeout)(double timeout);
diff --git a/StgSharp.Native/sn-src/load_glfw.c b/StgSharp.Native/sn-src/load_glfw.c
index 4187c923..f225f968 100644
--- a/StgSharp.Native/sn-src/load_glfw.c
+++ b/StgSharp.Native/sn-src/load_glfw.c
@@ -4,119 +4,120 @@
SN_API void SN_DECL load_glfw_functions(GLFWFunctionTable *table)
{
- table->glfwInit = glfwInit;
- table->glfwTerminate = glfwTerminate;
- table->glfwInitHint = glfwInitHint;
- table->glfwGetVersion = glfwGetVersion;
- table->glfwGetVersionString = glfwGetVersionString;
- table->glfwGetError = glfwGetError;
- table->glfwSetErrorCallback = glfwSetErrorCallback;
- table->glfwGetMonitors = glfwGetMonitors;
- table->glfwGetPrimaryMonitor = glfwGetPrimaryMonitor;
- table->glfwGetMonitorPos = glfwGetMonitorPos;
- table->glfwGetMonitorWorkarea = glfwGetMonitorWorkarea;
- table->glfwGetMonitorPhysicalSize = glfwGetMonitorPhysicalSize;
- table->glfwGetMonitorContentScale = glfwGetMonitorContentScale;
- table->glfwGetMonitorName = glfwGetMonitorName;
- table->glfwSetMonitorUserPointer = glfwSetMonitorUserPointer;
- table->glfwGetMonitorUserPointer = glfwGetMonitorUserPointer;
- table->glfwSetMonitorCallback = glfwSetMonitorCallback;
- table->glfwGetVideoModes = glfwGetVideoModes;
- table->glfwGetVideoMode = glfwGetVideoMode;
- table->glfwSetGamma = glfwSetGamma;
- table->glfwGetGammaRamp = glfwGetGammaRamp;
- table->glfwSetGammaRamp = glfwSetGammaRamp;
- table->glfwDefaultWindowHints = glfwDefaultWindowHints;
- table->glfwWindowHint = glfwWindowHint;
- table->glfwWindowHintString = glfwWindowHintString;
- table->glfwCreateWindow = glfwCreateWindow;
- table->glfwDestroyWindow = glfwDestroyWindow;
- table->glfwWindowShouldClose = glfwWindowShouldClose;
- table->glfwSetWindowShouldClose = glfwSetWindowShouldClose;
- table->glfwSetWindowTitle = glfwSetWindowTitle;
- table->glfwSetWindowIcon = glfwSetWindowIcon;
- table->glfwGetWindowPos = glfwGetWindowPos;
- table->glfwSetWindowPos = glfwSetWindowPos;
- table->glfwGetWindowSize = glfwGetWindowSize;
- table->glfwSetWindowSizeLimits = glfwSetWindowSizeLimits;
- table->glfwSetWindowAspectRatio = glfwSetWindowAspectRatio;
- table->glfwSetWindowSize = glfwSetWindowSize;
- table->glfwGetFramebufferSize = glfwGetFramebufferSize;
- table->glfwGetWindowFrameSize = glfwGetWindowFrameSize;
- table->glfwGetWindowContentScale = glfwGetWindowContentScale;
- table->glfwGetWindowOpacity = glfwGetWindowOpacity;
- table->glfwSetWindowOpacity = glfwSetWindowOpacity;
- table->glfwIconifyWindow = glfwIconifyWindow;
- table->glfwRestoreWindow = glfwRestoreWindow;
- table->glfwMaximizeWindow = glfwMaximizeWindow;
- table->glfwShowWindow = glfwShowWindow;
- table->glfwHideWindow = glfwHideWindow;
- table->glfwFocusWindow = glfwFocusWindow;
- table->glfwRequestWindowAttention = glfwRequestWindowAttention;
- table->glfwGetWindowMonitor = glfwGetWindowMonitor;
- table->glfwSetWindowMonitor = glfwSetWindowMonitor;
- table->glfwGetWindowAttrib = glfwGetWindowAttrib;
- table->glfwSetWindowAttrib = glfwSetWindowAttrib;
- table->glfwSetWindowUserPointer = glfwSetWindowUserPointer;
- table->glfwGetWindowUserPointer = glfwGetWindowUserPointer;
- table->glfwSetWindowPosCallback = glfwSetWindowPosCallback;
- table->glfwSetWindowSizeCallback = glfwSetWindowSizeCallback;
- table->glfwSetWindowCloseCallback = glfwSetWindowCloseCallback;
- table->glfwSetWindowRefreshCallback = glfwSetWindowRefreshCallback;
- table->glfwSetWindowFocusCallback = glfwSetWindowFocusCallback;
- table->glfwSetWindowIconifyCallback = glfwSetWindowIconifyCallback;
- table->glfwSetWindowMaximizeCallback = glfwSetWindowMaximizeCallback;
- table->glfwSetFramebufferSizeCallback = glfwSetFramebufferSizeCallback;
- table->glfwSetWindowContentScaleCallback = glfwSetWindowContentScaleCallback;
- table->glfwPollEvents = glfwPollEvents;
- table->glfwWaitEvents = glfwWaitEvents;
- table->glfwWaitEventsTimeout = glfwWaitEventsTimeout;
- table->glfwPostEmptyEvent = glfwPostEmptyEvent;
- table->glfwGetInputMode = glfwGetInputMode;
- table->glfwSetInputMode = glfwSetInputMode;
- table->glfwRawMouseMotionSupported = glfwRawMouseMotionSupported;
- table->glfwGetKeyName = glfwGetKeyName;
- table->glfwGetKeyScancode = glfwGetKeyScancode;
- table->glfwGetKey = glfwGetKey;
- table->glfwGetMouseButton = glfwGetMouseButton;
- table->glfwGetCursorPos = glfwGetCursorPos;
- table->glfwSetCursorPos = glfwSetCursorPos;
table->glfwCreateCursor = glfwCreateCursor;
table->glfwCreateStandardCursor = glfwCreateStandardCursor;
+ table->glfwCreateWindow = glfwCreateWindow;
+ table->glfwDefaultWindowHints = glfwDefaultWindowHints;
table->glfwDestroyCursor = glfwDestroyCursor;
- table->glfwSetCursor = glfwSetCursor;
- table->glfwSetKeyCallback = glfwSetKeyCallback;
- table->glfwSetCharCallback = glfwSetCharCallback;
- table->glfwSetCharModsCallback = glfwSetCharModsCallback;
- table->glfwSetMouseButtonCallback = glfwSetMouseButtonCallback;
- table->glfwSetCursorPosCallback = glfwSetCursorPosCallback;
- table->glfwSetCursorEnterCallback = glfwSetCursorEnterCallback;
- table->glfwSetScrollCallback = glfwSetScrollCallback;
- table->glfwSetDropCallback = glfwSetDropCallback;
- table->glfwJoystickPresent = glfwJoystickPresent;
+ table->glfwDestroyWindow = glfwDestroyWindow;
+ table->glfwExtensionSupported = glfwExtensionSupported;
+ table->glfwFocusWindow = glfwFocusWindow;
+ table->glfwGetClipboardString = glfwGetClipboardString;
+ table->glfwGetCurrentContext = glfwGetCurrentContext;
+ table->glfwGetCursorPos = glfwGetCursorPos;
+ table->glfwGetError = glfwGetError;
+ table->glfwGetFrameBufferSize = glfwGetFramebufferSize;
+ table->glfwGetGamepadName = glfwGetGamepadName;
+ table->glfwGetGamepadState = glfwGetGamepadState;
+ table->glfwGetGammaRamp = glfwGetGammaRamp;
+ table->glfwGetInputMode = glfwGetInputMode;
table->glfwGetJoystickAxes = glfwGetJoystickAxes;
table->glfwGetJoystickButtons = glfwGetJoystickButtons;
table->glfwGetJoystickGUID = glfwGetJoystickGUID;
table->glfwGetJoystickHats = glfwGetJoystickHats;
table->glfwGetJoystickName = glfwGetJoystickName;
table->glfwGetJoystickUserPointer = glfwGetJoystickUserPointer;
- table->glfwJoystickIsGamepad = glfwJoystickIsGamepad;
- table->glfwSetJoystickCallback = glfwSetJoystickCallback;
- table->glfwUpdateGamepadMappings = glfwUpdateGamepadMappings;
- table->glfwGetGamepadName = glfwGetGamepadName;
- table->glfwGetGamepadState = glfwGetGamepadState;
- table->glfwSetClipboardString = glfwSetClipboardString;
- table->glfwGetClipboardString = glfwGetClipboardString;
+ table->glfwGetKey = glfwGetKey;
+ table->glfwGetKeyName = glfwGetKeyName;
+ table->glfwGetKeyScancode = glfwGetKeyScancode;
+ table->glfwGetMonitorContentScale = glfwGetMonitorContentScale;
+ table->glfwGetMonitorName = glfwGetMonitorName;
+ table->glfwGetMonitorPhysicalSize = glfwGetMonitorPhysicalSize;
+ table->glfwGetMonitorPos = glfwGetMonitorPos;
+ table->glfwGetMonitors = glfwGetMonitors;
+ table->glfwGetMonitorUserPointer = glfwGetMonitorUserPointer;
+ table->glfwGetMonitorWorkarea = glfwGetMonitorWorkarea;
+ table->glfwGetMouseButton = glfwGetMouseButton;
+ table->glfwGetPrimaryMonitor = glfwGetPrimaryMonitor;
+ table->glfwGetProcAddress = glfwGetProcAddress;
+ table->glfwGetRequiredInstanceExtensions = glfwGetRequiredInstanceExtensions;
table->glfwGetTime = glfwGetTime;
- table->glfwSetTime = glfwSetTime;
- table->glfwGetTimerValue = glfwGetTimerValue;
table->glfwGetTimerFrequency = glfwGetTimerFrequency;
+ table->glfwGetTimerValue = glfwGetTimerValue;
+ table->glfwGetVersion = glfwGetVersion;
+ table->glfwGetVersionString = glfwGetVersionString;
+ table->glfwGetVideoMode = glfwGetVideoMode;
+ table->glfwGetVideoModes = glfwGetVideoModes;
+ table->glfwGetWindowAttrib = glfwGetWindowAttrib;
+ table->glfwGetWindowContentScale = glfwGetWindowContentScale;
+ table->glfwGetWindowFrameSize = glfwGetWindowFrameSize;
+ table->glfwGetWindowMonitor = glfwGetWindowMonitor;
+ table->glfwGetWindowOpacity = glfwGetWindowOpacity;
+ table->glfwGetWindowPos = glfwGetWindowPos;
+ table->glfwGetWindowSize = glfwGetWindowSize;
+ table->glfwGetWindowUserPointer = glfwGetWindowUserPointer;
+ table->glfwHideWindow = glfwHideWindow;
+ table->glfwIconifyWindow = glfwIconifyWindow;
+ table->glfwInit = glfwInit;
+ table->glfwInitHint = glfwInitHint;
+ table->glfwJoystickIsGamePad = glfwJoystickIsGamepad;
+ table->glfwJoystickPresent = glfwJoystickPresent;
table->glfwMakeContextCurrent = glfwMakeContextCurrent;
- table->glfwGetCurrentContext = glfwGetCurrentContext;
+ table->glfwMaximizeWindow = glfwMaximizeWindow;
+ table->glfwPollEvents = glfwPollEvents;
+ table->glfwPostEmptyEvent = glfwPostEmptyEvent;
+ table->glfwRawMouseMotionSupported = glfwRawMouseMotionSupported;
+ table->glfwRequestWindowAttention = glfwRequestWindowAttention;
+ table->glfwRestoreWindow = glfwRestoreWindow;
+ table->glfwSetCharCallback = glfwSetCharCallback;
+ table->glfwSetCharModsCallback = glfwSetCharModsCallback;
+ table->glfwSetClipboardString = glfwSetClipboardString;
+ table->glfwSetCursor = glfwSetCursor;
+ table->glfwSetCursorEnterCallback = glfwSetCursorEnterCallback;
+ table->glfwSetCursorPos = glfwSetCursorPos;
+ table->glfwSetCursorPosCallback = glfwSetCursorPosCallback;
+ table->glfwSetDropCallback = glfwSetDropCallback;
+ table->glfwSetErrorCallback = glfwSetErrorCallback;
+ table->glfwSetFramebufferSizeCallback = glfwSetFramebufferSizeCallback;
+ table->glfwSetGamma = glfwSetGamma;
+ table->glfwSetGammaRamp = glfwSetGammaRamp;
+ table->glfwSetInputMode = glfwSetInputMode;
+ table->glfwSetJoystickCallback = glfwSetJoystickCallback;
+ table->glfwSetJoystickUserPointer = glfwSetJoystickUserPointer;
+ table->glfwSetKeyCallback = glfwSetKeyCallback;
+ table->glfwSetMonitorCallback = glfwSetMonitorCallback;
+ table->glfwSetMonitorUserPointer = glfwSetMonitorUserPointer;
+ table->glfwSetMouseButtonCallback = glfwSetMouseButtonCallback;
+ table->glfwSetScrollCallback = glfwSetScrollCallback;
+ table->glfwSetTime = glfwSetTime;
+ table->glfwSetWindowAspectRatio = glfwSetWindowAspectRatio;
+ table->glfwSetWindowAttrib = glfwSetWindowAttrib;
+ table->glfwSetWindowCloseCallback = glfwSetWindowCloseCallback;
+ table->glfwSetWindowContentScaleCallback = glfwSetWindowContentScaleCallback;
+ table->glfwSetWindowFocusCallback = glfwSetWindowFocusCallback;
+ table->glfwSetWindowIcon = glfwSetWindowIcon;
+ table->glfwSetWindowIconifyCallback = glfwSetWindowIconifyCallback;
+ table->glfwSetWindowMaximizeCallback = glfwSetWindowMaximizeCallback;
+ table->glfwSetWindowMonitor = glfwSetWindowMonitor;
+ table->glfwSetWindowOpacity = glfwSetWindowOpacity;
+ table->glfwSetWindowPos = glfwSetWindowPos;
+ table->glfwSetWindowPosCallback = glfwSetWindowPosCallback;
+ table->glfwSetWindowRefreshCallback = glfwSetWindowRefreshCallback;
+ table->glfwSetWindowShouldClose = glfwSetWindowShouldClose;
+ table->glfwSetWindowSize = glfwSetWindowSize;
+ table->glfwSetWindowSizeCallback = glfwSetWindowSizeCallback;
+ table->glfwSetWindowSizeLimits = glfwSetWindowSizeLimits;
+ table->glfwSetWindowTitle = glfwSetWindowTitle;
+ table->glfwSetWindowUserPointer = glfwSetWindowUserPointer;
+ table->glfwShowWindow = glfwShowWindow;
table->glfwSwapBuffers = glfwSwapBuffers;
table->glfwSwapInterval = glfwSwapInterval;
- table->glfwExtensionSupported = glfwExtensionSupported;
- table->glfwGetProcAddress = glfwGetProcAddress;
+ table->glfwTerminate = glfwTerminate;
+ table->glfwUpdateGamePadMappings = glfwUpdateGamepadMappings;
table->glfwVulkanSupported = glfwVulkanSupported;
- table->glfwGetRequiredInstanceExtensions = glfwGetRequiredInstanceExtensions;
+ table->glfwWaitEvents = glfwWaitEvents;
+ table->glfwWaitEventsTimeout = glfwWaitEventsTimeout;
+ table->glfwWindowHint = glfwWindowHint;
+ table->glfwWindowHintString = glfwWindowHintString;
+ table->glfwWindowShouldClose = glfwWindowShouldClose;
}
diff --git a/StgSharpDebug/MatrixParallelThreadTest.cs b/StgSharpDebug/MatrixParallelThreadTest.cs
deleted file mode 100644
index 1027cc09..00000000
--- a/StgSharpDebug/MatrixParallelThreadTest.cs
+++ /dev/null
@@ -1,116 +0,0 @@
-//-----------------------------------------------------------------------
-// -----------------------------------------------------------------------
-// file="MatrixParallelThreadTest"
-// Project: StgSharp
-// AuthorGroup: Nitload Space
-// Copyright (c) Nitload Space. 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 StgSharp.Mathematics;
-using System;
-using System.Threading;
-
-namespace StgSharp
-{
- /*
- ///
- /// 矩阵计算并发调度器线程调度测试 修复的问题: 1. 添加了ResetThreadCount()方法,在每轮任务调度前重置线程计数 2.
- /// 调度器线程不再递减RemainThreadCount,只有工作线程才递减 3. 改进了控制台输出,显示更清晰的线程活动信息 使用方法:在Program.cs的Main方法中调用
- /// MatrixParallelThreadTest.TestMatrixParallelThreadScheduling(); 或者通过命令行运行程序时调用此方法
- ///
- public class MatrixParallelThreadTest
- {
-
- ///
- /// 测试矩阵并行计算线程调度
- ///
- public static void TestMatrixParallelThreadScheduling()
- {
- Console.WriteLine("=== 矩阵并行计算线程调度测试开始 ===");
- Console.WriteLine($"CPU核心数: {Environment.ProcessorCount}");
- Console.WriteLine($"支持并行计算: {MatrixParallel.SupportParallel}");
- Console.WriteLine($"并行计算有意义: {MatrixParallel.IsParallelMeaningful}");
-
- if (!MatrixParallel.SupportParallel)
- {
- Console.WriteLine("当前系统不支持并行计算,跳过测试");
- return;
- }
-
- try
- {
- // 初始化矩阵并行计算系统
- Console.WriteLine("\n--- 初始化矩阵并行计算系统 ---");
- MatrixParallel.Init();
- Console.WriteLine("矩阵并行计算系统初始化完成");
-
- // 启动并行计算
- Console.WriteLine("\n--- 启动并行计算 ---");
- MatrixParallel.LaunchParallel(SleepMode.QuickWakeup);
- Console.WriteLine("并行计算已启动");
-
- // 获取领导者句柄
- MatrixParallel.MatrixParallelWrap.Handle leaderHandle = MatrixParallel.LeaderHandle;
- Console.WriteLine($"领导者句柄获取成功: Wrap ID {leaderHandle.Wrap.ID}");
-
- // 模拟一些任务调度
- Console.WriteLine("\n--- 开始模拟任务调度 ---");
- for (int i = 0; i < 3; i++)
- {
- Console.WriteLine($"\n第 {i + 1} 轮任务调度:");
-
- // 开始并行计算
- MatrixParallel.LeadParallel();
- Console.WriteLine("已获取并行计算锁");
-
- // 设置所有Wrap
- MatrixParallel.SetAllWrap();
- Console.WriteLine("已通知所有Wrap开始工作");
-
- // 发布任务(当前为空实现)
- // MatrixParallel.PublishTask();
- Console.WriteLine("任务发布完成");
-
- // 等待一段时间让线程执行
- Thread.Sleep(100);
-
- // 结束并行计算
- MatrixParallel.EndParallel();
- Console.WriteLine("已释放并行计算锁");
-
- // 等待一段时间再进行下一轮
- Thread.Sleep(200);
- }
-
- Console.WriteLine("\n--- 测试完成 ---");
- Console.WriteLine("线程调度测试已成功完成,请观察上述输出中的线程活动信息");
- }
- catch (Exception ex)
- {
- Console.WriteLine($"测试过程中发生异常: {ex.Message}");
- Console.WriteLine($"异常详情: {ex}");
- }
- }
-
- }
- /**/
-}
diff --git a/StgSharpDebug/Program.cs b/StgSharpDebug/Program.cs
index f80a4c49..b21578b7 100644
--- a/StgSharpDebug/Program.cs
+++ b/StgSharpDebug/Program.cs
@@ -62,7 +62,7 @@ namespace StgSharp
try
{
World.Initialize().UseModule().UseModule().InitializeModule();
-
+ TestRender();
/*
// New test with synchronized start:
// - 8 producer threads, each pushing 1000 unique integers
@@ -177,7 +177,7 @@ namespace StgSharp
// Sanity check
Console.WriteLine($"Produced: {producerThreads * perProducer}, Popped+Remaining: {popped.Count + remaining.Count}");
- /**/
+ /*
// 原有的矩阵测试代码
HybridLayerSegregatedFitAllocator hlsf = new HybridLayerSegregatedFitAllocator(64 * 400 * 400);
diff --git a/StgSharpDebug/RenderStream/Partical.cs b/StgSharpDebug/RenderStream/Partical.cs
index 324b0d81..50d82edc 100644
--- a/StgSharpDebug/RenderStream/Partical.cs
+++ b/StgSharpDebug/RenderStream/Partical.cs
@@ -196,10 +196,10 @@ namespace StgSharp
internal static Vec3 DefaultRotation(TimeSpanProvider provider, out float rotation)
{
- rotation = provider.SpanLengthLowPrecession;
- float angle = provider.CurrentSecond * 2;
+ rotation = 1.0f / 60;
+ float angle = (float)provider.CurrentSecond * 2;
return (new Vec3(Scalar.Cos(angle), Scalar.Sin(angle),
- 0)) * provider.SpanLengthLowPrecession;
+ 0)) * rotation;
}
protected override void CustomizeDeinit() { }
diff --git a/StgSharpDevelop.sln b/StgSharpDevelop.sln
index bb69833d..0b62d83e 100644
--- a/StgSharpDevelop.sln
+++ b/StgSharpDevelop.sln
@@ -55,8 +55,8 @@ Global
{610B992B-BB34-49C3-9993-676C595FC12E}.Release|x64.ActiveCfg = Release|x64
{20CBFF26-6746-402E-992C-7B643E9423EB}.Debug|ARM64.ActiveCfg = Debug|ARM64
{20CBFF26-6746-402E-992C-7B643E9423EB}.Debug|ARM64.Build.0 = Debug|ARM64
- {20CBFF26-6746-402E-992C-7B643E9423EB}.Debug|x64.ActiveCfg = Release|x64
- {20CBFF26-6746-402E-992C-7B643E9423EB}.Debug|x64.Build.0 = Release|x64
+ {20CBFF26-6746-402E-992C-7B643E9423EB}.Debug|x64.ActiveCfg = Debug|x64
+ {20CBFF26-6746-402E-992C-7B643E9423EB}.Debug|x64.Build.0 = Debug|x64
{20CBFF26-6746-402E-992C-7B643E9423EB}.Release|ARM64.ActiveCfg = Release|ARM64
{20CBFF26-6746-402E-992C-7B643E9423EB}.Release|ARM64.Build.0 = Release|ARM64
{20CBFF26-6746-402E-992C-7B643E9423EB}.Release|x64.ActiveCfg = Release|x64