using System.Drawing; using OpenTK.Graphics.OpenGL; namespace Dashboard.Drawing.OpenGL { public class ContextExecutor : IDisposable { public GLEngine Engine { get; } public IGLContext Context { get; } public ContextResourcePool ResourcePool { get; } protected bool IsDisposed { get; private set; } = false; public bool IsInitialized { get; private set; } = false; private int _program = 0; public ContextExecutor(GLEngine engine, IGLContext context) { Engine = engine; Context = context; ResourcePool = Engine.ResourcePoolManager.Get(context); ResourcePool.IncrementReference(); } ~ContextExecutor() { DisposeInvoker(false); } public void Initialize() { if (IsInitialized) return; IsInitialized = true; LoadShaders(); } private void LoadShaders() { using Stream vsource = FetchEmbeddedResource("Dashboard.Drawing.OpenGL.default.vert"); using Stream fsource = FetchEmbeddedResource("Dashboard.Drawing.OpenGL.default.frag"); int vs = CompileShader(ShaderType.VertexShader, vsource); int fs = CompileShader(ShaderType.FragmentShader, fsource); _program = LinkProgram(vs, fs); GL.DeleteShader(vs); GL.DeleteShader(fs); } public void Draw(DrawQueue drawqueue) => Draw(drawqueue, new RectangleF(new PointF(0f,0f), Context.FramebufferSize)); public virtual void Draw(DrawQueue drawQueue, RectangleF bounds) { } public virtual void EndFrame() { } private void DisposeInvoker(bool disposing) { if (!IsDisposed) return; IsDisposed = true; if (disposing) GC.SuppressFinalize(this); Dispose(disposing); } protected virtual void Dispose(bool disposing) { ContextCollector.Global.DeleteProgram(_program); } public void Dispose() => DisposeInvoker(true); private static int CompileShader(ShaderType type, string source) { int shader = GL.CreateShader(type); GL.ShaderSource(shader, source); GL.CompileShader(shader); int compileStatus = 0; GL.GetShaderi(shader, ShaderParameterName.CompileStatus, out compileStatus); if (compileStatus == 0) { GL.GetShaderInfoLog(shader, out string log); GL.DeleteShader(shader); throw new Exception($"{type} Shader compilation failed: " + log); } return shader; } private static int CompileShader(ShaderType type, Stream stream) { using StreamReader reader = new StreamReader(stream, leaveOpen: true); return CompileShader(type, reader.ReadToEnd()); } private static int LinkProgram(int s1, int s2) { int program = GL.CreateProgram(); GL.AttachShader(program, s1); GL.AttachShader(program, s2); GL.LinkProgram(program); int linkStatus = 0; GL.GetProgrami(program, ProgramProperty.LinkStatus, out linkStatus); if (linkStatus == 0) { GL.GetProgramInfoLog(program, out string log); GL.DeleteProgram(program); throw new Exception("Shader program linking failed: " + log); } return program; } private static Stream FetchEmbeddedResource(string name) { return typeof(ContextExecutor).Assembly.GetManifestResourceStream(name)!; } } }