Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/lib.rs
9550 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_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 batching;
39
pub mod camera;
40
pub mod diagnostic;
41
pub mod erased_render_asset;
42
pub mod error_handler;
43
pub mod extract_component;
44
pub mod extract_instances;
45
mod extract_param;
46
pub mod extract_plugin;
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
pub mod occlusion_culling;
53
#[cfg(not(target_arch = "wasm32"))]
54
pub mod pipelined_rendering;
55
pub mod render_asset;
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
camera::NormalizedRenderTargetExt as _, texture::ManualTextureViews, view::Msaa,
73
ExtractSchedule,
74
};
75
}
76
77
pub use extract_param::Extract;
78
pub use extract_plugin::{ExtractSchedule, MainWorld};
79
80
use crate::{
81
camera::CameraPlugin,
82
error_handler::{RenderErrorHandler, RenderState},
83
extract_plugin::ExtractPlugin,
84
gpu_readback::GpuReadbackPlugin,
85
mesh::{MeshRenderAssetPlugin, RenderMesh},
86
render_asset::prepare_assets,
87
render_resource::PipelineCache,
88
renderer::{render_system, RenderAdapterInfo, RenderGraph},
89
settings::RenderCreation,
90
storage::StoragePlugin,
91
texture::TexturePlugin,
92
view::{ViewPlugin, WindowRenderPlugin},
93
};
94
use alloc::sync::Arc;
95
use batching::gpu_preprocessing::BatchingPlugin;
96
use bevy_app::{App, AppLabel, Plugin};
97
use bevy_asset::{AssetApp, AssetServer};
98
use bevy_derive::Deref;
99
use bevy_ecs::{prelude::*, schedule::ScheduleLabel};
100
use bevy_platform::time::Instant;
101
use bevy_shader::{load_shader_library, Shader, ShaderLoader};
102
use bevy_time::TimeSender;
103
use bevy_window::{PrimaryWindow, RawHandleWrapperHolder};
104
use bitflags::bitflags;
105
use globals::GlobalsPlugin;
106
use occlusion_culling::OcclusionCullingPlugin;
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
114
/// Contains the default Bevy rendering backend based on wgpu.
115
///
116
/// Rendering is done in a [`SubApp`](bevy_app::SubApp), which exchanges data with the main app
117
/// between main schedule iterations.
118
///
119
/// Rendering can be executed between iterations of the main schedule,
120
/// or it can be executed in parallel with main schedule when
121
/// [`PipelinedRenderingPlugin`](pipelined_rendering::PipelinedRenderingPlugin) is enabled.
122
#[derive(Default)]
123
pub struct RenderPlugin {
124
pub render_creation: RenderCreation,
125
/// If `true`, disables asynchronous pipeline compilation.
126
/// This has no effect on macOS, Wasm, iOS, or without the `multi_threaded` feature.
127
pub synchronous_pipeline_compilation: bool,
128
/// Debugging flags that can optionally be set when constructing the renderer.
129
pub debug_flags: RenderDebugFlags,
130
}
131
132
bitflags! {
133
/// Debugging flags that can optionally be set when constructing the renderer.
134
#[derive(Clone, Copy, PartialEq, Default, Debug)]
135
pub struct RenderDebugFlags: u8 {
136
/// If true, this sets the `COPY_SRC` flag on indirect draw parameters
137
/// so that they can be read back to CPU.
138
///
139
/// This is a debugging feature that may reduce performance. It
140
/// primarily exists for the `occlusion_culling` example.
141
const ALLOW_COPIES_FROM_INDIRECT_PARAMETERS = 1;
142
}
143
}
144
145
/// The systems sets of the default [`App`] rendering schedule.
146
///
147
/// These can be useful for ordering, but you almost never want to add your systems to these sets.
148
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
149
pub enum RenderSystems {
150
/// This is used for applying the commands from the [`ExtractSchedule`]
151
ExtractCommands,
152
/// Prepare assets that have been created/modified/removed this frame.
153
PrepareAssets,
154
/// Prepares extracted meshes.
155
PrepareMeshes,
156
/// Create any additional views such as those used for shadow mapping.
157
CreateViews,
158
/// Specialize material meshes and shadow views.
159
Specialize,
160
/// Prepare any additional views such as those used for shadow mapping.
161
PrepareViews,
162
/// Queue drawable entities as phase items in render phases ready for
163
/// sorting (if necessary)
164
Queue,
165
/// A sub-set within [`Queue`](RenderSystems::Queue) where mesh entity queue systems are executed. Ensures `prepare_assets::<RenderMesh>` is completed.
166
QueueMeshes,
167
/// A sub-set within [`Queue`](RenderSystems::Queue) where meshes that have
168
/// become invisible or changed phases are removed from the bins.
169
QueueSweep,
170
// TODO: This could probably be moved in favor of a system ordering
171
// abstraction in `Render` or `Queue`
172
/// Sort the [`SortedRenderPhase`](render_phase::SortedRenderPhase)s and
173
/// [`BinKey`](render_phase::BinnedPhaseItem::BinKey)s here.
174
PhaseSort,
175
/// Prepare render resources from extracted data for the GPU based on their sorted order.
176
/// Create [`BindGroups`](render_resource::BindGroup) that depend on those data.
177
Prepare,
178
/// A sub-set within [`Prepare`](RenderSystems::Prepare) for initializing buffers, textures and uniforms for use in bind groups.
179
PrepareResources,
180
/// A sub-set within [`Prepare`](RenderSystems::Prepare) that creates batches for render phases.
181
PrepareResourcesBatchPhases,
182
/// A sub-set within [`Prepare`](RenderSystems::Prepare) to collect phase buffers after
183
/// [`PrepareResourcesBatchPhases`](RenderSystems::PrepareResourcesBatchPhases) has run.
184
PrepareResourcesCollectPhaseBuffers,
185
/// Flush buffers after [`PrepareResources`](RenderSystems::PrepareResources), but before [`PrepareBindGroups`](RenderSystems::PrepareBindGroups).
186
PrepareResourcesFlush,
187
/// A sub-set within [`Prepare`](RenderSystems::Prepare) for constructing bind groups, or other data that relies on render resources prepared in [`PrepareResources`](RenderSystems::PrepareResources).
188
PrepareBindGroups,
189
/// Actual rendering happens here.
190
/// In most cases, only the render backend should insert resources here.
191
Render,
192
/// Cleanup render resources here.
193
Cleanup,
194
/// Final cleanup occurs: any entities with
195
/// [`TemporaryRenderEntity`](sync_world::TemporaryRenderEntity) will be despawned.
196
///
197
/// Runs after [`Cleanup`](RenderSystems::Cleanup).
198
PostCleanup,
199
}
200
201
/// The startup schedule of the [`RenderApp`].
202
/// This can potentially run multiple times, and not on a fresh render world.
203
/// Every time a new [`RenderDevice`](renderer::RenderDevice) is acquired,
204
/// this schedule runs to initialize any gpu resources needed for rendering on it.
205
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
206
pub struct RenderStartup;
207
208
/// The render recovery schedule. This schedule runs the [`Render`] schedule if
209
/// we are in [`RenderState::Ready`], and is otherwise hidden from users.
210
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
211
struct RenderRecovery;
212
213
/// The main render schedule.
214
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
215
pub struct Render;
216
217
impl Render {
218
/// Sets up the base structure of the rendering [`Schedule`].
219
///
220
/// The sets defined in this enum are configured to run in order.
221
pub fn base_schedule() -> Schedule {
222
use RenderSystems::*;
223
224
let mut schedule = Schedule::new(Self);
225
226
schedule.configure_sets(
227
(
228
ExtractCommands,
229
PrepareMeshes,
230
CreateViews,
231
Specialize,
232
PrepareViews,
233
Queue,
234
PhaseSort,
235
Prepare,
236
Render,
237
Cleanup,
238
PostCleanup,
239
)
240
.chain(),
241
);
242
schedule.ignore_ambiguity(Specialize, Specialize);
243
244
schedule.configure_sets((ExtractCommands, PrepareAssets, PrepareMeshes, Prepare).chain());
245
schedule.configure_sets(
246
(QueueMeshes, QueueSweep)
247
.chain()
248
.in_set(Queue)
249
.after(prepare_assets::<RenderMesh>),
250
);
251
schedule.configure_sets(
252
(
253
PrepareResources,
254
PrepareResourcesBatchPhases,
255
PrepareResourcesCollectPhaseBuffers,
256
PrepareResourcesFlush,
257
PrepareBindGroups,
258
)
259
.chain()
260
.in_set(Prepare),
261
);
262
263
schedule
264
}
265
}
266
267
#[derive(Resource, Default, Clone, Deref)]
268
pub(crate) struct FutureRenderResources(Arc<Mutex<Option<RenderResources>>>);
269
270
/// A label for the rendering sub-app.
271
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
272
pub struct RenderApp;
273
274
impl Plugin for RenderPlugin {
275
/// Initializes the renderer, sets up the [`RenderSystems`] and creates the rendering sub-app.
276
fn build(&self, app: &mut App) {
277
app.init_asset::<Shader>()
278
.init_asset_loader::<ShaderLoader>();
279
load_shader_library!(app, "maths.wgsl");
280
load_shader_library!(app, "color_operations.wgsl");
281
load_shader_library!(app, "bindless.wgsl");
282
283
if insert_future_resources(&self.render_creation, app.world_mut()) {
284
// We only create the render world and set up extraction if we
285
// have a rendering backend available.
286
app.add_plugins(ExtractPlugin {
287
pre_extract: error_handler::update_state,
288
});
289
};
290
291
app.add_plugins((
292
WindowRenderPlugin,
293
CameraPlugin,
294
ViewPlugin,
295
MeshRenderAssetPlugin,
296
GlobalsPlugin,
297
TexturePlugin,
298
BatchingPlugin {
299
debug_flags: self.debug_flags,
300
},
301
StoragePlugin,
302
GpuReadbackPlugin::default(),
303
OcclusionCullingPlugin,
304
#[cfg(feature = "tracing-tracy")]
305
diagnostic::RenderDiagnosticsPlugin,
306
));
307
308
let (sender, receiver) = bevy_time::create_time_channels();
309
app.insert_resource(receiver);
310
311
let asset_server = app.world().resource::<AssetServer>().clone();
312
app.init_resource::<RenderAssetBytesPerFrame>()
313
.init_resource::<RenderErrorHandler>();
314
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
315
render_app.init_resource::<RenderAssetBytesPerFrameLimiter>();
316
render_app.init_resource::<renderer::PendingCommandBuffers>();
317
render_app.insert_resource(sender);
318
render_app.insert_resource(asset_server);
319
render_app.insert_resource(RenderState::Initializing);
320
render_app.add_systems(
321
ExtractSchedule,
322
(
323
extract_render_asset_bytes_per_frame,
324
PipelineCache::extract_shaders,
325
),
326
);
327
328
render_app.add_schedule(RenderGraph::base_schedule());
329
330
render_app.init_schedule(RenderStartup);
331
render_app.update_schedule = Some(RenderRecovery.intern());
332
render_app.add_systems(
333
RenderRecovery,
334
(run_render_schedule.run_if(renderer_is_ready), send_time).chain(),
335
);
336
render_app.add_systems(
337
Render,
338
(
339
(PipelineCache::process_pipeline_queue_system, render_system)
340
.chain()
341
.in_set(RenderSystems::Render),
342
reset_render_asset_bytes_per_frame.in_set(RenderSystems::Cleanup),
343
),
344
);
345
}
346
}
347
348
fn ready(&self, app: &App) -> bool {
349
// This is a little tricky. `FutureRenderResources` is added in `build`, which runs synchronously before `ready`.
350
// It is only added if there is a wgpu backend and thus the renderer can be created.
351
// Hence, if we try and get the resource and it is not present, that means we are ready, because we dont need it.
352
// On the other hand, if the resource is present, then we try and lock on it. The lock can fail, in which case
353
// we currently can assume that means the `FutureRenderResources` is in the act of being populated, because
354
// that is the only other place the lock may be held. If it is being populated, we can assume we're ready. This
355
// happens via the `and_then` falling through to the same `unwrap_or(true)` case as when there's no resource.
356
// If the lock succeeds, we can straightforwardly check if it is populated. If it is not, then we're not ready.
357
app.world()
358
.get_resource::<FutureRenderResources>()
359
.and_then(|frr| frr.try_lock().map(|locked| locked.is_some()).ok())
360
.unwrap_or(true)
361
}
362
363
fn finish(&self, app: &mut App) {
364
if let Some(future_render_resources) =
365
app.world_mut().remove_resource::<FutureRenderResources>()
366
{
367
let bevy_app::SubApps { main, sub_apps } = app.sub_apps_mut();
368
let render = sub_apps.get_mut(&RenderApp.intern()).unwrap();
369
let render_resources = future_render_resources.0.lock().unwrap().take().unwrap();
370
371
render_resources.unpack_into(
372
main.world_mut(),
373
render.world_mut(),
374
self.synchronous_pipeline_compilation,
375
);
376
}
377
}
378
}
379
380
fn renderer_is_ready(state: Res<RenderState>) -> bool {
381
matches!(*state, RenderState::Ready)
382
}
383
384
fn run_render_schedule(world: &mut World) {
385
world.run_schedule(Render);
386
}
387
388
fn send_time(time_sender: Res<TimeSender>) {
389
// update the time and send it to the app world regardless of whether we render
390
if let Err(error) = time_sender.0.try_send(Instant::now()) {
391
match error {
392
bevy_time::TrySendError::Full(_) => {
393
panic!(
394
"The TimeSender channel should always be empty during render. \
395
You might need to add the bevy::core::time_system to your app."
396
);
397
}
398
bevy_time::TrySendError::Disconnected(_) => {
399
// ignore disconnected errors, the main world probably just got dropped during shutdown
400
}
401
}
402
}
403
}
404
405
/// Inserts a [`FutureRenderResources`] created from this [`RenderCreation`].
406
///
407
/// Returns true if creation was successful, false otherwise.
408
fn insert_future_resources(render_creation: &RenderCreation, main_world: &mut World) -> bool {
409
let primary_window = main_world
410
.query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
411
.single(main_world)
412
.ok()
413
.cloned();
414
415
#[cfg(feature = "raw_vulkan_init")]
416
let raw_vulkan_init_settings = main_world
417
.get_resource::<renderer::raw_vulkan_init::RawVulkanInitSettings>()
418
.cloned()
419
.unwrap_or_default();
420
421
let future_resources = FutureRenderResources::default();
422
let success = render_creation.create_render(
423
future_resources.clone(),
424
primary_window,
425
#[cfg(feature = "raw_vulkan_init")]
426
raw_vulkan_init_settings,
427
);
428
if success {
429
// Note that `future_resources` is not necessarily populated here yet.
430
main_world.insert_resource(future_resources);
431
}
432
success
433
}
434
435
/// If the [`RenderAdapterInfo`] is a Qualcomm Adreno, returns its model number.
436
///
437
/// This lets us work around hardware bugs.
438
pub fn get_adreno_model(adapter_info: &RenderAdapterInfo) -> Option<u32> {
439
if !cfg!(target_os = "android") {
440
return None;
441
}
442
443
let adreno_model = adapter_info.name.strip_prefix("Adreno (TM) ")?;
444
445
// Take suffixes into account (like Adreno 642L).
446
Some(
447
adreno_model
448
.chars()
449
.map_while(|c| c.to_digit(10))
450
.fold(0, |acc, digit| acc * 10 + digit),
451
)
452
}
453
454
/// Get the Mali driver version if the adapter is a Mali GPU.
455
pub fn get_mali_driver_version(adapter_info: &RenderAdapterInfo) -> Option<u32> {
456
if !cfg!(target_os = "android") {
457
return None;
458
}
459
460
if !adapter_info.name.contains("Mali") {
461
return None;
462
}
463
let driver_info = &adapter_info.driver_info;
464
if let Some(start_pos) = driver_info.find("v1.r")
465
&& let Some(end_pos) = driver_info[start_pos..].find('p')
466
{
467
let start_idx = start_pos + 4; // Skip "v1.r"
468
let end_idx = start_pos + end_pos;
469
470
return driver_info[start_idx..end_idx].parse::<u32>().ok();
471
}
472
473
None
474
}
475
476