Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/renderer/mod.rs
9334 views
1
#[cfg(feature = "raw_vulkan_init")]
2
pub mod raw_vulkan_init;
3
mod render_context;
4
mod render_device;
5
mod wgpu_wrapper;
6
7
pub use render_context::{
8
CurrentView, FlushCommands, PendingCommandBuffers, RenderContext, RenderContextState, ViewQuery,
9
};
10
pub use render_device::*;
11
pub use wgpu_wrapper::WgpuWrapper;
12
13
use crate::{
14
settings::{RenderResources, WgpuSettings, WgpuSettingsPriority},
15
view::{ExtractedWindows, ViewTarget},
16
};
17
use alloc::sync::Arc;
18
use bevy_camera::NormalizedRenderTarget;
19
use bevy_derive::{Deref, DerefMut};
20
use bevy_ecs::schedule::ScheduleLabel;
21
use bevy_ecs::{prelude::*, system::SystemState};
22
use bevy_log::{debug, info, info_span, warn};
23
use bevy_render::camera::ExtractedCamera;
24
use bevy_window::RawHandleWrapperHolder;
25
use wgpu::{
26
Adapter, AdapterInfo, Backends, DeviceType, Instance, Queue, RequestAdapterOptions, Trace,
27
};
28
29
/// Schedule label for the root render graph schedule. This schedule runs once per frame
30
/// in the [`render_system`] system and is responsible for driving the entire rendering process.
31
#[derive(ScheduleLabel, Debug, Clone, PartialEq, Eq, Hash, Default)]
32
pub struct RenderGraph;
33
34
impl RenderGraph {
35
pub fn base_schedule() -> Schedule {
36
Schedule::new(Self)
37
}
38
}
39
40
/// The main render system that drives the rendering process. This system runs the [`RenderGraph`]
41
/// schedule, runs any finalization commands like screenshot captures and GPU readbacks, and
42
/// calls present on swap chains that need to be presented.
43
pub fn render_system(
44
world: &mut World,
45
state: &mut SystemState<Query<(&ViewTarget, &ExtractedCamera)>>,
46
) {
47
#[cfg(feature = "trace")]
48
let _span = info_span!("main_render_schedule").entered();
49
50
world.run_schedule(RenderGraph);
51
52
{
53
let render_device = world.resource::<RenderDevice>();
54
let render_queue = world.resource::<RenderQueue>();
55
56
let mut encoder =
57
render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
58
59
crate::view::screenshot::submit_screenshot_commands(world, &mut encoder);
60
crate::gpu_readback::submit_readback_commands(world, &mut encoder);
61
62
render_queue.submit([encoder.finish()]);
63
}
64
65
{
66
let _span = info_span!("present_frames").entered();
67
68
world.resource_scope(|world, mut windows: Mut<ExtractedWindows>| {
69
let views = state.get(world);
70
for window in windows.values_mut() {
71
let view_needs_present = views.iter().any(|(view_target, camera)| {
72
matches!(
73
camera.target,
74
Some(NormalizedRenderTarget::Window(w)) if w.entity() == window.entity
75
) && view_target.needs_present()
76
});
77
78
if view_needs_present || window.needs_initial_present {
79
window.present();
80
window.needs_initial_present = false;
81
}
82
}
83
});
84
85
#[cfg(feature = "tracing-tracy")]
86
bevy_log::event!(
87
bevy_log::Level::INFO,
88
message = "finished frame",
89
tracy.frame_mark = true
90
);
91
}
92
93
crate::view::screenshot::collect_screenshots(world);
94
}
95
96
/// This queue is used to enqueue tasks for the GPU to execute asynchronously.
97
#[derive(Resource, Clone, Deref, DerefMut)]
98
pub struct RenderQueue(pub Arc<WgpuWrapper<Queue>>);
99
100
/// The handle to the physical device being used for rendering.
101
/// See [`Adapter`] for more info.
102
#[derive(Resource, Clone, Debug, Deref, DerefMut)]
103
pub struct RenderAdapter(pub Arc<WgpuWrapper<Adapter>>);
104
105
/// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`],
106
/// as well as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces).
107
#[derive(Resource, Clone, Deref, DerefMut)]
108
pub struct RenderInstance(pub Arc<WgpuWrapper<Instance>>);
109
110
/// The [`AdapterInfo`] of the adapter in use by the renderer.
111
#[derive(Resource, Clone, Deref, DerefMut)]
112
pub struct RenderAdapterInfo(pub WgpuWrapper<AdapterInfo>);
113
114
const GPU_NOT_FOUND_ERROR_MESSAGE: &str = if cfg!(target_os = "linux") {
115
"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"
116
} else {
117
"Unable to find a GPU! Make sure you have installed required drivers!"
118
};
119
120
#[cfg(not(target_family = "wasm"))]
121
async fn find_adapter_by_name(
122
instance: &Instance,
123
options: &WgpuSettings,
124
compatible_surface: Option<&wgpu::Surface<'_>>,
125
adapter_name: &str,
126
) -> Option<Adapter> {
127
for adapter in instance
128
.enumerate_adapters(options.backends.expect(
129
"The `backends` field of `WgpuSettings` must be set to use a specific adapter.",
130
))
131
.await
132
{
133
bevy_log::trace!("Checking adapter: {:?}", adapter.get_info());
134
let info = adapter.get_info();
135
if let Some(surface) = compatible_surface
136
&& !adapter.is_surface_supported(surface)
137
{
138
continue;
139
}
140
141
if info
142
.name
143
.to_lowercase()
144
.contains(&adapter_name.to_lowercase())
145
{
146
return Some(adapter);
147
}
148
}
149
None
150
}
151
152
/// Initializes the renderer by retrieving and preparing the GPU instance, device and queue
153
/// for the specified backend.
154
pub async fn initialize_renderer(
155
backends: Backends,
156
primary_window: Option<RawHandleWrapperHolder>,
157
options: &WgpuSettings,
158
#[cfg(feature = "raw_vulkan_init")]
159
raw_vulkan_init_settings: raw_vulkan_init::RawVulkanInitSettings,
160
) -> RenderResources {
161
let instance_descriptor = wgpu::InstanceDescriptor {
162
backends,
163
flags: options.instance_flags,
164
memory_budget_thresholds: options.instance_memory_budget_thresholds,
165
backend_options: wgpu::BackendOptions {
166
gl: wgpu::GlBackendOptions {
167
gles_minor_version: options.gles3_minor_version,
168
fence_behavior: wgpu::GlFenceBehavior::Normal,
169
},
170
dx12: wgpu::Dx12BackendOptions {
171
shader_compiler: options.dx12_shader_compiler.clone(),
172
presentation_system: wgpu::wgt::Dx12SwapchainKind::from_env().unwrap_or_default(),
173
latency_waitable_object: wgpu::wgt::Dx12UseFrameLatencyWaitableObject::from_env()
174
.unwrap_or_default(),
175
},
176
noop: wgpu::NoopBackendOptions { enable: false },
177
},
178
};
179
180
#[cfg(not(feature = "raw_vulkan_init"))]
181
let instance = Instance::new(&instance_descriptor);
182
#[cfg(feature = "raw_vulkan_init")]
183
let mut additional_vulkan_features = raw_vulkan_init::AdditionalVulkanFeatures::default();
184
#[cfg(feature = "raw_vulkan_init")]
185
let instance = raw_vulkan_init::create_raw_vulkan_instance(
186
&instance_descriptor,
187
&raw_vulkan_init_settings,
188
&mut additional_vulkan_features,
189
);
190
191
let surface = primary_window.and_then(|wrapper| {
192
let maybe_handle = wrapper
193
.0
194
.lock()
195
.expect("Couldn't get the window handle in time for renderer initialization");
196
if let Some(wrapper) = maybe_handle.as_ref() {
197
// SAFETY: Plugins should be set up on the main thread.
198
let handle = unsafe { wrapper.get_handle() };
199
Some(
200
instance
201
.create_surface(handle)
202
.expect("Failed to create wgpu surface"),
203
)
204
} else {
205
None
206
}
207
});
208
209
let force_fallback_adapter = std::env::var("WGPU_FORCE_FALLBACK_ADAPTER")
210
.map_or(options.force_fallback_adapter, |v| {
211
!(v.is_empty() || v == "0" || v == "false")
212
});
213
214
let desired_adapter_name = std::env::var("WGPU_ADAPTER_NAME")
215
.as_deref()
216
.map_or(options.adapter_name.clone(), |x| Some(x.to_lowercase()));
217
218
let request_adapter_options = RequestAdapterOptions {
219
power_preference: options.power_preference,
220
compatible_surface: surface.as_ref(),
221
force_fallback_adapter,
222
};
223
224
#[cfg(not(target_family = "wasm"))]
225
let mut selected_adapter = if let Some(adapter_name) = desired_adapter_name {
226
find_adapter_by_name(
227
&instance,
228
options,
229
request_adapter_options.compatible_surface,
230
&adapter_name,
231
)
232
.await
233
} else {
234
None
235
};
236
#[cfg(target_family = "wasm")]
237
let mut selected_adapter = None;
238
239
#[cfg(target_family = "wasm")]
240
if desired_adapter_name.is_some() {
241
warn!("Choosing an adapter is not supported on wasm.");
242
}
243
244
if selected_adapter.is_none() {
245
debug!(
246
"Searching for adapter with options: {:?}",
247
request_adapter_options
248
);
249
selected_adapter = instance
250
.request_adapter(&request_adapter_options)
251
.await
252
.ok();
253
}
254
255
let adapter = selected_adapter.expect(GPU_NOT_FOUND_ERROR_MESSAGE);
256
let adapter_info = adapter.get_info();
257
info!("{:?}", adapter_info);
258
259
if adapter_info.device_type == DeviceType::Cpu {
260
warn!(
261
"The selected adapter is using a driver that only supports software rendering. \
262
This is likely to be very slow. See https://bevy.org/learn/errors/b0006/"
263
);
264
}
265
266
// Maybe get features and limits based on what is supported by the adapter/backend
267
let mut features = wgpu::Features::empty();
268
let mut limits = options.limits.clone();
269
if matches!(options.priority, WgpuSettingsPriority::Functionality) {
270
features = adapter.features();
271
if adapter_info.device_type == DeviceType::DiscreteGpu {
272
// `MAPPABLE_PRIMARY_BUFFERS` can have a significant, negative performance impact for
273
// discrete GPUs due to having to transfer data across the PCI-E bus and so it
274
// should not be automatically enabled in this case. It is however beneficial for
275
// integrated GPUs.
276
features.remove(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS);
277
}
278
279
limits = adapter.limits();
280
}
281
282
// Enforce the disabled features
283
if let Some(disabled_features) = options.disabled_features {
284
features.remove(disabled_features);
285
}
286
// NOTE: |= is used here to ensure that any explicitly-enabled features are respected.
287
features |= options.features;
288
289
// Enforce the limit constraints
290
if let Some(constrained_limits) = options.constrained_limits.as_ref() {
291
// NOTE: Respect the configured limits as an 'upper bound'. This means for 'max' limits, we
292
// take the minimum of the calculated limits according to the adapter/backend and the
293
// specified max_limits. For 'min' limits, take the maximum instead. This is intended to
294
// err on the side of being conservative. We can't claim 'higher' limits that are supported
295
// but we can constrain to 'lower' limits.
296
limits = wgpu::Limits {
297
max_texture_dimension_1d: limits
298
.max_texture_dimension_1d
299
.min(constrained_limits.max_texture_dimension_1d),
300
max_texture_dimension_2d: limits
301
.max_texture_dimension_2d
302
.min(constrained_limits.max_texture_dimension_2d),
303
max_texture_dimension_3d: limits
304
.max_texture_dimension_3d
305
.min(constrained_limits.max_texture_dimension_3d),
306
max_texture_array_layers: limits
307
.max_texture_array_layers
308
.min(constrained_limits.max_texture_array_layers),
309
max_bind_groups: limits
310
.max_bind_groups
311
.min(constrained_limits.max_bind_groups),
312
max_dynamic_uniform_buffers_per_pipeline_layout: limits
313
.max_dynamic_uniform_buffers_per_pipeline_layout
314
.min(constrained_limits.max_dynamic_uniform_buffers_per_pipeline_layout),
315
max_dynamic_storage_buffers_per_pipeline_layout: limits
316
.max_dynamic_storage_buffers_per_pipeline_layout
317
.min(constrained_limits.max_dynamic_storage_buffers_per_pipeline_layout),
318
max_sampled_textures_per_shader_stage: limits
319
.max_sampled_textures_per_shader_stage
320
.min(constrained_limits.max_sampled_textures_per_shader_stage),
321
max_samplers_per_shader_stage: limits
322
.max_samplers_per_shader_stage
323
.min(constrained_limits.max_samplers_per_shader_stage),
324
max_storage_buffers_per_shader_stage: limits
325
.max_storage_buffers_per_shader_stage
326
.min(constrained_limits.max_storage_buffers_per_shader_stage),
327
max_storage_textures_per_shader_stage: limits
328
.max_storage_textures_per_shader_stage
329
.min(constrained_limits.max_storage_textures_per_shader_stage),
330
max_uniform_buffers_per_shader_stage: limits
331
.max_uniform_buffers_per_shader_stage
332
.min(constrained_limits.max_uniform_buffers_per_shader_stage),
333
max_binding_array_elements_per_shader_stage: limits
334
.max_binding_array_elements_per_shader_stage
335
.min(constrained_limits.max_binding_array_elements_per_shader_stage),
336
max_binding_array_sampler_elements_per_shader_stage: limits
337
.max_binding_array_sampler_elements_per_shader_stage
338
.min(constrained_limits.max_binding_array_sampler_elements_per_shader_stage),
339
max_uniform_buffer_binding_size: limits
340
.max_uniform_buffer_binding_size
341
.min(constrained_limits.max_uniform_buffer_binding_size),
342
max_storage_buffer_binding_size: limits
343
.max_storage_buffer_binding_size
344
.min(constrained_limits.max_storage_buffer_binding_size),
345
max_vertex_buffers: limits
346
.max_vertex_buffers
347
.min(constrained_limits.max_vertex_buffers),
348
max_vertex_attributes: limits
349
.max_vertex_attributes
350
.min(constrained_limits.max_vertex_attributes),
351
max_vertex_buffer_array_stride: limits
352
.max_vertex_buffer_array_stride
353
.min(constrained_limits.max_vertex_buffer_array_stride),
354
max_immediate_size: limits
355
.max_immediate_size
356
.min(constrained_limits.max_immediate_size),
357
min_uniform_buffer_offset_alignment: limits
358
.min_uniform_buffer_offset_alignment
359
.max(constrained_limits.min_uniform_buffer_offset_alignment),
360
min_storage_buffer_offset_alignment: limits
361
.min_storage_buffer_offset_alignment
362
.max(constrained_limits.min_storage_buffer_offset_alignment),
363
max_inter_stage_shader_components: limits
364
.max_inter_stage_shader_components
365
.min(constrained_limits.max_inter_stage_shader_components),
366
max_compute_workgroup_storage_size: limits
367
.max_compute_workgroup_storage_size
368
.min(constrained_limits.max_compute_workgroup_storage_size),
369
max_compute_invocations_per_workgroup: limits
370
.max_compute_invocations_per_workgroup
371
.min(constrained_limits.max_compute_invocations_per_workgroup),
372
max_compute_workgroup_size_x: limits
373
.max_compute_workgroup_size_x
374
.min(constrained_limits.max_compute_workgroup_size_x),
375
max_compute_workgroup_size_y: limits
376
.max_compute_workgroup_size_y
377
.min(constrained_limits.max_compute_workgroup_size_y),
378
max_compute_workgroup_size_z: limits
379
.max_compute_workgroup_size_z
380
.min(constrained_limits.max_compute_workgroup_size_z),
381
max_compute_workgroups_per_dimension: limits
382
.max_compute_workgroups_per_dimension
383
.min(constrained_limits.max_compute_workgroups_per_dimension),
384
max_buffer_size: limits
385
.max_buffer_size
386
.min(constrained_limits.max_buffer_size),
387
max_bindings_per_bind_group: limits
388
.max_bindings_per_bind_group
389
.min(constrained_limits.max_bindings_per_bind_group),
390
max_non_sampler_bindings: limits
391
.max_non_sampler_bindings
392
.min(constrained_limits.max_non_sampler_bindings),
393
max_blas_primitive_count: limits
394
.max_blas_primitive_count
395
.min(constrained_limits.max_blas_primitive_count),
396
max_blas_geometry_count: limits
397
.max_blas_geometry_count
398
.min(constrained_limits.max_blas_geometry_count),
399
max_tlas_instance_count: limits
400
.max_tlas_instance_count
401
.min(constrained_limits.max_tlas_instance_count),
402
max_color_attachments: limits
403
.max_color_attachments
404
.min(constrained_limits.max_color_attachments),
405
max_color_attachment_bytes_per_sample: limits
406
.max_color_attachment_bytes_per_sample
407
.min(constrained_limits.max_color_attachment_bytes_per_sample),
408
max_task_mesh_workgroup_total_count: limits
409
.max_task_mesh_workgroup_total_count
410
.min(constrained_limits.max_task_mesh_workgroup_total_count),
411
max_task_mesh_workgroups_per_dimension: limits
412
.max_task_mesh_workgroups_per_dimension
413
.min(constrained_limits.max_task_mesh_workgroups_per_dimension),
414
max_task_invocations_per_workgroup: limits
415
.max_task_invocations_per_workgroup
416
.min(constrained_limits.max_task_invocations_per_workgroup),
417
max_task_invocations_per_dimension: limits
418
.max_task_invocations_per_dimension
419
.min(constrained_limits.max_task_invocations_per_dimension),
420
max_mesh_invocations_per_workgroup: limits
421
.max_mesh_invocations_per_workgroup
422
.min(constrained_limits.max_mesh_invocations_per_workgroup),
423
max_mesh_invocations_per_dimension: limits
424
.max_mesh_invocations_per_dimension
425
.min(constrained_limits.max_mesh_invocations_per_dimension),
426
max_task_payload_size: limits
427
.max_task_payload_size
428
.min(constrained_limits.max_task_payload_size),
429
max_mesh_output_vertices: limits
430
.max_mesh_output_vertices
431
.min(constrained_limits.max_mesh_output_vertices),
432
max_mesh_output_primitives: limits
433
.max_mesh_output_primitives
434
.min(constrained_limits.max_mesh_output_primitives),
435
max_mesh_output_layers: limits
436
.max_mesh_output_layers
437
.min(constrained_limits.max_mesh_output_layers),
438
max_mesh_multiview_view_count: limits
439
.max_mesh_multiview_view_count
440
.min(constrained_limits.max_mesh_multiview_view_count),
441
max_acceleration_structures_per_shader_stage: limits
442
.max_acceleration_structures_per_shader_stage
443
.min(constrained_limits.max_acceleration_structures_per_shader_stage),
444
max_multiview_view_count: limits
445
.max_multiview_view_count
446
.min(constrained_limits.max_multiview_view_count),
447
};
448
}
449
450
let device_descriptor = wgpu::DeviceDescriptor {
451
label: options.device_label.as_ref().map(AsRef::as_ref),
452
required_features: features,
453
required_limits: limits,
454
// SAFETY: TODO, see https://github.com/bevyengine/bevy/issues/22082
455
experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
456
memory_hints: options.memory_hints.clone(),
457
// See https://github.com/gfx-rs/wgpu/issues/5974
458
trace: Trace::Off,
459
};
460
461
#[cfg(not(feature = "raw_vulkan_init"))]
462
let (device, queue) = adapter.request_device(&device_descriptor).await.unwrap();
463
464
#[cfg(feature = "raw_vulkan_init")]
465
let (device, queue) = raw_vulkan_init::create_raw_device(
466
&adapter,
467
&device_descriptor,
468
&raw_vulkan_init_settings,
469
&mut additional_vulkan_features,
470
)
471
.await
472
.unwrap();
473
474
debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
475
debug!("Configured wgpu adapter Features: {:#?}", device.features());
476
477
RenderResources(
478
RenderDevice::from(device),
479
RenderQueue(Arc::new(WgpuWrapper::new(queue))),
480
RenderAdapterInfo(WgpuWrapper::new(adapter_info)),
481
RenderAdapter(Arc::new(WgpuWrapper::new(adapter))),
482
RenderInstance(Arc::new(WgpuWrapper::new(instance))),
483
#[cfg(feature = "raw_vulkan_init")]
484
additional_vulkan_features,
485
)
486
}
487
488