forked from godotengine/godot
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a post-process shader template for CompositorEffect
- Loading branch information
Showing
3 changed files
with
244 additions
and
0 deletions.
There are no files selected for viewing
133 changes: 133 additions & 0 deletions
133
modules/gdscript/editor/script_templates/CompositorEffect/default.gd
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
# meta-description: Base template for CompositorEffect | ||
|
||
@tool | ||
# Having a class name is handy for picking the effect in the Inspector. | ||
class_name CompositorEffect_CLASS_ | ||
extends _BASE_ | ||
|
||
|
||
const SHADER_CODE: String = "#version 450 | ||
// 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; | ||
// Our push constant. | ||
layout(push_constant, std430) uniform Params { | ||
vec2 raster_size; | ||
vec2 reserved; | ||
} params; | ||
// The code we want to execute in each invocation. | ||
void main() { | ||
ivec2 uv = ivec2(gl_GlobalInvocationID.xy); | ||
ivec2 size = ivec2(params.raster_size); | ||
if (uv.x >= size.x || uv.y >= size.y) { | ||
return; | ||
} | ||
vec4 color = imageLoad(color_image, uv); | ||
// Place your code here. | ||
imageStore(color_image, uv, color); | ||
}" | ||
|
||
var rd: RenderingDevice | ||
var shader: RID | ||
var pipeline: RID | ||
|
||
|
||
# Called when this resource is constructed. | ||
func _init() -> void: | ||
effect_callback_type = EFFECT_CALLBACK_TYPE_POST_TRANSPARENT | ||
rd = RenderingServer.get_rendering_device() | ||
|
||
|
||
# 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! | ||
RenderingServer.free_rid(shader) | ||
|
||
|
||
# Check if our shader has changed and needs to be recompiled. | ||
func _check_shader() -> bool: | ||
if not rd: | ||
return false | ||
|
||
# Out with the old. | ||
if shader != null and shader.is_valid(): | ||
rd.free_rid(shader) | ||
shader = RID() | ||
pipeline = RID() | ||
|
||
# In with the new. | ||
var shader_source := RDShaderSource.new() | ||
shader_source.language = RenderingDevice.SHADER_LANGUAGE_GLSL | ||
shader_source.source_compute = SHADER_CODE | ||
var shader_spirv := rd.shader_compile_spirv_from_source(shader_source) | ||
|
||
if not shader_spirv.compile_error_compute.is_empty(): | ||
push_error(shader_spirv.compile_error_compute) | ||
return false | ||
|
||
shader = rd.shader_create_from_spirv(shader_spirv) | ||
if not shader.is_valid(): | ||
return false | ||
|
||
pipeline = rd.compute_pipeline_create(shader) | ||
return pipeline.is_valid() | ||
|
||
|
||
# Called by the rendering thread every frame. | ||
func _render_callback(effect_callback_type: int, render_data: RenderData) -> void: | ||
if rd and 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 := render_data.get_render_scene_buffers() | ||
if render_scene_buffers: | ||
# Get our render size, this is the 3D render resolution! | ||
var size := 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 | ||
|
||
# Push constant. | ||
var push_constant := 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) | ||
|
||
# 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) | ||
|
||
# Create a uniform set, this will be cached, | ||
# the cache will be cleared if our viewports configuration is changed. | ||
var uniform := 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]) | ||
|
||
# Run our compute shader. | ||
var compute_list := 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_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() |
108 changes: 108 additions & 0 deletions
108
modules/gdscript/editor/script_templates/CompositorEffect/no_comments.gd
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
# meta-description: Base template for CompositorEffect (no comments) | ||
|
||
@tool | ||
class_name CompositorEffect_CLASS_ | ||
extends _BASE_ | ||
|
||
|
||
const SHADER_CODE: String = "#version 450 | ||
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(push_constant, std430) uniform Params { | ||
vec2 raster_size; | ||
vec2 reserved; | ||
} params; | ||
void main() { | ||
ivec2 uv = ivec2(gl_GlobalInvocationID.xy); | ||
ivec2 size = ivec2(params.raster_size); | ||
if (uv.x >= size.x || uv.y >= size.y) { | ||
return; | ||
} | ||
vec4 color = imageLoad(color_image, uv); | ||
imageStore(color_image, uv, color); | ||
}" | ||
|
||
var rd: RenderingDevice | ||
var shader: RID | ||
var pipeline: RID | ||
|
||
|
||
func _init() -> void: | ||
effect_callback_type = EFFECT_CALLBACK_TYPE_POST_TRANSPARENT | ||
rd = RenderingServer.get_rendering_device() | ||
|
||
|
||
func _notification(what: int) -> void: | ||
if what == NOTIFICATION_PREDELETE: | ||
if shader.is_valid(): | ||
RenderingServer.free_rid(shader) | ||
|
||
|
||
func _check_shader() -> bool: | ||
if not rd: | ||
return false | ||
|
||
if shader != null and shader.is_valid(): | ||
rd.free_rid(shader) | ||
shader = RID() | ||
pipeline = RID() | ||
|
||
var shader_source := RDShaderSource.new() | ||
shader_source.language = RenderingDevice.SHADER_LANGUAGE_GLSL | ||
shader_source.source_compute = SHADER_CODE | ||
var shader_spirv := rd.shader_compile_spirv_from_source(shader_source) | ||
|
||
if not shader_spirv.compile_error_compute.is_empty(): | ||
push_error(shader_spirv.compile_error_compute) | ||
return false | ||
|
||
shader = rd.shader_create_from_spirv(shader_spirv) | ||
if not shader.is_valid(): | ||
return false | ||
|
||
pipeline = rd.compute_pipeline_create(shader) | ||
return pipeline.is_valid() | ||
|
||
|
||
func _render_callback(effect_callback_type: int, render_data: RenderData) -> void: | ||
if rd and effect_callback_type == EFFECT_CALLBACK_TYPE_POST_TRANSPARENT and _check_shader(): | ||
var render_scene_buffers := render_data.get_render_scene_buffers() | ||
if render_scene_buffers: | ||
var size := render_scene_buffers.get_internal_size() | ||
if size.x == 0 and size.y == 0: | ||
return | ||
|
||
var x_groups := (size.x - 1) / 8 + 1 | ||
var y_groups := (size.y - 1) / 8 + 1 | ||
var z_groups := 1 | ||
|
||
var push_constant := 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) | ||
|
||
var view_count := render_scene_buffers.get_view_count() | ||
for view in range(view_count): | ||
var input_image := render_scene_buffers.get_color_layer(view) | ||
|
||
var uniform := 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]) | ||
|
||
var compute_list := 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_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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters