Skip to content

Commit 60a6161

Browse files
committed
Add a post-process shader template for CompositorEffect
1 parent f0d15bb commit 60a6161

File tree

3 files changed

+248
-0
lines changed

3 files changed

+248
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# meta-description: Base template for CompositorEffect
2+
3+
@tool
4+
# Having a class name is handy for picking the effect in the Inspector.
5+
class_name CompositorEffect_CLASS_
6+
extends _BASE_
7+
8+
9+
const SHADER_CODE: String = "#version 450
10+
11+
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
12+
13+
layout(rgba16f, set = 0, binding = 0) uniform image2D color_image;
14+
15+
layout(push_constant, std430) uniform Params {
16+
vec2 raster_size;
17+
vec2 reserved;
18+
} params;
19+
20+
void main() {
21+
ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
22+
ivec2 size = ivec2(params.raster_size);
23+
24+
if (uv.x >= size.x || uv.y >= size.y) {
25+
return;
26+
}
27+
28+
vec4 color = imageLoad(color_image, uv);
29+
30+
// Place your code here.
31+
32+
imageStore(color_image, uv, color);
33+
}"
34+
35+
var rd: RenderingDevice
36+
var shader := RID()
37+
var pipeline := RID()
38+
39+
40+
func _init() -> void:
41+
effect_callback_type = EFFECT_CALLBACK_TYPE_POST_TRANSPARENT
42+
rd = RenderingServer.get_rendering_device()
43+
44+
45+
func _notification(what: int) -> void:
46+
if what == NOTIFICATION_PREDELETE:
47+
if shader.is_valid():
48+
RenderingServer.free_rid(shader)
49+
50+
51+
func _check_shader() -> bool:
52+
if not rd:
53+
return false
54+
55+
if shader.is_valid():
56+
rd.free_rid(shader)
57+
shader = RID()
58+
pipeline = RID()
59+
60+
var shader_source := RDShaderSource.new()
61+
shader_source.language = RenderingDevice.SHADER_LANGUAGE_GLSL
62+
shader_source.source_compute = SHADER_CODE
63+
var shader_spirv := rd.shader_compile_spirv_from_source(shader_source)
64+
65+
if not shader_spirv.compile_error_compute.is_empty():
66+
push_error(shader_spirv.compile_error_compute)
67+
return false
68+
69+
shader = rd.shader_create_from_spirv(shader_spirv)
70+
if not shader.is_valid():
71+
return false
72+
73+
pipeline = rd.compute_pipeline_create(shader)
74+
return pipeline.is_valid()
75+
76+
77+
func _render_callback(effect_callback_type: int, render_data: RenderData) -> void:
78+
if rd and effect_callback_type == EFFECT_CALLBACK_TYPE_POST_TRANSPARENT and _check_shader():
79+
var render_scene_buffers: RenderSceneBuffersRD = render_data.get_render_scene_buffers()
80+
if render_scene_buffers:
81+
var size := render_scene_buffers.get_internal_size()
82+
if size.x == 0 and size.y == 0:
83+
return
84+
85+
var x_groups := (size.x - 1) / 8 + 1
86+
var y_groups := (size.y - 1) / 8 + 1
87+
var z_groups := 1
88+
89+
var push_constant := PackedFloat32Array()
90+
push_constant.push_back(size.x)
91+
push_constant.push_back(size.y)
92+
push_constant.push_back(0.0)
93+
push_constant.push_back(0.0)
94+
95+
var view_count := render_scene_buffers.get_view_count()
96+
for view in range(view_count):
97+
var input_image := render_scene_buffers.get_color_layer(view)
98+
99+
var uniform := RDUniform.new()
100+
uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE
101+
uniform.binding = 0
102+
uniform.add_id(input_image)
103+
var uniform_set := UniformSetCacheRD.get_cache(shader, 0, [uniform])
104+
105+
var compute_list := rd.compute_list_begin()
106+
rd.compute_list_bind_compute_pipeline(compute_list, pipeline)
107+
rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0)
108+
rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(),
109+
push_constant.size() * 4)
110+
rd.compute_list_dispatch(compute_list, x_groups, y_groups, z_groups)
111+
rd.compute_list_end()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# meta-description: Base template for CompositorEffect with support for custom inline code insertion
2+
3+
@tool
4+
# Having a class name is handy for picking the effect in the Inspector.
5+
class_name CompositorEffect_CLASS_
6+
extends _BASE_
7+
8+
9+
const SHADER_CODE: String = "#version 450
10+
11+
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
12+
13+
layout(rgba16f, set = 0, binding = 0) uniform image2D color_image;
14+
15+
layout(push_constant, std430) uniform Params {
16+
vec2 raster_size;
17+
vec2 reserved;
18+
} params;
19+
20+
void main() {
21+
ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
22+
ivec2 size = ivec2(params.raster_size);
23+
24+
if (uv.x >= size.x || uv.y >= size.y) {
25+
return;
26+
}
27+
28+
vec4 color = imageLoad(color_image, uv);
29+
30+
#COMPUTE_CODE
31+
32+
imageStore(color_image, uv, color);
33+
}"
34+
35+
@export_multiline var shader_code: String = "":
36+
set(value):
37+
mutex.lock()
38+
shader_code = value
39+
shader_is_dirty = true
40+
mutex.unlock()
41+
42+
var rd: RenderingDevice
43+
var shader := RID()
44+
var pipeline := RID()
45+
46+
var mutex := Mutex.new()
47+
var shader_is_dirty := true
48+
49+
50+
func _init() -> void:
51+
effect_callback_type = EFFECT_CALLBACK_TYPE_POST_TRANSPARENT
52+
rd = RenderingServer.get_rendering_device()
53+
54+
55+
func _notification(what: int) -> void:
56+
if what == NOTIFICATION_PREDELETE:
57+
if shader.is_valid():
58+
RenderingServer.free_rid(shader)
59+
60+
61+
func _check_shader() -> bool:
62+
if not rd:
63+
return false
64+
65+
var new_shader_code: String = ""
66+
67+
mutex.lock()
68+
if shader_is_dirty:
69+
new_shader_code = shader_code
70+
shader_is_dirty = false
71+
mutex.unlock()
72+
73+
if new_shader_code.is_empty():
74+
return pipeline.is_valid()
75+
76+
new_shader_code = SHADER_CODE.replace("#COMPUTE_CODE", new_shader_code);
77+
78+
if shader.is_valid():
79+
rd.free_rid(shader)
80+
shader = RID()
81+
pipeline = RID()
82+
83+
var shader_source := RDShaderSource.new()
84+
shader_source.language = RenderingDevice.SHADER_LANGUAGE_GLSL
85+
shader_source.source_compute = new_shader_code
86+
var shader_spirv := rd.shader_compile_spirv_from_source(shader_source)
87+
88+
if not shader_spirv.compile_error_compute.is_empty():
89+
push_error(shader_spirv.compile_error_compute)
90+
return false
91+
92+
shader = rd.shader_create_from_spirv(shader_spirv)
93+
if not shader.is_valid():
94+
return false
95+
96+
pipeline = rd.compute_pipeline_create(shader)
97+
return pipeline.is_valid()
98+
99+
100+
func _render_callback(effect_callback_type: int, render_data: RenderData) -> void:
101+
if rd and effect_callback_type == EFFECT_CALLBACK_TYPE_POST_TRANSPARENT and _check_shader():
102+
var render_scene_buffers: RenderSceneBuffersRD = render_data.get_render_scene_buffers()
103+
if render_scene_buffers:
104+
var size := render_scene_buffers.get_internal_size()
105+
if size.x == 0 and size.y == 0:
106+
return
107+
108+
var x_groups := (size.x - 1) / 8 + 1
109+
var y_groups := (size.y - 1) / 8 + 1
110+
var z_groups := 1
111+
112+
var push_constant := PackedFloat32Array()
113+
push_constant.push_back(size.x)
114+
push_constant.push_back(size.y)
115+
push_constant.push_back(0.0)
116+
push_constant.push_back(0.0)
117+
118+
var view_count := render_scene_buffers.get_view_count()
119+
for view in range(view_count):
120+
var input_image := render_scene_buffers.get_color_layer(view)
121+
122+
var uniform := RDUniform.new()
123+
uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE
124+
uniform.binding = 0
125+
uniform.add_id(input_image)
126+
var uniform_set := UniformSetCacheRD.get_cache(shader, 0, [uniform])
127+
128+
var compute_list := rd.compute_list_begin()
129+
rd.compute_list_bind_compute_pipeline(compute_list, pipeline)
130+
rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0)
131+
rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(),
132+
push_constant.size() * 4)
133+
rd.compute_list_dispatch(compute_list, x_groups, y_groups, z_groups)
134+
rd.compute_list_end()

modules/gdscript/gdscript_editor.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri
8787
.replace(": Array[String]", "")
8888
.replace(": Node", "")
8989
.replace(": CharFXTransform", "")
90+
.replace(": RenderData", "")
91+
.replace(": RenderingDevice", "")
92+
.replace(": RenderSceneBuffersRD", "")
9093
.replace(":=", "=")
9194
.replace(" -> void", "")
9295
.replace(" -> bool", "")

0 commit comments

Comments
 (0)