bevy_render/
lib.rs

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