Compute shader: buffer_update() confusion!
Asked Answered
M

2

0

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])
Montpelier answered 22/4, 2023 at 18:40 Comment(0)
G
0

Montpelier You need to create a new compute list for each particular submit:

func _process(delta):
	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()
	
	rd.submit()
	rd.sync()
Gallnut answered 22/4, 2023 at 22:27 Comment(0)
M
0

Gallnut Thank you so much! That fixed the issue immediately. Although, I confess I don't really understand why/how. When it comes to compute lists and pipelines, I'm very much in the "copy the boiler plate code and hope for the best" stage...

Montpelier answered 22/4, 2023 at 22:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.