bevy_render/
lib.rs

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