└── addons └── compute_shader_plus ├── LICENSE ├── README.md ├── compute_helper.gd ├── image_format_helper.gd └── uniforms ├── image_uniform.gd ├── sampler_uniform.gd ├── shared_image_uniform.gd ├── storage_buffer_uniform.gd ├── uniform.gd ├── uniform_buffer_uniform.gd └── vertex_buffer_uniform.gd /addons/compute_shader_plus/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 DevPoodle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /addons/compute_shader_plus/README.md: -------------------------------------------------------------------------------- 1 | # Compute Shader Plus 2 | 3 | This Godot 4 plugin adds in a ComputeHelper class that keeps track of compute shaders and their uniforms. 4 | Here's a simple example of a shader that reads and then writes to a texture (ideally in the render thread): 5 | 6 | var image := Image.create(image_size.x, image_size.y, false, Image.FORMAT_RGBAF) 7 | image.fill(Color.BLACK) 8 | 9 | var compute_shader := ComputeHelper.create("res://compute-shader.glsl") 10 | var input_texture := ImageUniform.create(image) 11 | var output_texture := SharedImageUniform.create(input_texture) 12 | compute_shader.add_uniform_array([input_texture, output_texture]) 13 | 14 | var work_groups := Vector3i(image_size.x, image_size.y, 1) 15 | compute_shader.run(work_groups) 16 | ComputeHelper.sync() 17 | 18 | image = output_texture.get_image() 19 | 20 | Corresponding shader file: 21 | 22 | #[compute] 23 | #version 450 24 | 25 | layout(set = 0, binding = 0, rgba32f) readonly uniform image2D input_texture; 26 | layout(set = 0, binding = 1, rgba32f) writeonly restrict uniform image2D output_texture; 27 | 28 | layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; 29 | void main() { 30 | ivec2 id = ivec2(gl_GlobalInvocationID.xy); 31 | 32 | vec4 color = imageLoad(input_texture, id); 33 | vec3 grayscale = vec3((color.r + color.g + color.b) / 3.0); 34 | 35 | imageStore(output_texture, id, vec4(grayscale, 1.0)); 36 | } 37 | 38 | ## Demos 39 | 40 | I've made a few sample projects that use this plugin: 41 | - Slime Mold Simulation - https://github.com/DevPoodle/compute-helper-demo 42 | - Boids Simulation - https://github.com/DevPoodle/godot-boids 43 | 44 | ## Other Information 45 | 46 | For more detailed information, like future updates or currently known issues, check the Git page - https://github.com/DevPoodle/compute-shader-plus 47 | -------------------------------------------------------------------------------- /addons/compute_shader_plus/compute_helper.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Object 3 | class_name ComputeHelper 4 | ## Responsible for creating and running compute shaders. 5 | 6 | static var rd := RenderingServer.get_rendering_device() ## The global [RenderingDevice]. 7 | static var view := RDTextureView.new() ## A default [RDTextureView] used internally by [ImageUniform]. 8 | static var version: int = Engine.get_version_info()["minor"] 9 | 10 | var compute_shader: RID ## The [RID] of the shader specified in [method create]. 11 | var pipeline: RID ## The [RID] of the compute pipeline. 12 | var uniforms: Array[Uniform] ## An array of every bound [Uniform]. 13 | var uniform_set: RID ## The uniform set. Used internally. 14 | var uniform_set_dirty := true ## Keeps track of whether the uniform set needs to be updated. Used internally. 15 | 16 | ## Returns a new ComputeHelper object that uses the shader provided by [param shader_path]. 17 | static func create(shader_path: String) -> ComputeHelper: 18 | var compute_helper := ComputeHelper.new() 19 | var shader_file: RDShaderFile = load(shader_path) 20 | var shader_spirv := shader_file.get_spirv() 21 | 22 | compute_helper.compute_shader = rd.shader_create_from_spirv(shader_spirv) 23 | compute_helper.pipeline = rd.compute_pipeline_create(compute_helper.compute_shader) 24 | 25 | return compute_helper 26 | 27 | ## This function waits until all compute shaders currently running have finished. Doesn't do anything in versions past 4.2. 28 | static func sync() -> void: 29 | if version > 2: 30 | return 31 | rd.barrier(RenderingDevice.BARRIER_MASK_COMPUTE) 32 | 33 | ## Binds the given [param uniform]. The binding location depends on the order in which uniforms are added, starting at 0. 34 | func add_uniform(uniform: Uniform) -> void: 35 | uniforms.append(uniform) 36 | uniform.rid_updated.connect(make_uniform_set_dirty) 37 | uniform_set_dirty = true 38 | 39 | ## Binds all uniforms in the [param uniform_array]. Binding order is the same as the order of the array. 40 | func add_uniform_array(uniform_array: Array[Uniform]) -> void: 41 | uniforms.append_array(uniform_array) 42 | for uniform: Uniform in uniform_array: 43 | uniform.rid_updated.connect(make_uniform_set_dirty) 44 | uniform_set_dirty = true 45 | 46 | ## Runs the compute shader using the amount of [param groups]. [param push_constant] is optional and allows you to push extra data to the compute shader. 47 | func run(groups: Vector3i, push_constant := PackedByteArray()) -> void: 48 | if uniform_set_dirty: 49 | var bindings: Array[RDUniform] = [] 50 | for uniform_index in uniforms.size(): 51 | bindings.append(uniforms[uniform_index].get_rd_uniform(uniform_index)) 52 | if uniform_set.is_valid() and rd.uniform_set_is_valid(uniform_set): 53 | rd.free_rid(uniform_set) 54 | uniform_set = rd.uniform_set_create(bindings, compute_shader, 0) 55 | uniform_set_dirty = false 56 | var compute_list := rd.compute_list_begin() 57 | 58 | rd.compute_list_bind_compute_pipeline(compute_list, pipeline) 59 | rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0) 60 | 61 | if !push_constant.is_empty(): 62 | while push_constant.size() % 16 != 0: 63 | push_constant.append(0) 64 | rd.compute_list_set_push_constant(compute_list, push_constant, push_constant.size()) 65 | 66 | rd.compute_list_dispatch(compute_list, groups.x, groups.y, groups.z) 67 | rd.compute_list_end() 68 | 69 | ## Called from a bound uniform when it's RID changes. 70 | func make_uniform_set_dirty(_uniform: Uniform) -> void: 71 | uniform_set_dirty = true 72 | 73 | func _notification(what: int) -> void: 74 | if what == NOTIFICATION_PREDELETE: 75 | if compute_shader.is_valid(): 76 | rd.free_rid(compute_shader) 77 | if rd.uniform_set_is_valid(uniform_set): 78 | rd.free_rid(uniform_set) 79 | if rd.compute_pipeline_is_valid(pipeline): 80 | rd.free_rid(pipeline) 81 | -------------------------------------------------------------------------------- /addons/compute_shader_plus/image_format_helper.gd: -------------------------------------------------------------------------------- 1 | extends Object 2 | class_name ImageFormatHelper 3 | ## Helper class for working with [RDTextureFormat]s and [enum RenderingDevice.DataFormat]s. 4 | 5 | const image_format_to_data_format_array: Array[RenderingDevice.DataFormat] = [ 6 | # Based on https://github.com/godotengine/godot/blob/master/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp and _validate_texture_format() 7 | RenderingDevice.DATA_FORMAT_R8_UNORM, # 0 FORMAT_L8 8 | RenderingDevice.DATA_FORMAT_R8G8_UNORM, # 1 FORMAT_LA8 9 | RenderingDevice.DATA_FORMAT_R8_UNORM, # 2 FORMAT_R8 10 | RenderingDevice.DATA_FORMAT_R8G8_UNORM, # 3 FORMAT_RG8 11 | RenderingDevice.DATA_FORMAT_R8G8B8_UNORM, # 4 FORMAT_RGB8 12 | RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM, # 5 FORMAT_RGBA8 13 | RenderingDevice.DATA_FORMAT_R4G4B4A4_UNORM_PACK16, # 6 FORMAT_RGBA4444 14 | RenderingDevice.DATA_FORMAT_R5G6B5_UNORM_PACK16, # 7 FORMAT_RGB565 15 | RenderingDevice.DATA_FORMAT_R32_SFLOAT, # 8 FORMAT_RF 16 | RenderingDevice.DATA_FORMAT_R32G32_SFLOAT, # 9 FORMAT_RGF 17 | RenderingDevice.DATA_FORMAT_R32G32B32_SFLOAT, # 10 FORMAT_RGBF 18 | RenderingDevice.DATA_FORMAT_R32G32B32A32_SFLOAT, # 11 FORMAT_RGBAF 19 | RenderingDevice.DATA_FORMAT_R16_SFLOAT, # 12 FORMAT_RH 20 | RenderingDevice.DATA_FORMAT_R16G16_SFLOAT, # 13 FORMAT_RGH 21 | RenderingDevice.DATA_FORMAT_R16G16B16_SFLOAT, # 14 FORMAT_RGBH 22 | RenderingDevice.DATA_FORMAT_R16G16B16A16_SFLOAT, # 15 FORMAT_RGBAH 23 | RenderingDevice.DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32, # 16 FORMAT_RGBE9995 24 | RenderingDevice.DATA_FORMAT_BC1_RGB_UNORM_BLOCK, # 17 FORMAT_DXT1 25 | RenderingDevice.DATA_FORMAT_BC2_UNORM_BLOCK, # 18 FORMAT_DXT3 26 | RenderingDevice.DATA_FORMAT_BC3_UNORM_BLOCK, # 19 FORMAT_DXT5 27 | RenderingDevice.DATA_FORMAT_BC4_UNORM_BLOCK, # 20 FORMAT_RGTC_R 28 | RenderingDevice.DATA_FORMAT_BC5_UNORM_BLOCK, # 21 FORMAT_RGTC_RG 29 | RenderingDevice.DATA_FORMAT_BC7_UNORM_BLOCK, # 22 FORMAT_BPTC_RGBA 30 | RenderingDevice.DATA_FORMAT_BC6H_SFLOAT_BLOCK, # 23 FORMAT_BPTC_RGBF 31 | RenderingDevice.DATA_FORMAT_BC6H_UFLOAT_BLOCK, # 24 FORMAT_BPTC_RGBFU 32 | RenderingDevice.DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, # 25 FORMAT_ETC 33 | RenderingDevice.DATA_FORMAT_EAC_R11_UNORM_BLOCK, # 26 FORMAT_ETC2_R11 34 | RenderingDevice.DATA_FORMAT_EAC_R11_SNORM_BLOCK, # 27 FORMAT_ETC2_R11S 35 | RenderingDevice.DATA_FORMAT_EAC_R11G11_UNORM_BLOCK, # 28 FORMAT_ETC2_RG11 36 | RenderingDevice.DATA_FORMAT_EAC_R11G11_SNORM_BLOCK, # 29 FORMAT_ETC2_RG11S 37 | RenderingDevice.DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, # 30 FORMAT_ETC2_RGB8 38 | RenderingDevice.DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, # 31 FORMAT_ETC2_RGBA8 39 | RenderingDevice.DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, # 32 FORMAT_ETC2_RGB8A1 40 | RenderingDevice.DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, # 33 FORMAT_ETC2_RA_AS_RG 41 | RenderingDevice.DATA_FORMAT_BC3_UNORM_BLOCK, # 34 FORMAT_DXT5_RA_AS_RG 42 | RenderingDevice.DATA_FORMAT_ASTC_4x4_UNORM_BLOCK, # 35 FORMAT_ASTC_4x4 43 | RenderingDevice.DATA_FORMAT_ASTC_4x4_UNORM_BLOCK, # 36 FORMAT_ASTC_4x4_HDR 44 | RenderingDevice.DATA_FORMAT_ASTC_8x8_UNORM_BLOCK, # 37 FORMAT_ASTC_8x8 45 | RenderingDevice.DATA_FORMAT_ASTC_8x8_UNORM_BLOCK, # 38 FORMAT_ASTC_8x8_HDR 46 | # You might run into bugs using formats other than the standard RGBAF in shaders 47 | ] 48 | 49 | ## Returns a [enum RenderingDevice.DataFormat] corresponding to [param format]. 50 | static func convert_image_format_to_data_format(format: Image.Format) -> RenderingDevice.DataFormat: 51 | var data_format := RenderingDevice.DATA_FORMAT_MAX 52 | if int(format) < image_format_to_data_format_array.size(): 53 | data_format = image_format_to_data_format_array[format] 54 | assert(data_format != RenderingDevice.DATA_FORMAT_MAX, "Invalid image format used") 55 | return data_format 56 | 57 | ## Returns an [RDTextureFormat] with the [param format] and [param resolution] specified. 58 | static func create_rd_texture_format(format: Image.Format, resolution: Vector2i) -> RDTextureFormat: 59 | var texture_format := RDTextureFormat.new() 60 | texture_format.width = resolution.x 61 | texture_format.height = resolution.y 62 | texture_format.format = ImageFormatHelper.convert_image_format_to_data_format(format) 63 | texture_format.usage_bits = ( 64 | RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT + 65 | RenderingDevice.TEXTURE_USAGE_STORAGE_BIT + 66 | RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT + 67 | RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT 68 | ) 69 | return texture_format 70 | -------------------------------------------------------------------------------- /addons/compute_shader_plus/uniforms/image_uniform.gd: -------------------------------------------------------------------------------- 1 | extends Uniform 2 | class_name ImageUniform 3 | ## [Uniform] corresponding to a texture. Given to the shader as an image. 4 | 5 | var texture: RID ## The [RID] of the corresponding texture. Used internally. 6 | var texture_size: Vector2i ## The resolution of the texture. 7 | var image_format: Image.Format ## The [enum Image.Format] of the texture. 8 | var texture_format: RDTextureFormat ## The [RDTextureFormat] of the texture. 9 | 10 | signal async_image_retrieved(image: Image) 11 | 12 | ## Returns a new ImageUniform object using the given [param image]. 13 | static func create(image: Image) -> ImageUniform: 14 | var uniform := ImageUniform.new() 15 | uniform.texture_size = image.get_size() 16 | uniform.image_format = image.get_format() 17 | uniform.texture_format = ImageFormatHelper.create_rd_texture_format(uniform.image_format, uniform.texture_size) 18 | uniform.texture = ComputeHelper.rd.texture_create(uniform.texture_format, ComputeHelper.view, [image.get_data()]) 19 | return uniform 20 | 21 | ## ImageUniform's custom implementation of [method Uniform.get_rd_uniform]. 22 | func get_rd_uniform(binding: int) -> RDUniform: 23 | var uniform := RDUniform.new() 24 | uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE 25 | uniform.binding = binding 26 | uniform.add_id(texture) 27 | return uniform 28 | 29 | ## Updates the texture to match [param image]. 30 | func update_image(image: Image) -> void: 31 | if texture_size == image.get_size() and image_format == image.get_format(): 32 | ComputeHelper.rd.texture_update(texture, 0, image.get_data()) 33 | else: 34 | ComputeHelper.rd.free_rid(texture) 35 | image_format = image.get_format() 36 | texture_size = image.get_size() 37 | texture_format = ImageFormatHelper.create_rd_texture_format(image_format, texture_size) 38 | texture = ComputeHelper.rd.texture_create(texture_format, ComputeHelper.view, [image.get_data()]) 39 | rid_updated.emit(self) 40 | 41 | ## Returns a new [Image] that has the data of the texture. [b]Warning:[/b] Getting data from the GPU is very slow. 42 | func get_image() -> Image: 43 | var image_data := ComputeHelper.rd.texture_get_data(texture, 0) 44 | return Image.create_from_data(texture_size.x, texture_size.y, false, image_format, image_data) 45 | 46 | ## Gets the image's data asynchronously. Returns a [Signal] with an [Image] that will be emitted when the image is retrieved. The signal remains the same each time you call this function, so feel free to cache it. [b]Note:[\b] The delay to when the signal is emitted corresponds to the amount of frames specified by [member ProjectSettings.rendering/rendering_device/vsync/frame_queue_size]. Also, this function does nothing before Godot 4.4. 47 | func get_image_async() -> Signal: 48 | if ComputeHelper.version < 4: 49 | return async_image_retrieved 50 | ComputeHelper.rd.texture_get_data_async(texture, 0, async_image_callback) 51 | return async_image_retrieved 52 | 53 | ## Used internally for [get_image_async]. 54 | func async_image_callback(image_data: PackedByteArray) -> void: 55 | var image := Image.create_from_data(texture_size.x, texture_size.y, false, image_format, image_data) 56 | async_image_retrieved.emit(image) 57 | 58 | func _notification(what: int) -> void: 59 | if what == NOTIFICATION_PREDELETE: 60 | ComputeHelper.rd.free_rid(texture) 61 | -------------------------------------------------------------------------------- /addons/compute_shader_plus/uniforms/sampler_uniform.gd: -------------------------------------------------------------------------------- 1 | extends ImageUniform 2 | class_name SamplerUniform 3 | ## [Uniform] corresponding to a texture. Given to the shader as a sampler. 4 | 5 | var sampler: RID ## The [RID] of the corresponding texture. Used internally. 6 | var sampler_state: RDSamplerState ## The sampler's settings. 7 | 8 | ## Returns a new SamplerUniform object using the given [param image]. 9 | static func create(image: Image) -> SamplerUniform: 10 | var uniform := SamplerUniform.new() 11 | uniform.texture_size = image.get_size() 12 | uniform.image_format = image.get_format() 13 | uniform.texture_format = ImageFormatHelper.create_rd_texture_format(uniform.image_format, uniform.texture_size) 14 | uniform.texture = ComputeHelper.rd.texture_create(uniform.texture_format, ComputeHelper.view, [image.get_data()]) 15 | uniform.sampler_state = RDSamplerState.new() 16 | uniform.sampler = ComputeHelper.rd.sampler_create(uniform.sampler_state) 17 | return uniform 18 | 19 | ## SamplerUniform's custom implementation of [method Uniform.get_rd_uniform]. 20 | func get_rd_uniform(binding: int) -> RDUniform: 21 | if !sampler_state or !sampler: 22 | sampler_state = RDSamplerState.new() 23 | sampler = ComputeHelper.rd.sampler_create(sampler_state) 24 | 25 | var uniform := RDUniform.new() 26 | uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE 27 | uniform.binding = binding 28 | uniform.add_id(sampler) 29 | uniform.add_id(texture) 30 | return uniform 31 | 32 | func _notification(what: int) -> void: 33 | if what == NOTIFICATION_PREDELETE: 34 | ComputeHelper.rd.free_rid(sampler) 35 | ComputeHelper.rd.free_rid(texture) 36 | -------------------------------------------------------------------------------- /addons/compute_shader_plus/uniforms/shared_image_uniform.gd: -------------------------------------------------------------------------------- 1 | extends Uniform 2 | class_name SharedImageUniform 3 | ## [Uniform] corresponding to a texture. Shares data with an [ImageUniform]. Given to the shader as an image. 4 | 5 | var texture: RID ## The [RID] of the corresponding texture. Used internally. 6 | var texture_size: Vector2i ## The resolution of the texture. 7 | var base_image_uniform: ImageUniform ## The [ImageUniform] this uniform shares data with. 8 | 9 | ## Returns a new SharedImageUniform object using the given [param image_uniform]. 10 | static func create(image_uniform: ImageUniform) -> SharedImageUniform: 11 | var uniform := SharedImageUniform.new() 12 | uniform.texture = ComputeHelper.rd.texture_create_shared(ComputeHelper.view, image_uniform.texture) 13 | uniform.texture_size = image_uniform.texture_size 14 | uniform.base_image_uniform = image_uniform 15 | uniform.base_image_uniform.rid_updated.connect(uniform.update_uniform) 16 | return uniform 17 | 18 | ## SharedImageUniform's custom implementation of [method Uniform.get_rd_uniform]. 19 | func get_rd_uniform(binding: int) -> RDUniform: 20 | var uniform := RDUniform.new() 21 | uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE 22 | uniform.binding = binding 23 | uniform.add_id(texture) 24 | return uniform 25 | 26 | ## Updates the base image uniform to match [param image_uniform]. 27 | func update_uniform(image_uniform: ImageUniform) -> void: 28 | texture = ComputeHelper.rd.texture_create_shared(ComputeHelper.view, image_uniform.texture) 29 | texture_size = image_uniform.texture_size 30 | rid_updated.emit(self) 31 | if base_image_uniform != image_uniform: 32 | base_image_uniform.rid_updated.disconnect(update_uniform) 33 | base_image_uniform = image_uniform 34 | base_image_uniform.rid_updated.connect(update_uniform) 35 | 36 | ## Returns a new [Image] that has the data of the texture. [b]Warning:[/b] Getting data from the GPU is very slow. 37 | func get_image() -> Image: 38 | var image_data := ComputeHelper.rd.texture_get_data(texture, 0) 39 | return Image.create_from_data(texture_size.x, texture_size.y, false, base_image_uniform.image_format, image_data) 40 | 41 | func _notification(what: int) -> void: 42 | if what == NOTIFICATION_PREDELETE: 43 | ComputeHelper.rd.free_rid(texture) 44 | -------------------------------------------------------------------------------- /addons/compute_shader_plus/uniforms/storage_buffer_uniform.gd: -------------------------------------------------------------------------------- 1 | extends Uniform 2 | class_name StorageBufferUniform 3 | ## [Uniform] corresponding to arbitrary data. 4 | 5 | var storage_buffer: RID ## The [RID] of the corresponding storage buffer. Used internally. 6 | var storage_buffer_size := 0 ## The size of the data in bytes. 7 | 8 | signal async_data_retrieved(data: PackedByteArray) 9 | 10 | ## Returns a new StorageBufferUniform object using the given [param data]. 11 | static func create(data: PackedByteArray) -> StorageBufferUniform: 12 | var uniform := StorageBufferUniform.new() 13 | uniform.storage_buffer_size = data.size() 14 | uniform.storage_buffer = ComputeHelper.rd.storage_buffer_create(uniform.storage_buffer_size, data) 15 | return uniform 16 | 17 | ## Swaps data between two StorageBufferUniform objects. 18 | static func swap_buffers(storage_buffer_1: StorageBufferUniform, storage_buffer_2: StorageBufferUniform) -> void: 19 | var storage_buffer_1_rid := storage_buffer_1.storage_buffer 20 | var storage_buffer_1_size := storage_buffer_1.storage_buffer_size 21 | 22 | storage_buffer_1.storage_buffer = storage_buffer_2.storage_buffer 23 | storage_buffer_1.storage_buffer_size = storage_buffer_2.storage_buffer_size 24 | storage_buffer_2.storage_buffer = storage_buffer_1_rid 25 | storage_buffer_2.storage_buffer_size = storage_buffer_1_size 26 | 27 | storage_buffer_1.rid_updated.emit(storage_buffer_1) 28 | storage_buffer_2.rid_updated.emit(storage_buffer_2) 29 | 30 | ## StorageBufferUniform's custom implementation of [method Uniform.get_rd_uniform]. 31 | func get_rd_uniform(binding: int) -> RDUniform: 32 | var uniform := RDUniform.new() 33 | uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER 34 | uniform.binding = binding 35 | uniform.add_id(storage_buffer) 36 | return uniform 37 | 38 | ## Updates the currently stored data to match the given [param data]. 39 | func update_data(data: PackedByteArray) -> void: 40 | if storage_buffer_size == data.size(): 41 | ComputeHelper.rd.buffer_update(storage_buffer, 0, storage_buffer_size, data) 42 | else: 43 | ComputeHelper.rd.free_rid(storage_buffer) 44 | storage_buffer_size = data.size() 45 | storage_buffer = ComputeHelper.rd.storage_buffer_create(storage_buffer_size, data) 46 | rid_updated.emit(self) 47 | 48 | ## Returns a [PackedByteArray] with the current data. [b]Warning:[/b] This can lead to performance issues. 49 | func get_data() -> PackedByteArray: 50 | return ComputeHelper.rd.buffer_get_data(storage_buffer) 51 | 52 | ## Gets the buffer's data asynchronously. Returns a [Signal] with a [PackedByteArray] that will be emitted when the data is retrieved. The signal remains the same each time you call this function, so feel free to cache it. [b]Note:[\b] The delay to when the signal is emitted corresponds to the amount of frames specified by [member ProjectSettings.rendering/rendering_device/vsync/frame_queue_size]. Also, this function does nothing before Godot 4.4. 53 | func get_data_async() -> Signal: 54 | if ComputeHelper.version < 4: 55 | return async_data_retrieved 56 | ComputeHelper.rd.buffer_get_data_async(storage_buffer, async_data_retrieved.emit) 57 | return async_data_retrieved 58 | 59 | func _notification(what: int) -> void: 60 | if what == NOTIFICATION_PREDELETE: 61 | ComputeHelper.rd.free_rid(storage_buffer) 62 | -------------------------------------------------------------------------------- /addons/compute_shader_plus/uniforms/uniform.gd: -------------------------------------------------------------------------------- 1 | extends Object 2 | class_name Uniform 3 | ## The base class for all uniform types. 4 | 5 | ## Emitted when an internal RID is updated 6 | signal rid_updated(uniform: Uniform) 7 | 8 | ## Return an RDUniform object with the given [param binding]. Used internally by [ComputeHelper]. 9 | func get_rd_uniform(_binding: int) -> RDUniform: 10 | return null 11 | -------------------------------------------------------------------------------- /addons/compute_shader_plus/uniforms/uniform_buffer_uniform.gd: -------------------------------------------------------------------------------- 1 | extends StorageBufferUniform 2 | class_name UniformBufferUniform 3 | ## [Uniform] corresponding to arbitrary data. 4 | 5 | ## Returns a new UniformBufferUniform object using the given [param data]. 6 | static func create(data: PackedByteArray) -> UniformBufferUniform: 7 | while data.size() % 16 != 0: 8 | data.append(0) 9 | 10 | var uniform := UniformBufferUniform.new() 11 | uniform.storage_buffer_size = data.size() 12 | uniform.storage_buffer = ComputeHelper.rd.uniform_buffer_create(uniform.storage_buffer_size, data) 13 | return uniform 14 | 15 | ## UniformBufferUniform's custom implementation of [method Uniform.get_rd_uniform]. 16 | func get_rd_uniform(binding: int) -> RDUniform: 17 | var uniform := RDUniform.new() 18 | uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_UNIFORM_BUFFER 19 | uniform.binding = binding 20 | uniform.add_id(storage_buffer) 21 | return uniform 22 | 23 | ## Updates the currently stored data to match the given [param data]. 24 | func update_data(data: PackedByteArray) -> void: 25 | while data.size() % 16 != 0: 26 | data.append(0) 27 | 28 | if storage_buffer_size == data.size(): 29 | ComputeHelper.rd.buffer_update(storage_buffer, 0, storage_buffer_size, data) 30 | else: 31 | ComputeHelper.rd.free_rid(storage_buffer) 32 | storage_buffer_size = data.size() 33 | storage_buffer = ComputeHelper.rd.uniform_buffer_create(storage_buffer_size, data) 34 | rid_updated.emit(self) 35 | -------------------------------------------------------------------------------- /addons/compute_shader_plus/uniforms/vertex_buffer_uniform.gd: -------------------------------------------------------------------------------- 1 | extends StorageBufferUniform 2 | class_name VertexBufferUniform 3 | ## [Uniform] corresponding to vertex data. Use this when you want to reuse the data as a vertex buffer. 4 | 5 | ## Returns a new VertexBufferUniform object using the given [param data]. 6 | static func create(data: PackedByteArray) -> VertexBufferUniform: 7 | var uniform := VertexBufferUniform.new() 8 | uniform.storage_buffer_size = data.size() 9 | uniform.storage_buffer = ComputeHelper.rd.vertex_buffer_create(uniform.storage_buffer_size, data, true) 10 | return uniform 11 | 12 | ## Swaps data between two VertexBufferUniform objects. Both parameters should be VertexBufferUniforms. 13 | static func swap_buffers(storage_buffer_1: StorageBufferUniform, storage_buffer_2: StorageBufferUniform) -> void: 14 | if !storage_buffer_1.is_class("VertexBufferUniform") or !storage_buffer_2.is_class("VertexBufferUniform"): 15 | return 16 | 17 | var storage_buffer_1_rid := storage_buffer_1.storage_buffer 18 | var storage_buffer_1_size := storage_buffer_1.storage_buffer_size 19 | 20 | storage_buffer_1.storage_buffer = storage_buffer_2.storage_buffer 21 | storage_buffer_1.storage_buffer_size = storage_buffer_2.storage_buffer_size 22 | storage_buffer_2.storage_buffer = storage_buffer_1_rid 23 | storage_buffer_2.storage_buffer_size = storage_buffer_1_size 24 | 25 | storage_buffer_1.rid_updated.emit(storage_buffer_1) 26 | storage_buffer_2.rid_updated.emit(storage_buffer_2) 27 | 28 | ## Updates the currently stored data to match the given [param data]. 29 | func update_data(data: PackedByteArray) -> void: 30 | if storage_buffer_size == data.size(): 31 | ComputeHelper.rd.buffer_update(storage_buffer, 0, storage_buffer_size, data) 32 | else: 33 | ComputeHelper.rd.free_rid(storage_buffer) 34 | storage_buffer_size = data.size() 35 | storage_buffer = ComputeHelper.rd.vertex_buffer_create(storage_buffer_size, data, true) 36 | rid_updated.emit(self) 37 | --------------------------------------------------------------------------------