new simplified timer. and little other ops
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Build a <see cref="LinearBag{T}" /> from a read-only span. Capacity will exactly fit
|
||||
/// the input.
|
||||
/// </summary>
|
||||
public static LinearBag<T> FromSpan<T>(ReadOnlySpan<T> values)
|
||||
{
|
||||
if (values.IsEmpty || values.Length == 0) {
|
||||
if (values.IsEmpty) {
|
||||
return new LinearBag<T>(4);
|
||||
}
|
||||
LinearBag<T> bag = new(values.Length);
|
||||
@@ -47,12 +52,24 @@ namespace StgSharp.Collections
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Element type.
|
||||
/// </typeparam>
|
||||
[CollectionBuilder(typeof(LinearBagBuilder), nameof(LinearBagBuilder.FromSpan))]
|
||||
public class LinearBag<T>(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)];
|
||||
|
||||
/// <summary>
|
||||
/// Access element by index (0..Count-1). Throws if out of range.
|
||||
/// </summary>
|
||||
public T this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -68,10 +85,19 @@ namespace StgSharp.Collections
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Total allocated slots.
|
||||
/// </summary>
|
||||
public int Capacity => _items.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Number of currently stored elements (valid index range 0..Count-1).
|
||||
/// </summary>
|
||||
public int Count { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item (unordered). Expands internal array if needed.
|
||||
/// </summary>
|
||||
[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++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a span over the live portion of the bag (0..Count-1).
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<T> AsSpan()
|
||||
{
|
||||
return new Span<T>(_items, 0, Count);
|
||||
}
|
||||
public Span<T> AsSpan() => new Span<T>(_items, 0, Count);
|
||||
|
||||
/// <summary>
|
||||
/// Clears all elements and zeroes out references to avoid retention. Count becomes 0.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Clear()
|
||||
{
|
||||
@@ -95,18 +125,22 @@ namespace StgSharp.Collections
|
||||
Count = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fast clear: only resets Count (leaves underlying array unchanged). References are NOT
|
||||
/// released. Use only when you are certain old references can remain.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void FastClear()
|
||||
{
|
||||
// 只重置size,不清理数组内容
|
||||
Count = 0;
|
||||
}
|
||||
public void FastClear() => Count = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Removes the first occurrence of value (unordered). Does nothing if not found.
|
||||
/// </summary>
|
||||
public void Remove(T value)
|
||||
{
|
||||
EqualityComparer<T> comparer = EqualityComparer<T>.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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes element at index by swapping the last element into its slot (unless already
|
||||
/// last). Preserves O(1) removal at cost of ordering.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures capacity is at least <paramref name="min" />. Doubles size when growing.
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<TSource, TTarget>([NotNull]TSource source) where TSource: RenderStream
|
||||
where TTarget: RenderStream, new()
|
||||
public static TTarget ShareContextFrom<TSource, TTarget>([NotNull]TSource source) where TSource : RenderStream
|
||||
where TTarget : RenderStream, new()
|
||||
{
|
||||
TTarget target = new TTarget();
|
||||
target.Initialize(source.BindedViewPortContext, source._timeProvider);
|
||||
|
||||
@@ -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<byte> procName)
|
||||
{
|
||||
return glfwGetProcAddress(procName);
|
||||
fixed (byte* sPtr = procName) {
|
||||
return _glfw.glfwGetProcAddress(sPtr);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="glfwFunction"
|
||||
// file="glfw.function"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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);
|
||||
|
||||
@@ -84,6 +84,10 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove=".nuspec" />
|
||||
<None Remove="StgSharp.Common.deps.json" />
|
||||
<None Remove="StgSharp.Common.dll" />
|
||||
<None Remove="StgSharp.Common.pdb" />
|
||||
<None Remove="StgSharp.Common.xml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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 <see cref="MaxPermits" />. -
|
||||
/// Reducing <see cref="MaxPermits" /> does not revoke already issued permits; it only clamps
|
||||
/// future releases. - Striped MRES can reduce contention in highly parallel waits.
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Create a new variable-capacity semaphore.
|
||||
/// </summary>
|
||||
/// <param name="initialCount">
|
||||
/// Initial available permits (clamped to [0, maxPermits]).
|
||||
/// </param>
|
||||
/// <param name="maxPermits">
|
||||
/// Logical maximum permits (>= 1).
|
||||
/// </param>
|
||||
/// <param name="stripes">
|
||||
/// Optional stripe count for MRES (<= 0 to auto choose by CPU).
|
||||
/// </param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current number of available permits.
|
||||
/// </summary>
|
||||
public int CurrentCount => Volatile.Read(ref _current);
|
||||
|
||||
/// <summary>
|
||||
/// Logical maximum number of permits. Can be changed at runtime.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release a single permit if under <see cref="MaxPermits" />. Returns the previous
|
||||
/// count.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release multiple permits (capped by <see cref="MaxPermits" />). Returns the previous
|
||||
/// count.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to take a permit without blocking. Returns true if acquired.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Block until a permit is available, then consume one.
|
||||
/// </summary>
|
||||
[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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Block until a permit is available or canceled, then consume one.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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<TimeSpanProvider> _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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<float, float> 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<float, float> 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<float, float> func, float range) funcPair)
|
||||
{
|
||||
return new TimeFunction(_timeProvider, funcPair.func, funcPair.range);
|
||||
}
|
||||
|
||||
public virtual TimeFunction Renew(Func<float, float> 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<float, float> func, float range) funcPair)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override TimeFunction Renew(Func<float, float> func, float range)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
#pragma warning disable CA1822
|
||||
public override void Reset()
|
||||
{
|
||||
}
|
||||
#pragma warning restore CA1822
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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).
|
||||
/// </summary>
|
||||
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));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<int> 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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<int> s_unusedID = new ConcurrentBufferStack<int>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if there is at least one unconsumed span tick (PendingSpanCount > 0).
|
||||
/// </summary>
|
||||
public bool HasPendingSpan => PendingSpanCount > 0;
|
||||
|
||||
public int TokenID
|
||||
{
|
||||
get => _id;
|
||||
private set => _id = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of span ticks not yet consumed by Wait ( > 0 means last tick not waited).
|
||||
/// </summary>
|
||||
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.
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="currentTick">
|
||||
/// Tick passed since binded time source.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if the span provider is working. False if time is over (the sequence ended).
|
||||
/// </returns>
|
||||
[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();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public class TimeSpanProvider
|
||||
public partial class TimeSpanProvider
|
||||
{
|
||||
|
||||
private bool _subscribed;
|
||||
|
||||
private readonly ConcurrentQueue<TimeSpanAwaitingToken> _tokenToAdd = new();
|
||||
private readonly ConcurrentQueue<TimeSpanAwaitingToken> _tokenToQuit;
|
||||
private Counter<int> timeSpanCount = new(-1, 1, 1);
|
||||
private readonly HashSet<TimeSpanAwaitingToken> _awaitingTokens;
|
||||
private long _maxSpanCount;
|
||||
private long _spanBegin;
|
||||
private readonly long _spanLength;
|
||||
private int _emittedSpanCount; // legacy counter
|
||||
private readonly TimeSequence _sequence;
|
||||
private readonly TimeSourceProviderBase _timeSource;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="TimeSpanProvider" /> and define the length of time span.
|
||||
/// Length of a single time span is defined in microseconds.
|
||||
/// </summary>
|
||||
/// <param _label="spanMicroSeconds">
|
||||
///
|
||||
/// </param>
|
||||
/// <param _label="provider">
|
||||
///
|
||||
/// </param>
|
||||
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<TimeSpanAwaitingToken>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="TimeSpanProvider" /> and define the length of time span.
|
||||
/// Length of a single time span is defined in seconds.
|
||||
/// </summary>
|
||||
/// <param _label="spanSeconds">
|
||||
///
|
||||
/// </param>
|
||||
/// <param _label="provider">
|
||||
///
|
||||
/// </param>
|
||||
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<TimeSpanAwaitingToken>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="TimeSpanProvider" /> and define the length of time span.
|
||||
/// Length of a single time span is defined in microseconds.
|
||||
/// </summary>
|
||||
/// <param _label="spanLength">
|
||||
///
|
||||
/// </param>
|
||||
/// <param _label="maxSpan">
|
||||
///
|
||||
/// </param>
|
||||
/// <param _label="provider">
|
||||
///
|
||||
/// </param>
|
||||
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<TimeSpanAwaitingToken>();
|
||||
_tokenToQuit = new ConcurrentQueue<TimeSpanAwaitingToken>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Time source provider provided in <see cref="World" /> internal framework.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -44,13 +44,28 @@ namespace StgSharp.Mathematics.Numeric
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
internal static unsafe MatrixParallelHandle ScheduleTask<T>(MatrixParallelTaskPackage<T>* package)
|
||||
where T : unmanaged
|
||||
where T : unmanaged,INumber<T>
|
||||
{
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
+101
-100
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/*
|
||||
/// <summary>
|
||||
/// 矩阵计算并发调度器线程调度测试 修复的问题: 1. 添加了ResetThreadCount()方法,在每轮任务调度前重置线程计数 2.
|
||||
/// 调度器线程不再递减RemainThreadCount,只有工作线程才递减 3. 改进了控制台输出,显示更清晰的线程活动信息 使用方法:在Program.cs的Main方法中调用
|
||||
/// MatrixParallelThreadTest.TestMatrixParallelThreadScheduling(); 或者通过命令行运行程序时调用此方法
|
||||
/// </summary>
|
||||
public class MatrixParallelThreadTest
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 测试矩阵并行计算线程调度
|
||||
/// </summary>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/**/
|
||||
}
|
||||
@@ -62,7 +62,7 @@ namespace StgSharp
|
||||
try
|
||||
{
|
||||
World.Initialize().UseModule<NumericalModule>().UseModule<GraphicModule>().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);
|
||||
|
||||
@@ -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() { }
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user