Compute Shader cannot create uniform set

Godot Version

4.7

Question

I have made a compute shader that computes a heightfield for a mesh in a compute shader to mimic the result of a vertex displacement vertex shader so that I can use the output as a collision model - an attempt to render to vertex buffer. Unfortunately the output says “cannot create uniform set” … here is the code …

func generate_physics_shape(height_map:Image, 
							splat_map:Image, 
							tex1:Image, 
							tex2:Image, 
							grid_coord:Vector2,
							uv_scale:Vector4,
							splat_col:int,
							HEIGHT_SCALE:float,
							mesh_faces:PackedVector3Array):
								
	compute.load_shader("compute_height", "res://assets/compute/compute_physics_model.glsl")
		
	var image = height_map
	if image.is_compressed():
		image.decompress()	
	image.convert(Image.FORMAT_RF)
	var height_texture = ImageTexture.create_from_image(image)
	var source_size = 4096
	
	var splat_image = splat_map
	if splat_image.is_compressed():
		splat_image.decompress()	
	splat_image.convert(Image.FORMAT_RGBA8)
	
	var tex1_image = tex1
	if tex1_image.is_compressed():
		tex1_image.decompress()	
	tex1_image.convert(Image.FORMAT_RGBA8)
	
	var tex2_image = tex2
	if tex2_image.is_compressed():
		tex2_image.decompress()	
	tex2_image.convert(Image.FORMAT_RGBA8)	


	compute.register_sampler("height_tex", 0, source_size, source_size, image.get_data(), RenderingDevice.DATA_FORMAT_R32_SFLOAT)	
	compute.register_sampler("splat_tex", 1, source_size, source_size, splat_image.get_data(), RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM)	
	compute.register_sampler("tex1", 2, source_size, source_size, tex1_image.get_data(), RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM)	
	compute.register_sampler("tex3", 3, source_size, source_size, tex2_image.get_data(), RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM)	
		
	var mesh_compute_byte_data:PackedByteArray = mesh_faces.to_byte_array()
	compute.register_storage_buffer(
		"Faces",	# Name: used to identify the buffer later
		4, 					# Binding: this is the binding point in the shader
		mesh_compute_byte_data.size(), 	# Size: since we're passing the data, we don't need to specify the size
		mesh_compute_byte_data
	)	
	
	var params = PackedFloat32Array([grid_coord.x, grid_coord.y, 
									uv_scale.x, uv_scale.y, uv_scale.z, uv_scale.w, 
									splat_col, 
									HEIGHT_SCALE])
	compute.register_storage_buffer("Params", 5, 0, params.to_byte_array())
	compute.register_storage_buffer("OutputFaces", 6,mesh_compute_byte_data.size(),[] )
	
	# FIRST PASS, Find min and max height values

	compute.execute("compute_height", 8, 8)
	compute.sync()
	
	var final_mesh_data = compute.fetch_buffer("OutputFaces")
	var float_data = final_mesh_data.to_vector3_array()
	print(float_data)

The mesh_data is created as follows

func get_mesh_faces(mesh:Mesh)->Dictionary:
	var result := {
		"faces": []
	}	
	var mdt := MeshDataTool.new()
	
	for surface in mesh.get_surface_count():
		var err := mdt.create_from_surface(mesh, surface)
		if err != OK:
			print("Failed to read surface %d" % surface)
			continue

		# Extract indices (faces are always triangles)
		var face_count := mdt.get_face_count()
		for f in face_count:
			var i1 := mdt.get_face_vertex(f, 0)
			var i2 := mdt.get_face_vertex(f, 1)
			var i3 := mdt.get_face_vertex(f, 2)

			result["faces"].append(mdt.get_vertex(i1))
			result["faces"].append(mdt.get_vertex(i2))
			result["faces"].append(mdt.get_vertex(i3))

		mdt.clear()	
	
	return result

Compute Shader:

#[compute]
#version 450

layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;


layout(set = 0, binding = 0) uniform sampler2D height_tex;
layout(set = 0, binding = 1) uniform sampler2D splat_tex;

layout(set = 0, binding = 2) uniform sampler2D tex1;
layout(set = 0, binding = 3) uniform sampler2D tex3;


layout(std140,set = 0, binding = 4) readonly buffer Faces{

	vec3 data[];
} faces;


layout(set = 0, binding = 5, std430) restrict readonly buffer Params {
	vec2 grid_coord;
	vec4 uv_scale;
	float splat_col;
	float HEIGHT_SCALE;
	
}params;


layout(std140,set = 0, binding = 6) writeonly buffer OutputFaces{

	vec3 data[];
} outputfaces;




void main() {

	uint index = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * 127;
	
	if( index < (127 * 127) ) {

		vec3 VERTEX = faces.data[index];

		float world_pos_x = params.grid_coord.x + VERTEX.x;
		float world_pos_z = params.grid_coord.y + VERTEX.z;

		vec2 height_UV = vec2( world_pos_x / 4096.0, world_pos_z / 4096.0 );

		vec2 height_UV0 = vec2( (world_pos_x - 1.0) / 4096.0, (world_pos_z      ) / 4096.0 );
		vec2 height_UV1 = vec2( (world_pos_x + 1.0) / 4096.0, (world_pos_z      ) / 4096.0 );
		vec2 height_UV2 = vec2( (world_pos_x      ) / 4096.0, (world_pos_z - 1.0) / 4096.0 );
		vec2 height_UV3 = vec2( (world_pos_x      ) / 4096.0, (world_pos_z + 1.0) / 4096.0 );

		vec2 bump_UV = vec2( world_pos_x /127.0, world_pos_z /127.0 ); 
	
	
		float H0 = params.HEIGHT_SCALE*texture( height_tex, height_UV0 ).r;
		float H1 = params.HEIGHT_SCALE*texture( height_tex, height_UV1 ).r;
		float H2 = params.HEIGHT_SCALE*texture( height_tex, height_UV2 ).r;
		float H3 = params.HEIGHT_SCALE*texture( height_tex, height_UV3 ).r;
	
		vec3 NORMAL = -normalize(vec3(2.0*(H1 - H0), -4.0, 2.0*(H3-H2) ));

		VERTEX.y +=  params.HEIGHT_SCALE * texture( height_tex, height_UV ).r;

		
		
		float h1 =texture( tex1, bump_UV * params.uv_scale.x ).a; 
		float h3 =texture( tex3, bump_UV * params.uv_scale.z ).a; 


		vec4 splat_map = texture(splat_tex, height_UV );
		
		float splat_color = 0.0;
		if (params.splat_col == 0.0){
			splat_color = splat_map.r;
		} 
		else if (params.splat_col == 1.0){
			splat_color = splat_map.g;
		}
		else if (params.splat_col == 2.0){
			splat_color = splat_map.b;
		}
		else {
			splat_color = splat_map.a;
		}

		float hval = (splat_color* h1 + (1.0-splat_color)* h3);
		
		VERTEX +=  5.0 * hval * NORMAL;// - 2.5 * NORMAL;

		outputfaces.data[index] = VERTEX;
	
		
	}
}

Any help is appreciated.

Which line causes the error? Post the exact error message. What is compute?

The compute type is from the EasyCompute addon …

EasyCompute - Godot Asset Library

The error is an assert triggered in the EasyCompute.execute function …

	# Create uniform set if it doesn't exist
	if not uniform_set.is_valid():
		var uniforms = data_cache.values().map(func(obj):
			return obj["uniform"]
		)
		uniform_set = rd.uniform_set_create(uniforms, shader, 0)
		assert(uniform_set.is_valid(), "Failed to create uniform set")
		shader_cache[shader_name]["uniform_set"] = uniform_set

couple of modifications … (And theres another mistake … there are 3 verts per triangle lol)

I made the group layout 16, 16, 2, instead of 16, 16, 1 because there are two triangles per quad

#[compute]
#version 450

layout(local_size_x = 16, local_size_y = 16, local_size_z = 2) in;


layout(set = 0, binding = 0) uniform sampler2D height_tex;
layout(set = 0, binding = 1) uniform sampler2D splat_tex;

layout(set = 0, binding = 2) uniform sampler2D tex1;
layout(set = 0, binding = 3) uniform sampler2D tex3;


layout(std140,set = 0, binding = 4) readonly buffer Faces{

	vec3 data[];
} faces;


layout(set = 0, binding = 5, std430) restrict readonly buffer Params {
	vec2 grid_coord;
	vec4 uv_scale;
	float splat_col;
	float HEIGHT_SCALE;
	
}params;


layout(std140,set = 0, binding = 6) writeonly buffer OutputFaces{

	vec3 data[];
} outputfaces;




void main() {

	uint index = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * 127;
	
	if( index < (127 * 127 * 2) ) {

		vec3 VERTEX = faces.data[index];

		float world_pos_x = params.grid_coord.x + VERTEX.x;
		float world_pos_z = params.grid_coord.y + VERTEX.z;

		vec2 height_UV = vec2( world_pos_x / 4096.0, world_pos_z / 4096.0 );

		vec2 height_UV0 = vec2( (world_pos_x - 1.0) / 4096.0, (world_pos_z      ) / 4096.0 );
		vec2 height_UV1 = vec2( (world_pos_x + 1.0) / 4096.0, (world_pos_z      ) / 4096.0 );
		vec2 height_UV2 = vec2( (world_pos_x      ) / 4096.0, (world_pos_z - 1.0) / 4096.0 );
		vec2 height_UV3 = vec2( (world_pos_x      ) / 4096.0, (world_pos_z + 1.0) / 4096.0 );

		vec2 bump_UV = vec2( world_pos_x /127.0, world_pos_z /127.0 ); 
	
	
		float H0 = params.HEIGHT_SCALE*texture( height_tex, height_UV0 ).r;
		float H1 = params.HEIGHT_SCALE*texture( height_tex, height_UV1 ).r;
		float H2 = params.HEIGHT_SCALE*texture( height_tex, height_UV2 ).r;
		float H3 = params.HEIGHT_SCALE*texture( height_tex, height_UV3 ).r;
	
		vec3 NORMAL = -normalize(vec3(2.0*(H1 - H0), -4.0, 2.0*(H3-H2) ));

		VERTEX.y +=  params.HEIGHT_SCALE * texture( height_tex, height_UV ).r;

		
		
		float h1 =texture( tex1, bump_UV * params.uv_scale.x ).a; 
		float h3 =texture( tex3, bump_UV * params.uv_scale.z ).a; 


		vec4 splat_map = texture(splat_tex, height_UV );
		
		float splat_color = 0.0;
		if (params.splat_col == 0.0){
			splat_color = splat_map.r;
		} 
		else if (params.splat_col == 1.0){
			splat_color = splat_map.g;
		}
		else if (params.splat_col == 2.0){
			splat_color = splat_map.b;
		}
		else {
			splat_color = splat_map.a;
		}

		float hval = (splat_color* h1 + (1.0-splat_color)* h3);
		
		VERTEX +=  5.0 * hval * NORMAL;// - 2.5 * NORMAL;

		outputfaces.data[index] = VERTEX;
	
		
	}
}

and registering the ‘params’ is now this :

var params_byte_array = params.to_byte_array()
compute.register_storage_buffer("Params", 5, params_byte_array.size(), params_byte_array)

had to change again …

I meant to run the indices in a loop because there are 3 verts per triangle, still got (16,16,2) for the local size, because theres

127 * 127 * 2triangles

and the groups are launched in size (8,8,1) so that’s

8 * 8 * 16 * 16 * 2 = 64 * 256 * 2 = 32768 threads

And the data size is 127 * 127 * 2 * 3 = 32258

That’s why theres an `if (index < 127 * 127 * 2 * 3 )

whew this is confusing …

in the shader …

#[compute]
#version 450

layout(local_size_x = 16, local_size_y = 16, local_size_z = 2) in;


layout(set = 0, binding = 0) uniform sampler2D height_tex;
layout(set = 0, binding = 1) uniform sampler2D splat_tex;

layout(set = 0, binding = 2) uniform sampler2D tex1;
layout(set = 0, binding = 3) uniform sampler2D tex3;


layout(std140,set = 0, binding = 4) readonly buffer Faces{

	vec3 data[];
} faces;


layout(set = 0, binding = 5, std430) restrict readonly buffer Params {
	vec2 grid_coord;
	vec4 uv_scale;
	float splat_col;
	float HEIGHT_SCALE;
	
}params;


layout(std140,set = 0, binding = 6) writeonly buffer OutputFaces{

	vec3 data[];
} outputfaces;




void main() {


	for( uint i = 0; i < 3; i++ ){
		uint index = (gl_GlobalInvocationID.x + i) + gl_GlobalInvocationID.y * 127 * 2;
		if( index < (127 * 127 * 2 * 3) ) {

			vec3 VERTEX = faces.data[index];

			float world_pos_x = params.grid_coord.x + VERTEX.x;
			float world_pos_z = params.grid_coord.y + VERTEX.z;

			vec2 height_UV = vec2( world_pos_x / 4096.0, world_pos_z / 4096.0 );

			vec2 height_UV0 = vec2( (world_pos_x - 1.0) / 4096.0, (world_pos_z      ) / 4096.0 );
			vec2 height_UV1 = vec2( (world_pos_x + 1.0) / 4096.0, (world_pos_z      ) / 4096.0 );
			vec2 height_UV2 = vec2( (world_pos_x      ) / 4096.0, (world_pos_z - 1.0) / 4096.0 );
			vec2 height_UV3 = vec2( (world_pos_x      ) / 4096.0, (world_pos_z + 1.0) / 4096.0 );

			vec2 bump_UV = vec2( world_pos_x /127.0, world_pos_z /127.0 ); 
	
	
			float H0 = params.HEIGHT_SCALE*texture( height_tex, height_UV0 ).r;
			float H1 = params.HEIGHT_SCALE*texture( height_tex, height_UV1 ).r;
			float H2 = params.HEIGHT_SCALE*texture( height_tex, height_UV2 ).r;
			float H3 = params.HEIGHT_SCALE*texture( height_tex, height_UV3 ).r;
	
			vec3 NORMAL = -normalize(vec3(2.0*(H1 - H0), -4.0, 2.0*(H3-H2) ));

			VERTEX.y +=  params.HEIGHT_SCALE * texture( height_tex, height_UV ).r;

		
		
			float h1 =texture( tex1, bump_UV * params.uv_scale.x ).a; 
			float h3 =texture( tex3, bump_UV * params.uv_scale.z ).a; 


			vec4 splat_map = texture(splat_tex, height_UV );
		
			float splat_color = 0.0;
			if (params.splat_col == 0.0){
				splat_color = splat_map.r;
			} 
			else if (params.splat_col == 1.0){
				splat_color = splat_map.g;
			}
			else if (params.splat_col == 2.0){
				splat_color = splat_map.b;
			}
			else {
				splat_color = splat_map.a;
			}

			float hval = (splat_color* h1 + (1.0-splat_color)* h3);
		
			VERTEX +=  5.0 * hval * NORMAL;// - 2.5 * NORMAL;

			outputfaces.data[index] = VERTEX;
	
		}
	}
}

Ok scrap that … the same index would show up in the neighbour thread

I need to rethink it … is there any other way of rendering to a vertex buffer ?

Ok this might work … i use the vertex list instead of the face list to copy the buffer …

Then i use the indices in software to create the triangle list for the collision model.

But the uniforms are not valid … I will try to post a bug free version tomorrow.