Files
stgsharp/源代码/StgSharp/Graphics/Form.cs
T

443 lines
14 KiB
C#

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
{
/// <summary>
///
/// </summary>
public abstract partial class Form
{
#region metadata
internal LinkedList<Control> allControl;
internal Vector4 backgroundColor;
internal unsafe GLcontext* contextID;
internal DynamicArrayList<IntPtr> 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);
}
/// <summary>
/// OpenGL package used by current form context.
/// </summary>
public IglFunc GL
{
get => gl;
internal set => gl = value;
}
/// <summary>
/// Height of this form, represent in <see cref="int"/>
/// </summary>
public int Height
{
get => height;
set => height = value;
}
/// <summary>
/// Representing
/// </summary>
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);
}
}
/// <summary>
/// Width of this form, represent in <see cref="int"/>
/// </summary>
public Vec2d Size
{
get { return new Vec2d(width, height); }
set
{
width = (int)value.X;
height = (int)value.Y;
}
}
/// <summary>
/// Width of this form, represent in <see cref="int"/>
/// </summary>
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);
}
}
/// <summary>
/// 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.
/// </summary>
/// <exception cref="Exception">Something goes wrong and
/// StgSharp fails in creating a new OpenGL context. </exception>
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*<uint, IntPtr>)IntPtr.Zero;
gl = new GLonRender(contextID);
}
InternalIO.glfwMakeContextCurrent(windowID);
gl.SwitchContext();
}
/// <summary>
/// Set all necessary metadata to current form instance
/// </summary>
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;
}
/// <summary>
/// Resize the current form
/// </summary>
/// <param name="width">New width of the window</param>
/// <param name="height">New height of the window</param>
public void SetSize(int width, int height)
{
this.Width = width;
this.Height = height;
InternalIO.glfwSetWindowSize(this.windowID, width, height);
}
/// <summary>
/// Resize the current form
/// </summary>
/// <param name="size">
/// A <see cref="Vec2d"/> (width,height) representing the new size of current form
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetSize(Vec2d size)
{
this.Size = size;
InternalIO.glfwSetWindowSize(this.windowID, (int)size.X, (int)size.Y);
}
/// <summary>
/// Set the size limit of current form
/// </summary>
/// <param name="minWidth">Minimum width of current form </param>
/// <param name="minHeight">Minimum height of current form</param>
/// <param name="maxWidth">Maximum width of curent form</param>
/// <param name="maxHeight">Maximum height of current form</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetSizeLimit(int minWidth, int minHeight, int maxWidth, int maxHeight)
{
InternalIO.glfwSetWindowSizeLimits(
this.windowID,
minWidth, minHeight, maxWidth, maxHeight);
}
/// <summary>
/// Set the size limit of current form
/// </summary>
/// <param name="minSize">
/// Minimun size of current form. In form of a <see cref="Vec2d"/> of (width,height)
/// </param>
/// <param name="maxSize">
/// Maximum size of current form. In form of a <see cref="Vec2d"/> of (width,height)
/// </param>
[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*<void> pollEventPtr = &InternalIO.glfwPollEvents;
private static unsafe delegate*<void> swapBufferPtr = &InternalSwapBuffers;
public abstract void Deinit(params object[] args);
/// <summary>
/// Necessary operation to init this form.
/// </summary>
/// <param name="args"></param>
public abstract void Init(params object[] args);
/// <summary>
/// Behaviour of current form when in a render cycle.
/// When this form
/// </summary>
/// <param name="args"></param>
public abstract void Main(params object[] args);
public unsafe void PollEvents()
{
if (isSingleThread)
{
InternalIO.glfwPollEvents();
}
else
{
gl.frameworkHandleEnqueue((IntPtr)pollEventPtr);
}
}
/// <summary>
/// Init this form and render it on screen, and respond with operation
/// </summary>
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<ulong>(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);
}
/// <summary>
/// Get OpenGL library of this form's context.
/// </summary>
/// <returns>A <see cref="IglFunc"/> package contains all opengl APIs </returns>
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.");
}
}
/// <summary>
/// Define how current form render in one frame.
/// </summary>
/// <param name="leastThreds">
/// Please define this befor calling any OpenGL api and other graphic methods.
/// </param>
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*<uint, IntPtr>)IntPtr.Zero;
}
private unsafe void InternalRender()
{
#pragma warning disable CS8602
ConcurrentQueue<IntPtr> glActions = (gl as GLonUpdate).GLactionQueue;
#pragma warning restore CS8602
IntPtr currentOperation;
try
{
while (_shouldClose == 0)
{
if (glActions.TryDequeue(out currentOperation))
{
((delegate*<void>)currentOperation)();
}
}
}
catch (Exception e)
{
this.ShouldClose = true;
throw;
}
}
#endregion form main
}
}