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, 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};
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: 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`.")]
197pub type RenderSet = RenderSystems;
198
199/// The startup schedule of the [`RenderApp`]
200#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
201pub struct RenderStartup;
202
203/// The main render schedule.
204#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
205pub struct Render;
206
207impl 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)]
261pub 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)]
269pub struct MainWorld(World);
270
271impl Deref for MainWorld {
272    type Target = World;
273
274    fn deref(&self) -> &Self::Target {
275        &self.0
276    }
277}
278
279impl DerefMut for MainWorld {
280    fn deref_mut(&mut self) -> &mut Self::Target {
281        &mut self.0
282    }
283}
284
285pub 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)]
293struct FutureRenderResources(Arc<Mutex<Option<RenderResources>>>);
294
295/// A label for the rendering sub-app.
296#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
297pub struct RenderApp;
298
299impl 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            MeshRenderAssetPlugin,
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)]
453struct 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.
457fn 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.
472unsafe 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.
538fn 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.
550pub 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.
567pub 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}