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 settings;
mod upsampling_pipeline; mod upsampling_pipeline;
use bevy_color::{Gray, LinearRgba};
pub use settings::{Bloom, BloomCompositeMode, BloomPrefilter}; pub use settings::{Bloom, BloomCompositeMode, BloomPrefilter};
use crate::{ use crate::{
@ -11,6 +10,7 @@ use crate::{
}; };
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, weak_handle, Handle}; use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_color::{Gray, LinearRgba};
use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_math::{ops, UVec2}; use bevy_math::{ops, UVec2};
use bevy_render::{ use bevy_render::{
@ -30,6 +30,8 @@ use downsampling_pipeline::{
prepare_downsampling_pipeline, BloomDownsamplingPipeline, BloomDownsamplingPipelineIds, prepare_downsampling_pipeline, BloomDownsamplingPipeline, BloomDownsamplingPipelineIds,
BloomUniforms, BloomUniforms,
}; };
#[cfg(feature = "trace")]
use tracing::info_span;
use upsampling_pipeline::{ use upsampling_pipeline::{
prepare_upsampling_pipeline, BloomUpsamplingPipeline, UpsamplingPipelineIds, prepare_upsampling_pipeline, BloomUpsamplingPipeline, UpsamplingPipelineIds,
}; };
@ -108,10 +110,10 @@ impl ViewNode for BloomNode {
// Atypically for a post-processing effect, we do not need to // Atypically for a post-processing effect, we do not need to
// use a secondary texture normally provided by view_target.post_process_write(), // 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. // instead we write into our own bloom texture and then directly back onto main.
fn run( fn run<'w>(
&self, &self,
_graph: &mut RenderGraphContext, _graph: &mut RenderGraphContext,
render_context: &mut RenderContext, render_context: &mut RenderContext<'w>,
( (
camera, camera,
view_target, view_target,
@ -121,8 +123,8 @@ impl ViewNode for BloomNode {
bloom_settings, bloom_settings,
upsampling_pipeline_ids, upsampling_pipeline_ids,
downsampling_pipeline_ids, downsampling_pipeline_ids,
): QueryItem<Self::ViewQuery>, ): QueryItem<'w, Self::ViewQuery>,
world: &World, world: &'w World,
) -> Result<(), NodeRunError> { ) -> Result<(), NodeRunError> {
if bloom_settings.intensity == 0.0 { if bloom_settings.intensity == 0.0 {
return Ok(()); return Ok(());
@ -149,133 +151,152 @@ impl ViewNode for BloomNode {
return Ok(()); 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 diagnostics = render_context.diagnostic_recorder();
let command_encoder = render_context.command_encoder();
let time_span = diagnostics.time_span(command_encoder, "bloom");
// First downsample pass render_context.add_command_buffer_generation_task(move |render_device| {
{ #[cfg(feature = "trace")]
let downsampling_first_bind_group = render_context.render_device().create_bind_group( let _bloom_span = info_span!("bloom").entered();
"bloom_downsampling_first_bind_group",
&downsampling_pipeline_res.bind_group_layout,
&BindGroupEntries::sequential((
// Read from main texture directly
view_target.main_texture_view(),
&bind_groups.sampler,
uniforms.clone(),
)),
);
let view = &bloom_texture.view(0); let mut command_encoder =
let mut downsampling_first_pass = render_device.create_command_encoder(&CommandEncoderDescriptor {
render_context.begin_tracked_render_pass(RenderPassDescriptor { label: Some("bloom_command_encoder"),
label: Some("bloom_downsampling_first_pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view,
resolve_target: None,
ops: Operations::default(),
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
}); });
downsampling_first_pass.set_render_pipeline(downsampling_first_pipeline); command_encoder.push_debug_group("bloom");
downsampling_first_pass.set_bind_group( let time_span = diagnostics.time_span(&mut command_encoder, "bloom");
0,
&downsampling_first_bind_group,
&[uniform_index.index()],
);
downsampling_first_pass.draw(0..3, 0..1);
}
// Other downsample passes // First downsample pass
for mip in 1..bloom_texture.mip_count { {
let view = &bloom_texture.view(mip); let downsampling_first_bind_group = render_device.create_bind_group(
let mut downsampling_pass = "bloom_downsampling_first_bind_group",
render_context.begin_tracked_render_pass(RenderPassDescriptor { &downsampling_pipeline_res.bind_group_layout,
label: Some("bloom_downsampling_pass"), &BindGroupEntries::sequential((
color_attachments: &[Some(RenderPassColorAttachment { // Read from main texture directly
view, view_texture,
resolve_target: None, &bind_groups.sampler,
ops: Operations::default(), uniforms.clone(),
})], )),
depth_stencil_attachment: None, );
timestamp_writes: None,
occlusion_query_set: None,
});
downsampling_pass.set_render_pipeline(downsampling_pipeline);
downsampling_pass.set_bind_group(
0,
&bind_groups.downsampling_bind_groups[mip as usize - 1],
&[uniform_index.index()],
);
downsampling_pass.draw(0..3, 0..1);
}
// Upsample passes except the final one let view = &bloom_texture.view(0);
for mip in (1..bloom_texture.mip_count).rev() { let mut downsampling_first_pass =
let view = &bloom_texture.view(mip - 1); command_encoder.begin_render_pass(&RenderPassDescriptor {
let mut upsampling_pass = label: Some("bloom_downsampling_first_pass"),
render_context.begin_tracked_render_pass(RenderPassDescriptor { color_attachments: &[Some(RenderPassColorAttachment {
label: Some("bloom_upsampling_pass"), view,
color_attachments: &[Some(RenderPassColorAttachment { resolve_target: None,
view, ops: Operations::default(),
resolve_target: None, })],
ops: Operations { depth_stencil_attachment: None,
load: LoadOp::Load, timestamp_writes: None,
store: StoreOp::Store, occlusion_query_set: None,
}, });
})], downsampling_first_pass.set_pipeline(downsampling_first_pipeline);
depth_stencil_attachment: None, downsampling_first_pass.set_bind_group(
timestamp_writes: None, 0,
occlusion_query_set: None, &downsampling_first_bind_group,
}); &[uniform_index.index()],
upsampling_pass.set_render_pipeline(upsampling_pipeline); );
upsampling_pass.set_bind_group( downsampling_first_pass.draw(0..3, 0..1);
0,
&bind_groups.upsampling_bind_groups[(bloom_texture.mip_count - mip - 1) as usize],
&[uniform_index.index()],
);
let blend = compute_blend_factor(
bloom_settings,
mip as f32,
(bloom_texture.mip_count - 1) as f32,
);
upsampling_pass.set_blend_constant(LinearRgba::gray(blend));
upsampling_pass.draw(0..3, 0..1);
}
// Final upsample pass
// This is very similar to the above upsampling passes with the only difference
// being the pipeline (which itself is barely different) and the color attachment
{
let mut upsampling_final_pass =
render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("bloom_upsampling_final_pass"),
color_attachments: &[Some(view_target.get_unsampled_color_attachment())],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
upsampling_final_pass.set_render_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);
} }
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.draw(0..3, 0..1);
}
time_span.end(render_context.command_encoder()); // Other downsample passes
render_context.command_encoder().pop_debug_group(); for mip in 1..bloom_texture.mip_count {
let view = &bloom_texture.view(mip);
let mut downsampling_pass =
command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("bloom_downsampling_pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view,
resolve_target: None,
ops: Operations::default(),
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
downsampling_pass.set_pipeline(downsampling_pipeline);
downsampling_pass.set_bind_group(
0,
&bind_groups.downsampling_bind_groups[mip as usize - 1],
&[uniform_index.index()],
);
downsampling_pass.draw(0..3, 0..1);
}
// Upsample passes except the final one
for mip in (1..bloom_texture.mip_count).rev() {
let view = &bloom_texture.view(mip - 1);
let mut upsampling_pass =
command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("bloom_upsampling_pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view,
resolve_target: None,
ops: Operations {
load: LoadOp::Load,
store: StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
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],
&[uniform_index.index()],
);
let blend = compute_blend_factor(
bloom_settings,
mip as f32,
(bloom_texture.mip_count - 1) as f32,
);
upsampling_pass.set_blend_constant(LinearRgba::gray(blend).into());
upsampling_pass.draw(0..3, 0..1);
}
// Final upsample pass
// This is very similar to the above upsampling passes with the only difference
// being the pipeline (which itself is barely different) and the color attachment
{
let mut upsampling_final_pass =
command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("bloom_upsampling_final_pass"),
color_attachments: &[Some(view_texture_unsampled)],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
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_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).into());
upsampling_final_pass.draw(0..3, 0..1);
}
time_span.end(&mut command_encoder);
command_encoder.pop_debug_group();
command_encoder.finish()
});
Ok(()) Ok(())
} }