Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/renderer/mod.rs
6596 views
1
mod graph_runner;
2
#[cfg(feature = "raw_vulkan_init")]
3
pub mod raw_vulkan_init;
4
mod render_device;
5
mod wgpu_wrapper;
6
7
pub use graph_runner::*;
8
pub use render_device::*;
9
pub use wgpu_wrapper::WgpuWrapper;
10
11
use 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
};
19
use alloc::sync::Arc;
20
use bevy_derive::{Deref, DerefMut};
21
use bevy_ecs::{prelude::*, system::SystemState};
22
use bevy_platform::time::Instant;
23
use bevy_time::TimeSender;
24
use bevy_window::RawHandleWrapperHolder;
25
use tracing::{debug, error, info, info_span, warn};
26
use 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.
32
pub 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)]
124
pub 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)]
129
pub 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)]
134
pub struct RenderInstance(pub Arc<WgpuWrapper<Instance>>);
135
136
/// The [`AdapterInfo`] of the adapter in use by the renderer.
137
#[derive(Resource, Clone, Deref, DerefMut)]
138
pub struct RenderAdapterInfo(pub WgpuWrapper<AdapterInfo>);
139
140
const 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"))]
147
fn 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.
175
pub 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.
473
pub 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
480
impl<'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
656
enum 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
}
663
664