diff --git a/AComputeC.cs b/AComputeC.cs new file mode 100644 index 0000000..1662843 --- /dev/null +++ b/AComputeC.cs @@ -0,0 +1,80 @@ +using System.Diagnostics; +using Godot; + +public partial class AComputeC +{ + private readonly GodotObject m_AComputeShader; + + /// + /// Create an ACompute shader wrapper for use within C# scripts. + /// + /// + /// + public AComputeC(string shaderName, string aComputePath = "res://acompute.gd") + { + GDScript shaderResource = GD.Load(aComputePath); + Debug.Assert(shaderResource != null, "Make sure the aComputePath points to the location of the acompute.gd script."); + + // Create the acompute.gd node + this.m_AComputeShader = (GodotObject)shaderResource.New(shaderName); + } + + /// + /// Gets the Rid for the kernel at the specified index. + /// + /// + /// + public Rid GetKernel(int index) + { + return (Rid)m_AComputeShader.Call("get_kernel", index); + } + + /// + /// Set the shader push constant byte array. + /// + /// + public void SetPushConstant(ref byte[] pushConstants) + { + m_AComputeShader.Call("set_push_constant", pushConstants); + } + + /// + /// Set a texture resource bound to the binding. The binding value must match the value in the shader file. + /// + /// + /// + public void SetTexture(int binding, Rid texture) + { + m_AComputeShader.Call("set_texture", binding, texture); + } + + /// + /// Set a uniform buffer resource bound to the binding. The binding value must match the value in the shader file. + /// + /// + /// + public void SetUniformBuffer(int binding, ref byte[] uniformArray) + { + m_AComputeShader.Call("set_uniform_buffer", binding, uniformArray); + } + + /// + /// Set the compute shader to run. + /// + /// + /// + /// + /// + public void Dispatch(int kernelIndex, int xGroups, int yGroups, int zGroups) + { + m_AComputeShader.Call("dispatch", kernelIndex, xGroups, yGroups, zGroups); + } + + /// + /// Free the shader resources. + /// + public void Free() + { + m_AComputeShader.Call("free"); + } +} diff --git a/Examples/ExposureCompositorEffectC.cs b/Examples/ExposureCompositorEffectC.cs new file mode 100644 index 0000000..3bda7f3 --- /dev/null +++ b/Examples/ExposureCompositorEffectC.cs @@ -0,0 +1,63 @@ +using Godot; +using System.Runtime.InteropServices; + +[GlobalClass] +[Tool] +public partial class ExposureCompositorEffectC : CompositorEffect +{ + + [ExportGroup("Shader Settings")] + [Export] + private Vector4 m_exposure = new(2, 1, 1, 1); + + private AComputeC m_newShader; + + /// + /// Example compositor effect using the C# shader wrapper. + /// + public ExposureCompositorEffectC() : base() + { + RenderingServer.CallOnRenderThread(Callable.From(() => + { + this.m_newShader = new AComputeC("exposure_example"); + })); + } + + public override void _RenderCallback(int effectCallbackType, RenderData renderData) + { + if (!this.Enabled) return; + if (effectCallbackType != (int)EffectCallbackTypeEnum.PostTransparent) return; + + RenderSceneBuffersRD renderSceneBuffers = (RenderSceneBuffersRD)renderData.GetRenderSceneBuffers(); + + Vector2I size = renderSceneBuffers.GetInternalSize(); + if (size.X == 0 && size.Y == 0) + { + GD.PushError("Rendering to 0x0 buffer"); + return; + } + + int xGroups = (size.X - 1) / 8 + 1; + int yGroups = (size.Y - 1) / 8 + 1; + int zGroups = 1; + + byte[] pushConstant = MemoryMarshal.AsBytes([size.X, size.Y, 0.0f, 0.0f]).ToArray(); + byte[] uniformArray = MemoryMarshal.AsBytes([this.m_exposure.X, this.m_exposure.Y, this.m_exposure.Z, this.m_exposure.W]).ToArray(); + + Rid inputImage = renderSceneBuffers.GetColorLayer(0); + + this.m_newShader.SetPushConstant(ref pushConstant); + this.m_newShader.SetTexture(0, inputImage); + this.m_newShader.SetUniformBuffer(1, ref uniformArray); + this.m_newShader.Dispatch(0, xGroups, yGroups, zGroups); + } + + public override void _Notification(int what) + { + if (what == NotificationPredelete) + { + this.m_newShader.Free(); + } + } + +}