From 4bd5a222adb69df983c96cd97990715686aa5d80 Mon Sep 17 00:00:00 2001 From: betalars Date: Wed, 8 Oct 2025 00:25:42 +0200 Subject: [PATCH] updating post processing template --- .../post_process_shader_template.gd | 144 ++++++++++++------ 1 file changed, 101 insertions(+), 43 deletions(-) diff --git a/src/vfx/post_processing/post_process_shader_template.gd b/src/vfx/post_processing/post_process_shader_template.gd index fa58eb1..0e4e4f5 100644 --- a/src/vfx/post_processing/post_process_shader_template.gd +++ b/src/vfx/post_processing/post_process_shader_template.gd @@ -1,39 +1,54 @@ @tool -extends CompositorEffect class_name PostProcessShader +extends CompositorEffect -const template_shader: String = """ -#version 450 +const TEMPLATE_SHADER: String = """#version 450 -// Invocations in the (x, y, z) dimension +#define MAX_VIEWS 2 + +#include "godot/scene_data_inc.glsl" + +// Invocations in the (x, y, z) dimension. layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; -layout(rgba16f, set = 0, binding = 0) uniform image2D color_image; +layout(set = 0, binding = 0, std140) uniform SceneDataBlock { + SceneData data; + SceneData prev_data; +} +scene_data_block; -// Our push constant +layout(rgba16f, set = 0, binding = 1) uniform image2D color_image; +layout(set = 0, binding = 2) uniform sampler2D depth_texture; + +// Our push constant. +// Must be aligned to 16 bytes, just like the push constant we passed from the script. layout(push_constant, std430) uniform Params { vec2 raster_size; - vec2 reserved; + float view; + float pad; } params; -// The code we want to execute in each invocation +// The code we want to execute in each invocation. void main() { ivec2 uv = ivec2(gl_GlobalInvocationID.xy); ivec2 size = ivec2(params.raster_size); + int view = int(params.view); if (uv.x >= size.x || uv.y >= size.y) { return; } + vec2 uv_norm = vec2(uv) / params.raster_size; + vec4 color = imageLoad(color_image, uv); + float depth = texture(depth_texture, uv_norm).r; #COMPUTE_CODE imageStore(color_image, uv, color); -} -""" +}""" -@export_multiline var shader_code: String = "": +@export_multiline var shader_code := "": set(value): mutex.lock() shader_code = value @@ -43,20 +58,30 @@ void main() { var rd: RenderingDevice var shader: RID var pipeline: RID +var nearest_sampler: RID -var mutex: Mutex = Mutex.new() +var mutex := Mutex.new() var shader_is_dirty: bool = true -func _init(): + +func _init() -> void: effect_callback_type = EFFECT_CALLBACK_TYPE_POST_TRANSPARENT rd = RenderingServer.get_rendering_device() -func _notification(what): + +# System notifications, we want to react on the notification that +# alerts us we are about to be destroyed. +func _notification(what: int) -> void: if what == NOTIFICATION_PREDELETE: if shader.is_valid(): # Freeing our shader will also free any dependents such as the pipeline! - rd.free_rid(shader) + RenderingServer.free_rid(shader) + if nearest_sampler.is_valid(): + rd.free_rid(nearest_sampler) + +#region Code in this region runs on the rendering thread. +# Check if our shader has changed and needs to be recompiled. func _check_shader() -> bool: if not rd: return false @@ -75,7 +100,7 @@ func _check_shader() -> bool: return pipeline.is_valid() # Apply template. - new_shader_code = template_shader.replace("#COMPUTE_CODE", new_shader_code); + new_shader_code = TEMPLATE_SHADER.replace("#COMPUTE_CODE", new_shader_code); # Out with the old. if shader.is_valid(): @@ -84,7 +109,7 @@ func _check_shader() -> bool: pipeline = RID() # In with the new. - var shader_source: RDShaderSource = RDShaderSource.new() + var shader_source := RDShaderSource.new() shader_source.language = RenderingDevice.SHADER_LANGUAGE_GLSL shader_source.source_compute = new_shader_code var shader_spirv: RDShaderSPIRV = rd.shader_compile_spirv_from_source(shader_source) @@ -99,49 +124,82 @@ func _check_shader() -> bool: return false pipeline = rd.compute_pipeline_create(shader) + return pipeline.is_valid() -func _render_callback(p_effect_callback_type, p_render_data): + +# Called by the rendering thread every frame. +func _render_callback(p_effect_callback_type: EffectCallbackType, p_render_data: RenderData) -> void: if rd and p_effect_callback_type == EFFECT_CALLBACK_TYPE_POST_TRANSPARENT and _check_shader(): # Get our render scene buffers object, this gives us access to our render buffers. # Note that implementation differs per renderer hence the need for the cast. - var render_scene_buffers: RenderSceneBuffersRD = p_render_data.get_render_scene_buffers() - if render_scene_buffers: + var render_scene_buffers: RenderSceneBuffers = p_render_data.get_render_scene_buffers() + var scene_data: RenderSceneData = p_render_data.get_render_scene_data() + if render_scene_buffers and scene_data: # Get our render size, this is the 3D render resolution! - var size = render_scene_buffers.get_internal_size() + var size: Vector2i = render_scene_buffers.get_internal_size() if size.x == 0 and size.y == 0: return # We can use a compute shader here. - var x_groups = (size.x - 1) / 8 + 1 - var y_groups = (size.y - 1) / 8 + 1 - var z_groups = 1 + @warning_ignore("integer_division") + var x_groups: int = (size.x - 1) / 8 + 1 + @warning_ignore("integer_division") + var y_groups: int = (size.y - 1) / 8 + 1 + var z_groups: int = 1 - # Push constant. - var push_constant: PackedFloat32Array = PackedFloat32Array() - push_constant.push_back(size.x) - push_constant.push_back(size.y) - push_constant.push_back(0.0) - push_constant.push_back(0.0) + # Create push constant. + # Must be aligned to 16 bytes and be in the same order as defined in the shader. + var push_constant := PackedFloat32Array([ + size.x, + size.y, + 0.0, + 0.0, + ]) + + # Make sure we have a sampler. + if not nearest_sampler.is_valid(): + var sampler_state: RDSamplerState = RDSamplerState.new() + sampler_state.min_filter = RenderingDevice.SAMPLER_FILTER_NEAREST + sampler_state.mag_filter = RenderingDevice.SAMPLER_FILTER_NEAREST + nearest_sampler = rd.sampler_create(sampler_state) # Loop through views just in case we're doing stereo rendering. No extra cost if this is mono. - var view_count = render_scene_buffers.get_view_count() - for view in range(view_count): - # Get the RID for our color image, we will be reading from and writing to it. - var input_image = render_scene_buffers.get_color_layer(view) + var view_count: int = render_scene_buffers.get_view_count() + for view in view_count: + # Get the RID for our scene data buffer. + var scene_data_buffers: RID = scene_data.get_uniform_buffer() - # Create a uniform set. - # This will be cached; the cache will be cleared if our viewport's configuration is changed. - var uniform: RDUniform = RDUniform.new() - uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE - uniform.binding = 0 - uniform.add_id(input_image) - var uniform_set = UniformSetCacheRD.get_cache(shader, 0, [ uniform ]) + # Get the RID for our color image, we will be reading from and writing to it. + var color_image: RID = render_scene_buffers.get_color_layer(view) + + # Get the RID for our depth image, we will be reading from it. + var depth_image: RID = render_scene_buffers.get_depth_layer(view) + + # Create a uniform set, this will be cached, the cache will be cleared if our viewports configuration is changed. + var scene_data_uniform := RDUniform.new() + scene_data_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_UNIFORM_BUFFER + scene_data_uniform.binding = 0 + scene_data_uniform.add_id(scene_data_buffers) + var color_uniform := RDUniform.new() + color_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + color_uniform.binding = 1 + color_uniform.add_id(color_image) + var depth_uniform := RDUniform.new() + depth_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + depth_uniform.binding = 2 + depth_uniform.add_id(nearest_sampler) + depth_uniform.add_id(depth_image) + var uniform_set_rid: RID = UniformSetCacheRD.get_cache(shader, 0, [scene_data_uniform, color_uniform, depth_uniform]) + + # Set our view. + push_constant[2] = view # Run our compute shader. - var compute_list:= rd.compute_list_begin() + var compute_list: int = 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_bind_uniform_set(compute_list, uniform_set_rid, 0) rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(), push_constant.size() * 4) rd.compute_list_dispatch(compute_list, x_groups, y_groups, z_groups) rd.compute_list_end() +#endregion