diff --git a/Cargo.toml b/Cargo.toml index 0744b9b5a8..dd9e585c39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -249,6 +249,10 @@ path = "examples/app/return_after_run.rs" name = "thread_pool_resources" path = "examples/app/thread_pool_resources.rs" +[[example]] +name = "headless_defaults" +path = "examples/app/headless_defaults.rs" + [[example]] name = "without_winit" path = "examples/app/without_winit.rs" diff --git a/crates/bevy_core_pipeline/src/lib.rs b/crates/bevy_core_pipeline/src/lib.rs index 1ace533a02..88b06f93cb 100644 --- a/crates/bevy_core_pipeline/src/lib.rs +++ b/crates/bevy_core_pipeline/src/lib.rs @@ -95,7 +95,11 @@ impl Plugin for CorePipelinePlugin { fn build(&self, app: &mut App) { app.init_resource::(); - let render_app = app.sub_app_mut(RenderApp); + let render_app = match app.get_sub_app_mut(RenderApp) { + Ok(render_app) => render_app, + Err(_) => return, + }; + render_app .init_resource::>() .init_resource::>() diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 78324f02bf..180093f9ba 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -138,7 +138,11 @@ impl Plugin for PbrPlugin { }, ); - let render_app = app.sub_app_mut(RenderApp); + let render_app = match app.get_sub_app_mut(RenderApp) { + Ok(render_app) => render_app, + Err(_) => return, + }; + render_app .add_system_to_stage( RenderStage::Extract, diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index ab796439fc..3e8965aa28 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -53,11 +53,13 @@ impl Plugin for MeshRenderPlugin { app.add_plugin(UniformComponentPlugin::::default()); - app.sub_app_mut(RenderApp) - .init_resource::() - .add_system_to_stage(RenderStage::Extract, extract_meshes) - .add_system_to_stage(RenderStage::Queue, queue_mesh_bind_group) - .add_system_to_stage(RenderStage::Queue, queue_mesh_view_bind_groups); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app + .init_resource::() + .add_system_to_stage(RenderStage::Extract, extract_meshes) + .add_system_to_stage(RenderStage::Queue, queue_mesh_bind_group) + .add_system_to_stage(RenderStage::Queue, queue_mesh_view_bind_groups); + } } } diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 02b5d9dbee..9f1308179e 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -31,13 +31,15 @@ impl Plugin for WireframePlugin { app.init_resource::(); - app.sub_app_mut(RenderApp) - .add_render_command::() - .init_resource::() - .init_resource::>() - .add_system_to_stage(RenderStage::Extract, extract_wireframes) - .add_system_to_stage(RenderStage::Extract, extract_wireframe_config) - .add_system_to_stage(RenderStage::Queue, queue_wireframes); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app + .add_render_command::() + .init_resource::() + .init_resource::>() + .add_system_to_stage(RenderStage::Extract, extract_wireframes) + .add_system_to_stage(RenderStage::Extract, extract_wireframe_config) + .add_system_to_stage(RenderStage::Queue, queue_wireframes); + } } } diff --git a/crates/bevy_render/src/camera/mod.rs b/crates/bevy_render/src/camera/mod.rs index 20baece458..4df498f0bf 100644 --- a/crates/bevy_render/src/camera/mod.rs +++ b/crates/bevy_render/src/camera/mod.rs @@ -53,9 +53,11 @@ impl Plugin for CameraPlugin { CoreStage::PostUpdate, crate::camera::camera_system::, ); - app.sub_app_mut(RenderApp) - .init_resource::() - .add_system_to_stage(RenderStage::Extract, extract_cameras); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app + .init_resource::() + .add_system_to_stage(RenderStage::Extract, extract_cameras); + } } } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 69ee3ff2e9..788dd9365b 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -113,174 +113,178 @@ impl Plugin for RenderPlugin { .get_resource::() .cloned() .unwrap_or_default(); - let instance = wgpu::Instance::new(options.backends); - let surface = { - let world = app.world.cell(); - let windows = world.get_resource_mut::().unwrap(); - let raw_handle = windows.get_primary().map(|window| unsafe { - let handle = window.raw_window_handle().get_handle(); - instance.create_surface(&handle) - }); - raw_handle - }; - let request_adapter_options = wgpu::RequestAdapterOptions { - power_preference: options.power_preference, - compatible_surface: surface.as_ref(), - ..Default::default() - }; - let (device, queue) = futures_lite::future::block_on(renderer::initialize_renderer( - &instance, - &mut options, - &request_adapter_options, - )); - debug!("Configured wgpu adapter Limits: {:#?}", &options.limits); - debug!("Configured wgpu adapter Features: {:#?}", &options.features); - app.insert_resource(device.clone()) - .insert_resource(queue.clone()) - .insert_resource(options.clone()) - .add_asset::() + + app.add_asset::() .init_asset_loader::() - .init_resource::() - .register_type::() - .register_type::() - .register_type::(); - let render_pipeline_cache = RenderPipelineCache::new(device.clone()); - let asset_server = app.world.get_resource::().unwrap().clone(); + .register_type::(); - let mut render_app = App::empty(); - let mut extract_stage = - SystemStage::parallel().with_system(RenderPipelineCache::extract_shaders); - // don't apply buffers when the stage finishes running - // extract stage runs on the app world, but the buffers are applied to the render world - extract_stage.set_apply_buffers(false); - render_app - .add_stage(RenderStage::Extract, extract_stage) - .add_stage(RenderStage::Prepare, SystemStage::parallel()) - .add_stage(RenderStage::Queue, SystemStage::parallel()) - .add_stage(RenderStage::PhaseSort, SystemStage::parallel()) - .add_stage( - RenderStage::Render, - SystemStage::parallel() - .with_system(RenderPipelineCache::process_pipeline_queue_system) - .with_system(render_system.exclusive_system().at_end()), - ) - .add_stage(RenderStage::Cleanup, SystemStage::parallel()) - .insert_resource(instance) - .insert_resource(device) - .insert_resource(queue) - .insert_resource(options) - .insert_resource(render_pipeline_cache) - .insert_resource(asset_server) - .init_resource::(); + if let Some(backends) = options.backends { + let instance = wgpu::Instance::new(backends); + let surface = { + let world = app.world.cell(); + let windows = world.get_resource_mut::().unwrap(); + let raw_handle = windows.get_primary().map(|window| unsafe { + let handle = window.raw_window_handle().get_handle(); + instance.create_surface(&handle) + }); + raw_handle + }; + let request_adapter_options = wgpu::RequestAdapterOptions { + power_preference: options.power_preference, + compatible_surface: surface.as_ref(), + ..Default::default() + }; + let (device, queue) = futures_lite::future::block_on(renderer::initialize_renderer( + &instance, + &mut options, + &request_adapter_options, + )); + debug!("Configured wgpu adapter Limits: {:#?}", &options.limits); + debug!("Configured wgpu adapter Features: {:#?}", &options.features); + app.insert_resource(device.clone()) + .insert_resource(queue.clone()) + .insert_resource(options.clone()) + .init_resource::() + .register_type::() + .register_type::(); + let render_pipeline_cache = RenderPipelineCache::new(device.clone()); + let asset_server = app.world.get_resource::().unwrap().clone(); - app.add_sub_app(RenderApp, render_app, move |app_world, render_app| { - #[cfg(feature = "trace")] - let render_span = bevy_utils::tracing::info_span!("renderer subapp"); - #[cfg(feature = "trace")] - let _render_guard = render_span.enter(); - { + let mut render_app = App::empty(); + let mut extract_stage = + SystemStage::parallel().with_system(RenderPipelineCache::extract_shaders); + // don't apply buffers when the stage finishes running + // extract stage runs on the app world, but the buffers are applied to the render world + extract_stage.set_apply_buffers(false); + render_app + .add_stage(RenderStage::Extract, extract_stage) + .add_stage(RenderStage::Prepare, SystemStage::parallel()) + .add_stage(RenderStage::Queue, SystemStage::parallel()) + .add_stage(RenderStage::PhaseSort, SystemStage::parallel()) + .add_stage( + RenderStage::Render, + SystemStage::parallel() + .with_system(RenderPipelineCache::process_pipeline_queue_system) + .with_system(render_system.exclusive_system().at_end()), + ) + .add_stage(RenderStage::Cleanup, SystemStage::parallel()) + .insert_resource(instance) + .insert_resource(device) + .insert_resource(queue) + .insert_resource(options) + .insert_resource(render_pipeline_cache) + .insert_resource(asset_server) + .init_resource::(); + + app.add_sub_app(RenderApp, render_app, move |app_world, render_app| { #[cfg(feature = "trace")] - let stage_span = - bevy_utils::tracing::info_span!("stage", name = "reserve_and_flush"); + let render_span = bevy_utils::tracing::info_span!("renderer subapp"); #[cfg(feature = "trace")] - let _stage_guard = stage_span.enter(); + let _render_guard = render_span.enter(); + { + #[cfg(feature = "trace")] + let stage_span = + bevy_utils::tracing::info_span!("stage", name = "reserve_and_flush"); + #[cfg(feature = "trace")] + let _stage_guard = stage_span.enter(); - // reserve all existing app entities for use in render_app - // they can only be spawned using `get_or_spawn()` - let meta_len = app_world.entities().meta.len(); - render_app - .world - .entities() - .reserve_entities(meta_len as u32); + // reserve all existing app entities for use in render_app + // they can only be spawned using `get_or_spawn()` + let meta_len = app_world.entities().meta.len(); + render_app + .world + .entities() + .reserve_entities(meta_len as u32); - // flushing as "invalid" ensures that app world entities aren't added as "empty archetype" entities by default - // these entities cannot be accessed without spawning directly onto them - // this _only_ works as expected because clear_entities() is called at the end of every frame. - render_app.world.entities_mut().flush_as_invalid(); - } + // flushing as "invalid" ensures that app world entities aren't added as "empty archetype" entities by default + // these entities cannot be accessed without spawning directly onto them + // this _only_ works as expected because clear_entities() is called at the end of every frame. + render_app.world.entities_mut().flush_as_invalid(); + } - { - #[cfg(feature = "trace")] - let stage_span = bevy_utils::tracing::info_span!("stage", name = "extract"); - #[cfg(feature = "trace")] - let _stage_guard = stage_span.enter(); + { + #[cfg(feature = "trace")] + let stage_span = bevy_utils::tracing::info_span!("stage", name = "extract"); + #[cfg(feature = "trace")] + let _stage_guard = stage_span.enter(); - // extract - extract(app_world, render_app); - } + // extract + extract(app_world, render_app); + } - { - #[cfg(feature = "trace")] - let stage_span = bevy_utils::tracing::info_span!("stage", name = "prepare"); - #[cfg(feature = "trace")] - let _stage_guard = stage_span.enter(); + { + #[cfg(feature = "trace")] + let stage_span = bevy_utils::tracing::info_span!("stage", name = "prepare"); + #[cfg(feature = "trace")] + let _stage_guard = stage_span.enter(); - // prepare - let prepare = render_app - .schedule - .get_stage_mut::(&RenderStage::Prepare) - .unwrap(); - prepare.run(&mut render_app.world); - } + // prepare + let prepare = render_app + .schedule + .get_stage_mut::(&RenderStage::Prepare) + .unwrap(); + prepare.run(&mut render_app.world); + } - { - #[cfg(feature = "trace")] - let stage_span = bevy_utils::tracing::info_span!("stage", name = "queue"); - #[cfg(feature = "trace")] - let _stage_guard = stage_span.enter(); + { + #[cfg(feature = "trace")] + let stage_span = bevy_utils::tracing::info_span!("stage", name = "queue"); + #[cfg(feature = "trace")] + let _stage_guard = stage_span.enter(); - // queue - let queue = render_app - .schedule - .get_stage_mut::(&RenderStage::Queue) - .unwrap(); - queue.run(&mut render_app.world); - } + // queue + let queue = render_app + .schedule + .get_stage_mut::(&RenderStage::Queue) + .unwrap(); + queue.run(&mut render_app.world); + } - { - #[cfg(feature = "trace")] - let stage_span = bevy_utils::tracing::info_span!("stage", name = "sort"); - #[cfg(feature = "trace")] - let _stage_guard = stage_span.enter(); + { + #[cfg(feature = "trace")] + let stage_span = bevy_utils::tracing::info_span!("stage", name = "sort"); + #[cfg(feature = "trace")] + let _stage_guard = stage_span.enter(); - // phase sort - let phase_sort = render_app - .schedule - .get_stage_mut::(&RenderStage::PhaseSort) - .unwrap(); - phase_sort.run(&mut render_app.world); - } + // phase sort + let phase_sort = render_app + .schedule + .get_stage_mut::(&RenderStage::PhaseSort) + .unwrap(); + phase_sort.run(&mut render_app.world); + } - { - #[cfg(feature = "trace")] - let stage_span = bevy_utils::tracing::info_span!("stage", name = "render"); - #[cfg(feature = "trace")] - let _stage_guard = stage_span.enter(); + { + #[cfg(feature = "trace")] + let stage_span = bevy_utils::tracing::info_span!("stage", name = "render"); + #[cfg(feature = "trace")] + let _stage_guard = stage_span.enter(); - // render - let render = render_app - .schedule - .get_stage_mut::(&RenderStage::Render) - .unwrap(); - render.run(&mut render_app.world); - } + // render + let render = render_app + .schedule + .get_stage_mut::(&RenderStage::Render) + .unwrap(); + render.run(&mut render_app.world); + } - { - #[cfg(feature = "trace")] - let stage_span = bevy_utils::tracing::info_span!("stage", name = "cleanup"); - #[cfg(feature = "trace")] - let _stage_guard = stage_span.enter(); + { + #[cfg(feature = "trace")] + let stage_span = bevy_utils::tracing::info_span!("stage", name = "cleanup"); + #[cfg(feature = "trace")] + let _stage_guard = stage_span.enter(); - // cleanup - let cleanup = render_app - .schedule - .get_stage_mut::(&RenderStage::Cleanup) - .unwrap(); - cleanup.run(&mut render_app.world); + // cleanup + let cleanup = render_app + .schedule + .get_stage_mut::(&RenderStage::Cleanup) + .unwrap(); + cleanup.run(&mut render_app.world); - render_app.world.clear_entities(); - } - }); + render_app.world.clear_entities(); + } + }); + } app.add_plugin(WindowRenderPlugin) .add_plugin(CameraPlugin) diff --git a/crates/bevy_render/src/options.rs b/crates/bevy_render/src/options.rs index 294582af2c..2211cf9530 100644 --- a/crates/bevy_render/src/options.rs +++ b/crates/bevy_render/src/options.rs @@ -12,7 +12,7 @@ pub enum WgpuOptionsPriority { #[derive(Clone)] pub struct WgpuOptions { pub device_label: Option>, - pub backends: Backends, + pub backends: Option, pub power_preference: PowerPreference, pub priority: WgpuOptionsPriority, pub features: WgpuFeatures, @@ -27,7 +27,7 @@ impl Default for WgpuOptions { Backends::PRIMARY }; - let backends = wgpu::util::backend_bits_from_env().unwrap_or(default_backends); + let backends = Some(wgpu::util::backend_bits_from_env().unwrap_or(default_backends)); let priority = options_priority_from_env().unwrap_or(WgpuOptionsPriority::Functionality); diff --git a/crates/bevy_render/src/render_asset.rs b/crates/bevy_render/src/render_asset.rs index 021a338a77..5c43cd7dcc 100644 --- a/crates/bevy_render/src/render_asset.rs +++ b/crates/bevy_render/src/render_asset.rs @@ -54,14 +54,15 @@ impl Default for RenderAssetPlugin { impl Plugin for RenderAssetPlugin { fn build(&self, app: &mut App) { - let render_app = app.sub_app_mut(RenderApp); - let prepare_asset_system = PrepareAssetSystem::::system(&mut render_app.world); - render_app - .init_resource::>() - .init_resource::>() - .init_resource::>() - .add_system_to_stage(RenderStage::Extract, extract_render_asset::) - .add_system_to_stage(RenderStage::Prepare, prepare_asset_system); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + let prepare_asset_system = PrepareAssetSystem::::system(&mut render_app.world); + render_app + .init_resource::>() + .init_resource::>() + .init_resource::>() + .add_system_to_stage(RenderStage::Extract, extract_render_asset::) + .add_system_to_stage(RenderStage::Prepare, prepare_asset_system); + } } } diff --git a/crates/bevy_render/src/render_component.rs b/crates/bevy_render/src/render_component.rs index 1296c921b1..35774683f7 100644 --- a/crates/bevy_render/src/render_component.rs +++ b/crates/bevy_render/src/render_component.rs @@ -62,12 +62,14 @@ impl Default for UniformComponentPlugin { impl Plugin for UniformComponentPlugin { fn build(&self, app: &mut App) { - app.sub_app_mut(RenderApp) - .insert_resource(ComponentUniforms::::default()) - .add_system_to_stage( - RenderStage::Prepare, - prepare_uniform_components::.system(), - ); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app + .insert_resource(ComponentUniforms::::default()) + .add_system_to_stage( + RenderStage::Prepare, + prepare_uniform_components::.system(), + ); + } } } @@ -144,8 +146,9 @@ where { fn build(&self, app: &mut App) { let system = ExtractComponentSystem::::system(&mut app.world); - let render_app = app.sub_app_mut(RenderApp); - render_app.add_system_to_stage(RenderStage::Extract, system); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app.add_system_to_stage(RenderStage::Extract, system); + } } } diff --git a/crates/bevy_render/src/texture/mod.rs b/crates/bevy_render/src/texture/mod.rs index cb586af04e..e6658195bf 100644 --- a/crates/bevy_render/src/texture/mod.rs +++ b/crates/bevy_render/src/texture/mod.rs @@ -35,9 +35,11 @@ impl Plugin for ImagePlugin { .unwrap() .set_untracked(DEFAULT_IMAGE_HANDLE, Image::default()); - app.sub_app_mut(RenderApp) - .init_resource::() - .add_system_to_stage(RenderStage::Cleanup, update_texture_cache_system); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app + .init_resource::() + .add_system_to_stage(RenderStage::Cleanup, update_texture_cache_system); + } } } diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 9914c6fa16..8711c73723 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -26,14 +26,16 @@ impl Plugin for ViewPlugin { fn build(&self, app: &mut App) { app.init_resource::().add_plugin(VisibilityPlugin); - app.sub_app_mut(RenderApp) - .init_resource::() - .add_system_to_stage(RenderStage::Extract, extract_msaa) - .add_system_to_stage(RenderStage::Prepare, prepare_view_uniforms) - .add_system_to_stage( - RenderStage::Prepare, - prepare_view_targets.after(WindowSystem::Prepare), - ); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app + .init_resource::() + .add_system_to_stage(RenderStage::Extract, extract_msaa) + .add_system_to_stage(RenderStage::Prepare, prepare_view_uniforms) + .add_system_to_stage( + RenderStage::Prepare, + prepare_view_targets.after(WindowSystem::Prepare), + ); + } } } diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index ac7ac18a2c..94bf8a21c0 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -24,15 +24,17 @@ pub enum WindowSystem { impl Plugin for WindowRenderPlugin { fn build(&self, app: &mut App) { - app.sub_app_mut(RenderApp) - .init_resource::() - .init_resource::() - .init_resource::() - .add_system_to_stage(RenderStage::Extract, extract_windows) - .add_system_to_stage( - RenderStage::Prepare, - prepare_windows.label(WindowSystem::Prepare), - ); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app + .init_resource::() + .init_resource::() + .init_resource::() + .add_system_to_stage(RenderStage::Extract, extract_windows) + .add_system_to_stage( + RenderStage::Prepare, + prepare_windows.label(WindowSystem::Prepare), + ); + } } } diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index bad4870208..eb70848f04 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -59,20 +59,22 @@ impl Plugin for SpritePlugin { .register_type::() .add_plugin(Mesh2dRenderPlugin) .add_plugin(ColorMaterialPlugin); - let render_app = app.sub_app_mut(RenderApp); - render_app - .init_resource::() - .init_resource::() - .init_resource::>() - .init_resource::() - .init_resource::() - .init_resource::() - .add_render_command::() - .add_system_to_stage( - RenderStage::Extract, - render::extract_sprites.label(SpriteSystem::ExtractSprites), - ) - .add_system_to_stage(RenderStage::Extract, render::extract_sprite_events) - .add_system_to_stage(RenderStage::Queue, queue_sprites); + + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app + .init_resource::() + .init_resource::() + .init_resource::>() + .init_resource::() + .init_resource::() + .init_resource::() + .add_render_command::() + .add_system_to_stage( + RenderStage::Extract, + render::extract_sprites.label(SpriteSystem::ExtractSprites), + ) + .add_system_to_stage(RenderStage::Extract, render::extract_sprite_events) + .add_system_to_stage(RenderStage::Queue, queue_sprites); + }; } } diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 2b3177f705..7468990b47 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -61,12 +61,14 @@ impl Plugin for Mesh2dRenderPlugin { app.add_plugin(UniformComponentPlugin::::default()); - app.sub_app_mut(RenderApp) - .init_resource::() - .init_resource::>() - .add_system_to_stage(RenderStage::Extract, extract_mesh2d) - .add_system_to_stage(RenderStage::Queue, queue_mesh2d_bind_group) - .add_system_to_stage(RenderStage::Queue, queue_mesh2d_view_bind_groups); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app + .init_resource::() + .init_resource::>() + .add_system_to_stage(RenderStage::Extract, extract_mesh2d) + .add_system_to_stage(RenderStage::Queue, queue_mesh2d_bind_group) + .add_system_to_stage(RenderStage::Queue, queue_mesh2d_view_bind_groups); + } } } diff --git a/crates/bevy_text/src/lib.rs b/crates/bevy_text/src/lib.rs index 3eb062f44e..6ed6009ad7 100644 --- a/crates/bevy_text/src/lib.rs +++ b/crates/bevy_text/src/lib.rs @@ -48,10 +48,11 @@ impl Plugin for TextPlugin { .insert_resource(DefaultTextPipeline::default()) .add_system_to_stage(CoreStage::PostUpdate, text2d_system); - let render_app = app.sub_app_mut(RenderApp); - render_app.add_system_to_stage( - RenderStage::Extract, - extract_text2d_sprite.after(SpriteSystem::ExtractSprites), - ); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app.add_system_to_stage( + RenderStage::Extract, + extract_text2d_sprite.after(SpriteSystem::ExtractSprites), + ); + } } } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index e063cd4504..e632b4eee1 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -66,7 +66,11 @@ pub fn build_ui_render(app: &mut App) { let mut active_cameras = app.world.get_resource_mut::().unwrap(); active_cameras.add(CAMERA_UI); - let render_app = app.sub_app_mut(RenderApp); + let render_app = match app.get_sub_app_mut(RenderApp) { + Ok(render_app) => render_app, + Err(_) => return, + }; + render_app .init_resource::() .init_resource::>() diff --git a/examples/README.md b/examples/README.md index c540e58af7..81190f5886 100644 --- a/examples/README.md +++ b/examples/README.md @@ -121,6 +121,7 @@ Example | File | Description `empty` | [`app/empty.rs`](./app/empty.rs) | An empty application (does nothing) `empty_defaults` | [`app/empty_defaults.rs`](./app/empty_defaults.rs) | An empty application with default plugins `headless` | [`app/headless.rs`](./app/headless.rs) | An application that runs without default plugins +`headless_defaults` | [`app/headless_defaults.rs`](./app/headless_defaults.rs) | An application that runs with default plugins, but without an actual renderer `logs` | [`app/logs.rs`](./app/logs.rs) | Illustrate how to use generate log output `plugin` | [`app/plugin.rs`](./app/plugin.rs) | Demonstrates the creation and registration of a custom plugin `plugin_group` | [`app/plugin_group.rs`](./app/plugin_group.rs) | Demonstrates the creation and registration of a custom plugin group diff --git a/examples/app/headless_defaults.rs b/examples/app/headless_defaults.rs new file mode 100644 index 0000000000..e3836be9f5 --- /dev/null +++ b/examples/app/headless_defaults.rs @@ -0,0 +1,11 @@ +use bevy::{prelude::*, render::options::WgpuOptions}; + +fn main() { + App::new() + .insert_resource(WgpuOptions { + backends: None, + ..Default::default() + }) + .add_plugins(DefaultPlugins) + .run(); +}