Help with Compute Shaders Tutorial - I keep running into an "Unexpected Identifier in Class Body" error

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By JJones19

I’m having some trouble working on the Compute Shaders Tutorial from #Using compute shaders — Godot Engine (latest) documentation in English

I’ve been trying to get this working over the last month and so I’ve tried it in several Godot 4 betas, including the current Godot 4 beta 17.

I’ve created the compute_example.glsl file it details and left it in the res:// directory. Some errors start to show up once I try to implement the setupGLSL.gd script though.

for instance, this code snippet doesn’t work:

# Each float has 8 byte (32 bit) so 10 x 8 = 80 bytes
var buffer := rd.storage_buffer_create(input_bytes.size(), input_bytes)

# Create a uniform to assign the buffer to the rendering device
var uniform := RDUniform.new()

#The lines below are returning an "Unexpected Identifier in Class Body" error

uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
uniform.binding = 0 # this needs to match the "binding" in our shader file
uniform.add_id(buffer)

Each of the last three lines independently returns the “Unexpected Identifier in Class Body” error.

I’ve checked the documentation numerous times and all of the methods and properties used are directly from there.

Here’s the full code I used for the compute_example.glsl script:

#[compute]
#version 450

// Invocations in the (x, y, z) dimension
layout(local_size_x = 2, local_size_y = 1, local_size_z = 1) in;

// A binding to the buffer we create in our script
layout(set = 0, binding = 0, std430) restrict buffer MyDataBuffer {
	float data[];
}
my_data_buffer;

// The code we want to execute in each invocation
void main() {
	// gl_GlobalInvocationID.x uniquely identifies this invocation across all work groups
	my_data_buffer.data[gl_GlobalInvocationID.x] *= 2.0;
}

And here’s the complete code used for the setupGLSL.gd script (note that I commented out the problematic sections):

extends Node2D

#Compute Shaders Tutorial From:
#https://docs.godotengine.org/en/latest/tutorials/shaders/compute_shaders.html

#Working with compute shaders is a little cumbersome to start, but once you have the basics working in your program
# you can scale up the complexity of your shader without making many changes to your script.




# Create a local rendering device.
var rd := RenderingServer.create_local_rendering_device()

# Load GLSL shader
var shader_file := load("res://compute_example.glsl")
var shader_spirv: RDShaderSPIRV = shader_file.get_spirv()
var shader := rd.shader_create_from_spirv(shader_spirv)

# Prepare our data. We use floats in the shader, so we need 32 bit.
var input := PackedFloat32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
var input_bytes := input.to_byte_array()

# Create a storage buffer that can hold our float values.
# Each float has 8 byte (32 bit) so 10 x 8 = 80 bytes
var buffer := rd.storage_buffer_create(input_bytes.size(), input_bytes)

# Create a uniform to assign the buffer to the rendering device
var uniform := RDUniform.new()

#The lines below are returning an "Unexpected Identifier in Class Body" error

#uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
#uniform.binding = 0 # this needs to match the "binding" in our shader file
#uniform.add_id(buffer)

var uniform_set := rd.uniform_set_create([uniform], shader, 0) # the last parameter (the 0) needs to match the "set" in our shader file

#The next step is to create a set of instructions our GPU can execute.
#We need a pipeline and a compute list for that.

#The steps we need to do to compute our result are:

#1. Create a new pipeline.
#2. Begin a list of instructions for our GPU to execute.
#3. Bind our compute list to our pipeline
#4. Bind our buffer uniform to our pipeline
#5. Execute the logic of our shader
#6. End the list of instructions

# Create a compute pipeline
var pipeline := rd.compute_pipeline_create(shader)
var compute_list := rd.compute_list_begin()

#The lines below are returning an "Unexpected Identifier in Class Body" error

#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()

#Note that we are dispatching the compute shader with 5 work groups in the x-axis, and one in the others
#Since we have 2 local invocations in the x-axis (specified in our shader) 10 compute shader invocations will be launched in total.
#If you read or write to indices outside of the range of your buffer, 
#you may access memory outside of your shaders control or parts of other variables which may cause issues on some hardware.

#To execute our compute shader we just need to submit the pipeline to the GPU and wait for the execution to finish:

# Submit to GPU and wait for sync

#The lines below are returning an "Unexpected Identifier in Class Body" error

#rd.submit()
#rd.sync()

#Ideally, you would not synchronize the RenderingDevice right away as it will cause the CPU to wait for the GPU to finish working.
#In our example we synchronize right away because we want our data available for reading right away.
#In general, you will want to wait at least a few frames before synchronizing so that the GPU is able to run in parallel with the CPU.



# Called when the node enters the scene tree for the first time.
func _ready():
	#Congratulations you created and executed a compute shader. But wait, where are the results now?

	#You may remember from the beginning of this tutorial that compute shaders don't have inputs and outputs, they simply change memory.
	#This means we can retrieve the data from our buffer we created at the start of this tutorial.
	#The shader read from our array and stored the data in the same array again so our results are already there.

	#Let's retrieve the data and print the results to our console.
	
	# Read back the data from the buffer
	var output_bytes := rd.buffer_get_data(buffer)
	var output := output_bytes.to_float32_array()
	print("Input: ", input)
	print("Output: ", output)

If anyone has any experience with setting up compute shaders in Godot, I’d appreciate your help. Thanks in advance.

Only variable declarations are allowed in the body of a class in GDScript.
You must move those lines into a function.
Have a look at this short tutorial.

LeslieS | 2023-02-04 17:51

:bust_in_silhouette: Reply From: JJones19

Oops. I just realized I needed to move everything into the ready function.

setupGLSL.gd

extends Node2D

#Compute Shaders Tutorial From:
#https://docs.godotengine.org/en/latest/tutorials/shaders/compute_shaders.html

#Working with compute shaders is a little cumbersome to start, but once you have the basics working in your program
# you can scale up the complexity of your shader without making many changes to your script.

# Called when the node enters the scene tree for the first time.
func _ready():
	#Congratulations you created and executed a compute shader. But wait, where are the results now?

	#You may remember from the beginning of this tutorial that compute shaders don't have inputs and outputs, they simply change memory.
	#This means we can retrieve the data from our buffer we created at the start of this tutorial.
	#The shader read from our array and stored the data in the same array again so our results are already there.

	#Let's retrieve the data and print the results to our console.

	# Create a local rendering device.
	var rd := RenderingServer.create_local_rendering_device()

	# Load GLSL shader
	var shader_file := load("res://compute_example.glsl")
	var shader_spirv: RDShaderSPIRV = shader_file.get_spirv()
	var shader := rd.shader_create_from_spirv(shader_spirv)

	# Prepare our data. We use floats in the shader, so we need 32 bit.
	var input := PackedFloat32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
	var input_bytes := input.to_byte_array()

	# Create a storage buffer that can hold our float values.
	# Each float has 8 byte (32 bit) so 10 x 8 = 80 bytes
	var buffer := rd.storage_buffer_create(input_bytes.size(), input_bytes)

	# Create a uniform to assign the buffer to the rendering device
	var uniform := RDUniform.new()

	#The lines below are returning an "Unexpected Identifier in Class Body" error

	uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
	uniform.binding = 0 # this needs to match the "binding" in our shader file
	uniform.add_id(buffer)

	var uniform_set := rd.uniform_set_create([uniform], shader, 0) # the last parameter (the 0) needs to match the "set" in our shader file

	#The next step is to create a set of instructions our GPU can execute.
	#We need a pipeline and a compute list for that.

	#The steps we need to do to compute our result are:

	#1. Create a new pipeline.
	#2. Begin a list of instructions for our GPU to execute.
	#3. Bind our compute list to our pipeline
	#4. Bind our buffer uniform to our pipeline
	#5. Execute the logic of our shader
	#6. End the list of instructions

	# Create a compute pipeline
	var pipeline := rd.compute_pipeline_create(shader)
	var compute_list := rd.compute_list_begin()

	#The lines below are returning an "Unexpected Identifier in Class Body" error

	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()

	#Note that we are dispatching the compute shader with 5 work groups in the x-axis, and one in the others
	#Since we have 2 local invocations in the x-axis (specified in our shader) 10 compute shader invocations will be launched in total.
	#If you read or write to indices outside of the range of your buffer, 
	#you may access memory outside of your shaders control or parts of other variables which may cause issues on some hardware.

	#To execute our compute shader we just need to submit the pipeline to the GPU and wait for the execution to finish:

	# Submit to GPU and wait for sync

	#The lines below are returning an "Unexpected Identifier in Class Body" error

	rd.submit()
	rd.sync()

	#Ideally, you would not synchronize the RenderingDevice right away as it will cause the CPU to wait for the GPU to finish working.
	#In our example we synchronize right away because we want our data available for reading right away.
	#In general, you will want to wait at least a few frames before synchronizing so that the GPU is able to run in parallel with the CPU.


	
	# Read back the data from the buffer
	var output_bytes := rd.buffer_get_data(buffer)
	var output := output_bytes.to_float32_array()
	print("Input: ", input)
	print("Output: ", output)