using System; using System.IO; using System.Reflection; using Quik.OpenGL; using Quik.VertexGenerator; using Matrix4 = OpenTK.Mathematics.Matrix4; using static Quik.OpenGL.GLEnum; namespace Quik.OpenTK { public class GL30Driver : IDisposable { public GL30Driver() { Assembly asm = typeof(GL30Driver).Assembly; using (StreamReader vert = new StreamReader(asm.GetManifestResourceStream("Quik.OpenTK.glsl.glsl130.vert"))) using (StreamReader frag = new StreamReader(asm.GetManifestResourceStream("Quik.OpenTK.glsl.glsl130.frag"))) { int vs; int fs; vs = GL.CreateShader(GL_VERTEX_SHADER); fs = GL.CreateShader(GL_FRAGMENT_SHADER); _sp = GL.CreateProgram(); GL.ShaderSource(vs, vert.ReadToEnd()); GL.ShaderSource(fs, frag.ReadToEnd()); GL.CompileShader(vs); GL.CompileShader(fs); #if DEBUG int status; GL.GetShader(vs, GL_COMPILE_STATUS, out status); if (status == 0) { throw new Exception(GL.GetShaderInfoLog(vs)); } GL.GetShader(fs, GL_COMPILE_STATUS, out status); if (status == 0) { throw new Exception(GL.GetShaderInfoLog(fs)); } #endif GL.AttachShader(_sp, vs); GL.AttachShader(_sp, fs); GL.LinkProgram(_sp); #if DEBUG GL.GetProgram(_sp, GL_LINK_STATUS, out status); if (status == 0) { throw new Exception(GL.GetProgramInfoLog(_sp)); } #endif GL.DetachShader(_sp, vs); GL.DetachShader(_sp, fs); GL.DeleteShader(vs); GL.DeleteShader(fs); } LoadUniform(_nameM4View, out _locM4View); LoadUniform(_nameM4Model, out _locM4Model); LoadUniform(_nameIFlags, out _locIFlags); LoadUniform(_nameFSdfThreshold, out _locFSdfThreshold); LoadUniform(_nameFSdfAuxilliaryThreshold, out _locFSdfAuxilliaryThreshold); LoadUniform(_nameV4SdfAuxilliaryColor, out _locV4SdfAuxilliaryColor); LoadUniform(_nameX2Texture, out _locX2Texture); LoadUniform(_nameV2TextureOffset, out _locV2TextureOffset); LoadAttribute(_nameV2Postion, out _locV2Position); LoadAttribute(_nameV2Texture, out _locV2Texture); LoadAttribute(_nameV4Color, out _locV4Color); void LoadUniform(string name, out int location) { location = GL.GetUniformLocation(_sp, name); } void LoadAttribute(string name, out int location) { location = GL.GetAttribLocation(_sp, name); } _vbo = GL.GenBuffer(); _ebo = GL.GenBuffer(); _vao = GL.GenVertexArray(); } private readonly int _sp; private readonly int _vao; private readonly int _vbo; private readonly int _ebo; private const string _nameM4View = "m4View"; private readonly int _locM4View; private const string _nameM4Model = "m4Model"; private readonly int _locM4Model; private const string _nameV2Postion = "v2Position"; private readonly int _locV2Position; private const string _nameV2Texture = "v2Texture"; private readonly int _locV2Texture; private const string _nameV4Color = "v4Color"; private readonly int _locV4Color; private const string _nameIFlags = "iFlags"; private readonly int _locIFlags; private const string _nameFSdfThreshold = "fSdfThreshold"; private readonly int _locFSdfThreshold; private const string _nameFSdfAuxilliaryThreshold = "fSdfAuxilliaryThreshold"; private readonly int _locFSdfAuxilliaryThreshold; private const string _nameV4SdfAuxilliaryColor = "v4SdfAuxilliaryColor"; private readonly int _locV4SdfAuxilliaryColor; private const string _nameX2Texture = "x2Texture"; private readonly int _locX2Texture; private const string _nameV2TextureOffset = "v2TextureOffset"; private readonly int _locV2TextureOffset; [Flags] private enum Flags : int { Texture = 1 << 0, DiscardEnable = 1 << 1, Sdf = 1 << 2, SdfAuxEnable = 1 << 3, } public void Draw(DrawQueue queue) { GL.UseProgram(_sp); GL.BindVertexArray(_vao); GL.BindBuffer(GL_ARRAY_BUFFER, _vbo); GL.BufferData(GL_ARRAY_BUFFER, queue.VertexCount * QuikVertex.Stride, queue.VertexArray, GL_STREAM_DRAW); GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo); GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, queue.ElementCount * sizeof(int), queue.ElementArray, GL_STREAM_DRAW); GL.VertexAttribPointer(_locV2Position, 2, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.PositionOffset); GL.VertexAttribPointer(_locV2Texture, 2, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.TextureCoordinatesOffset); GL.VertexAttribPointer(_locV4Color, 4, GL_UNSIGNED_BYTE, false, QuikVertex.Stride, QuikVertex.ColorOffset); GL.EnableVertexAttribArray(_locV2Position); GL.EnableVertexAttribArray(_locV2Texture); GL.EnableVertexAttribArray(_locV4Color); GL.Enable(GL_BLEND); GL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); Matrix4 m = Matrix4.Identity; GL.UniformMatrix4(_locM4Model, false, ref m.Row0.X); foreach (DrawCall call in queue) { GL.BindTexture(GL_TEXTURE_2D, 0); m = Matrix4.CreateOrthographicOffCenter(0, call.Bounds.Right, 0, call.Bounds.Top, 1.0f, -1.0f); GL.UniformMatrix4(_locM4View, false, ref m.Row0.X); GL.DrawElements(GL_TRIANGLES, call.Count, GL_UNSIGNED_INT, call.Start); } } private bool _isDisposed = false; private void Dispose(bool disposing) { if (_isDisposed) return; if (disposing) { GL.DeleteProgram(_sp); } else { throw new Exception("OpenGL resource is leaked. Dispose unreferenced OpenGL objects in the context thread."); } _isDisposed = true; } public void Dispose() => Dispose(true); ~GL30Driver() { Dispose(false); } } }