We are going real old school GL.
This commit is contained in:
parent
1297365b38
commit
d72a07354a
283
Quik/OpenGL/GL21Driver.cs
Normal file
283
Quik/OpenGL/GL21Driver.cs
Normal file
@ -0,0 +1,283 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using Quik.VertexGenerator;
|
||||
using static Quik.OpenGL.GLEnum;
|
||||
|
||||
namespace Quik.OpenGL
|
||||
{
|
||||
public class GL21Driver : IDisposable
|
||||
{
|
||||
private int program;
|
||||
private int v2Position;
|
||||
private int iZIndex;
|
||||
private int v2TexPos;
|
||||
private int v4Color;
|
||||
private int m4Transforms;
|
||||
private int iMaxZ;
|
||||
private int iEnableSdf;
|
||||
private int iEnableTexture;
|
||||
private int iAlphaDiscard;
|
||||
private int fSdfThreshold;
|
||||
private int txTexture;
|
||||
private bool isDiposed;
|
||||
private readonly Dictionary<DrawQueue, DrawData> data = new Dictionary<DrawQueue, DrawData>();
|
||||
public bool IsInit { get; private set; } = false;
|
||||
public event Action<GL21Driver> OnGCDispose;
|
||||
|
||||
public GL21Driver()
|
||||
{
|
||||
}
|
||||
|
||||
~GL21Driver()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
if (IsInit) return;
|
||||
|
||||
int vs = CreateShader(GL_VERTEX_SHADER, "Quik.res.gl21.vert");
|
||||
int fs = CreateShader(GL_FRAGMENT_SHADER, "Quik.res.gl21.frag");
|
||||
|
||||
program = GL.CreateProgram();
|
||||
GL.AttachShader(program, vs);
|
||||
GL.AttachShader(program, fs);
|
||||
|
||||
GL.LinkProgram(program);
|
||||
|
||||
if (CheckProgram(program, out string msg) == false)
|
||||
{
|
||||
GraphicsException ex = new GraphicsException("Could not link shader program.");
|
||||
ex.Data.Add("Program Info Log", msg);
|
||||
}
|
||||
|
||||
GL.DeleteShader(vs);
|
||||
GL.DeleteShader(fs);
|
||||
|
||||
v2Position = GL.GetAttribLocation(program, nameof(v2Position));
|
||||
iZIndex = GL.GetAttribLocation(program, nameof(iZIndex));
|
||||
v2TexPos = GL.GetAttribLocation(program, nameof(v2TexPos));
|
||||
v4Color = GL.GetAttribLocation(program, nameof(v4Color));
|
||||
|
||||
m4Transforms = GL.GetUniformLocation(program, nameof(m4Transforms));
|
||||
iMaxZ = GL.GetUniformLocation(program, nameof(iMaxZ));
|
||||
iEnableSdf = GL.GetUniformLocation(program, nameof(iEnableSdf));
|
||||
iEnableTexture = GL.GetUniformLocation(program, nameof(iEnableSdf));
|
||||
iAlphaDiscard = GL.GetUniformLocation(program, nameof(iAlphaDiscard));
|
||||
txTexture = GL.GetUniformLocation(program, nameof(txTexture));
|
||||
|
||||
IsInit = true;
|
||||
}
|
||||
|
||||
private void AssertInit()
|
||||
{
|
||||
if (!IsInit) throw new InvalidOperationException("Initialize the driver first.");
|
||||
}
|
||||
|
||||
public void Draw(DrawQueue queue, in QRectangle view)
|
||||
{
|
||||
AssertInit();
|
||||
|
||||
if (data.TryGetValue(queue, out DrawData draw))
|
||||
{
|
||||
draw = new DrawData(this, queue);
|
||||
}
|
||||
|
||||
// This already binds the vertex array for me.
|
||||
draw.PrepareFrame();
|
||||
|
||||
QVec2 size = view.Size;
|
||||
// TODO: can i has matrices?
|
||||
float[] matrix = new float[] {
|
||||
1/size.X, 0, 0, 0,
|
||||
0, 1/size.Y, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
-0.5f, -0.5f, 0, 1
|
||||
};
|
||||
|
||||
GL.UseProgram(program);
|
||||
GL.Uniform1(iMaxZ, queue.ZDepth);
|
||||
GL.UniformMatrix4(m4Transforms, false, ref matrix[0]);
|
||||
GL.Uniform1(fSdfThreshold, 0.0f);
|
||||
GL.Uniform1(txTexture, 0);
|
||||
GL.Enable(GL_BLEND);
|
||||
GL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
foreach (DrawCall call in queue)
|
||||
{
|
||||
GL.ActiveTexture(GL_TEXTURE0);
|
||||
GL.BindTexture(GL_TEXTURE_2D, 0);
|
||||
if (call.Texture != null)
|
||||
{
|
||||
// TODO: actually use textures?
|
||||
GL.Uniform1(iEnableTexture, 1);
|
||||
GL.Uniform1(iEnableSdf, call.Texture.SignedDistanceField ? 1 : 0);
|
||||
GL.Uniform1(iAlphaDiscard, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.Uniform1(iEnableTexture, 0);
|
||||
}
|
||||
|
||||
GL.DrawArrays(GL_TRIANGLES, call.Count, call.Start);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearDrawQueue(DrawQueue queue)
|
||||
{
|
||||
AssertInit();
|
||||
|
||||
if (!data.TryGetValue(queue, out DrawData draw))
|
||||
return;
|
||||
draw.Dispose();
|
||||
data.Remove(queue);
|
||||
}
|
||||
|
||||
private static int CreateShader(GLEnum type, string name)
|
||||
{
|
||||
StreamReader source = new StreamReader(typeof(GL21Driver).Assembly.GetManifestResourceStream(name));
|
||||
string text = source.ReadToEnd();
|
||||
source.Dispose();
|
||||
|
||||
int shader = GL.CreateShader(type);
|
||||
GL.ShaderSource(shader, text);
|
||||
|
||||
if (CheckShader(shader, out string msg) == false)
|
||||
{
|
||||
GraphicsException ex = new GraphicsException($"Failed to compile {type} shader stage.");
|
||||
ex.Data.Add("Shader Stage", type);
|
||||
ex.Data.Add("Shader Info Log", msg);
|
||||
ex.Data.Add("Shader Source", text);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
private static bool CheckShader(int shader, out string message)
|
||||
{
|
||||
message = string.Empty;
|
||||
|
||||
GL.GetShader(shader, GL_COMPILE_STATUS, out int i);
|
||||
|
||||
if (i != (int)GL_OK)
|
||||
{
|
||||
message = GL.GetShaderInfoLog(shader);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CheckProgram(int program, out string message)
|
||||
{
|
||||
message = string.Empty;
|
||||
|
||||
GL.GetProgram(program, GL_LINK_STATUS, out int i);
|
||||
|
||||
if (i != (int)GL_OK)
|
||||
{
|
||||
message = GL.GetProgramInfoLog(program);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (isDiposed) return;
|
||||
|
||||
if (!IsInit)
|
||||
{
|
||||
isDiposed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!disposing)
|
||||
{
|
||||
if (OnGCDispose == null)
|
||||
{
|
||||
throw new Exception("This object must strictly be disposed from the owning thread, not GC");
|
||||
}
|
||||
else
|
||||
{
|
||||
OnGCDispose(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GL.DeleteProgram(program);
|
||||
|
||||
isDiposed = true;
|
||||
}
|
||||
|
||||
public void Dispose() => Dispose(true);
|
||||
|
||||
private struct DrawData : IDisposable
|
||||
{
|
||||
public DrawQueue Queue { get; }
|
||||
public int VertexArray { get; }
|
||||
|
||||
private readonly GL21Driver driver;
|
||||
private int vbo1, vbo2;
|
||||
private int ebo1, ebo2;
|
||||
|
||||
public DrawData(GL21Driver driver, DrawQueue queue)
|
||||
{
|
||||
int a = 0, b = 0, c = 0, d = 0;
|
||||
Queue = queue;
|
||||
this.driver = driver;
|
||||
VertexArray = GL.GenVertexArray();
|
||||
GL.GenBuffers(4, out a);
|
||||
vbo1 = a;
|
||||
vbo2 = b;
|
||||
ebo1 = c;
|
||||
ebo2 = d;
|
||||
isDisposed = false;
|
||||
}
|
||||
|
||||
public void PrepareFrame()
|
||||
{
|
||||
int vbo, ebo;
|
||||
vbo = Swap(ref vbo1, ref vbo2);
|
||||
ebo = Swap(ref ebo1, ref ebo2);
|
||||
|
||||
GL.BindVertexArray(VertexArray);
|
||||
GL.BindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
GL.BufferData(GL_ARRAY_BUFFER, QuikVertex.Stride * Queue.VertexCount, Queue.VertexArray, GL_STREAM_DRAW);
|
||||
|
||||
GL.VertexAttribPointer(driver.v2Position, 2, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.PositionOffset);
|
||||
GL.VertexAttribIPointer(driver.iZIndex, 1, GL_UNSIGNED_INT, QuikVertex.Stride, QuikVertex.ZIndexOffset);
|
||||
GL.VertexAttribPointer(driver.v2TexPos, 2, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.TextureCoordinatesOffset);
|
||||
GL.VertexAttribPointer(driver.v4Color, 4, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.ColorOffset);
|
||||
|
||||
GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, Queue.ElementCount * sizeof(int), Queue.ElementArray, GL_STREAM_DRAW);
|
||||
|
||||
int Swap(ref int a, ref int b)
|
||||
{
|
||||
a ^= b;
|
||||
b ^= a;
|
||||
a ^= b;
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
private bool isDisposed;
|
||||
public void Dispose()
|
||||
{
|
||||
if (isDisposed) return;
|
||||
|
||||
GL.DeleteVertexArray(VertexArray);
|
||||
GL.DeleteBuffer(vbo1);
|
||||
GL.DeleteBuffer(vbo2);
|
||||
GL.DeleteBuffer(ebo1);
|
||||
GL.DeleteBuffer(ebo2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
46
Quik/OpenGL/GraphicsException.cs
Normal file
46
Quik/OpenGL/GraphicsException.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using static Quik.OpenGL.GLEnum;
|
||||
|
||||
namespace Quik.OpenGL
|
||||
{
|
||||
[System.Serializable]
|
||||
public class GraphicsException : System.Exception
|
||||
{
|
||||
public GraphicsException()
|
||||
{
|
||||
AddExtraData();
|
||||
}
|
||||
|
||||
public GraphicsException(string message) : base(message)
|
||||
{
|
||||
AddExtraData();
|
||||
}
|
||||
|
||||
public GraphicsException(string message, System.Exception inner) : base(message, inner)
|
||||
{
|
||||
AddExtraData();
|
||||
}
|
||||
|
||||
protected GraphicsException(
|
||||
System.Runtime.Serialization.SerializationInfo info,
|
||||
System.Runtime.Serialization.StreamingContext context) : base(info, context)
|
||||
{
|
||||
AddExtraData();
|
||||
}
|
||||
|
||||
private void AddExtraData()
|
||||
{
|
||||
GL.Get(GL_MAJOR_VERSION, out int major);
|
||||
GL.Get(GL_MINOR_VERSION, out int minor);
|
||||
|
||||
string version = GL.GetString(GL_VERSION);
|
||||
string vendor = GL.GetString(GL_VENDOR);
|
||||
string renderer = GL.GetString(GL_RENDERER);
|
||||
|
||||
Data.Add("OpenGL Version", new Version(major, minor));
|
||||
Data.Add("OpenGL Version String", version);
|
||||
Data.Add("OpenGL Vendor", vendor);
|
||||
Data.Add("OpenGL Renderer", renderer);
|
||||
}
|
||||
}
|
||||
}
|
40
Quik/res/gl21.frag
Normal file
40
Quik/res/gl21.frag
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* QUIK: User Interface Kit
|
||||
* Copyright (C) 2023 Halit Utku Maden, et al.
|
||||
*/
|
||||
#version 110
|
||||
|
||||
varying vec2 fv2TexPos;
|
||||
varying vec4 fv4Color;
|
||||
|
||||
uniform int iEnableSdf;
|
||||
uniform int iEnableTexture;
|
||||
uniform int iAlphaDiscard;
|
||||
uniform float fSdfThreshold;
|
||||
uniform sampler2D txTexture;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec4 albedo = fv4Color;
|
||||
|
||||
if (iEnableTexture != 0)
|
||||
{
|
||||
vec4 value = texture2D(txTexture, fv2TexPos);
|
||||
|
||||
if (iEnableSdf != 0)
|
||||
{
|
||||
float a = max(value.r, value.a);
|
||||
|
||||
value = (a >= fSdfThreshold) ? vec4(1,1,1,1) : vec4(0,0,0,0);
|
||||
}
|
||||
|
||||
if (iAlphaDiscard != 0 && value.a == 0.0)
|
||||
{
|
||||
discard;
|
||||
}
|
||||
|
||||
albedo = albedo * value;
|
||||
}
|
||||
|
||||
gl_FragColor = albedo;
|
||||
}
|
25
Quik/res/gl21.vert
Normal file
25
Quik/res/gl21.vert
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* QUIK: User Interface Kit
|
||||
* Copyright (C) 2023 Halit Utku Maden, et al.
|
||||
*/
|
||||
#version 110
|
||||
|
||||
attribute vec2 v2Position; /**< The vertex position.*/
|
||||
attribute int iZIndex; /**< The z index. */
|
||||
attribute vec2 v2TexPos; /**< The texture coorindates. */
|
||||
attribute vec4 v4Color; /**< The vertex color. */
|
||||
|
||||
varying vec2 fv2TexPos;
|
||||
varying vec4 fv4Color;
|
||||
|
||||
uniform mat4 m4Transforms; /**< The view matrix. */
|
||||
uniform int iMaxZ; /**< Highest Z coordinate. */
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec4 v = vec4(v2Position, float(iZIndex)/float(iMaxZ), 1);
|
||||
gl_Position = v * m4Transforms;
|
||||
|
||||
fv2TexPos = v2TexPos;
|
||||
fv4Color = fv4Color;
|
||||
}
|
Loading…
Reference in New Issue
Block a user