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 corruptionThe + /// 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