using StgSharp; using StgSharp.Controlling; using StgSharp.Logic; using StgSharp.Math; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Principal; using System.Text; using System.Threading; namespace StgSharp.Graphics { /// /// /// public abstract partial class Form { #region metadata internal LinkedList allControl; internal Vector4 backgroundColor; internal unsafe GLcontext* contextID; internal DynamicArrayList formBuffer; internal IglFunc gl; internal int height; internal IntPtr monitor; internal string name; internal Form sharedForm; internal int width; internal IntPtr windowID; #endregion metadata #region form thread setting internal FrameUpdateTimer timer = new FrameUpdateTimer(); private SemaphoreSlim _renderFinishSignal = new SemaphoreSlim(0, 1); private SemaphoreSlim _renderStartSignal = new SemaphoreSlim(1, 1); private bool isSingleThread = true; #endregion form thread setting #region glapicount private int missingAPIcount = 0; private int problemAPIcount = 0; #endregion glapicount protected Form() { } unsafe ~Form() { Marshal.FreeHGlobal((IntPtr)contextID); } /// /// OpenGL package used by current form context. /// public IglFunc GL { get => gl; internal set => gl = value; } /// /// Height of this form, represent in /// public int Height { get => height; set => height = value; } /// /// Representing /// public bool IsDoubleRendering { get { return isSingleThread; } } private int _shouldClose = 0; public bool ShouldClose { get { Interlocked.Exchange(ref _shouldClose, InternalIO.glfwWindowShouldClose(windowID)); return 0 != _shouldClose; } set { InternalIO.glfwSetWindowShouldClose(windowID, value ? 1 : 0); Interlocked.Exchange(ref _shouldClose, value ? 1 : 0); } } /// /// Width of this form, represent in /// public Vec2d Size { get { return new Vec2d(width, height); } set { width = (int)value.X; height = (int)value.Y; } } /// /// Width of this form, represent in /// public int Width { get => width; set => width = value; } internal bool IsSingleThread { get { return isSingleThread; } } internal SemaphoreSlim RenderFinishSignal { get { return _renderFinishSignal; } set { _renderFinishSignal = value; } } internal SemaphoreSlim RenderStrartSignal { get { return _renderStartSignal; } set { _renderStartSignal = value; } } protected FrameBufferSizeHandler FrameBufferSizeCallback { set { IntPtr handlerPtr = Marshal.GetFunctionPointerForDelegate(value); InternalIO.glfwSetFramebufferSizeCallback(windowID, handlerPtr); } } /// /// Init an OpenGl context if current form has not been binded to any context. /// and set the context binded to current form as current OpenGL context. /// /// Something goes wrong and /// StgSharp fails in creating a new OpenGL context. public unsafe void MakeAsCurrentContext() { if (windowID == default) { IntPtr windowPtr; byte[] tittleBytes = Encoding.UTF8.GetBytes(name); if (sharedForm == null) { windowPtr = InternalIO.glfwCreateWindow( width, height, tittleBytes, monitor, IntPtr.Zero); } else { windowPtr = InternalIO.glfwCreateWindow( width, height, tittleBytes, monitor, sharedForm.windowID); } if ((long)windowPtr == -1 || (long)windowPtr == -2) { throw new Exception("Cannot create window instance!"); } else { windowID = windowPtr; } IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(GLcontext))); contextID = (GLcontext*)ptr; *contextID = new GLcontext(); contextID->glGetString = (delegate*)IntPtr.Zero; gl = new GLonRender(contextID); } InternalIO.glfwMakeContextCurrent(windowID); gl.SwitchContext(); } /// /// Set all necessary metadata to current form instance /// public void SetMetadata(int width, int height, string name, IntPtr monitor, Form sharedForm) { this.width = width; this.height = height; this.name = name; this.sharedForm = sharedForm; this.monitor = monitor; } /// /// Resize the current form /// /// New width of the window /// New height of the window public void SetSize(int width, int height) { this.Width = width; this.Height = height; InternalIO.glfwSetWindowSize(this.windowID, width, height); } /// /// Resize the current form /// /// /// A (width,height) representing the new size of current form /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetSize(Vec2d size) { this.Size = size; InternalIO.glfwSetWindowSize(this.windowID, (int)size.X, (int)size.Y); } /// /// Set the size limit of current form /// /// Minimum width of current form /// Minimum height of current form /// Maximum width of curent form /// Maximum height of current form [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetSizeLimit(int minWidth, int minHeight, int maxWidth, int maxHeight) { InternalIO.glfwSetWindowSizeLimits( this.windowID, minWidth, minHeight, maxWidth, maxHeight); } /// /// Set the size limit of current form /// /// /// Minimun size of current form. In form of a of (width,height) /// /// /// Maximum size of current form. In form of a of (width,height) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetSizeLimit(Vec2d minSize, Vec2d maxSize) { InternalIO.glfwSetWindowSizeLimits( this.windowID, (int)minSize.X, (int)minSize.Y, (int)maxSize.X, (int)maxSize.Y); } #region form main private static unsafe delegate* pollEventPtr = &InternalIO.glfwPollEvents; private static unsafe delegate* swapBufferPtr = &InternalSwapBuffers; public abstract void Deinit(params object[] args); /// /// Necessary operation to init this form. /// /// public abstract void Init(params object[] args); /// /// Behaviour of current form when in a render cycle. /// When this form /// /// public abstract void Main(params object[] args); public unsafe void PollEvents() { if (isSingleThread) { InternalIO.glfwPollEvents(); } else { gl.frameworkHandleEnqueue((IntPtr)pollEventPtr); } } /// /// Init this form and render it on screen, and respond with operation /// public unsafe void Show(params object[] args) { if (gl == null) { InternalIO.InternalWriteLog($"Attempt to show form {name} before init", LogType.Error); } else if (isSingleThread) { while (!ShouldClose) { Main(args); } } else { gl = new GLonUpdate(contextID); Thread renderThread = new Thread(new ThreadStart(InternalRender)); renderThread.Start(); int a = 0; while (!ShouldClose) { if (gl.FrameCanUpdate) { Main(args); } } //TODO 对逻辑线程和渲染线程进行同步,两种gl模式包需要改名字 renderThread.Join(); //Thread.Sleep(1000 * 20); //ShouldClose = true; //(gl as GLonUpdate).GLactionQueue.Clear(); } } public unsafe void SwapBuffers() { if (isSingleThread) { InternalIO.glfwSwapBuffers(windowID); } else { M128 m = new M128(); m.Write(0, (ulong)windowID); gl.frameworkHandleEnqueue((IntPtr)swapBufferPtr, m); } } internal static unsafe void InternalSwapBuffers() { IntPtr winHandle = default; GLonUpdate.ParamQueue.TryDequeue(out winHandle); InternalIO.glfwSwapBuffers(winHandle); } internal void InternalDeinit(params object[] args) { Deinit(args); } internal void InternalInit(params object[] args) { Init(args); } /// /// Get OpenGL library of this form's context. /// /// A package contains all opengl APIs protected IglFunc GetGL() { if (Platform.API == GraphicAPI.GL) { return this.GL; } else { InternalIO.InternalWriteLog("Attempt to get OpenGL API package in other graphic envitronment.", LogType.Error); throw new InvalidOperationException("Attempt to get OpenGL API package in other graphic envitronment."); } } /// /// Define how current form render in one frame. /// /// /// Please define this befor calling any OpenGL api and other graphic methods. /// protected unsafe void SetRenderThreads(int leastThreds) { if (gl != default) { throw new InvalidOperationException("Cannot define threads after loading gl context."); } if (leastThreds == 1) { isSingleThread = true; } else if (leastThreds >= 2) { isSingleThread = false; } else { throw new ArgumentException("Amount of threads should be bigger than 0"); } IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(GLcontext))); contextID = (GLcontext*)ptr; *contextID = new GLcontext(); contextID->glGetString = (delegate*)IntPtr.Zero; } private unsafe void InternalRender() { #pragma warning disable CS8602 ConcurrentQueue glActions = (gl as GLonUpdate).GLactionQueue; #pragma warning restore CS8602 IntPtr currentOperation; try { while (_shouldClose == 0) { if (glActions.TryDequeue(out currentOperation)) { ((delegate*)currentOperation)(); } } } catch (Exception e) { this.ShouldClose = true; throw; } } #endregion form main } }