Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/lib.rs
6595 views
1
//! # Useful Environment Variables
2
//!
3
//! Both `bevy_render` and `wgpu` have a number of environment variable options for changing the runtime behavior
4
//! of both crates. Many of these may be useful in development or release environments.
5
//!
6
//! - `WGPU_DEBUG=1` enables debug labels, which can be useful in release builds.
7
//! - `WGPU_VALIDATION=0` disables validation layers. This can help with particularly spammy errors.
8
//! - `WGPU_FORCE_FALLBACK_ADAPTER=1` attempts to force software rendering. This typically matches what is used in CI.
9
//! - `WGPU_ADAPTER_NAME` allows selecting a specific adapter by name.
10
//! - `WGPU_SETTINGS_PRIO=webgl2` uses webgl2 limits.
11
//! - `WGPU_SETTINGS_PRIO=compatibility` uses webgpu limits.
12
//! - `VERBOSE_SHADER_ERROR=1` prints more detailed information about WGSL compilation errors, such as shader defs and shader entrypoint.
13
14
#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
15
#![expect(unsafe_code, reason = "Unsafe code is used to improve performance.")]
16
#![cfg_attr(
17
any(docsrs, docsrs_dep),
18
expect(
19
internal_features,
20
reason = "rustdoc_internals is needed for fake_variadic"
21
)
22
)]
23
#![cfg_attr(any(docsrs, docsrs_dep), feature(doc_auto_cfg, rustdoc_internals))]
24
#![doc(
25
html_logo_url = "https://bevy.org/assets/icon.png",
26
html_favicon_url = "https://bevy.org/assets/icon.png"
27
)]
28
29
#[cfg(target_pointer_width = "16")]
30
compile_error!("bevy_render cannot compile for a 16-bit platform.");
31
32
extern crate alloc;
33
extern crate core;
34
35
// Required to make proc macros work in bevy itself.
36
extern crate self as bevy_render;
37
38
pub mod alpha;
39
pub mod batching;
40
pub mod camera;
41
pub mod diagnostic;
42
pub mod erased_render_asset;
43
pub mod experimental;
44
pub mod extract_component;
45
pub mod extract_instances;
46
mod extract_param;
47
pub mod extract_resource;
48
pub mod globals;
49
pub mod gpu_component_array_buffer;
50
pub mod gpu_readback;
51
pub mod mesh;
52
#[cfg(not(target_arch = "wasm32"))]
53
pub mod pipelined_rendering;
54
pub mod render_asset;
55
pub mod render_graph;
56
pub mod render_phase;
57
pub mod render_resource;
58
pub mod renderer;
59
pub mod settings;
60
pub mod storage;
61
pub mod sync_component;
62
pub mod sync_world;
63
pub mod texture;
64
pub mod view;
65
66
/// The render prelude.
67
///
68
/// This includes the most common types in this crate, re-exported for your convenience.
69
pub mod prelude {
70
#[doc(hidden)]
71
pub use crate::{
72
alpha::AlphaMode, camera::NormalizedRenderTargetExt as _, texture::ManualTextureViews,
73
view::Msaa, ExtractSchedule,
74
};
75
}
76
77
pub use extract_param::Extract;
78
79
use crate::{
80
camera::CameraPlugin,
81
gpu_readback::GpuReadbackPlugin,
82
mesh::{MeshPlugin, MorphPlugin, RenderMesh},
83
render_asset::prepare_assets,
84
render_resource::{init_empty_bind_group_layout, PipelineCache},
85
renderer::{render_system, RenderAdapterInfo},
86
settings::RenderCreation,
87
storage::StoragePlugin,
88
texture::TexturePlugin,
89
view::{ViewPlugin, WindowRenderPlugin},
90
};
91
use alloc::sync::Arc;
92
use batching::gpu_preprocessing::BatchingPlugin;
93
use bevy_app::{App, AppLabel, Plugin, SubApp};
94
use bevy_asset::{AssetApp, AssetServer};
95
use bevy_ecs::{
96
prelude::*,
97
schedule::{ScheduleBuildSettings, ScheduleLabel},
98
};
99
use bevy_image::{CompressedImageFormatSupport, CompressedImageFormats};
100
use bevy_shader::{load_shader_library, Shader, ShaderLoader};
101
use bevy_utils::prelude::default;
102
use bevy_window::{PrimaryWindow, RawHandleWrapperHolder};
103
use bitflags::bitflags;
104
use core::ops::{Deref, DerefMut};
105
use experimental::occlusion_culling::OcclusionCullingPlugin;
106
use globals::GlobalsPlugin;
107
use render_asset::{
108
extract_render_asset_bytes_per_frame, reset_render_asset_bytes_per_frame,
109
RenderAssetBytesPerFrame, RenderAssetBytesPerFrameLimiter,
110
};
111
use settings::RenderResources;
112
use std::sync::Mutex;
113
use sync_world::{despawn_temporary_render_entities, entity_sync_system, SyncWorldPlugin};
114
115
/// Contains the default Bevy rendering backend based on wgpu.
116
///
117
/// Rendering is done in a [`SubApp`], which exchanges data with the main app
118
/// between main schedule iterations.
119
///
120
/// Rendering can be executed between iterations of the main schedule,
121
/// or it can be executed in parallel with main schedule when
122
/// [`PipelinedRenderingPlugin`](pipelined_rendering::PipelinedRenderingPlugin) is enabled.
123
#[derive(Default)]
124
pub struct RenderPlugin {
125
pub render_creation: RenderCreation,
126
/// If `true`, disables asynchronous pipeline compilation.
127
/// This has no effect on macOS, Wasm, iOS, or without the `multi_threaded` feature.
128
pub synchronous_pipeline_compilation: bool,
129
/// Debugging flags that can optionally be set when constructing the renderer.
130
pub debug_flags: RenderDebugFlags,
131
}
132
133
bitflags! {
134
/// Debugging flags that can optionally be set when constructing the renderer.
135
#[derive(Clone, Copy, PartialEq, Default, Debug)]
136
pub struct RenderDebugFlags: u8 {
137
/// If true, this sets the `COPY_SRC` flag on indirect draw parameters
138
/// so that they can be read back to CPU.
139
///
140
/// This is a debugging feature that may reduce performance. It
141
/// primarily exists for the `occlusion_culling` example.
142
const ALLOW_COPIES_FROM_INDIRECT_PARAMETERS = 1;
143
}
144
}
145
146
/// The systems sets of the default [`App`] rendering schedule.
147
///
148
/// These can be useful for ordering, but you almost never want to add your systems to these sets.
149
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
150
pub enum RenderSystems {
151
/// This is used for applying the commands from the [`ExtractSchedule`]
152
ExtractCommands,
153
/// Prepare assets that have been created/modified/removed this frame.
154
PrepareAssets,
155
/// Prepares extracted meshes.
156
PrepareMeshes,
157
/// Create any additional views such as those used for shadow mapping.
158
ManageViews,
159
/// Queue drawable entities as phase items in render phases ready for
160
/// sorting (if necessary)
161
Queue,
162
/// A sub-set within [`Queue`](RenderSystems::Queue) where mesh entity queue systems are executed. Ensures `prepare_assets::<RenderMesh>` is completed.
163
QueueMeshes,
164
/// A sub-set within [`Queue`](RenderSystems::Queue) where meshes that have
165
/// become invisible or changed phases are removed from the bins.
166
QueueSweep,
167
// TODO: This could probably be moved in favor of a system ordering
168
// abstraction in `Render` or `Queue`
169
/// Sort the [`SortedRenderPhase`](render_phase::SortedRenderPhase)s and
170
/// [`BinKey`](render_phase::BinnedPhaseItem::BinKey)s here.
171
PhaseSort,
172
/// Prepare render resources from extracted data for the GPU based on their sorted order.
173
/// Create [`BindGroups`](render_resource::BindGroup) that depend on those data.
174
Prepare,
175
/// A sub-set within [`Prepare`](RenderSystems::Prepare) for initializing buffers, textures and uniforms for use in bind groups.
176
PrepareResources,
177
/// Collect phase buffers after
178
/// [`PrepareResources`](RenderSystems::PrepareResources) has run.
179
PrepareResourcesCollectPhaseBuffers,
180
/// Flush buffers after [`PrepareResources`](RenderSystems::PrepareResources), but before [`PrepareBindGroups`](RenderSystems::PrepareBindGroups).
181
PrepareResourcesFlush,
182
/// A sub-set within [`Prepare`](RenderSystems::Prepare) for constructing bind groups, or other data that relies on render resources prepared in [`PrepareResources`](RenderSystems::PrepareResources).
183
PrepareBindGroups,
184
/// Actual rendering happens here.
185
/// In most cases, only the render backend should insert resources here.
186
Render,
187
/// Cleanup render resources here.
188
Cleanup,
189
/// Final cleanup occurs: all entities will be despawned.
190
///
191
/// Runs after [`Cleanup`](RenderSystems::Cleanup).
192
PostCleanup,
193
}
194
195
/// Deprecated alias for [`RenderSystems`].
196
#[deprecated(since = "0.17.0", note = "Renamed to `RenderSystems`.")]
197
pub type RenderSet = RenderSystems;
198
199
/// The startup schedule of the [`RenderApp`]
200
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
201
pub struct RenderStartup;
202
203
/// The main render schedule.
204
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
205
pub struct Render;
206
207
impl Render {
208
/// Sets up the base structure of the rendering [`Schedule`].
209
///
210
/// The sets defined in this enum are configured to run in order.
211
pub fn base_schedule() -> Schedule {
212
use RenderSystems::*;
213
214
let mut schedule = Schedule::new(Self);
215
216
schedule.configure_sets(
217
(
218
ExtractCommands,
219
PrepareMeshes,
220
ManageViews,
221
Queue,
222
PhaseSort,
223
Prepare,
224
Render,
225
Cleanup,
226
PostCleanup,
227
)
228
.chain(),
229
);
230
231
schedule.configure_sets((ExtractCommands, PrepareAssets, PrepareMeshes, Prepare).chain());
232
schedule.configure_sets(
233
(QueueMeshes, QueueSweep)
234
.chain()
235
.in_set(Queue)
236
.after(prepare_assets::<RenderMesh>),
237
);
238
schedule.configure_sets(
239
(
240
PrepareResources,
241
PrepareResourcesCollectPhaseBuffers,
242
PrepareResourcesFlush,
243
PrepareBindGroups,
244
)
245
.chain()
246
.in_set(Prepare),
247
);
248
249
schedule
250
}
251
}
252
253
/// Schedule which extract data from the main world and inserts it into the render world.
254
///
255
/// This step should be kept as short as possible to increase the "pipelining potential" for
256
/// running the next frame while rendering the current frame.
257
///
258
/// This schedule is run on the main world, but its buffers are not applied
259
/// until it is returned to the render world.
260
#[derive(ScheduleLabel, PartialEq, Eq, Debug, Clone, Hash, Default)]
261
pub struct ExtractSchedule;
262
263
/// The simulation [`World`] of the application, stored as a resource.
264
///
265
/// This resource is only available during [`ExtractSchedule`] and not
266
/// during command application of that schedule.
267
/// See [`Extract`] for more details.
268
#[derive(Resource, Default)]
269
pub struct MainWorld(World);
270
271
impl Deref for MainWorld {
272
type Target = World;
273
274
fn deref(&self) -> &Self::Target {
275
&self.0
276
}
277
}
278
279
impl DerefMut for MainWorld {
280
fn deref_mut(&mut self) -> &mut Self::Target {
281
&mut self.0
282
}
283
}
284
285
pub mod graph {
286
use crate::render_graph::RenderLabel;
287
288
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
289
pub struct CameraDriverLabel;
290
}
291
292
#[derive(Resource)]
293
struct FutureRenderResources(Arc<Mutex<Option<RenderResources>>>);
294
295
/// A label for the rendering sub-app.
296
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
297
pub struct RenderApp;
298
299
impl Plugin for RenderPlugin {
300
/// Initializes the renderer, sets up the [`RenderSystems`] and creates the rendering sub-app.
301
fn build(&self, app: &mut App) {
302
app.init_asset::<Shader>()
303
.init_asset_loader::<ShaderLoader>();
304
305
match &self.render_creation {
306
RenderCreation::Manual(resources) => {
307
let future_render_resources_wrapper = Arc::new(Mutex::new(Some(resources.clone())));
308
app.insert_resource(FutureRenderResources(
309
future_render_resources_wrapper.clone(),
310
));
311
// SAFETY: Plugins should be set up on the main thread.
312
unsafe { initialize_render_app(app) };
313
}
314
RenderCreation::Automatic(render_creation) => {
315
if let Some(backends) = render_creation.backends {
316
let future_render_resources_wrapper = Arc::new(Mutex::new(None));
317
app.insert_resource(FutureRenderResources(
318
future_render_resources_wrapper.clone(),
319
));
320
321
let primary_window = app
322
.world_mut()
323
.query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
324
.single(app.world())
325
.ok()
326
.cloned();
327
328
let settings = render_creation.clone();
329
330
#[cfg(feature = "raw_vulkan_init")]
331
let raw_vulkan_init_settings = app
332
.world_mut()
333
.get_resource::<renderer::raw_vulkan_init::RawVulkanInitSettings>()
334
.cloned()
335
.unwrap_or_default();
336
337
let async_renderer = async move {
338
let render_resources = renderer::initialize_renderer(
339
backends,
340
primary_window,
341
&settings,
342
#[cfg(feature = "raw_vulkan_init")]
343
raw_vulkan_init_settings,
344
)
345
.await;
346
347
*future_render_resources_wrapper.lock().unwrap() = Some(render_resources);
348
};
349
350
// In wasm, spawn a task and detach it for execution
351
#[cfg(target_arch = "wasm32")]
352
bevy_tasks::IoTaskPool::get()
353
.spawn_local(async_renderer)
354
.detach();
355
// Otherwise, just block for it to complete
356
#[cfg(not(target_arch = "wasm32"))]
357
bevy_tasks::block_on(async_renderer);
358
359
// SAFETY: Plugins should be set up on the main thread.
360
unsafe { initialize_render_app(app) };
361
}
362
}
363
};
364
365
app.add_plugins((
366
WindowRenderPlugin,
367
CameraPlugin,
368
ViewPlugin,
369
MeshPlugin,
370
GlobalsPlugin,
371
MorphPlugin,
372
TexturePlugin,
373
BatchingPlugin {
374
debug_flags: self.debug_flags,
375
},
376
SyncWorldPlugin,
377
StoragePlugin,
378
GpuReadbackPlugin::default(),
379
OcclusionCullingPlugin,
380
#[cfg(feature = "tracing-tracy")]
381
diagnostic::RenderDiagnosticsPlugin,
382
));
383
384
app.init_resource::<RenderAssetBytesPerFrame>();
385
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
386
render_app.init_resource::<RenderAssetBytesPerFrameLimiter>();
387
render_app
388
.add_systems(ExtractSchedule, extract_render_asset_bytes_per_frame)
389
.add_systems(
390
Render,
391
reset_render_asset_bytes_per_frame.in_set(RenderSystems::Cleanup),
392
);
393
394
render_app.add_systems(RenderStartup, init_empty_bind_group_layout);
395
}
396
}
397
398
fn ready(&self, app: &App) -> bool {
399
app.world()
400
.get_resource::<FutureRenderResources>()
401
.and_then(|frr| frr.0.try_lock().map(|locked| locked.is_some()).ok())
402
.unwrap_or(true)
403
}
404
405
fn finish(&self, app: &mut App) {
406
load_shader_library!(app, "maths.wgsl");
407
load_shader_library!(app, "color_operations.wgsl");
408
load_shader_library!(app, "bindless.wgsl");
409
if let Some(future_render_resources) =
410
app.world_mut().remove_resource::<FutureRenderResources>()
411
{
412
let render_resources = future_render_resources.0.lock().unwrap().take().unwrap();
413
let RenderResources(device, queue, adapter_info, render_adapter, instance, ..) =
414
render_resources;
415
416
let compressed_image_format_support = CompressedImageFormatSupport(
417
CompressedImageFormats::from_features(device.features()),
418
);
419
420
app.insert_resource(device.clone())
421
.insert_resource(queue.clone())
422
.insert_resource(adapter_info.clone())
423
.insert_resource(render_adapter.clone())
424
.insert_resource(compressed_image_format_support);
425
426
let render_app = app.sub_app_mut(RenderApp);
427
428
#[cfg(feature = "raw_vulkan_init")]
429
{
430
let additional_vulkan_features: renderer::raw_vulkan_init::AdditionalVulkanFeatures =
431
render_resources.5;
432
render_app.insert_resource(additional_vulkan_features);
433
}
434
435
render_app
436
.insert_resource(instance)
437
.insert_resource(PipelineCache::new(
438
device.clone(),
439
render_adapter.clone(),
440
self.synchronous_pipeline_compilation,
441
))
442
.insert_resource(device)
443
.insert_resource(queue)
444
.insert_resource(render_adapter)
445
.insert_resource(adapter_info);
446
}
447
}
448
}
449
450
/// A "scratch" world used to avoid allocating new worlds every frame when
451
/// swapping out the [`MainWorld`] for [`ExtractSchedule`].
452
#[derive(Resource, Default)]
453
struct ScratchMainWorld(World);
454
455
/// Executes the [`ExtractSchedule`] step of the renderer.
456
/// This updates the render world with the extracted ECS data of the current frame.
457
fn extract(main_world: &mut World, render_world: &mut World) {
458
// temporarily add the app world to the render world as a resource
459
let scratch_world = main_world.remove_resource::<ScratchMainWorld>().unwrap();
460
let inserted_world = core::mem::replace(main_world, scratch_world.0);
461
render_world.insert_resource(MainWorld(inserted_world));
462
render_world.run_schedule(ExtractSchedule);
463
464
// move the app world back, as if nothing happened.
465
let inserted_world = render_world.remove_resource::<MainWorld>().unwrap();
466
let scratch_world = core::mem::replace(main_world, inserted_world.0);
467
main_world.insert_resource(ScratchMainWorld(scratch_world));
468
}
469
470
/// # Safety
471
/// This function must be called from the main thread.
472
unsafe fn initialize_render_app(app: &mut App) {
473
app.init_resource::<ScratchMainWorld>();
474
475
let mut render_app = SubApp::new();
476
render_app.update_schedule = Some(Render.intern());
477
478
let mut extract_schedule = Schedule::new(ExtractSchedule);
479
// We skip applying any commands during the ExtractSchedule
480
// so commands can be applied on the render thread.
481
extract_schedule.set_build_settings(ScheduleBuildSettings {
482
auto_insert_apply_deferred: false,
483
..default()
484
});
485
extract_schedule.set_apply_final_deferred(false);
486
487
render_app
488
.add_schedule(extract_schedule)
489
.add_schedule(Render::base_schedule())
490
.init_resource::<render_graph::RenderGraph>()
491
.insert_resource(app.world().resource::<AssetServer>().clone())
492
.add_systems(ExtractSchedule, PipelineCache::extract_shaders)
493
.add_systems(
494
Render,
495
(
496
// This set applies the commands from the extract schedule while the render schedule
497
// is running in parallel with the main app.
498
apply_extract_commands.in_set(RenderSystems::ExtractCommands),
499
(PipelineCache::process_pipeline_queue_system, render_system)
500
.chain()
501
.in_set(RenderSystems::Render),
502
despawn_temporary_render_entities.in_set(RenderSystems::PostCleanup),
503
),
504
);
505
506
// We want the closure to have a flag to only run the RenderStartup schedule once, but the only
507
// way to have the closure store this flag is by capturing it. This variable is otherwise
508
// unused.
509
let mut should_run_startup = true;
510
render_app.set_extract(move |main_world, render_world| {
511
if should_run_startup {
512
// Run the `RenderStartup` if it hasn't run yet. This does mean `RenderStartup` blocks
513
// the rest of the app extraction, but this is necessary since extraction itself can
514
// depend on resources initialized in `RenderStartup`.
515
render_world.run_schedule(RenderStartup);
516
should_run_startup = false;
517
}
518
519
{
520
#[cfg(feature = "trace")]
521
let _stage_span = tracing::info_span!("entity_sync").entered();
522
entity_sync_system(main_world, render_world);
523
}
524
525
// run extract schedule
526
extract(main_world, render_world);
527
});
528
529
let (sender, receiver) = bevy_time::create_time_channels();
530
render_app.insert_resource(sender);
531
app.insert_resource(receiver);
532
app.insert_sub_app(RenderApp, render_app);
533
}
534
535
/// Applies the commands from the extract schedule. This happens during
536
/// the render schedule rather than during extraction to allow the commands to run in parallel with the
537
/// main app when pipelined rendering is enabled.
538
fn apply_extract_commands(render_world: &mut World) {
539
render_world.resource_scope(|render_world, mut schedules: Mut<Schedules>| {
540
schedules
541
.get_mut(ExtractSchedule)
542
.unwrap()
543
.apply_deferred(render_world);
544
});
545
}
546
547
/// If the [`RenderAdapterInfo`] is a Qualcomm Adreno, returns its model number.
548
///
549
/// This lets us work around hardware bugs.
550
pub fn get_adreno_model(adapter_info: &RenderAdapterInfo) -> Option<u32> {
551
if !cfg!(target_os = "android") {
552
return None;
553
}
554
555
let adreno_model = adapter_info.name.strip_prefix("Adreno (TM) ")?;
556
557
// Take suffixes into account (like Adreno 642L).
558
Some(
559
adreno_model
560
.chars()
561
.map_while(|c| c.to_digit(10))
562
.fold(0, |acc, digit| acc * 10 + digit),
563
)
564
}
565
566
/// Get the Mali driver version if the adapter is a Mali GPU.
567
pub fn get_mali_driver_version(adapter_info: &RenderAdapterInfo) -> Option<u32> {
568
if !cfg!(target_os = "android") {
569
return None;
570
}
571
572
if !adapter_info.name.contains("Mali") {
573
return None;
574
}
575
let driver_info = &adapter_info.driver_info;
576
if let Some(start_pos) = driver_info.find("v1.r")
577
&& let Some(end_pos) = driver_info[start_pos..].find('p')
578
{
579
let start_idx = start_pos + 4; // Skip "v1.r"
580
let end_idx = start_pos + end_pos;
581
582
return driver_info[start_idx..end_idx].parse::<u32>().ok();
583
}
584
585
None
586
}
587
588