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