Hey! I've been using Godot for a while, but I've just started trying to write a compute shader and I'm beyond confused.
In my script, I have a large array called asteroids. I'm using various encode functions to convert it to a PackedByteArray and am sending it to my GLSL shader:
#[compute]
#version 450
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
struct Asteroid {
ivec2 initial_position;
vec2 position;
vec2 velocity;
int collision_frame;
};
layout(set = 0, binding = 0, std430) restrict buffer Asteroids {
Asteroid asteroids[];
};
void main() {
asteroids[0].position.x += 0.1;
}
It's really simple - I was just testing that I'd managed to encode and decode between my gdscript variable and the buffer by printing the shader's output. It all worked correctly, but, I was surprised (in my naivety!) to find that the asteroids[0].position.x value was increasing by 0.1 from its original gdscript value, but not increasing by 0.1 every time the shader is run.
I'm assuming that's entirely intended behaviour, but now I'm unsure how to get what I need, which is for the buffer to be "persistent" each time the shader is submitted. I tried using buffer_update and feeding in the output of the shader, but that isn't making any difference.
I'm very out of my depth! If anyone can help, I'd really appreciate it.
Here's my full script, in case it's useful:
extends Node
var rd: RenderingDevice
var asteroids_RID
var asteroids := [[Vector2i(0, 1), Vector2(2.1, 3.1), Vector2(4.1, 5.1), 6],
[Vector2i(7, 8), Vector2(9.1, 10.1), Vector2(11.1, 12.1), 13]]
var planets := []
func _ready():
# Create a local rendering device
rd = RenderingServer.create_local_rendering_device()
# Load GLSL shader
var shader_file := load("res://scripts/asteroids.glsl")
var shader_spirv: RDShaderSPIRV = shader_file.get_spirv()
var shader := rd.shader_create_from_spirv(shader_spirv)
# Create an asteroids RID
var asteroids_bytes := PackedByteArray()
asteroids_bytes.resize(asteroids.size() * 32)
for i in asteroids.size():
asteroids_bytes.encode_s32(i * 32, asteroids[i][0].x)
asteroids_bytes.encode_s32(i * 32 + 4, asteroids[i][0].y)
asteroids_bytes.encode_float(i * 32 + 8, asteroids[i][1].x)
asteroids_bytes.encode_float(i * 32 + 12, asteroids[i][1].y)
asteroids_bytes.encode_float(i * 32 + 16, asteroids[i][2].x)
asteroids_bytes.encode_float(i * 32 + 20, asteroids[i][2].y)
asteroids_bytes.encode_s64(i * 32 + 24, asteroids[i][3])
asteroids_RID = rd.storage_buffer_create(asteroids_bytes.size(), asteroids_bytes)
# Create an asteroids uniform
var asteroids_uniform := RDUniform.new()
asteroids_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
asteroids_uniform.binding = 0
asteroids_uniform.add_id(asteroids_RID)
# Create uniform set
var binding := [asteroids_uniform]
var uniform_set := rd.uniform_set_create(binding, 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, 1, 1, 1)
rd.compute_list_end()
func _process(_delta):
# Submit to GPU and wait for sync
rd.submit()
rd.sync()
# Read back the data from the buffer and decode it
var asteroids_output := rd.buffer_get_data(asteroids_RID)
asteroids = []
for i in int(asteroids_output.size() / 32):
var initial_position = Vector2i(asteroids_output.decode_s32(i * 32), asteroids_output.decode_s32(i * 32 + 4))
var position = Vector2(asteroids_output.decode_float(i * 32 + 8), asteroids_output.decode_float(i * 32 + 12))
var velocity = Vector2(asteroids_output.decode_float(i * 32 + 16), asteroids_output.decode_float(i * 32 + 20))
var collision_frame = asteroids_output.decode_s64(i * 32 + 24)
asteroids.append([initial_position, position, velocity, collision_frame])
rd.buffer_update(asteroids_RID, 0, asteroids_output.size(), asteroids_output)
print(asteroids[0])