bevy_render/
lib.rs

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")]
30compile_error!("bevy_render cannot compile for a 16-bit platform.");
31
32extern crate alloc;
33extern crate core;
34
35// Required to make proc macros work in bevy itself.
36extern crate self as bevy_render;
37
38pub mod alpha;
39pub mod batching;
40pub mod camera;
41pub mod diagnostic;
42pub mod erased_render_asset;
43pub mod experimental;
44pub mod extract_component;
45pub mod extract_instances;
46mod extract_param;
47pub mod extract_resource;
48pub mod globals;
49pub mod gpu_component_array_buffer;
50pub mod gpu_readback;
51pub mod mesh;
52#[cfg(not(target_arch = "wasm32"))]
53pub mod pipelined_rendering;
54pub mod render_asset;
55pub mod render_graph;
56pub mod render_phase;
57pub mod render_resource;
58pub mod renderer;
59pub mod settings;
60pub mod storage;
61pub mod sync_component;
62pub mod sync_world;
63pub mod texture;
64pub mod view;
65
66/// The render prelude.
67///
68/// This includes the most common types in this crate, re-exported for your convenience.
69pub mod prelude {
70    #[doc(hidden)]
71    pub use crate::{
72        alpha::AlphaMode, camera::NormalizedRenderTargetExt as _, texture::ManualTextureViews,
73        view::Msaa, ExtractSchedule,
74    };
75}
76
77pub use extract_param::Extract;
78
79use crate::{
80    camera::CameraPlugin,
81    gpu_readback::GpuReadbackPlugin,
82    mesh::{MeshRenderAssetPlugin, RenderMesh},
83    render_asset::prepare_assets,
84    render_resource::PipelineCache,
85    renderer::{render_system, RenderAdapterInfo},
86    settings::RenderCreation,
87    storage::StoragePlugin,
88    texture::TexturePlugin,
89    view::{ViewPlugin, WindowRenderPlugin},
90};
91use alloc::sync::Arc;
92use batching::gpu_preprocessing::BatchingPlugin;
93use bevy_app::{App, AppLabel, Plugin, SubApp};
94use bevy_asset::{AssetApp, AssetServer};
95use bevy_ecs::{
96    prelude::*,
97    schedule::{ScheduleBuildSettings, ScheduleLabel},
98};
99use bevy_image::{CompressedImageFormatSupport, CompressedImageFormats};
100use bevy_shader::{load_shader_library, Shader, ShaderLoader};
101use bevy_utils::prelude::default;
102use bevy_window::{PrimaryWindow, RawHandleWrapperHolder};
103use bitflags::bitflags;
104use core::ops::{Deref, DerefMut};
105use experimental::occlusion_culling::OcclusionCullingPlugin;
106use globals::GlobalsPlugin;
107use render_asset::{
108    extract_render_asset_bytes_per_frame, reset_render_asset_bytes_per_frame,
109    RenderAssetBytesPerFrame, RenderAssetBytesPerFrameLimiter,
110};
111use settings::RenderResources;
112use std::sync::Mutex;
113use 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)]
124pub 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
133bitflags! {
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)]
150pub 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: any entities with
190    /// [`TemporaryRenderEntity`](sync_world::TemporaryRenderEntity) will be despawned.
191    ///
192    /// Runs after [`Cleanup`](RenderSystems::Cleanup).
193    PostCleanup,
194}
195
196/// The startup schedule of the [`RenderApp`]
197#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
198pub struct RenderStartup;
199
200/// The main render schedule.
201#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
202pub struct Render;
203
204impl Render {
205    /// Sets up the base structure of the rendering [`Schedule`].
206    ///
207    /// The sets defined in this enum are configured to run in order.
208    pub fn base_schedule() -> Schedule {
209        use RenderSystems::*;
210
211        let mut schedule = Schedule::new(Self);
212
213        schedule.configure_sets(
214            (
215                ExtractCommands,
216                PrepareMeshes,
217                ManageViews,
218                Queue,
219                PhaseSort,
220                Prepare,
221                Render,
222                Cleanup,
223                PostCleanup,
224            )
225                .chain(),
226        );
227
228        schedule.configure_sets((ExtractCommands, PrepareAssets, PrepareMeshes, Prepare).chain());
229        schedule.configure_sets(
230            (QueueMeshes, QueueSweep)
231                .chain()
232                .in_set(Queue)
233                .after(prepare_assets::<RenderMesh>),
234        );
235        schedule.configure_sets(
236            (
237                PrepareResources,
238                PrepareResourcesCollectPhaseBuffers,
239                PrepareResourcesFlush,
240                PrepareBindGroups,
241            )
242                .chain()
243                .in_set(Prepare),
244        );
245
246        schedule
247    }
248}
249
250/// Schedule in which data from the main world is 'extracted' into the render world.
251///
252/// This step should be kept as short as possible to increase the "pipelining potential" for
253/// running the next frame while rendering the current frame.
254///
255/// This schedule is run on the render world, but it also has access to the main world.
256/// See [`MainWorld`] and [`Extract`] for details on how to access main world data from this schedule.
257#[derive(ScheduleLabel, PartialEq, Eq, Debug, Clone, Hash, Default)]
258pub struct ExtractSchedule;
259
260/// The simulation [`World`] of the application, stored as a resource.
261///
262/// This resource is only available during [`ExtractSchedule`] and not
263/// during command application of that schedule.
264/// See [`Extract`] for more details.
265#[derive(Resource, Default)]
266pub struct MainWorld(World);
267
268impl Deref for MainWorld {
269    type Target = World;
270
271    fn deref(&self) -> &Self::Target {
272        &self.0
273    }
274}
275
276impl DerefMut for MainWorld {
277    fn deref_mut(&mut self) -> &mut Self::Target {
278        &mut self.0
279    }
280}
281
282pub mod graph {
283    use crate::render_graph::RenderLabel;
284
285    #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
286    pub struct CameraDriverLabel;
287}
288
289#[derive(Resource)]
290struct FutureRenderResources(Arc<Mutex<Option<RenderResources>>>);
291
292/// A label for the rendering sub-app.
293#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
294pub struct RenderApp;
295
296impl Plugin for RenderPlugin {
297    /// Initializes the renderer, sets up the [`RenderSystems`] and creates the rendering sub-app.
298    fn build(&self, app: &mut App) {
299        app.init_asset::<Shader>()
300            .init_asset_loader::<ShaderLoader>();
301
302        match &self.render_creation {
303            RenderCreation::Manual(resources) => {
304                let future_render_resources_wrapper = Arc::new(Mutex::new(Some(resources.clone())));
305                app.insert_resource(FutureRenderResources(
306                    future_render_resources_wrapper.clone(),
307                ));
308                // SAFETY: Plugins should be set up on the main thread.
309                unsafe { initialize_render_app(app) };
310            }
311            RenderCreation::Automatic(render_creation) => {
312                if let Some(backends) = render_creation.backends {
313                    let future_render_resources_wrapper = Arc::new(Mutex::new(None));
314                    app.insert_resource(FutureRenderResources(
315                        future_render_resources_wrapper.clone(),
316                    ));
317
318                    let primary_window = app
319                        .world_mut()
320                        .query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
321                        .single(app.world())
322                        .ok()
323                        .cloned();
324
325                    let settings = render_creation.clone();
326
327                    #[cfg(feature = "raw_vulkan_init")]
328                    let raw_vulkan_init_settings = app
329                        .world_mut()
330                        .get_resource::<renderer::raw_vulkan_init::RawVulkanInitSettings>()
331                        .cloned()
332                        .unwrap_or_default();
333
334                    let async_renderer = async move {
335                        let render_resources = renderer::initialize_renderer(
336                            backends,
337                            primary_window,
338                            &settings,
339                            #[cfg(feature = "raw_vulkan_init")]
340                            raw_vulkan_init_settings,
341                        )
342                        .await;
343
344                        *future_render_resources_wrapper.lock().unwrap() = Some(render_resources);
345                    };
346
347                    // In wasm, spawn a task and detach it for execution
348                    #[cfg(target_arch = "wasm32")]
349                    bevy_tasks::IoTaskPool::get()
350                        .spawn_local(async_renderer)
351                        .detach();
352                    // Otherwise, just block for it to complete
353                    #[cfg(not(target_arch = "wasm32"))]
354                    bevy_tasks::block_on(async_renderer);
355
356                    // SAFETY: Plugins should be set up on the main thread.
357                    unsafe { initialize_render_app(app) };
358                }
359            }
360        };
361
362        app.add_plugins((
363            WindowRenderPlugin,
364            CameraPlugin,
365            ViewPlugin,
366            MeshRenderAssetPlugin,
367            GlobalsPlugin,
368            #[cfg(feature = "morph")]
369            mesh::MorphPlugin,
370            TexturePlugin,
371            BatchingPlugin {
372                debug_flags: self.debug_flags,
373            },
374            SyncWorldPlugin,
375            StoragePlugin,
376            GpuReadbackPlugin::default(),
377            OcclusionCullingPlugin,
378            #[cfg(feature = "tracing-tracy")]
379            diagnostic::RenderDiagnosticsPlugin,
380        ));
381
382        app.init_resource::<RenderAssetBytesPerFrame>();
383        if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
384            render_app.init_resource::<RenderAssetBytesPerFrameLimiter>();
385            render_app
386                .add_systems(ExtractSchedule, extract_render_asset_bytes_per_frame)
387                .add_systems(
388                    Render,
389                    reset_render_asset_bytes_per_frame.in_set(RenderSystems::Cleanup),
390                );
391        }
392    }
393
394    fn ready(&self, app: &App) -> bool {
395        app.world()
396            .get_resource::<FutureRenderResources>()
397            .and_then(|frr| frr.0.try_lock().map(|locked| locked.is_some()).ok())
398            .unwrap_or(true)
399    }
400
401    fn finish(&self, app: &mut App) {
402        load_shader_library!(app, "maths.wgsl");
403        load_shader_library!(app, "color_operations.wgsl");
404        load_shader_library!(app, "bindless.wgsl");
405        if let Some(future_render_resources) =
406            app.world_mut().remove_resource::<FutureRenderResources>()
407        {
408            let render_resources = future_render_resources.0.lock().unwrap().take().unwrap();
409            let RenderResources(device, queue, adapter_info, render_adapter, instance, ..) =
410                render_resources;
411
412            let compressed_image_format_support = CompressedImageFormatSupport(
413                CompressedImageFormats::from_features(device.features()),
414            );
415
416            app.insert_resource(device.clone())
417                .insert_resource(queue.clone())
418                .insert_resource(adapter_info.clone())
419                .insert_resource(render_adapter.clone())
420                .insert_resource(compressed_image_format_support);
421
422            let render_app = app.sub_app_mut(RenderApp);
423
424            #[cfg(feature = "raw_vulkan_init")]
425            {
426                let additional_vulkan_features: renderer::raw_vulkan_init::AdditionalVulkanFeatures =
427                    render_resources.5;
428                render_app.insert_resource(additional_vulkan_features);
429            }
430
431            render_app
432                .insert_resource(instance)
433                .insert_resource(PipelineCache::new(
434                    device.clone(),
435                    render_adapter.clone(),
436                    self.synchronous_pipeline_compilation,
437                ))
438                .insert_resource(device)
439                .insert_resource(queue)
440                .insert_resource(render_adapter)
441                .insert_resource(adapter_info);
442        }
443    }
444}
445
446/// A "scratch" world used to avoid allocating new worlds every frame when
447/// swapping out the [`MainWorld`] for [`ExtractSchedule`].
448#[derive(Resource, Default)]
449struct ScratchMainWorld(World);
450
451/// Executes the [`ExtractSchedule`] step of the renderer.
452/// This updates the render world with the extracted ECS data of the current frame.
453fn extract(main_world: &mut World, render_world: &mut World) {
454    // temporarily add the app world to the render world as a resource
455    let scratch_world = main_world.remove_resource::<ScratchMainWorld>().unwrap();
456    let inserted_world = core::mem::replace(main_world, scratch_world.0);
457    render_world.insert_resource(MainWorld(inserted_world));
458    render_world.run_schedule(ExtractSchedule);
459
460    // move the app world back, as if nothing happened.
461    let inserted_world = render_world.remove_resource::<MainWorld>().unwrap();
462    let scratch_world = core::mem::replace(main_world, inserted_world.0);
463    main_world.insert_resource(ScratchMainWorld(scratch_world));
464}
465
466/// # Safety
467/// This function must be called from the main thread.
468unsafe fn initialize_render_app(app: &mut App) {
469    app.init_resource::<ScratchMainWorld>();
470
471    let mut render_app = SubApp::new();
472    render_app.update_schedule = Some(Render.intern());
473
474    let mut extract_schedule = Schedule::new(ExtractSchedule);
475    // We skip applying any commands during the ExtractSchedule
476    // so commands can be applied on the render thread.
477    extract_schedule.set_build_settings(ScheduleBuildSettings {
478        auto_insert_apply_deferred: false,
479        ..default()
480    });
481    extract_schedule.set_apply_final_deferred(false);
482
483    render_app
484        .add_schedule(extract_schedule)
485        .add_schedule(Render::base_schedule())
486        .init_resource::<render_graph::RenderGraph>()
487        .insert_resource(app.world().resource::<AssetServer>().clone())
488        .add_systems(ExtractSchedule, PipelineCache::extract_shaders)
489        .add_systems(
490            Render,
491            (
492                // This set applies the commands from the extract schedule while the render schedule
493                // is running in parallel with the main app.
494                apply_extract_commands.in_set(RenderSystems::ExtractCommands),
495                (PipelineCache::process_pipeline_queue_system, render_system)
496                    .chain()
497                    .in_set(RenderSystems::Render),
498                despawn_temporary_render_entities.in_set(RenderSystems::PostCleanup),
499            ),
500        );
501
502    // We want the closure to have a flag to only run the RenderStartup schedule once, but the only
503    // way to have the closure store this flag is by capturing it. This variable is otherwise
504    // unused.
505    let mut should_run_startup = true;
506    render_app.set_extract(move |main_world, render_world| {
507        if should_run_startup {
508            // Run the `RenderStartup` if it hasn't run yet. This does mean `RenderStartup` blocks
509            // the rest of the app extraction, but this is necessary since extraction itself can
510            // depend on resources initialized in `RenderStartup`.
511            render_world.run_schedule(RenderStartup);
512            should_run_startup = false;
513        }
514
515        {
516            #[cfg(feature = "trace")]
517            let _stage_span = tracing::info_span!("entity_sync").entered();
518            entity_sync_system(main_world, render_world);
519        }
520
521        // run extract schedule
522        extract(main_world, render_world);
523    });
524
525    let (sender, receiver) = bevy_time::create_time_channels();
526    render_app.insert_resource(sender);
527    app.insert_resource(receiver);
528    app.insert_sub_app(RenderApp, render_app);
529}
530
531/// Applies the commands from the extract schedule. This happens during
532/// the render schedule rather than during extraction to allow the commands to run in parallel with the
533/// main app when pipelined rendering is enabled.
534fn apply_extract_commands(render_world: &mut World) {
535    render_world.resource_scope(|render_world, mut schedules: Mut<Schedules>| {
536        schedules
537            .get_mut(ExtractSchedule)
538            .unwrap()
539            .apply_deferred(render_world);
540    });
541}
542
543/// If the [`RenderAdapterInfo`] is a Qualcomm Adreno, returns its model number.
544///
545/// This lets us work around hardware bugs.
546pub fn get_adreno_model(adapter_info: &RenderAdapterInfo) -> Option<u32> {
547    if !cfg!(target_os = "android") {
548        return None;
549    }
550
551    let adreno_model = adapter_info.name.strip_prefix("Adreno (TM) ")?;
552
553    // Take suffixes into account (like Adreno 642L).
554    Some(
555        adreno_model
556            .chars()
557            .map_while(|c| c.to_digit(10))
558            .fold(0, |acc, digit| acc * 10 + digit),
559    )
560}
561
562/// Get the Mali driver version if the adapter is a Mali GPU.
563pub fn get_mali_driver_version(adapter_info: &RenderAdapterInfo) -> Option<u32> {
564    if !cfg!(target_os = "android") {
565        return None;
566    }
567
568    if !adapter_info.name.contains("Mali") {
569        return None;
570    }
571    let driver_info = &adapter_info.driver_info;
572    if let Some(start_pos) = driver_info.find("v1.r")
573        && let Some(end_pos) = driver_info[start_pos..].find('p')
574    {
575        let start_idx = start_pos + 4; // Skip "v1.r"
576        let end_idx = start_pos + end_pos;
577
578        return driver_info[start_idx..end_idx].parse::<u32>().ok();
579    }
580
581    None
582}