Record bloom render commands in parallel (#18330)

Closes https://github.com/bevyengine/bevy/issues/18304.

Requesting that someone more experienced with tracy test performance on
a larger scene please!
This commit is contained in:
JMS55 2025-03-24 21:06:42 -07:00 committed by François Mockers
parent a88f2fec42
commit 0702f3652d

View File

@ -2,7 +2,6 @@ mod downsampling_pipeline;
mod settings;
mod upsampling_pipeline;
use bevy_color::{Gray, LinearRgba};
pub use settings::{Bloom, BloomCompositeMode, BloomPrefilter};
use crate::{
@ -11,6 +10,7 @@ use crate::{
};
use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_color::{Gray, LinearRgba};
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_math::{ops, UVec2};
use bevy_render::{
@ -30,6 +30,8 @@ use downsampling_pipeline::{
prepare_downsampling_pipeline, BloomDownsamplingPipeline, BloomDownsamplingPipelineIds,
BloomUniforms,
};
#[cfg(feature = "trace")]
use tracing::info_span;
use upsampling_pipeline::{
prepare_upsampling_pipeline, BloomUpsamplingPipeline, UpsamplingPipelineIds,
};
@ -108,10 +110,10 @@ impl ViewNode for BloomNode {
// Atypically for a post-processing effect, we do not need to
// use a secondary texture normally provided by view_target.post_process_write(),
// instead we write into our own bloom texture and then directly back onto main.
fn run(
fn run<'w>(
&self,
_graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
render_context: &mut RenderContext<'w>,
(
camera,
view_target,
@ -121,8 +123,8 @@ impl ViewNode for BloomNode {
bloom_settings,
upsampling_pipeline_ids,
downsampling_pipeline_ids,
): QueryItem<Self::ViewQuery>,
world: &World,
): QueryItem<'w, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
if bloom_settings.intensity == 0.0 {
return Ok(());
@ -149,20 +151,29 @@ impl ViewNode for BloomNode {
return Ok(());
};
render_context.command_encoder().push_debug_group("bloom");
let view_texture = view_target.main_texture_view();
let view_texture_unsampled = view_target.get_unsampled_color_attachment();
let diagnostics = render_context.diagnostic_recorder();
let command_encoder = render_context.command_encoder();
let time_span = diagnostics.time_span(command_encoder, "bloom");
render_context.add_command_buffer_generation_task(move |render_device| {
#[cfg(feature = "trace")]
let _bloom_span = info_span!("bloom").entered();
let mut command_encoder =
render_device.create_command_encoder(&CommandEncoderDescriptor {
label: Some("bloom_command_encoder"),
});
command_encoder.push_debug_group("bloom");
let time_span = diagnostics.time_span(&mut command_encoder, "bloom");
// First downsample pass
{
let downsampling_first_bind_group = render_context.render_device().create_bind_group(
let downsampling_first_bind_group = render_device.create_bind_group(
"bloom_downsampling_first_bind_group",
&downsampling_pipeline_res.bind_group_layout,
&BindGroupEntries::sequential((
// Read from main texture directly
view_target.main_texture_view(),
view_texture,
&bind_groups.sampler,
uniforms.clone(),
)),
@ -170,7 +181,7 @@ impl ViewNode for BloomNode {
let view = &bloom_texture.view(0);
let mut downsampling_first_pass =
render_context.begin_tracked_render_pass(RenderPassDescriptor {
command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("bloom_downsampling_first_pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view,
@ -181,7 +192,7 @@ impl ViewNode for BloomNode {
timestamp_writes: None,
occlusion_query_set: None,
});
downsampling_first_pass.set_render_pipeline(downsampling_first_pipeline);
downsampling_first_pass.set_pipeline(downsampling_first_pipeline);
downsampling_first_pass.set_bind_group(
0,
&downsampling_first_bind_group,
@ -194,7 +205,7 @@ impl ViewNode for BloomNode {
for mip in 1..bloom_texture.mip_count {
let view = &bloom_texture.view(mip);
let mut downsampling_pass =
render_context.begin_tracked_render_pass(RenderPassDescriptor {
command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("bloom_downsampling_pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view,
@ -205,7 +216,7 @@ impl ViewNode for BloomNode {
timestamp_writes: None,
occlusion_query_set: None,
});
downsampling_pass.set_render_pipeline(downsampling_pipeline);
downsampling_pass.set_pipeline(downsampling_pipeline);
downsampling_pass.set_bind_group(
0,
&bind_groups.downsampling_bind_groups[mip as usize - 1],
@ -218,7 +229,7 @@ impl ViewNode for BloomNode {
for mip in (1..bloom_texture.mip_count).rev() {
let view = &bloom_texture.view(mip - 1);
let mut upsampling_pass =
render_context.begin_tracked_render_pass(RenderPassDescriptor {
command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("bloom_upsampling_pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view,
@ -232,10 +243,11 @@ impl ViewNode for BloomNode {
timestamp_writes: None,
occlusion_query_set: None,
});
upsampling_pass.set_render_pipeline(upsampling_pipeline);
upsampling_pass.set_pipeline(upsampling_pipeline);
upsampling_pass.set_bind_group(
0,
&bind_groups.upsampling_bind_groups[(bloom_texture.mip_count - mip - 1) as usize],
&bind_groups.upsampling_bind_groups
[(bloom_texture.mip_count - mip - 1) as usize],
&[uniform_index.index()],
);
let blend = compute_blend_factor(
@ -243,7 +255,7 @@ impl ViewNode for BloomNode {
mip as f32,
(bloom_texture.mip_count - 1) as f32,
);
upsampling_pass.set_blend_constant(LinearRgba::gray(blend));
upsampling_pass.set_blend_constant(LinearRgba::gray(blend).into());
upsampling_pass.draw(0..3, 0..1);
}
@ -252,30 +264,39 @@ impl ViewNode for BloomNode {
// being the pipeline (which itself is barely different) and the color attachment
{
let mut upsampling_final_pass =
render_context.begin_tracked_render_pass(RenderPassDescriptor {
command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("bloom_upsampling_final_pass"),
color_attachments: &[Some(view_target.get_unsampled_color_attachment())],
color_attachments: &[Some(view_texture_unsampled)],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
upsampling_final_pass.set_render_pipeline(upsampling_final_pipeline);
upsampling_final_pass.set_pipeline(upsampling_final_pipeline);
upsampling_final_pass.set_bind_group(
0,
&bind_groups.upsampling_bind_groups[(bloom_texture.mip_count - 1) as usize],
&[uniform_index.index()],
);
if let Some(viewport) = camera.viewport.as_ref() {
upsampling_final_pass.set_camera_viewport(viewport);
upsampling_final_pass.set_viewport(
viewport.physical_position.x as f32,
viewport.physical_position.y as f32,
viewport.physical_size.x as f32,
viewport.physical_size.y as f32,
viewport.depth.start,
viewport.depth.end,
);
}
let blend =
compute_blend_factor(bloom_settings, 0.0, (bloom_texture.mip_count - 1) as f32);
upsampling_final_pass.set_blend_constant(LinearRgba::gray(blend));
upsampling_final_pass.set_blend_constant(LinearRgba::gray(blend).into());
upsampling_final_pass.draw(0..3, 0..1);
}
time_span.end(render_context.command_encoder());
render_context.command_encoder().pop_debug_group();
time_span.end(&mut command_encoder);
command_encoder.pop_debug_group();
command_encoder.finish()
});
Ok(())
}