layed groundwork for executing compute shaders
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user