layed groundwork for executing compute shaders

This commit is contained in:
2026-01-03 22:42:44 +01:00
parent e94d544043
commit 9365e82cbf
5 changed files with 120 additions and 2 deletions

View File

@@ -49,8 +49,9 @@ internal class OpenGLEngine : Engine
public override InputService GetInputService() => _inputService;
private void InitialiseShaders(IEnumerable<string> 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<float>(0.0f, 0.0f, 3.0f), _window.FramebufferSize);
InitialiseShaders(Directory.GetFiles("./assets/shaders", "*.vert"));
InitialiseShaders();
SetCurrentScene(_defaultSceneName);
}

View File

@@ -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.
/// <summary>
/// Initialises the compute shader, by compiling it and making it ready to be used in the application. <br/>
/// Only initialises the first time it is called. <b>Is a NoOp every subsequent call.</b>
/// </summary>
/// <param name="glContext"></param>
internal void Initialize(GL glContext)
{
if (_gl is not null || _shaderProgramId is not null)
{
return;
}
_gl = glContext;
var computeShaderId = CompileShader(_computeShaderCode);
_shaderProgramId = CreateProgram(computeShaderId);
}
/// <summary>
/// Can only be called after <see cref="Initialize"/> has been called.
/// </summary>
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;
}
}

View File

@@ -13,6 +13,7 @@ internal abstract class Engine : IEngine
}
protected readonly Dictionary<string, Shader> _shaders = [];
protected readonly Dictionary<string, ComputeShader> _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.");
}
}