Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/lib.rs
9328 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},
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
ManageViews,
158
/// Queue drawable entities as phase items in render phases ready for
159
/// sorting (if necessary)
160
Queue,
161
/// A sub-set within [`Queue`](RenderSystems::Queue) where mesh entity queue systems are executed. Ensures `prepare_assets::<RenderMesh>` is completed.
162
QueueMeshes,
163
/// A sub-set within [`Queue`](RenderSystems::Queue) where meshes that have
164
/// become invisible or changed phases are removed from the bins.
165
QueueSweep,
166
// TODO: This could probably be moved in favor of a system ordering
167
// abstraction in `Render` or `Queue`
168
/// Sort the [`SortedRenderPhase`](render_phase::SortedRenderPhase)s and
169
/// [`BinKey`](render_phase::BinnedPhaseItem::BinKey)s here.
170
PhaseSort,
171
/// Prepare render resources from extracted data for the GPU based on their sorted order.
172
/// Create [`BindGroups`](render_resource::BindGroup) that depend on those data.
173
Prepare,
174
/// A sub-set within [`Prepare`](RenderSystems::Prepare) for initializing buffers, textures and uniforms for use in bind groups.
175
PrepareResources,
176
/// Collect phase buffers after
177
/// [`PrepareResources`](RenderSystems::PrepareResources) has run.
178
PrepareResourcesCollectPhaseBuffers,
179
/// Flush buffers after [`PrepareResources`](RenderSystems::PrepareResources), but before [`PrepareBindGroups`](RenderSystems::PrepareBindGroups).
180
PrepareResourcesFlush,
181
/// A sub-set within [`Prepare`](RenderSystems::Prepare) for constructing bind groups, or other data that relies on render resources prepared in [`PrepareResources`](RenderSystems::PrepareResources).
182
PrepareBindGroups,
183
/// Actual rendering happens here.
184
/// In most cases, only the render backend should insert resources here.
185
Render,
186
/// Cleanup render resources here.
187
Cleanup,
188
/// Final cleanup occurs: any entities with
189
/// [`TemporaryRenderEntity`](sync_world::TemporaryRenderEntity) will be despawned.
190
///
191
/// Runs after [`Cleanup`](RenderSystems::Cleanup).
192
PostCleanup,
193
}
194
195
/// The startup schedule of the [`RenderApp`].
196
/// This can potentially run multiple times, and not on a fresh render world.
197
/// Every time a new [`RenderDevice`](renderer::RenderDevice) is acquired,
198
/// this schedule runs to initialize any gpu resources needed for rendering on it.
199
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
200
pub struct RenderStartup;
201
202
/// The render recovery schedule. This schedule runs the [`Render`] schedule if
203
/// we are in [`RenderState::Ready`], and is otherwise hidden from users.
204
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
205
struct RenderRecovery;
206
207
/// The main render schedule.
208
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
209
pub struct Render;
210
211
impl Render {
212
/// Sets up the base structure of the rendering [`Schedule`].
213
///
214
/// The sets defined in this enum are configured to run in order.
215
pub fn base_schedule() -> Schedule {
216
use RenderSystems::*;
217
218
let mut schedule = Schedule::new(Self);
219
220
schedule.configure_sets(
221
(
222
ExtractCommands,
223
PrepareMeshes,
224
ManageViews,
225
Queue,
226
PhaseSort,
227
Prepare,
228
Render,
229
Cleanup,
230
PostCleanup,
231
)
232
.chain(),
233
);
234
235
schedule.configure_sets((ExtractCommands, PrepareAssets, PrepareMeshes, Prepare).chain());
236
schedule.configure_sets(
237
(QueueMeshes, QueueSweep)
238
.chain()
239
.in_set(Queue)
240
.after(prepare_assets::<RenderMesh>),
241
);
242
schedule.configure_sets(
243
(
244
PrepareResources,
245
PrepareResourcesCollectPhaseBuffers,
246
PrepareResourcesFlush,
247
PrepareBindGroups,
248
)
249
.chain()
250
.in_set(Prepare),
251
);
252
253
schedule
254
}
255
}
256
257
#[derive(Resource, Default, Clone, Deref)]
258
pub(crate) struct FutureRenderResources(Arc<Mutex<Option<RenderResources>>>);
259
260
/// A label for the rendering sub-app.
261
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
262
pub struct RenderApp;
263
264
impl Plugin for RenderPlugin {
265
/// Initializes the renderer, sets up the [`RenderSystems`] and creates the rendering sub-app.
266
fn build(&self, app: &mut App) {
267
app.init_asset::<Shader>()
268
.init_asset_loader::<ShaderLoader>();
269
load_shader_library!(app, "maths.wgsl");
270
load_shader_library!(app, "color_operations.wgsl");
271
load_shader_library!(app, "bindless.wgsl");
272
273
if insert_future_resources(&self.render_creation, app.world_mut()) {
274
// We only create the render world and set up extraction if we
275
// have a rendering backend available.
276
app.add_plugins(ExtractPlugin {
277
pre_extract: error_handler::update_state,
278
});
279
};
280
281
app.add_plugins((
282
WindowRenderPlugin,
283
CameraPlugin,
284
ViewPlugin,
285
MeshRenderAssetPlugin,
286
GlobalsPlugin,
287
#[cfg(feature = "morph")]
288
mesh::MorphPlugin,
289
TexturePlugin,
290
BatchingPlugin {
291
debug_flags: self.debug_flags,
292
},
293
StoragePlugin,
294
GpuReadbackPlugin::default(),
295
OcclusionCullingPlugin,
296
#[cfg(feature = "tracing-tracy")]
297
diagnostic::RenderDiagnosticsPlugin,
298
));
299
300
let asset_server = app.world().resource::<AssetServer>().clone();
301
app.init_resource::<RenderAssetBytesPerFrame>()
302
.init_resource::<RenderErrorHandler>();
303
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
304
render_app.init_resource::<RenderAssetBytesPerFrameLimiter>();
305
render_app.init_resource::<renderer::PendingCommandBuffers>();
306
render_app.insert_resource(asset_server);
307
render_app.insert_resource(RenderState::Initializing);
308
render_app.add_systems(
309
ExtractSchedule,
310
(
311
extract_render_asset_bytes_per_frame,
312
PipelineCache::extract_shaders,
313
),
314
);
315
316
render_app.init_schedule(RenderStartup);
317
render_app.update_schedule = Some(RenderRecovery.intern());
318
render_app.add_systems(
319
RenderRecovery,
320
(run_render_schedule.run_if(renderer_is_ready), send_time).chain(),
321
);
322
render_app.add_systems(
323
Render,
324
(
325
(PipelineCache::process_pipeline_queue_system, render_system)
326
.chain()
327
.in_set(RenderSystems::Render),
328
reset_render_asset_bytes_per_frame.in_set(RenderSystems::Cleanup),
329
),
330
);
331
}
332
}
333
334
fn ready(&self, app: &App) -> bool {
335
// This is a little tricky. `FutureRenderResources` is added in `build`, which runs synchronously before `ready`.
336
// It is only added if there is a wgpu backend and thus the renderer can be created.
337
// Hence, if we try and get the resource and it is not present, that means we are ready, because we dont need it.
338
// On the other hand, if the resource is present, then we try and lock on it. The lock can fail, in which case
339
// we currently can assume that means the `FutureRenderResources` is in the act of being populated, because
340
// that is the only other place the lock may be held. If it is being populated, we can assume we're ready. This
341
// happens via the `and_then` falling through to the same `unwrap_or(true)` case as when there's no resource.
342
// If the lock succeeds, we can straightforwardly check if it is populated. If it is not, then we're not ready.
343
app.world()
344
.get_resource::<FutureRenderResources>()
345
.and_then(|frr| frr.try_lock().map(|locked| locked.is_some()).ok())
346
.unwrap_or(true)
347
}
348
349
fn finish(&self, app: &mut App) {
350
if let Some(future_render_resources) =
351
app.world_mut().remove_resource::<FutureRenderResources>()
352
{
353
let bevy_app::SubApps { main, sub_apps } = app.sub_apps_mut();
354
let render = sub_apps.get_mut(&RenderApp.intern()).unwrap();
355
let render_resources = future_render_resources.0.lock().unwrap().take().unwrap();
356
357
render_resources.unpack_into(
358
main.world_mut(),
359
render.world_mut(),
360
self.synchronous_pipeline_compilation,
361
);
362
}
363
}
364
}
365
366
fn renderer_is_ready(state: Res<RenderState>) -> bool {
367
matches!(*state, RenderState::Ready)
368
}
369
370
fn run_render_schedule(world: &mut World) {
371
world.run_schedule(Render);
372
}
373
374
fn send_time(time_sender: Res<TimeSender>) {
375
// update the time and send it to the app world regardless of whether we render
376
if let Err(error) = time_sender.0.try_send(Instant::now()) {
377
match error {
378
bevy_time::TrySendError::Full(_) => {
379
panic!(
380
"The TimeSender channel should always be empty during render. \
381
You might need to add the bevy::core::time_system to your app."
382
);
383
}
384
bevy_time::TrySendError::Disconnected(_) => {
385
// ignore disconnected errors, the main world probably just got dropped during shutdown
386
}
387
}
388
}
389
}
390
391
/// Inserts a [`FutureRenderResources`] created from this [`RenderCreation`].
392
///
393
/// Returns true if creation was successful, false otherwise.
394
fn insert_future_resources(render_creation: &RenderCreation, main_world: &mut World) -> bool {
395
let primary_window = main_world
396
.query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
397
.single(main_world)
398
.ok()
399
.cloned();
400
401
#[cfg(feature = "raw_vulkan_init")]
402
let raw_vulkan_init_settings = main_world
403
.get_resource::<renderer::raw_vulkan_init::RawVulkanInitSettings>()
404
.cloned()
405
.unwrap_or_default();
406
407
let future_resources = FutureRenderResources::default();
408
let success = render_creation.create_render(
409
future_resources.clone(),
410
primary_window,
411
#[cfg(feature = "raw_vulkan_init")]
412
raw_vulkan_init_settings,
413
);
414
if success {
415
// Note that `future_resources` is not necessarily populated here yet.
416
main_world.insert_resource(future_resources);
417
}
418
success
419
}
420
421
/// If the [`RenderAdapterInfo`] is a Qualcomm Adreno, returns its model number.
422
///
423
/// This lets us work around hardware bugs.
424
pub fn get_adreno_model(adapter_info: &RenderAdapterInfo) -> Option<u32> {
425
if !cfg!(target_os = "android") {
426
return None;
427
}
428
429
let adreno_model = adapter_info.name.strip_prefix("Adreno (TM) ")?;
430
431
// Take suffixes into account (like Adreno 642L).
432
Some(
433
adreno_model
434
.chars()
435
.map_while(|c| c.to_digit(10))
436
.fold(0, |acc, digit| acc * 10 + digit),
437
)
438
}
439
440
/// Get the Mali driver version if the adapter is a Mali GPU.
441
pub fn get_mali_driver_version(adapter_info: &RenderAdapterInfo) -> Option<u32> {
442
if !cfg!(target_os = "android") {
443
return None;
444
}
445
446
if !adapter_info.name.contains("Mali") {
447
return None;
448
}
449
let driver_info = &adapter_info.driver_info;
450
if let Some(start_pos) = driver_info.find("v1.r")
451
&& let Some(end_pos) = driver_info[start_pos..].find('p')
452
{
453
let start_idx = start_pos + 4; // Skip "v1.r"
454
let end_idx = start_pos + end_pos;
455
456
return driver_info[start_idx..end_idx].parse::<u32>().ok();
457
}
458
459
None
460
}
461
462