extends Node # Utility class to ease buffer registering class ShaderBuffer: var rid : RID var type : RenderingDevice.UniformType var size : int var cache : PackedByteArray func _init(r : RID, t : RenderingDevice.UniformType, s : int, c : PackedByteArray = PackedByteArray()) -> void: rid = r type = t size = s cache = c if not cache.is_empty(): assert(cache.size() == s) func set_parameter_vi(value : Vector2i) -> void: cache.encode_s32(0, value.x) cache.encode_s32(4, value.y) func set_parameter(idx : int, value : float) -> void: var pos : int = idx * 4 assert(pos < cache.size()) cache.encode_float(pos, value) func set_parameter_i(idx : int, value : int) -> void: var pos : int = idx * 4; assert(pos < cache.size()) cache.encode_u32(pos, value) func copy_parameter(src : int, dst : int) -> void: var s : int = src * 4; var d : int = dst * 4 assert(s < cache.size() and d < cache.size()) cache[d] = cache[s] cache[d + 1] = cache[s + 1] cache[d + 2] = cache[s + 2] cache[d + 3] = cache[s + 3] func set_parameter_v(values : PackedFloat32Array) -> void: assert(values.size() <= cache.size() * 4) for i : int in range(values.size()): cache.encode_float(i * 4, values[i]) func build_empty_cache() -> void: cache = PackedByteArray() cache.resize(size) cache.fill(0) func set_cache(c : PackedByteArray) -> void: assert(c.size() == size) cache = c func sync(rd : RenderingDevice) -> void: if not cache.is_empty(): rd.buffer_update(rid, 0, cache.size(), cache) func clear(rd : RenderingDevice, clearing_buf : PackedByteArray) -> void: rd.buffer_update(rid, 0, size, clearing_buf) func print_content(count : int) -> void: if not cache.is_empty(): for i : int in range(count): print("#%d : %f" % [i , cache.decode_float(i * 4)]) ## Compute Shader General Purpose structure. class ComputeShader: var ios : Array[ShaderBuffer] var shader : RID var uniform_set : RID var pipeline : RID var identifier : String var path_to_source : String var rendering_device : RenderingDevice func run_deffered(dispatch : Vector3i) -> void: RenderingServer.call_on_render_thread(run.bind(dispatch)) func run(dispatch : Vector3i) -> void: assert(rendering_device != null) var render_list : int = rendering_device.compute_list_begin() assert(pipeline != null and pipeline.is_valid()) rendering_device.compute_list_bind_compute_pipeline(render_list, pipeline) assert(uniform_set != null and uniform_set.is_valid()) rendering_device.compute_list_bind_uniform_set(render_list, uniform_set, 0) @warning_ignore("integer_division") rendering_device.compute_list_dispatch(render_list, dispatch.x, dispatch.y, dispatch.z) rendering_device.compute_list_end() func _init(n : String, p : String, rd : RenderingDevice, uniforms : Array[ShaderBuffer]) -> void: identifier = n path_to_source = p ios = uniforms rendering_device = rd shader = _init_shader(path_to_source) _link_shader() func _init_shader(path : String) -> RID: var shader_file : RDShaderFile = load(path) assert(shader_file != null) var shader_spirv : RDShaderSPIRV = shader_file.get_spirv() if shader_spirv.compile_error_compute != "": push_error(shader_spirv.compile_error_compute) assert(false) return rendering_device.shader_create_from_spirv(shader_spirv) func _link_shader() -> void: var uniforms : Array[RDUniform] = [] for i : int in range(ios.size()): _register_uniform_in_set(ios[i], i, uniforms) uniform_set = rendering_device.uniform_set_create(uniforms, shader, 0) pipeline = rendering_device.compute_pipeline_create(shader) func _register_uniform_in_set(buffer : ShaderBuffer, binding : int, uniform_list : Array[RDUniform]) -> void: var uniform : RDUniform = RDUniform.new() uniform.uniform_type = buffer.type uniform.binding = binding uniform.add_id(buffer.rid) uniform_list.append(uniform) ## Shader Buffer creation functions for several kind of buffers. Image, storage, uniform. func create_hires_image_buffer(rd : RenderingDevice, sz : Vector2i) -> ShaderBuffer: var format : RDTextureFormat = RDTextureFormat.new() format.width = sz.x format.height = sz.y format.depth = 1 format.format = RenderingDevice.DATA_FORMAT_R32G32B32A32_SFLOAT format.texture_type = RenderingDevice.TEXTURE_TYPE_2D format.array_layers = 1 format.mipmaps = 1 format.usage_bits = RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT + RenderingDevice.TEXTURE_USAGE_COLOR_ATTACHMENT_BIT + RenderingDevice.TEXTURE_USAGE_STORAGE_BIT + RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT + RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT var init_data : PackedFloat32Array = PackedFloat32Array() init_data.resize(sz.x * sz.y * 4) init_data.fill(0.0) # Debug fill. It should display a mix of red and green gradient depending on UV. for i : int in range(sz.y): for j : int in range(sz.x): var k : int = i * sz.x + j var idx : int = k * 4 init_data[idx + 0] = (i / float(sz.x)) init_data[idx + 1] = (j / float(sz.y)) init_data[idx + 3] = 1.0 return ShaderBuffer.new(rd.texture_create(format, RDTextureView.new(), [ init_data.to_byte_array() ]), RenderingDevice.UNIFORM_TYPE_IMAGE, sz.x * sz.y * 16) func create_uniform_buffer(rd : RenderingDevice, parameters_count : int, bytes : PackedByteArray = PackedByteArray()) -> ShaderBuffer: # we count each parameter as a 32bit float. However, we will assume that we align on vec4. var rounded_size : int = ceil(parameters_count / 4.0) * 16 # 4 * 32bit components return ShaderBuffer.new(rd.uniform_buffer_create(rounded_size, bytes), RenderingDevice.UNIFORM_TYPE_UNIFORM_BUFFER, rounded_size, bytes) func create_storage_buffer(rd : RenderingDevice, struct_size : int, element_count : int, bytes : PackedByteArray = PackedByteArray()) -> ShaderBuffer: var rounded_size : int = ceil(struct_size / 4.0) * 16 var total_size : int = rounded_size * element_count return ShaderBuffer.new(rd.storage_buffer_create(total_size, bytes), RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER, total_size)
or share this direct link: