From 0702f3652d23ecf23573dc0cc87c58035d9de771 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 24 Mar 2025 21:06:42 -0700 Subject: [PATCH] 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! --- crates/bevy_core_pipeline/src/bloom/mod.rs | 269 +++++++++++---------- 1 file changed, 145 insertions(+), 124 deletions(-) diff --git a/crates/bevy_core_pipeline/src/bloom/mod.rs b/crates/bevy_core_pipeline/src/bloom/mod.rs index 295b04c6ad..8717b9096e 100644 --- a/crates/bevy_core_pipeline/src/bloom/mod.rs +++ b/crates/bevy_core_pipeline/src/bloom/mod.rs @@ -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, - world: &World, + ): QueryItem<'w, Self::ViewQuery>, + world: &'w World, ) -> Result<(), NodeRunError> { if bloom_settings.intensity == 0.0 { return Ok(()); @@ -149,133 +151,152 @@ 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"); - // First downsample pass - { - let downsampling_first_bind_group = render_context.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(), - &bind_groups.sampler, - uniforms.clone(), - )), - ); + render_context.add_command_buffer_generation_task(move |render_device| { + #[cfg(feature = "trace")] + let _bloom_span = info_span!("bloom").entered(); - let view = &bloom_texture.view(0); - let mut downsampling_first_pass = - render_context.begin_tracked_render_pass(RenderPassDescriptor { - 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, + let mut command_encoder = + render_device.create_command_encoder(&CommandEncoderDescriptor { + label: Some("bloom_command_encoder"), }); - downsampling_first_pass.set_render_pipeline(downsampling_first_pipeline); - downsampling_first_pass.set_bind_group( - 0, - &downsampling_first_bind_group, - &[uniform_index.index()], - ); - downsampling_first_pass.draw(0..3, 0..1); - } + command_encoder.push_debug_group("bloom"); + let time_span = diagnostics.time_span(&mut command_encoder, "bloom"); - // Other downsample passes - 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 { - 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_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); - } + // First downsample pass + { + 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_texture, + &bind_groups.sampler, + uniforms.clone(), + )), + ); - // 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 = - render_context.begin_tracked_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_render_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)); - 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 view = &bloom_texture.view(0); + let mut downsampling_first_pass = + command_encoder.begin_render_pass(&RenderPassDescriptor { + 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_pipeline(downsampling_first_pipeline); + downsampling_first_pass.set_bind_group( + 0, + &downsampling_first_bind_group, + &[uniform_index.index()], + ); + downsampling_first_pass.draw(0..3, 0..1); } - 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()); - render_context.command_encoder().pop_debug_group(); + // Other downsample passes + 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(()) }