From 895676b7672666600fcd88708618abaddbc70b34 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 16 Jan 2023 19:57:13 -0800 Subject: [PATCH 1/9] Add support for multipple command buffers in RenderContext --- crates/bevy_core_pipeline/src/fxaa/node.rs | 6 +- .../src/tonemapping/node.rs | 6 +- .../bevy_core_pipeline/src/upscaling/node.rs | 6 +- .../src/camera/camera_driver_node.rs | 2 +- .../bevy_render/src/renderer/graph_runner.rs | 10 +-- crates/bevy_render/src/renderer/mod.rs | 64 +++++++++++++++++-- 6 files changed, 69 insertions(+), 25 deletions(-) diff --git a/crates/bevy_core_pipeline/src/fxaa/node.rs b/crates/bevy_core_pipeline/src/fxaa/node.rs index 6e12151c2fe63..5050e3c4b3920 100644 --- a/crates/bevy_core_pipeline/src/fxaa/node.rs +++ b/crates/bevy_core_pipeline/src/fxaa/node.rs @@ -78,7 +78,7 @@ impl Node for FxaaNode { Some((id, bind_group)) if source.id() == *id => bind_group, cached_bind_group => { let sampler = render_context - .render_device + .render_device() .create_sampler(&SamplerDescriptor { mipmap_filter: FilterMode::Linear, mag_filter: FilterMode::Linear, @@ -88,7 +88,7 @@ impl Node for FxaaNode { let bind_group = render_context - .render_device + .render_device() .create_bind_group(&BindGroupDescriptor { label: None, layout: &fxaa_pipeline.texture_bind_group, @@ -120,7 +120,7 @@ impl Node for FxaaNode { }; let mut render_pass = render_context - .command_encoder + .command_encoder() .begin_render_pass(&pass_descriptor); render_pass.set_pipeline(pipeline); diff --git a/crates/bevy_core_pipeline/src/tonemapping/node.rs b/crates/bevy_core_pipeline/src/tonemapping/node.rs index f9edf882c73fa..c814de5c00ed7 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/node.rs +++ b/crates/bevy_core_pipeline/src/tonemapping/node.rs @@ -72,12 +72,12 @@ impl Node for TonemappingNode { Some((id, bind_group)) if source.id() == *id => bind_group, cached_bind_group => { let sampler = render_context - .render_device + .render_device() .create_sampler(&SamplerDescriptor::default()); let bind_group = render_context - .render_device + .render_device() .create_bind_group(&BindGroupDescriptor { label: None, layout: &tonemapping_pipeline.texture_bind_group, @@ -112,7 +112,7 @@ impl Node for TonemappingNode { }; let mut render_pass = render_context - .command_encoder + .command_encoder() .begin_render_pass(&pass_descriptor); render_pass.set_pipeline(pipeline); diff --git a/crates/bevy_core_pipeline/src/upscaling/node.rs b/crates/bevy_core_pipeline/src/upscaling/node.rs index 895c3e5e1b0a2..44cf195f724ca 100644 --- a/crates/bevy_core_pipeline/src/upscaling/node.rs +++ b/crates/bevy_core_pipeline/src/upscaling/node.rs @@ -63,12 +63,12 @@ impl Node for UpscalingNode { Some((id, bind_group)) if upscaled_texture.id() == *id => bind_group, cached_bind_group => { let sampler = render_context - .render_device + .render_device() .create_sampler(&SamplerDescriptor::default()); let bind_group = render_context - .render_device + .render_device() .create_bind_group(&BindGroupDescriptor { label: None, layout: &upscaling_pipeline.texture_bind_group, @@ -108,7 +108,7 @@ impl Node for UpscalingNode { }; let mut render_pass = render_context - .command_encoder + .command_encoder() .begin_render_pass(&pass_descriptor); render_pass.set_pipeline(pipeline); diff --git a/crates/bevy_render/src/camera/camera_driver_node.rs b/crates/bevy_render/src/camera/camera_driver_node.rs index f57929f30caeb..7e538a0f3a66d 100644 --- a/crates/bevy_render/src/camera/camera_driver_node.rs +++ b/crates/bevy_render/src/camera/camera_driver_node.rs @@ -98,7 +98,7 @@ impl Node for CameraDriverNode { }; render_context - .command_encoder + .command_encoder() .begin_render_pass(&pass_descriptor); } diff --git a/crates/bevy_render/src/renderer/graph_runner.rs b/crates/bevy_render/src/renderer/graph_runner.rs index 1513d1c4e5697..e4432cd901825 100644 --- a/crates/bevy_render/src/renderer/graph_runner.rs +++ b/crates/bevy_render/src/renderer/graph_runner.rs @@ -58,18 +58,12 @@ impl RenderGraphRunner { queue: &wgpu::Queue, world: &World, ) -> Result<(), RenderGraphRunnerError> { - let command_encoder = - render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); - let mut render_context = RenderContext { - render_device, - command_encoder, - }; - + let mut render_context = RenderContext::new(render_device); Self::run_graph(graph, None, &mut render_context, world, &[])?; { #[cfg(feature = "trace")] let _span = info_span!("submit_graph_commands").entered(); - queue.submit(vec![render_context.command_encoder.finish()]); + queue.submit(render_context.finish()); } Ok(()) } diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 19cee9c9585a5..0f185d522b07a 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -17,7 +17,9 @@ use bevy_ecs::prelude::*; use bevy_time::TimeSender; use bevy_utils::Instant; use std::sync::Arc; -use wgpu::{Adapter, AdapterInfo, CommandEncoder, Instance, Queue, RequestAdapterOptions}; +use wgpu::{ + Adapter, AdapterInfo, CommandBuffer, CommandEncoder, Instance, Queue, RequestAdapterOptions, +}; /// Updates the [`RenderGraph`] with all of its nodes and then runs it to render the entire frame. pub fn render_system(world: &mut World) { @@ -278,20 +280,68 @@ pub async fn initialize_renderer( /// The [`RenderDevice`] is used to create render resources and the /// the [`CommandEncoder`] is used to record a series of GPU operations. pub struct RenderContext { - pub render_device: RenderDevice, - pub command_encoder: CommandEncoder, + render_device: RenderDevice, + command_encoder: Option, + command_buffers: Vec, } impl RenderContext { + pub fn new(render_device: RenderDevice) -> Self { + Self { + render_device, + command_encoder: None, + command_buffers: Vec::new(), + } + } + + pub fn render_device(&self) -> &RenderDevice { + &self.render_device + } + + pub fn command_encoder(&mut self) -> &mut CommandEncoder { + if self.command_encoder.is_none() { + self.command_encoder = Some( + self.render_device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()), + ); + } + self.command_encoder.as_mut().unwrap() + } + /// Creates a new [`TrackedRenderPass`] for the context, /// configured using the provided `descriptor`. pub fn begin_tracked_render_pass<'a>( &'a mut self, descriptor: RenderPassDescriptor<'a, '_>, ) -> TrackedRenderPass<'a> { - TrackedRenderPass::new( - &self.render_device, - self.command_encoder.begin_render_pass(&descriptor), - ) + // Cannot use command_encoder() as we need to split the borrow on self + if self.command_encoder.is_none() { + self.command_encoder = Some( + self.render_device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()), + ); + } + let render_pass = self + .command_encoder + .as_mut() + .unwrap() + .begin_render_pass(&descriptor); + TrackedRenderPass::new(&self.render_device, render_pass) + } + + pub fn add_command_buffer(&mut self, command_buffer: CommandBuffer) { + self.flush_encoder(); + self.command_buffers.push(command_buffer); + } + + pub fn finish(mut self) -> Vec { + self.flush_encoder(); + self.command_buffers + } + + fn flush_encoder(&mut self) { + if let Some(encoder) = self.command_encoder.take() { + self.command_buffers.push(encoder.finish()); + } } } From 13ddbc4cba1f89f6e061b0b1885b8599a5db3954 Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 17 Jan 2023 20:43:14 -0800 Subject: [PATCH 2/9] Fix CI --- crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs | 2 +- crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs index da3cbf3c17242..b5660c4c0aa58 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs @@ -101,7 +101,7 @@ impl Node for MainPass2dNode { }; render_context - .command_encoder + .command_encoder() .begin_render_pass(&pass_descriptor); } diff --git a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs index a836e0bcbe450..81143dfa72a2b 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs @@ -186,7 +186,7 @@ impl Node for MainPass3dNode { }; render_context - .command_encoder + .command_encoder() .begin_render_pass(&pass_descriptor); } From 5bb1071c1429a87578a7b383a53f1be96dde3316 Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 17 Jan 2023 20:51:54 -0800 Subject: [PATCH 3/9] Document more of the RenderContext APIs --- crates/bevy_render/src/renderer/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 0f185d522b07a..2b9f693824660 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -286,6 +286,7 @@ pub struct RenderContext { } impl RenderContext { + /// Creates a new [`RenderContext`] from a [`RenderDevice`]. pub fn new(render_device: RenderDevice) -> Self { Self { render_device, @@ -294,10 +295,12 @@ impl RenderContext { } } + /// Gets the underlying [`RenderDevice`]. pub fn render_device(&self) -> &RenderDevice { &self.render_device } + /// Gets the current [`CommandEncoder`]. pub fn command_encoder(&mut self) -> &mut CommandEncoder { if self.command_encoder.is_none() { self.command_encoder = Some( @@ -329,11 +332,17 @@ impl RenderContext { TrackedRenderPass::new(&self.render_device, render_pass) } + /// Append a [`CommandBuffer`] to the queue. + /// + /// If present, this will flush the currently unflushed [`CommandEncoder`] + /// into a [`CommandBuffer`] into the queue before append the provided + /// buffer. pub fn add_command_buffer(&mut self, command_buffer: CommandBuffer) { self.flush_encoder(); self.command_buffers.push(command_buffer); } + /// Finalizes the queue and returns the queue of [`CommandBuffer`]s. pub fn finish(mut self) -> Vec { self.flush_encoder(); self.command_buffers From 2d970cd8b6b1c796c87c6c0824fb20a8fc54f588 Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 17 Jan 2023 20:53:22 -0800 Subject: [PATCH 4/9] Fix examples --- examples/shader/compute_shader_game_of_life.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index 74b5cba036a04..03420480d501d 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -215,7 +215,7 @@ impl render_graph::Node for GameOfLifeNode { let pipeline = world.resource::(); let mut pass = render_context - .command_encoder + .command_encoder() .begin_compute_pass(&ComputePassDescriptor::default()); pass.set_bind_group(0, texture_bind_group, &[]); From c47443b6813533e1761e4865f61ec2dc70567e28 Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 17 Jan 2023 20:53:35 -0800 Subject: [PATCH 5/9] Formatting --- crates/bevy_render/src/renderer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 2b9f693824660..caa98809dcbd1 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -335,7 +335,7 @@ impl RenderContext { /// Append a [`CommandBuffer`] to the queue. /// /// If present, this will flush the currently unflushed [`CommandEncoder`] - /// into a [`CommandBuffer`] into the queue before append the provided + /// into a [`CommandBuffer`] into the queue before append the provided /// buffer. pub fn add_command_buffer(&mut self, command_buffer: CommandBuffer) { self.flush_encoder(); From 5a1fb211b487481eb3e6f4c0a62b423b81d00d84 Mon Sep 17 00:00:00 2001 From: James Liu Date: Wed, 18 Jan 2023 14:29:32 -0800 Subject: [PATCH 6/9] Use Option combinators instead of using if block Co-authored-by: JoJoJet <21144246+JoJoJet@users.noreply.github.com> --- crates/bevy_render/src/renderer/mod.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index caa98809dcbd1..7d8a97ceca04b 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -318,17 +318,11 @@ impl RenderContext { descriptor: RenderPassDescriptor<'a, '_>, ) -> TrackedRenderPass<'a> { // Cannot use command_encoder() as we need to split the borrow on self - if self.command_encoder.is_none() { - self.command_encoder = Some( - self.render_device - .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()), - ); - } - let render_pass = self - .command_encoder - .as_mut() - .unwrap() - .begin_render_pass(&descriptor); + let command_encoder = self.command_encoder.get_or_insert_with(|| { + self.render_device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()) + }); + let render_pass = command_encoder.begin_render_pass(&descriptor); TrackedRenderPass::new(&self.render_device, render_pass) } From b0f20c90a415dea1c62dd2669521a3aacf61f9fb Mon Sep 17 00:00:00 2001 From: James Liu Date: Wed, 18 Jan 2023 19:17:10 -0800 Subject: [PATCH 7/9] More combinators Co-authored-by: JoJoJet <21144246+JoJoJet@users.noreply.github.com> --- crates/bevy_render/src/renderer/mod.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 7d8a97ceca04b..a0775f084ec1c 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -302,13 +302,10 @@ impl RenderContext { /// Gets the current [`CommandEncoder`]. pub fn command_encoder(&mut self) -> &mut CommandEncoder { - if self.command_encoder.is_none() { - self.command_encoder = Some( - self.render_device - .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()), - ); - } - self.command_encoder.as_mut().unwrap() + self.command_encoder.get_or_init(|| { + self.render_device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default() + }) } /// Creates a new [`TrackedRenderPass`] for the context, From d496811e5e8d2ed2a3fda4710ed83ad7adba68cb Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 18 Jan 2023 19:22:36 -0800 Subject: [PATCH 8/9] Fix CI --- crates/bevy_render/src/renderer/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index a0775f084ec1c..5885003bfb76e 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -302,9 +302,9 @@ impl RenderContext { /// Gets the current [`CommandEncoder`]. pub fn command_encoder(&mut self) -> &mut CommandEncoder { - self.command_encoder.get_or_init(|| { + self.command_encoder.get_or_insert_with(|| { self.render_device - .create_command_encoder(&wgpu::CommandEncoderDescriptor::default() + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()) }) } From 3f0089ad441ed0f47ae608597083bf440ed7d633 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 21 Jan 2023 15:33:01 -0800 Subject: [PATCH 9/9] Fix build --- crates/bevy_core_pipeline/src/prepass/node.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_core_pipeline/src/prepass/node.rs b/crates/bevy_core_pipeline/src/prepass/node.rs index 017f063e129e7..81724ee4ba3b1 100644 --- a/crates/bevy_core_pipeline/src/prepass/node.rs +++ b/crates/bevy_core_pipeline/src/prepass/node.rs @@ -122,7 +122,7 @@ impl Node for PrepassNode { if let Some(prepass_depth_texture) = &view_prepass_textures.depth { // Copy depth buffer to texture - render_context.command_encoder.copy_texture_to_texture( + render_context.command_encoder().copy_texture_to_texture( view_depth_texture.texture.as_image_copy(), prepass_depth_texture.texture.as_image_copy(), view_prepass_textures.size,