bevy_render/
lib.rs

1// FIXME(15321): solve CI failures, then replace with `#![expect()]`.
2#![allow(missing_docs, reason = "Not all docs are written yet, see #3492.")]
3#![allow(unsafe_code)]
4// `rustdoc_internals` is needed for `#[doc(fake_variadics)]`
5#![allow(internal_features)]
6#![cfg_attr(any(docsrs, docsrs_dep), feature(doc_auto_cfg, rustdoc_internals))]
7#![doc(
8    html_logo_url = "https://bevyengine.org/assets/icon.png",
9    html_favicon_url = "https://bevyengine.org/assets/icon.png"
10)]
11
12#[cfg(target_pointer_width = "16")]
13compile_error!("bevy_render cannot compile for a 16-bit platform.");
14
15extern crate alloc;
16extern crate core;
17
18pub mod alpha;
19pub mod batching;
20pub mod camera;
21pub mod diagnostic;
22pub mod extract_component;
23pub mod extract_instances;
24mod extract_param;
25pub mod extract_resource;
26pub mod globals;
27pub mod gpu_component_array_buffer;
28pub mod gpu_readback;
29pub mod mesh;
30#[cfg(not(target_arch = "wasm32"))]
31pub mod pipelined_rendering;
32pub mod primitives;
33pub mod render_asset;
34pub mod render_graph;
35pub mod render_phase;
36pub mod render_resource;
37pub mod renderer;
38pub mod settings;
39mod spatial_bundle;
40pub mod storage;
41pub mod sync_component;
42pub mod sync_world;
43pub mod texture;
44pub mod view;
45
46/// The render prelude.
47///
48/// This includes the most common types in this crate, re-exported for your convenience.
49#[expect(deprecated)]
50pub mod prelude {
51    #[doc(hidden)]
52    pub use crate::{
53        alpha::AlphaMode,
54        camera::{
55            Camera, ClearColor, ClearColorConfig, OrthographicProjection, PerspectiveProjection,
56            Projection,
57        },
58        mesh::{
59            morph::MorphWeights, primitives::MeshBuilder, primitives::Meshable, Mesh, Mesh2d,
60            Mesh3d,
61        },
62        render_resource::Shader,
63        spatial_bundle::SpatialBundle,
64        texture::ImagePlugin,
65        view::{InheritedVisibility, Msaa, ViewVisibility, Visibility, VisibilityBundle},
66        ExtractSchedule,
67    };
68}
69use batching::gpu_preprocessing::BatchingPlugin;
70use bevy_ecs::schedule::ScheduleBuildSettings;
71use bevy_utils::prelude::default;
72pub use extract_param::Extract;
73
74use bevy_hierarchy::ValidParentCheckPlugin;
75use bevy_window::{PrimaryWindow, RawHandleWrapperHolder};
76use extract_resource::ExtractResourcePlugin;
77use globals::GlobalsPlugin;
78use render_asset::RenderAssetBytesPerFrame;
79use renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue};
80use sync_world::{
81    despawn_temporary_render_entities, entity_sync_system, SyncToRenderWorld, SyncWorldPlugin,
82};
83
84use crate::gpu_readback::GpuReadbackPlugin;
85use crate::{
86    camera::CameraPlugin,
87    mesh::{MeshPlugin, MorphPlugin, RenderMesh},
88    render_asset::prepare_assets,
89    render_resource::{PipelineCache, Shader, ShaderLoader},
90    renderer::{render_system, RenderInstance, WgpuWrapper},
91    settings::RenderCreation,
92    storage::StoragePlugin,
93    view::{ViewPlugin, WindowRenderPlugin},
94};
95use alloc::sync::Arc;
96use bevy_app::{App, AppLabel, Plugin, SubApp};
97use bevy_asset::{load_internal_asset, AssetApp, AssetServer, Handle};
98use bevy_ecs::{prelude::*, schedule::ScheduleLabel, system::SystemState};
99use bevy_utils::tracing::debug;
100use core::ops::{Deref, DerefMut};
101use std::sync::Mutex;
102
103/// Contains the default Bevy rendering backend based on wgpu.
104///
105/// Rendering is done in a [`SubApp`], which exchanges data with the main app
106/// between main schedule iterations.
107///
108/// Rendering can be executed between iterations of the main schedule,
109/// or it can be executed in parallel with main schedule when
110/// [`PipelinedRenderingPlugin`](pipelined_rendering::PipelinedRenderingPlugin) is enabled.
111#[derive(Default)]
112pub struct RenderPlugin {
113    pub render_creation: RenderCreation,
114    /// If `true`, disables asynchronous pipeline compilation.
115    /// This has no effect on macOS, Wasm, iOS, or without the `multi_threaded` feature.
116    pub synchronous_pipeline_compilation: bool,
117}
118
119/// The systems sets of the default [`App`] rendering schedule.
120///
121/// These can be useful for ordering, but you almost never want to add your systems to these sets.
122#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
123pub enum RenderSet {
124    /// This is used for applying the commands from the [`ExtractSchedule`]
125    ExtractCommands,
126    /// Prepare assets that have been created/modified/removed this frame.
127    PrepareAssets,
128    /// Create any additional views such as those used for shadow mapping.
129    ManageViews,
130    /// Queue drawable entities as phase items in render phases ready for
131    /// sorting (if necessary)
132    Queue,
133    /// A sub-set within [`Queue`](RenderSet::Queue) where mesh entity queue systems are executed. Ensures `prepare_assets::<RenderMesh>` is completed.
134    QueueMeshes,
135    // TODO: This could probably be moved in favor of a system ordering
136    // abstraction in `Render` or `Queue`
137    /// Sort the [`SortedRenderPhase`](render_phase::SortedRenderPhase)s and
138    /// [`BinKey`](render_phase::BinnedPhaseItem::BinKey)s here.
139    PhaseSort,
140    /// Prepare render resources from extracted data for the GPU based on their sorted order.
141    /// Create [`BindGroups`](render_resource::BindGroup) that depend on those data.
142    Prepare,
143    /// A sub-set within [`Prepare`](RenderSet::Prepare) for initializing buffers, textures and uniforms for use in bind groups.
144    PrepareResources,
145    /// Flush buffers after [`PrepareResources`](RenderSet::PrepareResources), but before [`PrepareBindGroups`](RenderSet::PrepareBindGroups).
146    PrepareResourcesFlush,
147    /// A sub-set within [`Prepare`](RenderSet::Prepare) for constructing bind groups, or other data that relies on render resources prepared in [`PrepareResources`](RenderSet::PrepareResources).
148    PrepareBindGroups,
149    /// Actual rendering happens here.
150    /// In most cases, only the render backend should insert resources here.
151    Render,
152    /// Cleanup render resources here.
153    Cleanup,
154    /// Final cleanup occurs: all entities will be despawned.
155    ///
156    /// Runs after [`Cleanup`](RenderSet::Cleanup).
157    PostCleanup,
158}
159
160/// The main render schedule.
161#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
162pub struct Render;
163
164impl Render {
165    /// Sets up the base structure of the rendering [`Schedule`].
166    ///
167    /// The sets defined in this enum are configured to run in order.
168    pub fn base_schedule() -> Schedule {
169        use RenderSet::*;
170
171        let mut schedule = Schedule::new(Self);
172
173        schedule.configure_sets(
174            (
175                ExtractCommands,
176                ManageViews,
177                Queue,
178                PhaseSort,
179                Prepare,
180                Render,
181                Cleanup,
182                PostCleanup,
183            )
184                .chain(),
185        );
186
187        schedule.configure_sets((ExtractCommands, PrepareAssets, Prepare).chain());
188        schedule.configure_sets(
189            QueueMeshes
190                .in_set(Queue)
191                .after(prepare_assets::<RenderMesh>),
192        );
193        schedule.configure_sets(
194            (PrepareResources, PrepareResourcesFlush, PrepareBindGroups)
195                .chain()
196                .in_set(Prepare),
197        );
198
199        schedule
200    }
201}
202
203/// Schedule which extract data from the main world and inserts it into the render world.
204///
205/// This step should be kept as short as possible to increase the "pipelining potential" for
206/// running the next frame while rendering the current frame.
207///
208/// This schedule is run on the main world, but its buffers are not applied
209/// until it is returned to the render world.
210#[derive(ScheduleLabel, PartialEq, Eq, Debug, Clone, Hash)]
211pub struct ExtractSchedule;
212
213/// The simulation [`World`] of the application, stored as a resource.
214///
215/// This resource is only available during [`ExtractSchedule`] and not
216/// during command application of that schedule.
217/// See [`Extract`] for more details.
218#[derive(Resource, Default)]
219pub struct MainWorld(World);
220
221impl Deref for MainWorld {
222    type Target = World;
223
224    fn deref(&self) -> &Self::Target {
225        &self.0
226    }
227}
228
229impl DerefMut for MainWorld {
230    fn deref_mut(&mut self) -> &mut Self::Target {
231        &mut self.0
232    }
233}
234
235pub mod graph {
236    use crate::render_graph::RenderLabel;
237
238    #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
239    pub struct CameraDriverLabel;
240}
241
242#[derive(Resource)]
243struct FutureRendererResources(
244    Arc<
245        Mutex<
246            Option<(
247                RenderDevice,
248                RenderQueue,
249                RenderAdapterInfo,
250                RenderAdapter,
251                RenderInstance,
252            )>,
253        >,
254    >,
255);
256
257/// A label for the rendering sub-app.
258#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
259pub struct RenderApp;
260
261pub const INSTANCE_INDEX_SHADER_HANDLE: Handle<Shader> =
262    Handle::weak_from_u128(10313207077636615845);
263pub const MATHS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(10665356303104593376);
264pub const COLOR_OPERATIONS_SHADER_HANDLE: Handle<Shader> =
265    Handle::weak_from_u128(1844674407370955161);
266
267impl Plugin for RenderPlugin {
268    /// Initializes the renderer, sets up the [`RenderSet`] and creates the rendering sub-app.
269    fn build(&self, app: &mut App) {
270        app.init_asset::<Shader>()
271            .init_asset_loader::<ShaderLoader>();
272
273        match &self.render_creation {
274            RenderCreation::Manual(device, queue, adapter_info, adapter, instance) => {
275                let future_renderer_resources_wrapper = Arc::new(Mutex::new(Some((
276                    device.clone(),
277                    queue.clone(),
278                    adapter_info.clone(),
279                    adapter.clone(),
280                    instance.clone(),
281                ))));
282                app.insert_resource(FutureRendererResources(
283                    future_renderer_resources_wrapper.clone(),
284                ));
285                // SAFETY: Plugins should be set up on the main thread.
286                unsafe { initialize_render_app(app) };
287            }
288            RenderCreation::Automatic(render_creation) => {
289                if let Some(backends) = render_creation.backends {
290                    let future_renderer_resources_wrapper = Arc::new(Mutex::new(None));
291                    app.insert_resource(FutureRendererResources(
292                        future_renderer_resources_wrapper.clone(),
293                    ));
294
295                    let mut system_state: SystemState<
296                        Query<&RawHandleWrapperHolder, With<PrimaryWindow>>,
297                    > = SystemState::new(app.world_mut());
298                    let primary_window = system_state.get(app.world()).get_single().ok().cloned();
299                    let settings = render_creation.clone();
300                    let async_renderer = async move {
301                        let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
302                            backends,
303                            dx12_shader_compiler: settings.dx12_shader_compiler.clone(),
304                            flags: settings.instance_flags,
305                            gles_minor_version: settings.gles3_minor_version,
306                        });
307
308                        // SAFETY: Plugins should be set up on the main thread.
309                        let surface = primary_window.and_then(|wrapper| unsafe {
310                            let maybe_handle = wrapper.0.lock().expect(
311                                "Couldn't get the window handle in time for renderer initialization",
312                            );
313                            if let Some(wrapper) = maybe_handle.as_ref() {
314                                let handle = wrapper.get_handle();
315                                Some(
316                                    instance
317                                        .create_surface(handle)
318                                        .expect("Failed to create wgpu surface"),
319                                )
320                            } else {
321                                None
322                            }
323                        });
324
325                        let request_adapter_options = wgpu::RequestAdapterOptions {
326                            power_preference: settings.power_preference,
327                            compatible_surface: surface.as_ref(),
328                            ..Default::default()
329                        };
330
331                        let (device, queue, adapter_info, render_adapter) =
332                            renderer::initialize_renderer(
333                                &instance,
334                                &settings,
335                                &request_adapter_options,
336                            )
337                            .await;
338                        debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
339                        debug!("Configured wgpu adapter Features: {:#?}", device.features());
340                        let mut future_renderer_resources_inner =
341                            future_renderer_resources_wrapper.lock().unwrap();
342                        *future_renderer_resources_inner = Some((
343                            device,
344                            queue,
345                            adapter_info,
346                            render_adapter,
347                            RenderInstance(Arc::new(WgpuWrapper::new(instance))),
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                    futures_lite::future::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            ValidParentCheckPlugin::<view::InheritedVisibility>::default(),
367            WindowRenderPlugin,
368            CameraPlugin,
369            ViewPlugin,
370            MeshPlugin,
371            GlobalsPlugin,
372            MorphPlugin,
373            BatchingPlugin,
374            SyncWorldPlugin,
375            StoragePlugin,
376            GpuReadbackPlugin::default(),
377        ));
378
379        app.init_resource::<RenderAssetBytesPerFrame>()
380            .add_plugins(ExtractResourcePlugin::<RenderAssetBytesPerFrame>::default());
381
382        app.register_type::<alpha::AlphaMode>()
383            // These types cannot be registered in bevy_color, as it does not depend on the rest of Bevy
384            .register_type::<bevy_color::Color>()
385            .register_type::<primitives::Aabb>()
386            .register_type::<primitives::CascadesFrusta>()
387            .register_type::<primitives::CubemapFrusta>()
388            .register_type::<primitives::Frustum>()
389            .register_type::<SyncToRenderWorld>();
390    }
391
392    fn ready(&self, app: &App) -> bool {
393        app.world()
394            .get_resource::<FutureRendererResources>()
395            .and_then(|frr| frr.0.try_lock().map(|locked| locked.is_some()).ok())
396            .unwrap_or(true)
397    }
398
399    fn finish(&self, app: &mut App) {
400        load_internal_asset!(app, MATHS_SHADER_HANDLE, "maths.wgsl", Shader::from_wgsl);
401        load_internal_asset!(
402            app,
403            COLOR_OPERATIONS_SHADER_HANDLE,
404            "color_operations.wgsl",
405            Shader::from_wgsl
406        );
407        if let Some(future_renderer_resources) =
408            app.world_mut().remove_resource::<FutureRendererResources>()
409        {
410            let (device, queue, adapter_info, render_adapter, instance) =
411                future_renderer_resources.0.lock().unwrap().take().unwrap();
412
413            app.insert_resource(device.clone())
414                .insert_resource(queue.clone())
415                .insert_resource(adapter_info.clone())
416                .insert_resource(render_adapter.clone());
417
418            let render_app = app.sub_app_mut(RenderApp);
419
420            render_app
421                .insert_resource(instance)
422                .insert_resource(PipelineCache::new(
423                    device.clone(),
424                    render_adapter.clone(),
425                    self.synchronous_pipeline_compilation,
426                ))
427                .insert_resource(device)
428                .insert_resource(queue)
429                .insert_resource(render_adapter)
430                .insert_resource(adapter_info)
431                .add_systems(
432                    Render,
433                    (|mut bpf: ResMut<RenderAssetBytesPerFrame>| {
434                        bpf.reset();
435                    })
436                    .in_set(RenderSet::Cleanup),
437                );
438        }
439    }
440}
441
442/// A "scratch" world used to avoid allocating new worlds every frame when
443/// swapping out the [`MainWorld`] for [`ExtractSchedule`].
444#[derive(Resource, Default)]
445struct ScratchMainWorld(World);
446
447/// Executes the [`ExtractSchedule`] step of the renderer.
448/// This updates the render world with the extracted ECS data of the current frame.
449fn extract(main_world: &mut World, render_world: &mut World) {
450    // temporarily add the app world to the render world as a resource
451    let scratch_world = main_world.remove_resource::<ScratchMainWorld>().unwrap();
452    let inserted_world = core::mem::replace(main_world, scratch_world.0);
453    render_world.insert_resource(MainWorld(inserted_world));
454    render_world.run_schedule(ExtractSchedule);
455
456    // move the app world back, as if nothing happened.
457    let inserted_world = render_world.remove_resource::<MainWorld>().unwrap();
458    let scratch_world = core::mem::replace(main_world, inserted_world.0);
459    main_world.insert_resource(ScratchMainWorld(scratch_world));
460}
461
462/// # Safety
463/// This function must be called from the main thread.
464unsafe fn initialize_render_app(app: &mut App) {
465    app.init_resource::<ScratchMainWorld>();
466
467    let mut render_app = SubApp::new();
468    render_app.update_schedule = Some(Render.intern());
469
470    let mut extract_schedule = Schedule::new(ExtractSchedule);
471    // We skip applying any commands during the ExtractSchedule
472    // so commands can be applied on the render thread.
473    extract_schedule.set_build_settings(ScheduleBuildSettings {
474        auto_insert_apply_deferred: false,
475        ..default()
476    });
477    extract_schedule.set_apply_final_deferred(false);
478
479    render_app
480        .add_schedule(extract_schedule)
481        .add_schedule(Render::base_schedule())
482        .init_resource::<render_graph::RenderGraph>()
483        .insert_resource(app.world().resource::<AssetServer>().clone())
484        .add_systems(ExtractSchedule, PipelineCache::extract_shaders)
485        .add_systems(
486            Render,
487            (
488                // This set applies the commands from the extract schedule while the render schedule
489                // is running in parallel with the main app.
490                apply_extract_commands.in_set(RenderSet::ExtractCommands),
491                (
492                    PipelineCache::process_pipeline_queue_system.before(render_system),
493                    render_system,
494                )
495                    .in_set(RenderSet::Render),
496                despawn_temporary_render_entities.in_set(RenderSet::PostCleanup),
497            ),
498        );
499
500    render_app.set_extract(|main_world, render_world| {
501        {
502            #[cfg(feature = "trace")]
503            let _stage_span = bevy_utils::tracing::info_span!("entity_sync").entered();
504            entity_sync_system(main_world, render_world);
505        }
506
507        // run extract schedule
508        extract(main_world, render_world);
509    });
510
511    let (sender, receiver) = bevy_time::create_time_channels();
512    render_app.insert_resource(sender);
513    app.insert_resource(receiver);
514    app.insert_sub_app(RenderApp, render_app);
515}
516
517/// Applies the commands from the extract schedule. This happens during
518/// the render schedule rather than during extraction to allow the commands to run in parallel with the
519/// main app when pipelined rendering is enabled.
520fn apply_extract_commands(render_world: &mut World) {
521    render_world.resource_scope(|render_world, mut schedules: Mut<Schedules>| {
522        schedules
523            .get_mut(ExtractSchedule)
524            .unwrap()
525            .apply_deferred(render_world);
526    });
527}