From 9365e82cbfb1094c00f08b92b0a3648751cb946f Mon Sep 17 00:00:00 2001 From: daniel Date: Sat, 3 Jan 2026 22:42:44 +0100 Subject: [PATCH] layed groundwork for executing compute shaders --- .../EngineSharp.Core/OpenGLEngine.cs | 14 ++- .../Rendering/ComputeShader.cs | 91 +++++++++++++++++++ .../EngineSharp.Core/Rendering/Engine.cs | 7 ++ src/EngineSharp/Program.cs | 5 + .../assets/shaders/planet_generator.comp | 5 + 5 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 src/EngineSharp.Core/EngineSharp.Core/Rendering/ComputeShader.cs create mode 100644 src/EngineSharp/assets/shaders/planet_generator.comp diff --git a/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs b/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs index b630469..67113fc 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/OpenGLEngine.cs @@ -49,8 +49,9 @@ internal class OpenGLEngine : Engine public override InputService GetInputService() => _inputService; - private void InitialiseShaders(IEnumerable vertexShaderFiles) + private void InitialiseShaders() { + var vertexShaderFiles = Directory.GetFiles("./assets/shaders", "*.vert"); foreach (var vertexShaderFile in vertexShaderFiles) { var fragmentShaderFile = vertexShaderFile.Replace(".vert", ".frag"); @@ -59,6 +60,15 @@ internal class OpenGLEngine : Engine shader.Initialize(_gl); _shaders.Add(vertexShaderFile, shader); } + + var computeShaderFiles = Directory.GetFiles("./assets/shaders", "*.comp"); + foreach (var computeShaderFile in computeShaderFiles) + { + var shader = new ComputeShader(computeShaderFile); + + shader.Initialize(_gl); + _computeShaders.Add(computeShaderFile, shader); + } } internal override Scene CreateScene(string name) @@ -100,7 +110,7 @@ internal class OpenGLEngine : Engine _camera = new PerspectiveCamera(new Vector3D(0.0f, 0.0f, 3.0f), _window.FramebufferSize); - InitialiseShaders(Directory.GetFiles("./assets/shaders", "*.vert")); + InitialiseShaders(); SetCurrentScene(_defaultSceneName); } diff --git a/src/EngineSharp.Core/EngineSharp.Core/Rendering/ComputeShader.cs b/src/EngineSharp.Core/EngineSharp.Core/Rendering/ComputeShader.cs new file mode 100644 index 0000000..ddc64ff --- /dev/null +++ b/src/EngineSharp.Core/EngineSharp.Core/Rendering/ComputeShader.cs @@ -0,0 +1,91 @@ +using System.Diagnostics.CodeAnalysis; +using EngineSharp.Core.Rendering.Exceptions; +using Silk.NET.OpenGL; + +namespace EngineSharp.Core.Rendering; + +// https://learnopengl.com/Guest-Articles/2022/Compute-Shaders/Introduction +public class ComputeShader +{ + private readonly string _computeShaderCode; + + private GL? _gl; // not as a variable. In the future it should be possible to compile the shaders beforehand. Right now, all shaders are JIT compiled which isn't great + private uint? _shaderProgramId; + + internal ComputeShader(string pathToComputeShader) + { + _computeShaderCode = File.ReadAllText(pathToComputeShader); + } + + public void Execute(uint numGroupsX = 1, uint numGroupsY = 1, uint numGroupsZ = 1) + { + Use(); + _gl!.DispatchCompute(numGroupsX, numGroupsY, numGroupsZ); + } + + // TODO: Add public SetFloat(...) etc. + + /// + /// Initialises the compute shader, by compiling it and making it ready to be used in the application.
+ /// Only initialises the first time it is called. Is a NoOp every subsequent call. + ///
+ /// + internal void Initialize(GL glContext) + { + if (_gl is not null || _shaderProgramId is not null) + { + return; + } + + _gl = glContext; + var computeShaderId = CompileShader(_computeShaderCode); + + _shaderProgramId = CreateProgram(computeShaderId); + } + + /// + /// Can only be called after has been called. + /// + private void Use() + { + if (_shaderProgramId is null || _gl is null) + { + throw new InvalidOperationException("This shader has not been initialized and can therefore not be used."); + } + + _gl.UseProgram(_shaderProgramId.Value); + } + + private uint CompileShader(string shaderCode) + { + var shader = _gl.CreateShader(ShaderType.ComputeShader); + _gl.ShaderSource(shader, shaderCode); + _gl.CompileShader(shader); + + _gl.GetShader(shader, ShaderParameterName.CompileStatus, out var status); + + if (status != (int)GLEnum.True) + { + throw new ShaderCompileException(ShaderType.ComputeShader, $"Failed to compile shader with message: \n {_gl.GetShaderInfoLog(shader)}"); + } + + return shader; + } + + private uint CreateProgram(uint shaderId) + { + var program = _gl.CreateProgram(); + _gl.AttachShader(program, shaderId); + _gl.LinkProgram(program); + + _gl.GetProgram(program, ProgramPropertyARB.LinkStatus, out var status); + if (status != (int)GLEnum.True) + throw new ShaderLinkException("Error occured while trying to link a shader with message: \n" + _gl.GetProgramInfoLog(program)); + + _gl.DetachShader(program, shaderId); + _gl.DeleteShader(shaderId); + + return program; + } + +} \ No newline at end of file diff --git a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Engine.cs b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Engine.cs index e8a37d4..45149e4 100644 --- a/src/EngineSharp.Core/EngineSharp.Core/Rendering/Engine.cs +++ b/src/EngineSharp.Core/EngineSharp.Core/Rendering/Engine.cs @@ -13,6 +13,7 @@ internal abstract class Engine : IEngine } protected readonly Dictionary _shaders = []; + protected readonly Dictionary _computeShaders = []; protected string? _defaultSceneName; public static Engine Instance => SingletonInstance ?? throw new InvalidOperationException("No engine has been created yet. Cannot return an instance."); @@ -42,5 +43,11 @@ internal abstract class Engine : IEngine Instance._shaders.TryGetValue(vertexShaderPath, out var shader); return shader ?? throw new ArgumentException($"Shader '{vertexShaderPath}' does not exist."); } + + public ComputeShader GetComputeShader(string computeShaderPath) + { + Instance._computeShaders.TryGetValue(computeShaderPath, out var shader); + return shader ?? throw new ArgumentException($"Shader '{computeShaderPath}' does not exist."); + } } \ No newline at end of file diff --git a/src/EngineSharp/Program.cs b/src/EngineSharp/Program.cs index 7314c17..cb86c64 100644 --- a/src/EngineSharp/Program.cs +++ b/src/EngineSharp/Program.cs @@ -14,6 +14,11 @@ internal static class Program { Title = "EngineSharp Engine", Size = new Vector2D(800, 600), + API = new Silk.NET.Windowing.GraphicsAPI + { + API = ContextAPI.OpenGL, + Version = new APIVersion(4, 3), + }, }; var engine = EngineFactory.Create(GraphicsAPI.OpenGL, options); diff --git a/src/EngineSharp/assets/shaders/planet_generator.comp b/src/EngineSharp/assets/shaders/planet_generator.comp new file mode 100644 index 0000000..5419c04 --- /dev/null +++ b/src/EngineSharp/assets/shaders/planet_generator.comp @@ -0,0 +1,5 @@ +#version 430 core + +void main() { + +} \ No newline at end of file