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 353425e0dcfb6..5003fbfd538f1 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 @@ -205,7 +205,7 @@ impl Node for MainPass3dNode { }; render_context - .command_encoder + .command_encoder() .begin_render_pass(&pass_descriptor); } 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/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, 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 5280324e736ea..539383cd56e4a 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 0c392810cfb37..e11c61f6ca064 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..5885003bfb76e 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 { + /// Creates a new [`RenderContext`] from a [`RenderDevice`]. + pub fn new(render_device: RenderDevice) -> Self { + Self { + render_device, + command_encoder: None, + command_buffers: Vec::new(), + } + } + + /// 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 { + self.command_encoder.get_or_insert_with(|| { + self.render_device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()) + }) + } + /// 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 + 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) + } + + /// 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 + } + + fn flush_encoder(&mut self) { + if let Some(encoder) = self.command_encoder.take() { + self.command_buffers.push(encoder.finish()); + } } } diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index 84cf86160fa32..87b57a4abbd6f 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -216,7 +216,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, &[]);