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,20 +151,29 @@ 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"); 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 // 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", "bloom_downsampling_first_bind_group",
&downsampling_pipeline_res.bind_group_layout, &downsampling_pipeline_res.bind_group_layout,
&BindGroupEntries::sequential(( &BindGroupEntries::sequential((
// Read from main texture directly // Read from main texture directly
view_target.main_texture_view(), view_texture,
&bind_groups.sampler, &bind_groups.sampler,
uniforms.clone(), uniforms.clone(),
)), )),
@ -170,7 +181,7 @@ impl ViewNode for BloomNode {
let view = &bloom_texture.view(0); let view = &bloom_texture.view(0);
let mut downsampling_first_pass = let mut downsampling_first_pass =
render_context.begin_tracked_render_pass(RenderPassDescriptor { command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("bloom_downsampling_first_pass"), label: Some("bloom_downsampling_first_pass"),
color_attachments: &[Some(RenderPassColorAttachment { color_attachments: &[Some(RenderPassColorAttachment {
view, view,
@ -181,7 +192,7 @@ impl ViewNode for BloomNode {
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: 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( downsampling_first_pass.set_bind_group(
0, 0,
&downsampling_first_bind_group, &downsampling_first_bind_group,
@ -194,7 +205,7 @@ impl ViewNode for BloomNode {
for mip in 1..bloom_texture.mip_count { for mip in 1..bloom_texture.mip_count {
let view = &bloom_texture.view(mip); let view = &bloom_texture.view(mip);
let mut downsampling_pass = let mut downsampling_pass =
render_context.begin_tracked_render_pass(RenderPassDescriptor { command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("bloom_downsampling_pass"), label: Some("bloom_downsampling_pass"),
color_attachments: &[Some(RenderPassColorAttachment { color_attachments: &[Some(RenderPassColorAttachment {
view, view,
@ -205,7 +216,7 @@ impl ViewNode for BloomNode {
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
}); });
downsampling_pass.set_render_pipeline(downsampling_pipeline); downsampling_pass.set_pipeline(downsampling_pipeline);
downsampling_pass.set_bind_group( downsampling_pass.set_bind_group(
0, 0,
&bind_groups.downsampling_bind_groups[mip as usize - 1], &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() { for mip in (1..bloom_texture.mip_count).rev() {
let view = &bloom_texture.view(mip - 1); let view = &bloom_texture.view(mip - 1);
let mut upsampling_pass = let mut upsampling_pass =
render_context.begin_tracked_render_pass(RenderPassDescriptor { command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("bloom_upsampling_pass"), label: Some("bloom_upsampling_pass"),
color_attachments: &[Some(RenderPassColorAttachment { color_attachments: &[Some(RenderPassColorAttachment {
view, view,
@ -232,10 +243,11 @@ impl ViewNode for BloomNode {
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
}); });
upsampling_pass.set_render_pipeline(upsampling_pipeline); upsampling_pass.set_pipeline(upsampling_pipeline);
upsampling_pass.set_bind_group( upsampling_pass.set_bind_group(
0, 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()], &[uniform_index.index()],
); );
let blend = compute_blend_factor( let blend = compute_blend_factor(
@ -243,7 +255,7 @@ impl ViewNode for BloomNode {
mip as f32, mip as f32,
(bloom_texture.mip_count - 1) 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); 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 // being the pipeline (which itself is barely different) and the color attachment
{ {
let mut upsampling_final_pass = let mut upsampling_final_pass =
render_context.begin_tracked_render_pass(RenderPassDescriptor { command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("bloom_upsampling_final_pass"), 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, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: 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( upsampling_final_pass.set_bind_group(
0, 0,
&bind_groups.upsampling_bind_groups[(bloom_texture.mip_count - 1) as usize], &bind_groups.upsampling_bind_groups[(bloom_texture.mip_count - 1) as usize],
&[uniform_index.index()], &[uniform_index.index()],
); );
if let Some(viewport) = camera.viewport.as_ref() { 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 = let blend =
compute_blend_factor(bloom_settings, 0.0, (bloom_texture.mip_count - 1) as f32); 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); upsampling_final_pass.draw(0..3, 0..1);
} }
time_span.end(render_context.command_encoder()); time_span.end(&mut command_encoder);
render_context.command_encoder().pop_debug_group(); command_encoder.pop_debug_group();
command_encoder.finish()
});
Ok(()) Ok(())
} }