bevy_render/renderer/
mod.rs

1mod graph_runner;
2#[cfg(feature = "raw_vulkan_init")]
3pub mod raw_vulkan_init;
4mod render_device;
5mod wgpu_wrapper;
6
7pub use graph_runner::*;
8pub use render_device::*;
9pub use wgpu_wrapper::WgpuWrapper;
10
11use crate::{
12    diagnostic::{internal::DiagnosticsRecorder, RecordDiagnostics},
13    render_graph::RenderGraph,
14    render_phase::TrackedRenderPass,
15    render_resource::RenderPassDescriptor,
16    settings::{RenderResources, WgpuSettings, WgpuSettingsPriority},
17    view::{ExtractedWindows, ViewTarget},
18};
19use alloc::sync::Arc;
20use bevy_camera::NormalizedRenderTarget;
21use bevy_derive::{Deref, DerefMut};
22use bevy_ecs::{prelude::*, system::SystemState};
23use bevy_platform::time::Instant;
24use bevy_render::camera::ExtractedCamera;
25use bevy_time::TimeSender;
26use bevy_window::RawHandleWrapperHolder;
27use tracing::{debug, error, info, info_span, warn};
28use wgpu::{
29    Adapter, AdapterInfo, Backends, CommandBuffer, CommandEncoder, DeviceType, Instance, Queue,
30    RequestAdapterOptions, Trace,
31};
32
33/// Updates the [`RenderGraph`] with all of its nodes and then runs it to render the entire frame.
34pub fn render_system(
35    world: &mut World,
36    state: &mut SystemState<Query<(&ViewTarget, &ExtractedCamera)>>,
37) {
38    world.resource_scope(|world, mut graph: Mut<RenderGraph>| {
39        graph.update(world);
40    });
41
42    let diagnostics_recorder = world.remove_resource::<DiagnosticsRecorder>();
43
44    let graph = world.resource::<RenderGraph>();
45    let render_device = world.resource::<RenderDevice>();
46    let render_queue = world.resource::<RenderQueue>();
47
48    let res = RenderGraphRunner::run(
49        graph,
50        render_device.clone(), // TODO: is this clone really necessary?
51        diagnostics_recorder,
52        &render_queue.0,
53        world,
54        |encoder| {
55            crate::view::screenshot::submit_screenshot_commands(world, encoder);
56            crate::gpu_readback::submit_readback_commands(world, encoder);
57        },
58    );
59
60    match res {
61        Ok(Some(diagnostics_recorder)) => {
62            world.insert_resource(diagnostics_recorder);
63        }
64        Ok(None) => {}
65        Err(e) => {
66            error!("Error running render graph:");
67            {
68                let mut src: &dyn core::error::Error = &e;
69                loop {
70                    error!("> {}", src);
71                    match src.source() {
72                        Some(s) => src = s,
73                        None => break,
74                    }
75                }
76            }
77
78            panic!("Error running render graph: {e}");
79        }
80    }
81
82    {
83        let _span = info_span!("present_frames").entered();
84
85        world.resource_scope(|world, mut windows: Mut<ExtractedWindows>| {
86            let views = state.get(world);
87            for window in windows.values_mut() {
88                let view_needs_present = views.iter().any(|(view_target, camera)| {
89                    matches!(
90                        camera.target,
91                        Some(NormalizedRenderTarget::Window(w)) if w.entity() == window.entity
92                    ) && view_target.needs_present()
93                });
94
95                if view_needs_present || window.needs_initial_present {
96                    window.present();
97                    window.needs_initial_present = false;
98                }
99            }
100        });
101
102        #[cfg(feature = "tracing-tracy")]
103        tracing::event!(
104            tracing::Level::INFO,
105            message = "finished frame",
106            tracy.frame_mark = true
107        );
108    }
109
110    crate::view::screenshot::collect_screenshots(world);
111
112    // update the time and send it to the app world
113    let time_sender = world.resource::<TimeSender>();
114    if let Err(error) = time_sender.0.try_send(Instant::now()) {
115        match error {
116            bevy_time::TrySendError::Full(_) => {
117                panic!("The TimeSender channel should always be empty during render. You might need to add the bevy::core::time_system to your app.");
118            }
119            bevy_time::TrySendError::Disconnected(_) => {
120                // ignore disconnected errors, the main world probably just got dropped during shutdown
121            }
122        }
123    }
124}
125
126/// This queue is used to enqueue tasks for the GPU to execute asynchronously.
127#[derive(Resource, Clone, Deref, DerefMut)]
128pub struct RenderQueue(pub Arc<WgpuWrapper<Queue>>);
129
130/// The handle to the physical device being used for rendering.
131/// See [`Adapter`] for more info.
132#[derive(Resource, Clone, Debug, Deref, DerefMut)]
133pub struct RenderAdapter(pub Arc<WgpuWrapper<Adapter>>);
134
135/// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`],
136/// as well as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces).
137#[derive(Resource, Clone, Deref, DerefMut)]
138pub struct RenderInstance(pub Arc<WgpuWrapper<Instance>>);
139
140/// The [`AdapterInfo`] of the adapter in use by the renderer.
141#[derive(Resource, Clone, Deref, DerefMut)]
142pub struct RenderAdapterInfo(pub WgpuWrapper<AdapterInfo>);
143
144const GPU_NOT_FOUND_ERROR_MESSAGE: &str = if cfg!(target_os = "linux") {
145    "Unable to find a GPU! Make sure you have installed required drivers! For extra information, see: https://github.com/bevyengine/bevy/blob/latest/docs/linux_dependencies.md"
146} else {
147    "Unable to find a GPU! Make sure you have installed required drivers!"
148};
149
150#[cfg(not(target_family = "wasm"))]
151fn find_adapter_by_name(
152    instance: &Instance,
153    options: &WgpuSettings,
154    compatible_surface: Option<&wgpu::Surface<'_>>,
155    adapter_name: &str,
156) -> Option<Adapter> {
157    for adapter in
158        instance.enumerate_adapters(options.backends.expect(
159            "The `backends` field of `WgpuSettings` must be set to use a specific adapter.",
160        ))
161    {
162        tracing::trace!("Checking adapter: {:?}", adapter.get_info());
163        let info = adapter.get_info();
164        if let Some(surface) = compatible_surface
165            && !adapter.is_surface_supported(surface)
166        {
167            continue;
168        }
169
170        if info
171            .name
172            .to_lowercase()
173            .contains(&adapter_name.to_lowercase())
174        {
175            return Some(adapter);
176        }
177    }
178    None
179}
180
181/// Initializes the renderer by retrieving and preparing the GPU instance, device and queue
182/// for the specified backend.
183pub async fn initialize_renderer(
184    backends: Backends,
185    primary_window: Option<RawHandleWrapperHolder>,
186    options: &WgpuSettings,
187    #[cfg(feature = "raw_vulkan_init")]
188    raw_vulkan_init_settings: raw_vulkan_init::RawVulkanInitSettings,
189) -> RenderResources {
190    let instance_descriptor = wgpu::InstanceDescriptor {
191        backends,
192        flags: options.instance_flags,
193        memory_budget_thresholds: options.instance_memory_budget_thresholds,
194        backend_options: wgpu::BackendOptions {
195            gl: wgpu::GlBackendOptions {
196                gles_minor_version: options.gles3_minor_version,
197                fence_behavior: wgpu::GlFenceBehavior::Normal,
198            },
199            dx12: wgpu::Dx12BackendOptions {
200                shader_compiler: options.dx12_shader_compiler.clone(),
201                presentation_system: wgpu::wgt::Dx12SwapchainKind::from_env().unwrap_or_default(),
202                latency_waitable_object: wgpu::wgt::Dx12UseFrameLatencyWaitableObject::from_env()
203                    .unwrap_or_default(),
204            },
205            noop: wgpu::NoopBackendOptions { enable: false },
206        },
207    };
208
209    #[cfg(not(feature = "raw_vulkan_init"))]
210    let instance = Instance::new(&instance_descriptor);
211    #[cfg(feature = "raw_vulkan_init")]
212    let mut additional_vulkan_features = raw_vulkan_init::AdditionalVulkanFeatures::default();
213    #[cfg(feature = "raw_vulkan_init")]
214    let instance = raw_vulkan_init::create_raw_vulkan_instance(
215        &instance_descriptor,
216        &raw_vulkan_init_settings,
217        &mut additional_vulkan_features,
218    );
219
220    let surface = primary_window.and_then(|wrapper| {
221        let maybe_handle = wrapper
222            .0
223            .lock()
224            .expect("Couldn't get the window handle in time for renderer initialization");
225        if let Some(wrapper) = maybe_handle.as_ref() {
226            // SAFETY: Plugins should be set up on the main thread.
227            let handle = unsafe { wrapper.get_handle() };
228            Some(
229                instance
230                    .create_surface(handle)
231                    .expect("Failed to create wgpu surface"),
232            )
233        } else {
234            None
235        }
236    });
237
238    let force_fallback_adapter = std::env::var("WGPU_FORCE_FALLBACK_ADAPTER")
239        .map_or(options.force_fallback_adapter, |v| {
240            !(v.is_empty() || v == "0" || v == "false")
241        });
242
243    let desired_adapter_name = std::env::var("WGPU_ADAPTER_NAME")
244        .as_deref()
245        .map_or(options.adapter_name.clone(), |x| Some(x.to_lowercase()));
246
247    let request_adapter_options = RequestAdapterOptions {
248        power_preference: options.power_preference,
249        compatible_surface: surface.as_ref(),
250        force_fallback_adapter,
251    };
252
253    #[cfg(not(target_family = "wasm"))]
254    let mut selected_adapter = desired_adapter_name.and_then(|adapter_name| {
255        find_adapter_by_name(
256            &instance,
257            options,
258            request_adapter_options.compatible_surface,
259            &adapter_name,
260        )
261    });
262    #[cfg(target_family = "wasm")]
263    let mut selected_adapter = None;
264
265    #[cfg(target_family = "wasm")]
266    if desired_adapter_name.is_some() {
267        warn!("Choosing an adapter is not supported on wasm.");
268    }
269
270    if selected_adapter.is_none() {
271        debug!(
272            "Searching for adapter with options: {:?}",
273            request_adapter_options
274        );
275        selected_adapter = instance
276            .request_adapter(&request_adapter_options)
277            .await
278            .ok();
279    }
280
281    let adapter = selected_adapter.expect(GPU_NOT_FOUND_ERROR_MESSAGE);
282    let adapter_info = adapter.get_info();
283    info!("{:?}", adapter_info);
284
285    if adapter_info.device_type == DeviceType::Cpu {
286        warn!(
287            "The selected adapter is using a driver that only supports software rendering. \
288             This is likely to be very slow. See https://bevy.org/learn/errors/b0006/"
289        );
290    }
291
292    // Maybe get features and limits based on what is supported by the adapter/backend
293    let mut features = wgpu::Features::empty();
294    let mut limits = options.limits.clone();
295    if matches!(options.priority, WgpuSettingsPriority::Functionality) {
296        features = adapter.features();
297        if adapter_info.device_type == DeviceType::DiscreteGpu {
298            // `MAPPABLE_PRIMARY_BUFFERS` can have a significant, negative performance impact for
299            // discrete GPUs due to having to transfer data across the PCI-E bus and so it
300            // should not be automatically enabled in this case. It is however beneficial for
301            // integrated GPUs.
302            features.remove(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS);
303        }
304
305        limits = adapter.limits();
306    }
307
308    // Enforce the disabled features
309    if let Some(disabled_features) = options.disabled_features {
310        features.remove(disabled_features);
311    }
312    // NOTE: |= is used here to ensure that any explicitly-enabled features are respected.
313    features |= options.features;
314
315    // Enforce the limit constraints
316    if let Some(constrained_limits) = options.constrained_limits.as_ref() {
317        // NOTE: Respect the configured limits as an 'upper bound'. This means for 'max' limits, we
318        // take the minimum of the calculated limits according to the adapter/backend and the
319        // specified max_limits. For 'min' limits, take the maximum instead. This is intended to
320        // err on the side of being conservative. We can't claim 'higher' limits that are supported
321        // but we can constrain to 'lower' limits.
322        limits = wgpu::Limits {
323            max_texture_dimension_1d: limits
324                .max_texture_dimension_1d
325                .min(constrained_limits.max_texture_dimension_1d),
326            max_texture_dimension_2d: limits
327                .max_texture_dimension_2d
328                .min(constrained_limits.max_texture_dimension_2d),
329            max_texture_dimension_3d: limits
330                .max_texture_dimension_3d
331                .min(constrained_limits.max_texture_dimension_3d),
332            max_texture_array_layers: limits
333                .max_texture_array_layers
334                .min(constrained_limits.max_texture_array_layers),
335            max_bind_groups: limits
336                .max_bind_groups
337                .min(constrained_limits.max_bind_groups),
338            max_dynamic_uniform_buffers_per_pipeline_layout: limits
339                .max_dynamic_uniform_buffers_per_pipeline_layout
340                .min(constrained_limits.max_dynamic_uniform_buffers_per_pipeline_layout),
341            max_dynamic_storage_buffers_per_pipeline_layout: limits
342                .max_dynamic_storage_buffers_per_pipeline_layout
343                .min(constrained_limits.max_dynamic_storage_buffers_per_pipeline_layout),
344            max_sampled_textures_per_shader_stage: limits
345                .max_sampled_textures_per_shader_stage
346                .min(constrained_limits.max_sampled_textures_per_shader_stage),
347            max_samplers_per_shader_stage: limits
348                .max_samplers_per_shader_stage
349                .min(constrained_limits.max_samplers_per_shader_stage),
350            max_storage_buffers_per_shader_stage: limits
351                .max_storage_buffers_per_shader_stage
352                .min(constrained_limits.max_storage_buffers_per_shader_stage),
353            max_storage_textures_per_shader_stage: limits
354                .max_storage_textures_per_shader_stage
355                .min(constrained_limits.max_storage_textures_per_shader_stage),
356            max_uniform_buffers_per_shader_stage: limits
357                .max_uniform_buffers_per_shader_stage
358                .min(constrained_limits.max_uniform_buffers_per_shader_stage),
359            max_binding_array_elements_per_shader_stage: limits
360                .max_binding_array_elements_per_shader_stage
361                .min(constrained_limits.max_binding_array_elements_per_shader_stage),
362            max_binding_array_sampler_elements_per_shader_stage: limits
363                .max_binding_array_sampler_elements_per_shader_stage
364                .min(constrained_limits.max_binding_array_sampler_elements_per_shader_stage),
365            max_uniform_buffer_binding_size: limits
366                .max_uniform_buffer_binding_size
367                .min(constrained_limits.max_uniform_buffer_binding_size),
368            max_storage_buffer_binding_size: limits
369                .max_storage_buffer_binding_size
370                .min(constrained_limits.max_storage_buffer_binding_size),
371            max_vertex_buffers: limits
372                .max_vertex_buffers
373                .min(constrained_limits.max_vertex_buffers),
374            max_vertex_attributes: limits
375                .max_vertex_attributes
376                .min(constrained_limits.max_vertex_attributes),
377            max_vertex_buffer_array_stride: limits
378                .max_vertex_buffer_array_stride
379                .min(constrained_limits.max_vertex_buffer_array_stride),
380            max_push_constant_size: limits
381                .max_push_constant_size
382                .min(constrained_limits.max_push_constant_size),
383            min_uniform_buffer_offset_alignment: limits
384                .min_uniform_buffer_offset_alignment
385                .max(constrained_limits.min_uniform_buffer_offset_alignment),
386            min_storage_buffer_offset_alignment: limits
387                .min_storage_buffer_offset_alignment
388                .max(constrained_limits.min_storage_buffer_offset_alignment),
389            max_inter_stage_shader_components: limits
390                .max_inter_stage_shader_components
391                .min(constrained_limits.max_inter_stage_shader_components),
392            max_compute_workgroup_storage_size: limits
393                .max_compute_workgroup_storage_size
394                .min(constrained_limits.max_compute_workgroup_storage_size),
395            max_compute_invocations_per_workgroup: limits
396                .max_compute_invocations_per_workgroup
397                .min(constrained_limits.max_compute_invocations_per_workgroup),
398            max_compute_workgroup_size_x: limits
399                .max_compute_workgroup_size_x
400                .min(constrained_limits.max_compute_workgroup_size_x),
401            max_compute_workgroup_size_y: limits
402                .max_compute_workgroup_size_y
403                .min(constrained_limits.max_compute_workgroup_size_y),
404            max_compute_workgroup_size_z: limits
405                .max_compute_workgroup_size_z
406                .min(constrained_limits.max_compute_workgroup_size_z),
407            max_compute_workgroups_per_dimension: limits
408                .max_compute_workgroups_per_dimension
409                .min(constrained_limits.max_compute_workgroups_per_dimension),
410            max_buffer_size: limits
411                .max_buffer_size
412                .min(constrained_limits.max_buffer_size),
413            max_bindings_per_bind_group: limits
414                .max_bindings_per_bind_group
415                .min(constrained_limits.max_bindings_per_bind_group),
416            max_non_sampler_bindings: limits
417                .max_non_sampler_bindings
418                .min(constrained_limits.max_non_sampler_bindings),
419            max_blas_primitive_count: limits
420                .max_blas_primitive_count
421                .min(constrained_limits.max_blas_primitive_count),
422            max_blas_geometry_count: limits
423                .max_blas_geometry_count
424                .min(constrained_limits.max_blas_geometry_count),
425            max_tlas_instance_count: limits
426                .max_tlas_instance_count
427                .min(constrained_limits.max_tlas_instance_count),
428            max_color_attachments: limits
429                .max_color_attachments
430                .min(constrained_limits.max_color_attachments),
431            max_color_attachment_bytes_per_sample: limits
432                .max_color_attachment_bytes_per_sample
433                .min(constrained_limits.max_color_attachment_bytes_per_sample),
434            min_subgroup_size: limits
435                .min_subgroup_size
436                .max(constrained_limits.min_subgroup_size),
437            max_subgroup_size: limits
438                .max_subgroup_size
439                .min(constrained_limits.max_subgroup_size),
440            max_acceleration_structures_per_shader_stage: limits
441                .max_acceleration_structures_per_shader_stage
442                .min(constrained_limits.max_acceleration_structures_per_shader_stage),
443            max_task_workgroup_total_count: limits
444                .max_task_workgroup_total_count
445                .min(constrained_limits.max_task_workgroup_total_count),
446            max_task_workgroups_per_dimension: limits
447                .max_task_workgroups_per_dimension
448                .min(constrained_limits.max_task_workgroups_per_dimension),
449            max_mesh_output_layers: limits
450                .max_mesh_output_layers
451                .min(constrained_limits.max_mesh_output_layers),
452            max_mesh_multiview_count: limits
453                .max_mesh_multiview_count
454                .min(constrained_limits.max_mesh_multiview_count),
455        };
456    }
457
458    let device_descriptor = wgpu::DeviceDescriptor {
459        label: options.device_label.as_ref().map(AsRef::as_ref),
460        required_features: features,
461        required_limits: limits,
462        // SAFETY: TODO, see https://github.com/bevyengine/bevy/issues/22082
463        experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
464        memory_hints: options.memory_hints.clone(),
465        // See https://github.com/gfx-rs/wgpu/issues/5974
466        trace: Trace::Off,
467    };
468
469    #[cfg(not(feature = "raw_vulkan_init"))]
470    let (device, queue) = adapter.request_device(&device_descriptor).await.unwrap();
471
472    #[cfg(feature = "raw_vulkan_init")]
473    let (device, queue) = raw_vulkan_init::create_raw_device(
474        &adapter,
475        &device_descriptor,
476        &raw_vulkan_init_settings,
477        &mut additional_vulkan_features,
478    )
479    .await
480    .unwrap();
481
482    debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
483    debug!("Configured wgpu adapter Features: {:#?}", device.features());
484
485    RenderResources(
486        RenderDevice::from(device),
487        RenderQueue(Arc::new(WgpuWrapper::new(queue))),
488        RenderAdapterInfo(WgpuWrapper::new(adapter_info)),
489        RenderAdapter(Arc::new(WgpuWrapper::new(adapter))),
490        RenderInstance(Arc::new(WgpuWrapper::new(instance))),
491        #[cfg(feature = "raw_vulkan_init")]
492        additional_vulkan_features,
493    )
494}
495
496/// The context with all information required to interact with the GPU.
497///
498/// The [`RenderDevice`] is used to create render resources and the
499/// [`CommandEncoder`] is used to record a series of GPU operations.
500pub struct RenderContext<'w> {
501    render_device: RenderDevice,
502    command_encoder: Option<CommandEncoder>,
503    command_buffer_queue: Vec<QueuedCommandBuffer<'w>>,
504    diagnostics_recorder: Option<Arc<DiagnosticsRecorder>>,
505}
506
507impl<'w> RenderContext<'w> {
508    /// Creates a new [`RenderContext`] from a [`RenderDevice`].
509    pub fn new(
510        render_device: RenderDevice,
511        diagnostics_recorder: Option<DiagnosticsRecorder>,
512    ) -> Self {
513        Self {
514            render_device,
515            command_encoder: None,
516            command_buffer_queue: Vec::new(),
517            diagnostics_recorder: diagnostics_recorder.map(Arc::new),
518        }
519    }
520
521    /// Gets the underlying [`RenderDevice`].
522    pub fn render_device(&self) -> &RenderDevice {
523        &self.render_device
524    }
525
526    /// Gets the diagnostics recorder, used to track elapsed time and pipeline statistics
527    /// of various render and compute passes.
528    pub fn diagnostic_recorder(&self) -> impl RecordDiagnostics + use<> {
529        self.diagnostics_recorder.clone()
530    }
531
532    /// Gets the current [`CommandEncoder`].
533    pub fn command_encoder(&mut self) -> &mut CommandEncoder {
534        self.command_encoder.get_or_insert_with(|| {
535            self.render_device
536                .create_command_encoder(&wgpu::CommandEncoderDescriptor::default())
537        })
538    }
539
540    pub(crate) fn has_commands(&mut self) -> bool {
541        self.command_encoder.is_some() || !self.command_buffer_queue.is_empty()
542    }
543
544    /// Creates a new [`TrackedRenderPass`] for the context,
545    /// configured using the provided `descriptor`.
546    pub fn begin_tracked_render_pass<'a>(
547        &'a mut self,
548        descriptor: RenderPassDescriptor<'_>,
549    ) -> TrackedRenderPass<'a> {
550        // Cannot use command_encoder() as we need to split the borrow on self
551        let command_encoder = self.command_encoder.get_or_insert_with(|| {
552            self.render_device
553                .create_command_encoder(&wgpu::CommandEncoderDescriptor::default())
554        });
555
556        let render_pass = command_encoder.begin_render_pass(&descriptor);
557        TrackedRenderPass::new(&self.render_device, render_pass)
558    }
559
560    /// Append a [`CommandBuffer`] to the command buffer queue.
561    ///
562    /// If present, this will flush the currently unflushed [`CommandEncoder`]
563    /// into a [`CommandBuffer`] into the queue before appending the provided
564    /// buffer.
565    pub fn add_command_buffer(&mut self, command_buffer: CommandBuffer) {
566        self.flush_encoder();
567
568        self.command_buffer_queue
569            .push(QueuedCommandBuffer::Ready(command_buffer));
570    }
571
572    /// Append a function that will generate a [`CommandBuffer`] to the
573    /// command buffer queue, to be ran later.
574    ///
575    /// If present, this will flush the currently unflushed [`CommandEncoder`]
576    /// into a [`CommandBuffer`] into the queue before appending the provided
577    /// buffer.
578    pub fn add_command_buffer_generation_task(
579        &mut self,
580        #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
581        task: impl FnOnce(RenderDevice) -> CommandBuffer + 'w + Send,
582        #[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
583        task: impl FnOnce(RenderDevice) -> CommandBuffer + 'w,
584    ) {
585        self.flush_encoder();
586
587        self.command_buffer_queue
588            .push(QueuedCommandBuffer::Task(Box::new(task)));
589    }
590
591    /// Finalizes and returns the queue of [`CommandBuffer`]s.
592    ///
593    /// This function will wait until all command buffer generation tasks are complete
594    /// by running them in parallel (where supported).
595    ///
596    /// The [`CommandBuffer`]s will be returned in the order that they were added.
597    pub fn finish(
598        mut self,
599    ) -> (
600        Vec<CommandBuffer>,
601        RenderDevice,
602        Option<DiagnosticsRecorder>,
603    ) {
604        self.flush_encoder();
605
606        let mut command_buffers = Vec::with_capacity(self.command_buffer_queue.len());
607
608        #[cfg(feature = "trace")]
609        let _command_buffer_generation_tasks_span =
610            info_span!("command_buffer_generation_tasks").entered();
611
612        #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
613        {
614            let mut task_based_command_buffers =
615                bevy_tasks::ComputeTaskPool::get().scope(|task_pool| {
616                    for (i, queued_command_buffer) in
617                        self.command_buffer_queue.into_iter().enumerate()
618                    {
619                        match queued_command_buffer {
620                            QueuedCommandBuffer::Ready(command_buffer) => {
621                                command_buffers.push((i, command_buffer));
622                            }
623                            QueuedCommandBuffer::Task(command_buffer_generation_task) => {
624                                let render_device = self.render_device.clone();
625                                task_pool.spawn(async move {
626                                    (i, command_buffer_generation_task(render_device))
627                                });
628                            }
629                        }
630                    }
631                });
632            command_buffers.append(&mut task_based_command_buffers);
633        }
634
635        #[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
636        for (i, queued_command_buffer) in self.command_buffer_queue.into_iter().enumerate() {
637            match queued_command_buffer {
638                QueuedCommandBuffer::Ready(command_buffer) => {
639                    command_buffers.push((i, command_buffer));
640                }
641                QueuedCommandBuffer::Task(command_buffer_generation_task) => {
642                    let render_device = self.render_device.clone();
643                    command_buffers.push((i, command_buffer_generation_task(render_device)));
644                }
645            }
646        }
647
648        #[cfg(feature = "trace")]
649        drop(_command_buffer_generation_tasks_span);
650
651        command_buffers.sort_unstable_by_key(|(i, _)| *i);
652
653        let mut command_buffers = command_buffers
654            .into_iter()
655            .map(|(_, cb)| cb)
656            .collect::<Vec<CommandBuffer>>();
657
658        let mut diagnostics_recorder = self.diagnostics_recorder.take().map(|v| {
659            Arc::try_unwrap(v)
660                .ok()
661                .expect("diagnostic recorder shouldn't be held longer than necessary")
662        });
663
664        if let Some(recorder) = &mut diagnostics_recorder {
665            let mut command_encoder = self
666                .render_device
667                .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
668            recorder.resolve(&mut command_encoder);
669            command_buffers.push(command_encoder.finish());
670        }
671
672        (command_buffers, self.render_device, diagnostics_recorder)
673    }
674
675    fn flush_encoder(&mut self) {
676        if let Some(encoder) = self.command_encoder.take() {
677            self.command_buffer_queue
678                .push(QueuedCommandBuffer::Ready(encoder.finish()));
679        }
680    }
681}
682
683enum QueuedCommandBuffer<'w> {
684    Ready(CommandBuffer),
685    #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
686    Task(Box<dyn FnOnce(RenderDevice) -> CommandBuffer + 'w + Send>),
687    #[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
688    Task(Box<dyn FnOnce(RenderDevice) -> CommandBuffer + 'w>),
689}