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();
+ }
+ }
+
+}