-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add demo showing the use of RenderingEffects to do post processing #942
Add demo showing the use of RenderingEffects to do post processing #942
Conversation
3d8d84c
to
d6d0fd8
Compare
Next to the very simple grayscale example I've added a work in progress, and not working correctly yet, Stocastic SSR implementation. Decided to already push this in as it's a much bigger example on how to create different type of passes, how to create additional buffers, how to obtain state around the rendering, and how to do a raster pass. |
While trying the hooks PR yesterday, I noticed that you use Which reminds me, I need to go through the built-in godot shaders. We use this strange max approximation way too often. |
d6d0fd8
to
87b88e9
Compare
@Ansraer , changed the grayscale logic :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've used this demo as a reference point and found parts of it using an outdated API. Here's my findings.
Note that my comments are not exhaustive, so "apply changes" via the GitHub GUI won't find all occurrences.
extends RenderingEffect | ||
class_name RenderingEffectGrayScale |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API renamed to CompositorEffect, should be changed in multiple files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, I need to revisit this
# Barrier | ||
rd.barrier(RenderingDevice.BARRIER_MASK_ALL_BARRIERS, RenderingDevice.BARRIER_MASK_COMPUTE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Manual barriers are deprecated and should be removed, should be changed in multiple files.
compute/post_effects/main.tscn
Outdated
tonemap_mode = 2 | ||
ssr_depth_tolerance = 3.41 | ||
glow_enabled = true | ||
rendering_effects = Array[RenderingEffect]([SubResource("RenderingEffect_rprpc"), SubResource("RenderingEffect_sm2nk"), SubResource("RenderingEffect_jcxua")]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Must use the new Compositor API.
if nearest_sampler.is_valid(): | ||
rd.free_rid(nearest_sampler) | ||
if linear_sampler.is_valid(): | ||
rd.free_rid(linear_sampler) | ||
if reflection_shader.is_valid(): | ||
rd.free_rid(reflection_shader) | ||
if overlay_shader.is_valid(): | ||
rd.free_rid(overlay_shader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some RIDs are not being cleaned up here, such as the *_pipeline
variables
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As before, dependent RIDs are automatically freed.
if shader.is_valid(): | ||
rd.free_rid(shader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pipeline
RID is not being freed here.
if shader.is_valid(): | ||
rd.free_rid(shader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pipeline
RID is not being freed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you free a shader, all dependencies of the shader will be freed, including all pipelines related to that shader.
rd.compute_list_bind_uniform_set(compute_list, depth_set, 0) | ||
rd.compute_list_bind_uniform_set(compute_list, normal_set, 1) | ||
rd.compute_list_bind_uniform_set(compute_list, color_set, 2) | ||
rd.compute_list_bind_uniform_set(compute_list, reflect_set, 3) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not nearly as experienced with compute shaders as you are, but I'll still risk asking a potentially stupid question here:
Why create multiple uniform sets, with one binding each? Why not create a single uniform set with multiple bindings? The docs also only ever do it the way that's done here without further elaborating. But in my testing, both seem to work fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've noticed that I can use the same set with different bindings as well, so I've just started looking at the following document:
One thing the article notes:
We recommend making use of the different set numbers, to avoid redundant bindings. Putting many bindings that have very different frequencies in the same DescriptorSet can be bad for overall performance. Imagine a DescriptorSet with several textures and uniform buffer binding of which only one changes, that’s potentially a lot of data being sent to the GPU that effectively doesn’t do anything.
☝️ I'm not entirely sure I understand comprehensively yet, but it seems to me that separating different uniforms into sets can help with optimizing GPU usage. That said, it's hard for me to understand what would be ideal for a typical game project in Godot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the article, it's hard to find resources on this topic! On that note, most post processing shaders will be run every single frame and always update all of their bound uniforms, so I'm not sure if there's any benefit or drawback to doing it one way over the other here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no one size fits all answer here. As a general rule it is best to combine uniforms into a single set but there can be optimisations in splitting this out if certain uniforms remain unchanged over multiple frames while others change every frame.
Other than that its really personal preference, in this case I'm splitting them out to keep the code straight forward and easy to read.
effect_callback_type = RenderingEffect.EFFECT_CALLBACK_TYPE_POST_TRANSPARENT | ||
RenderingServer.call_on_render_thread(_initialize_compute) | ||
|
||
func _notification(what): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func _notification(what): | |
func _notification(what): |
var shader : RID | ||
var pipeline : RID | ||
|
||
func _initialize_compute(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func _initialize_compute(): | |
func _initialize_compute(): |
shader = rd.shader_create_from_spirv(shader_spirv) | ||
pipeline = rd.compute_pipeline_create(shader) | ||
|
||
func _render_callback(p_effect_callback_type, p_render_data): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func _render_callback(p_effect_callback_type, p_render_data): | |
func _render_callback(effect_callback_type, render_data): |
compute/post_effects/main.gd
Outdated
var apply_sssr_effect : RenderingEffectApplySSSR | ||
var gray_scale_effect : RenderingEffectGrayScale | ||
|
||
# Called when the node enters the scene tree for the first time. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Called when the node enters the scene tree for the first time. | |
# Called when the node enters the scene tree for the first time. |
compute/post_effects/main.gd
Outdated
var cam_x = 0.0 | ||
var cam_y = 0.0 | ||
|
||
func _input(event): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func _input(event): | |
func _input(event): |
uniform.add_id(input_image) | ||
var uniform_set = UniformSetCacheRD.get_cache(shader, 0, [ uniform ]) | ||
|
||
# Run our compute shader |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Run our compute shader | |
# Run our compute shader. |
if size.x == 0 and size.y == 0: | ||
return | ||
|
||
# We can use a compute shader here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# We can use a compute shader here | |
# We can use a compute shader here . |
var x_groups = (size.x - 1) / 8 + 1 | ||
var y_groups = (size.y - 1) / 8 + 1 | ||
|
||
# Barrier |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Barrier | |
# Barrier. |
if !rd: | ||
return | ||
|
||
# Create our shader |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Create our shader | |
# Create our shader. |
|
||
func _initialize_compute(): | ||
rd = RenderingServer.get_rendering_device() | ||
if !rd: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if !rd: | |
if not rd: |
I've updated the demo for 4.3.dev6 by pushing an additional commit, which incorporates @AThousandShips' suggestions. The demo works out of the box now. However, I get error spam when MSAA 3D is disabled in the project settings (despite the scene rendering correctly):
PS: Is there a way to prevent the reflection from showing up on the sky? I've tried decreasing Max Distance in the SSSR effect but I need to decrease it to a very low value to prevent this, which breaks the reflection on other objects. |
db81367
to
adb296c
Compare
This has been replaced by #1058 |
Adding a demo to show the use of RenderingEffects to create post processing.
Implements godotengine/godot#80214
This is still a pretty simple example, will work on making this more elaborate at some point.