using System.Text; using Dashboard.Drawing; using OpenTK.Graphics.OpenGL; namespace Dashboard.OpenGL.Drawing { public static class KhronosShaderHelper { public static int CompileStage(ShaderType type, string source, out string? log) { log = null; int shader = GL.CreateShader(type); GL.ShaderSource(shader, source); GL.CompileShader(shader); GL.GetShaderi(shader, ShaderParameterName.CompileStatus, out int flag); if (flag != 0) return shader; GL.GetShaderInfoLog(shader, out log); GL.DeleteShader(shader); return 0; } public static int LinkStages(ReadOnlySpan stages, out string? log) { log = null; int program = GL.CreateProgram(); foreach (int stage in stages) { GL.AttachShader(program, stage); } GL.LinkProgram(program); GL.GetProgrami(program, ProgramProperty.LinkStatus, out int flag); if (flag != 0) return program; GL.GetProgramInfoLog(program, out log); GL.DeleteProgram(program); return 0; } public static int CreateProgram(this GlslShaderCreateInfo glsl) { Span stages = stackalloc int[5]; int failed = 0; StringBuilder builder = new StringBuilder(); int count = 0; Compile(ShaderType.VertexShader, glsl.VertexShader, "Vertex", stages); Compile(ShaderType.FragmentShader, glsl.FragmentShader, "Fragment", stages); Compile(ShaderType.GeometryShader, glsl.GeometryShader, "Geometry", stages); Compile(ShaderType.TessControlShader, glsl.TesselationControl, "Tesselation Control", stages); Compile(ShaderType.TessEvaluationShader, glsl.TesselationEvaluation, "Tesselation Evaluation", stages); if (failed > 0) { throw new Exception($"{failed} shader stage(s) failed to compile. See the exception data for details.") { Data = { ["Log"] = builder.ToString(), }, }; } int program = KhronosShaderHelper.LinkStages(stages[..count], out string? log); if (program == 0) { throw new Exception($"Shader program failed to link. See the exception data for details.") { Data = { ["Log"] = log, }, }; } return program; void Compile(ShaderType type, string? source, string name, Span stages) { if (source == null) return; int stage = CompileStage(type, source, out string? log); if (stage == 0) { failed++; builder.Append($"=== {name} Shader Compile Log Begin ===\n{log}\n=== {name} Shader Compile Log End ===\n\n"); } else { stages[count++] = stage; } } } public static int CreateProgram(this GlslComputeShaderCreateInfo glsl) { int compute = CompileStage(ShaderType.ComputeShader, glsl.Source, out string? log); if (compute == 0) { throw new Exception("Failed to compile compute shader. See the exception data for details.") { Data = { ["Log"] = log, }, }; } int program = KhronosShaderHelper.LinkStages([compute], out log); if (program != 0) return program; throw new Exception("Failed to link compute shader. See the exception data for details.") { Data = { ["Log"] = log, }, }; } public static int CreateProgram(this SpirvShaderCreateInfo spirv) { int program = GL.CreateProgram(); GL.ProgramBinary(program, (All)spirv.Format, spirv.Binary, spirv.Binary.Length); GL.GetProgrami(program, ProgramProperty.LinkStatus, out int flag); if (flag == 0) return program; GL.GetProgramInfoLog(program, out string? log); GL.DeleteProgram(program); throw new Exception("Failed to load program binary. See the exception data for details.") { Data = { ["Log"] = log, }, }; } } }