Skip to content

Instantly share code, notes, and snippets.

@winston-yallow
Last active January 25, 2024 06:32
Show Gist options
  • Save winston-yallow/aab20fa437bfa3dd80bfb0ed6605d28e to your computer and use it in GitHub Desktop.
Save winston-yallow/aab20fa437bfa3dd80bfb0ed6605d28e to your computer and use it in GitHub Desktop.
Godot 4.x Compute Example
extends Node
# Based on this tweet by Clay John:
# https://twitter.com/john_clayjohn/status/1306447928932753408
func _ready() -> void:
# Create a local rendering device.
var rd := RenderingServer.create_local_rendering_device()
# Load GLSL shader
var shader_file := load("res://test.glsl")
var shader_spirv: RDShaderSPIRV = shader_file.get_spirv()
var shader := rd.shader_create_from_spirv(shader_spirv)
# Prepare our data. We use doubles in the shader, so we need 64 bit.
var input := PackedFloat64Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
var input_bytes := input.to_byte_array()
# Create a storage buffer that can hold 10 double values. Each
# double has 8 byte (64 bit) so 10 x 8 = 80 bytes
var buffer := rd.storage_buffer_create(80, input_bytes)
var uniform := RDUniform.new()
uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
uniform.binding = 0
uniform.add_id(buffer)
var uniform_set := rd.uniform_set_create([uniform], shader, 0)
# Create a compute pipeline
var pipeline := rd.compute_pipeline_create(shader)
var compute_list := rd.compute_list_begin()
rd.compute_list_bind_compute_pipeline(compute_list, pipeline)
rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0)
rd.compute_list_dispatch(compute_list, 5, 1, 1)
rd.compute_list_end()
# Submit to GPU and wait for sync
rd.submit()
rd.sync()
# Read back the data from the buffers
var output_bytes := rd.buffer_get_data(buffer)
var output := output_bytes.to_float64_array()
print(output)
#[compute]
#version 450
// Based on this tweet by Clay John:
// https://twitter.com/john_clayjohn/status/1306447928932753408
layout(local_size_x = 4) in;
layout(set = 0, binding = 0, std430) restrict buffer MyBuffer {
double data[];
}
my_buffer;
void main() {
// gl_GlobalInvocationID.x uniquely identifies this invocation within the work group
my_buffer.data[gl_GlobalInvocationID.x] *= 2.0;
}
@AndreSacilotto
Copy link

AndreSacilotto commented Oct 5, 2022

I was testing to see how it works and I use c#, so here is the converted code.

using System;
using Godot;

using System;
using Godot;

public partial class ComputeExemple : Node
{
	public override void _Ready()
	{
		// Create a local rendering device.;
		var rd = RenderingServer.CreateLocalRenderingDevice();

		// Load GLSL shader;
		var shaderFile = GD.Load<RDShaderFile>("res://test.glsl");
		var shaderSpirv = shaderFile.GetSpirv();
		var shader = rd.ShaderCreateFromSpirv(shaderSpirv);

		// Prepare our data. We use doubles in the shader, so we need 64 bit.;
		var input = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		var inputBytes = DoubleArrToByteArr(input);
		
		// Create a storage buffer that can hold 10 double values. Each;
		// double has 8 byte (64 bit) so 10 x 8 = 80 bytes;
		var buffer = rd.StorageBufferCreate(((uint)input.Length) * sizeof(double), inputBytes);
		var uniform = new RDUniform {
			UniformType = RenderingDevice.UniformType.StorageBuffer,
			Binding = 0,
		};
		uniform.AddId(buffer);
		var uniformArr = new Godot.Collections.Array<RDUniform> { uniform };
		var uniformSet = rd.UniformSetCreate(uniformArr, shader, 0);

		// Create a compute pipeline;
		var pipeline = rd.ComputePipelineCreate(shader);
		var computeList = rd.ComputeListBegin();
		rd.ComputeListBindComputePipeline(computeList, pipeline);
		rd.ComputeListBindUniformSet(computeList, uniformSet, 0);
		rd.ComputeListDispatch(computeList, 5, 1, 1);
		rd.ComputeListEnd();

		// Submit to GPU and wait for sync;
		rd.Submit();
		rd.Sync();

		// Read back the data from the buffers;
		var outputBytes = rd.BufferGetData(buffer);
		var output = ByteArrToDoubleArr(outputBytes);

		//Print
		GD.Print(output.Join("\n"));
	}

	private static byte[] DoubleArrToByteArr(double[] values)
	{
		var result = new byte[values.Length * sizeof(double)];
		Buffer.BlockCopy(values, 0, result, 0, result.Length);
		return result;
	}

	private static double[] ByteArrToDoubleArr(byte[] bytes)
	{
		var result = new double[bytes.Length / sizeof(double)];
		Buffer.BlockCopy(bytes, 0, result, 0, bytes.Length);
		return result;
	}
}

@padreputativo
Copy link

I'm having lot of trouble trying to launch it in loop. I'm only able to launch it one time and the data do not seems to be updated anymore... Suggestions?

@winston-yallow
Copy link
Author

winston-yallow commented Feb 6, 2023

@padreputativo The docs were updated to include a compute example, they are newer than this gist: Using compute shaders

For your question: I'm not sure, but it should be possible to do stuff repeatedly. You can even re-use the RD, buffers, shader and so on and don't need to recreate them each time.

@padreputativo
Copy link

¡Found it! I needed to start the loop from this line and not only submit and sync:

var compute_list := rd.compute_list_begin()

@winston-yallow
Copy link
Author

Yeah that should do since the compute list is basically the thing you submit. So when calling submit without doing that, then there is nothing to submit. Glad you got it working! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment