From 8c42799d316571b92a666e4619bba5f73005d45e Mon Sep 17 00:00:00 2001 From: Wybe Westra Date: Sat, 29 Jan 2022 16:15:33 +0100 Subject: [PATCH 01/26] Make raw_window_handle optional for unit testing purposes. --- crates/bevy_render/src/lib.rs | 22 ++++---- crates/bevy_render/src/view/window.rs | 70 +++++++++++++------------- crates/bevy_window/src/window.rs | 11 ++-- crates/bevy_winit/src/winit_windows.rs | 2 +- 4 files changed, 57 insertions(+), 48 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 788dd9365b48f..7ea698db735e1 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -45,6 +45,7 @@ use bevy_app::{App, AppLabel, Plugin}; use bevy_asset::{AddAsset, AssetServer}; use bevy_ecs::prelude::*; use std::ops::{Deref, DerefMut}; +use wgpu::Surface; /// Contains the default Bevy rendering backend based on wgpu. #[derive(Default)] @@ -120,15 +121,7 @@ impl Plugin for RenderPlugin { 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 surface = try_create_surface(app, &instance); let request_adapter_options = wgpu::RequestAdapterOptions { power_preference: options.power_preference, compatible_surface: surface.as_ref(), @@ -294,6 +287,17 @@ impl Plugin for RenderPlugin { } } +fn try_create_surface(app: &mut App, wgpu_instance: &wgpu::Instance) -> Option { + let world = app.world.cell(); + let windows = world.get_resource_mut::().unwrap(); + windows.get_primary().and_then(|window| unsafe { + window.raw_window_handle().map(|handle_wrapper| { + let window_handle = handle_wrapper.get_handle(); + wgpu_instance.create_surface(&window_handle) + }) + }) +} + /// Executes the [`Extract`](RenderStage::Extract) stage of the renderer. /// This updates the render world with the extracted ECS data of the current frame. fn extract(app_world: &mut World, render_app: &mut App) { diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index 94bf8a21c0fdb..9c28f17b95285 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -40,7 +40,7 @@ impl Plugin for WindowRenderPlugin { pub struct ExtractedWindow { pub id: WindowId, - pub handle: RawWindowHandleWrapper, + pub handle: Option, pub physical_width: u32, pub physical_height: u32, pub vsync: bool, @@ -125,42 +125,44 @@ pub fn prepare_windows( ) { let window_surfaces = window_surfaces.deref_mut(); for window in windows.windows.values_mut() { - let surface = window_surfaces - .surfaces - .entry(window.id) - .or_insert_with(|| unsafe { - // NOTE: On some OSes this MUST be called from the main thread. - render_instance.create_surface(&window.handle.get_handle()) - }); - - let swap_chain_descriptor = wgpu::SurfaceConfiguration { - format: TextureFormat::bevy_default(), - width: window.physical_width, - height: window.physical_height, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - present_mode: if window.vsync { - wgpu::PresentMode::Fifo - } else { - wgpu::PresentMode::Immediate - }, - }; - - // Do the initial surface configuration if it hasn't been configured yet - if window_surfaces.configured_windows.insert(window.id) || window.size_changed { - render_device.configure_surface(surface, &swap_chain_descriptor); - } + if let Some(window_handle_wrapper) = &window.handle { + let surface = window_surfaces + .surfaces + .entry(window.id) + .or_insert_with(|| unsafe { + // NOTE: On some OSes this MUST be called from the main thread. + render_instance.create_surface(&window_handle_wrapper.get_handle()) + }); - let frame = match surface.get_current_texture() { - Ok(swap_chain_frame) => swap_chain_frame, - Err(wgpu::SurfaceError::Outdated) => { + let swap_chain_descriptor = wgpu::SurfaceConfiguration { + format: TextureFormat::bevy_default(), + width: window.physical_width, + height: window.physical_height, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + present_mode: if window.vsync { + wgpu::PresentMode::Fifo + } else { + wgpu::PresentMode::Immediate + }, + }; + + // Do the initial surface configuration if it hasn't been configured yet + if window_surfaces.configured_windows.insert(window.id) || window.size_changed { render_device.configure_surface(surface, &swap_chain_descriptor); - surface - .get_current_texture() - .expect("Error reconfiguring surface") } - err => err.expect("Failed to acquire next swap chain texture!"), - }; - window.swap_chain_texture = Some(TextureView::from(frame)); + let frame = match surface.get_current_texture() { + Ok(swap_chain_frame) => swap_chain_frame, + Err(wgpu::SurfaceError::Outdated) => { + render_device.configure_surface(surface, &swap_chain_descriptor); + surface + .get_current_texture() + .expect("Error reconfiguring surface") + } + err => err.expect("Failed to acquire next swap chain texture!"), + }; + + window.swap_chain_texture = Some(TextureView::from(frame)); + } } } diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 7381a32134ef7..8e309e4881dae 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -109,6 +109,9 @@ impl WindowResizeConstraints { /// requested size due to operating system limits on the window size, or the /// quantization of the logical size when converting the physical size to the /// logical size through the scaling factor. +/// +/// ## Headless Testing +/// To run tests without needing to create an actual window, set `raw_window_handle` to `None`. #[derive(Debug)] pub struct Window { id: WindowId, @@ -128,7 +131,7 @@ pub struct Window { cursor_visible: bool, cursor_locked: bool, physical_cursor_position: Option, - raw_window_handle: RawWindowHandleWrapper, + raw_window_handle: Option, focused: bool, mode: WindowMode, #[cfg(target_arch = "wasm32")] @@ -209,7 +212,7 @@ impl Window { physical_height: u32, scale_factor: f64, position: Option, - raw_window_handle: RawWindowHandle, + raw_window_handle: Option, ) -> Self { Window { id, @@ -229,7 +232,7 @@ impl Window { cursor_locked: window_descriptor.cursor_locked, cursor_icon: CursorIcon::Default, physical_cursor_position: None, - raw_window_handle: RawWindowHandleWrapper::new(raw_window_handle), + raw_window_handle: raw_window_handle.map(|handle| RawWindowHandleWrapper::new(handle)), focused: true, mode: window_descriptor.mode, #[cfg(target_arch = "wasm32")] @@ -544,7 +547,7 @@ impl Window { self.focused } - pub fn raw_window_handle(&self) -> RawWindowHandleWrapper { + pub fn raw_window_handle(&self) -> Option { self.raw_window_handle.clone() } } diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index f3fd83c6e8420..76f39c46c734d 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -171,7 +171,7 @@ impl WinitWindows { inner_size.height, scale_factor, position, - raw_window_handle, + Some(raw_window_handle), ) } From b0475ca4245f07b44ead4f745ac6dcfee406cb4f Mon Sep 17 00:00:00 2001 From: Wybe Westra Date: Mon, 31 Jan 2022 22:09:46 +0100 Subject: [PATCH 02/26] Add example of testing ui positioning without a real window. --- crates/bevy_window/src/window.rs | 2 +- tests/how_to_test_ui.rs | 91 ++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tests/how_to_test_ui.rs diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 8e309e4881dae..631c98c71b241 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -232,7 +232,7 @@ impl Window { cursor_locked: window_descriptor.cursor_locked, cursor_icon: CursorIcon::Default, physical_cursor_position: None, - raw_window_handle: raw_window_handle.map(|handle| RawWindowHandleWrapper::new(handle)), + raw_window_handle: raw_window_handle.map(RawWindowHandleWrapper::new), focused: true, mode: window_descriptor.mode, #[cfg(target_arch = "wasm32")] diff --git a/tests/how_to_test_ui.rs b/tests/how_to_test_ui.rs new file mode 100644 index 0000000000000..39ec9b03f379d --- /dev/null +++ b/tests/how_to_test_ui.rs @@ -0,0 +1,91 @@ +use bevy::prelude::*; +use bevy_internal::asset::AssetPlugin; +use bevy_internal::core_pipeline::CorePipelinePlugin; +use bevy_internal::input::InputPlugin; +use bevy_internal::render::options::WgpuOptions; +use bevy_internal::render::RenderPlugin; +use bevy_internal::sprite::SpritePlugin; +use bevy_internal::text::TextPlugin; +use bevy_internal::ui::UiPlugin; +use bevy_internal::window::{WindowId, WindowPlugin}; + +const WINDOW_WIDTH: u32 = 200; +const WINDOW_HEIGHT: u32 = 100; + +struct HeadlessUiPlugin; + +impl Plugin for HeadlessUiPlugin { + fn build(&self, app: &mut App) { + // These tests are meant to be ran on systems without gpu, or display. + // To make this work, we tell bevy not to look for any rendering backends. + app.insert_resource(WgpuOptions { + backends: None, + ..Default::default() + }) + // To test the positioning of UI elements, + // we first need a window to position these elements in. + .insert_resource({ + let mut windows = Windows::default(); + windows.add(Window::new( + // At the moment, all ui elements are placed in the primary window. + WindowId::primary(), + &WindowDescriptor::default(), + WINDOW_WIDTH, + WINDOW_HEIGHT, + 1.0, + None, + // Because this test is running without a real window, we pass `None` here. + None, + )); + windows + }) + .add_plugins(MinimalPlugins) + .add_plugin(TransformPlugin) + .add_plugin(WindowPlugin::default()) + .add_plugin(InputPlugin) + .add_plugin(AssetPlugin) + .add_plugin(RenderPlugin) + .add_plugin(CorePipelinePlugin) + .add_plugin(SpritePlugin) + .add_plugin(TextPlugin) + .add_plugin(UiPlugin); + } +} + +#[test] +fn test_button_translation() { + let mut app = App::new(); + app.add_plugin(HeadlessUiPlugin) + .add_startup_system(setup_button_test); + + // First call to `update` also runs the startup systems. + app.update(); + + let mut query = app.world.query_filtered::>(); + let button = *query.iter(&app.world).collect::>().first().unwrap(); + + // The button's translation got updated because the UI system had a window to place it in. + // If we hadn't added a window, the button's translation would at this point be all zero's. + let button_transform = app.world.entity(button).get::().unwrap(); + assert_eq!( + button_transform.translation.x.floor() as u32, + WINDOW_WIDTH / 2 + ); + assert_eq!( + button_transform.translation.y.floor() as u32, + WINDOW_HEIGHT / 2 + ); +} + +fn setup_button_test(mut commands: Commands) { + commands.spawn_bundle(UiCameraBundle::default()); + commands.spawn_bundle(ButtonBundle { + style: Style { + size: Size::new(Val::Px(150.0), Val::Px(65.0)), + // Center this button in the middle of the window. + margin: Rect::all(Val::Auto), + ..Default::default() + }, + ..Default::default() + }); +} From 482f1346996a016dfd2c36879f26611dfd9cc016 Mon Sep 17 00:00:00 2001 From: Wybe Westra Date: Mon, 31 Jan 2022 22:14:54 +0100 Subject: [PATCH 03/26] Minor typo --- tests/how_to_test_ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/how_to_test_ui.rs b/tests/how_to_test_ui.rs index 39ec9b03f379d..39199e830e0ba 100644 --- a/tests/how_to_test_ui.rs +++ b/tests/how_to_test_ui.rs @@ -65,7 +65,7 @@ fn test_button_translation() { let button = *query.iter(&app.world).collect::>().first().unwrap(); // The button's translation got updated because the UI system had a window to place it in. - // If we hadn't added a window, the button's translation would at this point be all zero's. + // If we hadn't added a window, the button's translation would at this point be all zeros. let button_transform = app.world.entity(button).get::().unwrap(); assert_eq!( button_transform.translation.x.floor() as u32, From 10c93769d90ad129fc0fbe656b9646c99523b7d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 18 Oct 2021 01:23:40 +0200 Subject: [PATCH 04/26] panic when the same plugin is added twice --- crates/bevy_app/src/app.rs | 58 +++++++++++++++++++++++++++-- crates/bevy_app/src/plugin_group.rs | 7 ++-- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 6dc3ef9bcf9c2..14126ae7e2bbc 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -10,7 +10,7 @@ use bevy_ecs::{ world::World, }; use bevy_utils::{tracing::debug, HashMap}; -use std::fmt::Debug; +use std::{any::TypeId, fmt::Debug}; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -55,6 +55,7 @@ pub struct App { /// A container of [`Stage`]s set to be run in a linear order. pub schedule: Schedule, sub_apps: HashMap, SubApp>, + plugins: HashMap>, } /// Each [`SubApp`] has its own [`Schedule`] and [`World`], enabling a separation of concerns. @@ -98,6 +99,7 @@ impl App { schedule: Default::default(), runner: Box::new(run_once), sub_apps: HashMap::default(), + plugins: Default::default(), } } @@ -769,10 +771,38 @@ impl App { T: Plugin, { debug!("added plugin: {}", plugin.name()); + self.register_plugin(&std::any::TypeId::of::(), plugin.name(), None); plugin.build(self); self } + /// Checks that a plugin has not already been added to an application. It will panic with an + /// helpful message the second time a plugin is being added. + pub(crate) fn register_plugin( + &mut self, + plugin_type: &TypeId, + plugin_name: &str, + from_group: Option<&'static str>, + ) { + if let Some(existing_from_group) = self.plugins.insert(*plugin_type, from_group) { + match (from_group, existing_from_group) { + (None, None) => panic!("Plugin \"{}\" was already added", plugin_name), + (None, Some(existing_from_group)) => panic!( + "Plugin \"{}\" was already added with group \"{}\"", + plugin_name, existing_from_group + ), + (Some(from_group), None) => panic!( + "Plugin \"{}\" from group \"{}\" was already added", + plugin_name, from_group + ), + (Some(from_group), Some(existing_from_group)) => panic!( + "Plugin \"{}\" from group \"{}\" was already added with group \"{}\"", + plugin_name, from_group, existing_from_group + ), + }; + } + } + /// Adds a group of plugins /// /// Bevy plugins can be grouped into a set of plugins. Bevy provides @@ -794,9 +824,20 @@ impl App { /// .add_plugins(MinimalPlugins); /// ``` pub fn add_plugins(&mut self, mut group: T) -> &mut Self { + if self + .plugins + .insert(std::any::TypeId::of::(), None) + .is_some() + { + panic!( + "Plugin Group \"{}\" was already added", + std::any::type_name::() + ); + } + let mut plugin_group_builder = PluginGroupBuilder::default(); group.build(&mut plugin_group_builder); - plugin_group_builder.finish(self); + plugin_group_builder.finish::(self); self } @@ -834,10 +875,21 @@ impl App { T: PluginGroup, F: FnOnce(&mut PluginGroupBuilder) -> &mut PluginGroupBuilder, { + if self + .plugins + .insert(std::any::TypeId::of::(), None) + .is_some() + { + panic!( + "Plugin Group \"{}\" was already added", + std::any::type_name::() + ); + } + let mut plugin_group_builder = PluginGroupBuilder::default(); group.build(&mut plugin_group_builder); func(&mut plugin_group_builder); - plugin_group_builder.finish(self); + plugin_group_builder.finish::(self); self } diff --git a/crates/bevy_app/src/plugin_group.rs b/crates/bevy_app/src/plugin_group.rs index aecb3dd05acda..e5e560e067661 100644 --- a/crates/bevy_app/src/plugin_group.rs +++ b/crates/bevy_app/src/plugin_group.rs @@ -3,7 +3,7 @@ use bevy_utils::{tracing::debug, HashMap}; use std::any::TypeId; /// Combines multiple [`Plugin`]s into a single unit. -pub trait PluginGroup { +pub trait PluginGroup: 'static { /// Configures the [`Plugin`]s that are to be added. fn build(&mut self, group: &mut PluginGroupBuilder); } @@ -110,11 +110,12 @@ impl PluginGroupBuilder { } /// Consumes the [`PluginGroupBuilder`] and [builds](Plugin::build) the contained [`Plugin`]s. - pub fn finish(self, app: &mut App) { + pub fn finish(self, app: &mut App) { for ty in self.order.iter() { if let Some(entry) = self.plugins.get(ty) { if entry.enabled { - debug!("added plugin: {}", entry.plugin.name()); + debug!("added plugin from group: {}", entry.plugin.name()); + app.register_plugin(ty, entry.plugin.name(), Some(std::any::type_name::())); entry.plugin.build(app); } } From 2b301ee26ac733eb3ea7018f50bae7b71d2038e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 18 Oct 2021 02:03:15 +0200 Subject: [PATCH 05/26] add startup resources --- crates/bevy_app/src/app.rs | 49 +++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 14126ae7e2bbc..684c4591d16e0 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -9,13 +9,19 @@ use bevy_ecs::{ system::Resource, world::World, }; -use bevy_utils::{tracing::debug, HashMap}; +use bevy_utils::{ + tracing::{debug, error}, + HashMap, +}; use std::{any::TypeId, fmt::Debug}; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; bevy_utils::define_label!(AppLabel); +/// Wrapper struct for setting startup resource aside +struct Startup(T); + #[allow(clippy::needless_doctest_main)] /// Containers of app logic and data /// @@ -56,6 +62,7 @@ pub struct App { pub schedule: Schedule, sub_apps: HashMap, SubApp>, plugins: HashMap>, + startup_resources: HashMap, } /// Each [`SubApp`] has its own [`Schedule`] and [`World`], enabling a separation of concerns. @@ -100,6 +107,7 @@ impl App { runner: Box::new(run_once), sub_apps: HashMap::default(), plugins: Default::default(), + startup_resources: Default::default(), } } @@ -129,6 +137,8 @@ impl App { #[cfg(feature = "trace")] let _bevy_app_run_guard = bevy_app_run_span.enter(); + self.check_all_startup_resources_consumed(); + let mut app = std::mem::replace(self, App::empty()); let runner = std::mem::replace(&mut app.runner, Box::new(run_once)); (runner)(app); @@ -621,6 +631,43 @@ impl App { .add_system_to_stage(CoreStage::First, Events::::update_system) } + /// Inserts a startup resource to the current [App] and overwrites any resource previously + /// added of the same type. + /// + /// A startup resource is used at startup for plugin initialisation and configuration. All + /// startup resources inserted must be consumed before the application is ran. + pub fn insert_startup_resource(&mut self, resource: T) -> &mut Self + where + T: Resource, + { + self.startup_resources + .insert(std::any::TypeId::of::(), std::any::type_name::()); + self.insert_resource(Startup(resource)); + self + } + + /// Consumes a startup resource, and removes it from the current [App] so that a plugin + /// can use it for its configuration. + pub fn consume_startup_resource(&mut self) -> Option + where + T: Resource, + { + self.startup_resources.remove(&std::any::TypeId::of::()); + self.world + .remove_resource::>() + .map(|startup| startup.0) + } + + /// Check that all startup resources have been consumed, panicing otherwise. + fn check_all_startup_resources_consumed(&self) { + self.startup_resources + .values() + .for_each(|v| error!("Startup resource \"{}\" has not been consumed", v)); + if !self.startup_resources.is_empty() { + panic!("All startup resources have not been consumed. This can happen if you inserted a startup resource after the plugin consuming it.") + } + } + /// Inserts a resource to the current [App] and overwrites any resource previously added of the same type. /// /// A resource in Bevy represents globally unique data. Resources must be added to Bevy Apps From ca4327e618e934195165f94bcaab257895285899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 18 Oct 2021 02:03:34 +0200 Subject: [PATCH 06/26] move WindowDescriptor to a startup resource --- crates/bevy_window/src/event.rs | 4 ++-- crates/bevy_window/src/lib.rs | 6 ++---- crates/bevy_window/src/window.rs | 10 +++++----- crates/bevy_winit/src/winit_windows.rs | 6 +++--- examples/ios/src/lib.rs | 3 +-- examples/tools/bevymark.rs | 2 +- examples/ui/text_debug.rs | 4 ++-- examples/window/scale_factor_override.rs | 2 +- examples/window/window_settings.rs | 2 +- 9 files changed, 18 insertions(+), 21 deletions(-) diff --git a/crates/bevy_window/src/event.rs b/crates/bevy_window/src/event.rs index 72d6483d9b0df..ee017d11d3fd7 100644 --- a/crates/bevy_window/src/event.rs +++ b/crates/bevy_window/src/event.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use super::{WindowDescriptor, WindowId}; +use super::{WindowId, WindowInitializationDescriptor}; use bevy_math::{IVec2, Vec2}; /// A window event that is sent whenever a windows logical size has changed @@ -17,7 +17,7 @@ pub struct WindowResized { #[derive(Debug, Clone)] pub struct CreateWindow { pub id: WindowId, - pub descriptor: WindowDescriptor, + pub descriptor: WindowInitializationDescriptor, } /// An event that indicates a window should be closed. diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index a63ca57a96b7d..18c25d6c9ef78 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -16,7 +16,7 @@ pub mod prelude { #[doc(hidden)] pub use crate::{ CursorEntered, CursorIcon, CursorLeft, CursorMoved, FileDragAndDrop, ReceivedCharacter, - Window, WindowDescriptor, WindowMoved, Windows, + Window, WindowInitializationDescriptor, WindowMoved, Windows, }; } @@ -56,9 +56,7 @@ impl Plugin for WindowPlugin { if self.add_primary_window { let window_descriptor = app - .world - .get_resource::() - .map(|descriptor| (*descriptor).clone()) + .consume_startup_resource::() .unwrap_or_default(); let mut create_window_event = app .world diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 631c98c71b241..8c26a003968d0 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -207,7 +207,7 @@ pub enum WindowMode { impl Window { pub fn new( id: WindowId, - window_descriptor: &WindowDescriptor, + window_descriptor: &WindowInitializationDescriptor, physical_width: u32, physical_height: u32, scale_factor: f64, @@ -553,7 +553,7 @@ impl Window { } #[derive(Debug, Clone)] -pub struct WindowDescriptor { +pub struct WindowInitializationDescriptor { pub width: f32, pub height: f32, pub position: Option, @@ -578,10 +578,10 @@ pub struct WindowDescriptor { pub canvas: Option, } -impl Default for WindowDescriptor { +impl Default for WindowInitializationDescriptor { fn default() -> Self { - WindowDescriptor { - title: "bevy".to_string(), + WindowInitializationDescriptor { + title: "app".to_string(), width: 1280., height: 720., position: None, diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index 76f39c46c734d..eedc6a962a9fe 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -1,6 +1,6 @@ use bevy_math::IVec2; use bevy_utils::HashMap; -use bevy_window::{Window, WindowDescriptor, WindowId, WindowMode}; +use bevy_window::{Window, WindowId, WindowInitializationDescriptor, WindowMode}; use raw_window_handle::HasRawWindowHandle; use winit::dpi::LogicalSize; @@ -16,7 +16,7 @@ impl WinitWindows { &mut self, event_loop: &winit::event_loop::EventLoopWindowTarget<()>, window_id: WindowId, - window_descriptor: &WindowDescriptor, + window_descriptor: &WindowInitializationDescriptor, ) -> Window { #[cfg(target_os = "windows")] let mut winit_window_builder = { @@ -44,7 +44,7 @@ impl WinitWindows { )), )), _ => { - let WindowDescriptor { + let WindowInitializationDescriptor { width, height, position, diff --git a/examples/ios/src/lib.rs b/examples/ios/src/lib.rs index f79caa85d9625..0448fe4ca392f 100644 --- a/examples/ios/src/lib.rs +++ b/examples/ios/src/lib.rs @@ -4,8 +4,7 @@ use bevy::{input::touch::TouchPhase, prelude::*, window::WindowMode}; #[bevy_main] fn main() { App::new() - .insert_resource(WindowDescriptor { - vsync: true, + .insert_startup_resource(WindowInitializationDescriptor { resizable: false, mode: WindowMode::BorderlessFullscreen, ..Default::default() diff --git a/examples/tools/bevymark.rs b/examples/tools/bevymark.rs index 290fdf42594aa..b35107a4b4eb6 100644 --- a/examples/tools/bevymark.rs +++ b/examples/tools/bevymark.rs @@ -24,7 +24,7 @@ struct Bird { fn main() { App::new() - .insert_resource(WindowDescriptor { + .insert_startup_resource(WindowInitializationDescriptor { title: "BevyMark".to_string(), width: 800., height: 600., diff --git a/examples/ui/text_debug.rs b/examples/ui/text_debug.rs index 867a7aae382ea..f8a343fe7ba23 100644 --- a/examples/ui/text_debug.rs +++ b/examples/ui/text_debug.rs @@ -6,8 +6,8 @@ use bevy::{ /// This example is for debugging text layout fn main() { App::new() - .insert_resource(WindowDescriptor { - vsync: false, + .insert_startup_resource(WindowInitializationDescriptor { + present_mode: PresentMode::Immediate, ..Default::default() }) .add_plugins(DefaultPlugins) diff --git a/examples/window/scale_factor_override.rs b/examples/window/scale_factor_override.rs index ab00c3ae106b8..cbc839c82de85 100644 --- a/examples/window/scale_factor_override.rs +++ b/examples/window/scale_factor_override.rs @@ -3,7 +3,7 @@ use bevy::prelude::*; /// This example illustrates how to customize the default window settings fn main() { App::new() - .insert_resource(WindowDescriptor { + .insert_startup_resource(WindowInitializationDescriptor { width: 500., height: 300., ..Default::default() diff --git a/examples/window/window_settings.rs b/examples/window/window_settings.rs index e00ba07416809..3516c88dbfd85 100644 --- a/examples/window/window_settings.rs +++ b/examples/window/window_settings.rs @@ -3,7 +3,7 @@ use bevy::prelude::*; /// This example illustrates how to customize the default window settings fn main() { App::new() - .insert_resource(WindowDescriptor { + .insert_startup_resource(WindowInitializationDescriptor { title: "I am a window!".to_string(), width: 500., height: 300., From 10761230e4974b3c7305e520d576b2a86d4e2281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 18 Oct 2021 02:09:54 +0200 Subject: [PATCH 07/26] move LogSettings to a startup resource --- crates/bevy_log/src/lib.rs | 8 +++++--- examples/app/logs.rs | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/bevy_log/src/lib.rs b/crates/bevy_log/src/lib.rs index c9e04cafde31f..c60b0bc33ab67 100644 --- a/crates/bevy_log/src/lib.rs +++ b/crates/bevy_log/src/lib.rs @@ -41,7 +41,7 @@ use tracing_subscriber::{prelude::*, registry::Registry, EnvFilter}; /// * Using [`tracing-wasm`](https://crates.io/crates/tracing-wasm) in WASM, logging /// to the browser console. /// -/// You can configure this plugin using the resource [`LogSettings`]. +/// You can configure this plugin using the startup resource [`LogSettings`]. /// ```no_run /// # use bevy_internal::DefaultPlugins; /// # use bevy_app::App; @@ -49,7 +49,7 @@ use tracing_subscriber::{prelude::*, registry::Registry, EnvFilter}; /// # use bevy_utils::tracing::Level; /// fn main() { /// App::new() -/// .insert_resource(LogSettings { +/// .insert_startup_resource(LogSettings { /// level: Level::DEBUG, /// filter: "wgpu=error,bevy_render=info".to_string(), /// }) @@ -106,7 +106,9 @@ impl Default for LogSettings { impl Plugin for LogPlugin { fn build(&self, app: &mut App) { let default_filter = { - let settings = app.world.get_resource_or_insert_with(LogSettings::default); + let settings = app + .consume_startup_resource::() + .unwrap_or_default(); format!("{},{}", settings.level, settings.filter) }; LogTracer::init().unwrap(); diff --git a/examples/app/logs.rs b/examples/app/logs.rs index cdc6b5db01274..8471010f5176f 100644 --- a/examples/app/logs.rs +++ b/examples/app/logs.rs @@ -4,7 +4,7 @@ use bevy::prelude::*; fn main() { App::new() // Uncomment this to override the default log settings: - // .insert_resource(bevy::log::LogSettings { + // .insert_startup_resource(bevy::log::LogSettings { // level: bevy::log::Level::TRACE, // filter: "wgpu=warn,bevy_ecs=info".to_string(), // }) @@ -23,7 +23,7 @@ fn log_system() { error!("something failed"); // by default, trace and debug logs are ignored because they are "noisy" - // you can control what level is logged by adding the LogSettings resource + // you can control what level is logged by adding the LogSettings startup resource // alternatively you can set the log level via the RUST_LOG=LEVEL environment variable // ex: RUST_LOG=trace, RUST_LOG=info,bevy_ecs=warn // the format used here is super flexible. check out this documentation for more info: From 478dcdc3e7663b22b8882fd93575c0438ddd0799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 18 Oct 2021 12:12:06 +0200 Subject: [PATCH 08/26] can override plugin uniqueness --- crates/bevy_app/src/app.rs | 4 +++- crates/bevy_app/src/plugin.rs | 5 +++++ crates/bevy_app/src/plugin_group.rs | 8 +++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 684c4591d16e0..0a3dfce2dabf1 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -818,7 +818,9 @@ impl App { T: Plugin, { debug!("added plugin: {}", plugin.name()); - self.register_plugin(&std::any::TypeId::of::(), plugin.name(), None); + if plugin.is_unique() { + self.register_plugin(&std::any::TypeId::of::(), plugin.name(), None); + } plugin.build(self); self } diff --git a/crates/bevy_app/src/plugin.rs b/crates/bevy_app/src/plugin.rs index c6876db0b3b60..f6ad0914bd68e 100644 --- a/crates/bevy_app/src/plugin.rs +++ b/crates/bevy_app/src/plugin.rs @@ -12,6 +12,11 @@ pub trait Plugin: Any + Send + Sync { fn name(&self) -> &str { std::any::type_name::() } + /// If the plugin can be instantiated several times in an [`App`](crate::App), override this + /// method to return `false`. + fn is_unique(&self) -> bool { + true + } } /// Type representing an unsafe function that returns a mutable pointer to a [`Plugin`]. diff --git a/crates/bevy_app/src/plugin_group.rs b/crates/bevy_app/src/plugin_group.rs index e5e560e067661..2bd487a513eb4 100644 --- a/crates/bevy_app/src/plugin_group.rs +++ b/crates/bevy_app/src/plugin_group.rs @@ -115,7 +115,13 @@ impl PluginGroupBuilder { if let Some(entry) = self.plugins.get(ty) { if entry.enabled { debug!("added plugin from group: {}", entry.plugin.name()); - app.register_plugin(ty, entry.plugin.name(), Some(std::any::type_name::())); + if entry.plugin.is_unique() { + app.register_plugin( + ty, + entry.plugin.name(), + Some(std::any::type_name::()), + ); + } entry.plugin.build(app); } } From 8018dc906d81bbf64207bfb6c848a4dbde3ef4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 18 Oct 2021 12:12:28 +0200 Subject: [PATCH 09/26] add tests on plugin controls --- crates/bevy_app/src/app.rs | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 0a3dfce2dabf1..0588fd5f4ff3a 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1018,3 +1018,49 @@ fn run_once(mut app: App) { /// An event that indicates the app should exit. This will fully exit the app process. #[derive(Debug, Clone)] pub struct AppExit; + +#[cfg(test)] +mod tests { + use crate::{App, Plugin}; + + struct PluginA; + impl Plugin for PluginA { + fn build(&self, _app: &mut crate::App) {} + } + struct PluginB; + impl Plugin for PluginB { + fn build(&self, _app: &mut crate::App) {} + } + struct PluginC(T); + impl Plugin for PluginC { + fn build(&self, _app: &mut crate::App) {} + } + struct PluginD; + impl Plugin for PluginD { + fn build(&self, _app: &mut crate::App) {} + fn is_unique(&self) -> bool { + false + } + } + + #[test] + fn can_add_two_plugins() { + App::new().add_plugin(PluginA).add_plugin(PluginB); + } + + #[test] + #[should_panic] + fn cant_add_twice_the_same_plugin() { + App::new().add_plugin(PluginA).add_plugin(PluginA); + } + + #[test] + fn can_add_twice_the_same_plugin_with_different_type_param() { + App::new().add_plugin(PluginC(0)).add_plugin(PluginC(true)); + } + + #[test] + fn can_add_twice_the_same_plugin_not_unique() { + App::new().add_plugin(PluginD).add_plugin(PluginD); + } +} From d32e8671ff3d24ee08291644a16931471b4630ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 18 Oct 2021 12:12:55 +0200 Subject: [PATCH 10/26] fix log when adding a plugin --- crates/bevy_app/src/app.rs | 2 +- crates/bevy_app/src/plugin_group.rs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 0588fd5f4ff3a..23b4ed3c12b8a 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -817,7 +817,7 @@ impl App { where T: Plugin, { - debug!("added plugin: {}", plugin.name()); + debug!("added plugin {}", plugin.name()); if plugin.is_unique() { self.register_plugin(&std::any::TypeId::of::(), plugin.name(), None); } diff --git a/crates/bevy_app/src/plugin_group.rs b/crates/bevy_app/src/plugin_group.rs index 2bd487a513eb4..7ba3f0d31231f 100644 --- a/crates/bevy_app/src/plugin_group.rs +++ b/crates/bevy_app/src/plugin_group.rs @@ -114,7 +114,11 @@ impl PluginGroupBuilder { for ty in self.order.iter() { if let Some(entry) = self.plugins.get(ty) { if entry.enabled { - debug!("added plugin from group: {}", entry.plugin.name()); + debug!( + "added plugin {} from group {}", + entry.plugin.name(), + std::any::type_name::() + ); if entry.plugin.is_unique() { app.register_plugin( ty, From e2c05252f5ac35e08096c4c2ccd3aa1b82f7727f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 18 Oct 2021 12:13:26 +0200 Subject: [PATCH 11/26] rename startup to initialization --- crates/bevy_app/src/app.rs | 44 +++++++++++++----------- crates/bevy_log/src/lib.rs | 4 +-- crates/bevy_window/src/lib.rs | 2 +- examples/app/logs.rs | 2 +- examples/ios/src/lib.rs | 2 +- examples/tools/bevymark.rs | 2 +- examples/ui/text_debug.rs | 2 +- examples/window/scale_factor_override.rs | 2 +- examples/window/window_settings.rs | 2 +- 9 files changed, 32 insertions(+), 30 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 23b4ed3c12b8a..6e0f32def90db 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -19,8 +19,8 @@ use std::{any::TypeId, fmt::Debug}; use bevy_utils::tracing::info_span; bevy_utils::define_label!(AppLabel); -/// Wrapper struct for setting startup resource aside -struct Startup(T); +/// Wrapper struct for setting initialization resources aside +struct Init(T); #[allow(clippy::needless_doctest_main)] /// Containers of app logic and data @@ -62,7 +62,7 @@ pub struct App { pub schedule: Schedule, sub_apps: HashMap, SubApp>, plugins: HashMap>, - startup_resources: HashMap, + initialization_resources: HashMap, } /// Each [`SubApp`] has its own [`Schedule`] and [`World`], enabling a separation of concerns. @@ -107,7 +107,7 @@ impl App { runner: Box::new(run_once), sub_apps: HashMap::default(), plugins: Default::default(), - startup_resources: Default::default(), + initialization_resources: Default::default(), } } @@ -137,7 +137,7 @@ impl App { #[cfg(feature = "trace")] let _bevy_app_run_guard = bevy_app_run_span.enter(); - self.check_all_startup_resources_consumed(); + self.check_all_initialization_resources_consumed(); let mut app = std::mem::replace(self, App::empty()); let runner = std::mem::replace(&mut app.runner, Box::new(run_once)); @@ -631,40 +631,42 @@ impl App { .add_system_to_stage(CoreStage::First, Events::::update_system) } - /// Inserts a startup resource to the current [App] and overwrites any resource previously - /// added of the same type. + /// Inserts an initialization resource to the current [App] and overwrites any resource + /// previously added of the same type. /// - /// A startup resource is used at startup for plugin initialisation and configuration. All - /// startup resources inserted must be consumed before the application is ran. - pub fn insert_startup_resource(&mut self, resource: T) -> &mut Self + /// An initialization resource is used at startup for plugin initialisation and configuration. + /// All initialization resources inserted must be consumed by a plugin and removed before the + /// application is ran. + pub fn insert_initialization_resource(&mut self, resource: T) -> &mut Self where T: Resource, { - self.startup_resources + self.initialization_resources .insert(std::any::TypeId::of::(), std::any::type_name::()); - self.insert_resource(Startup(resource)); + self.insert_resource(Init(resource)); self } /// Consumes a startup resource, and removes it from the current [App] so that a plugin /// can use it for its configuration. - pub fn consume_startup_resource(&mut self) -> Option + pub fn consume_initialization_resource(&mut self) -> Option where T: Resource, { - self.startup_resources.remove(&std::any::TypeId::of::()); + self.initialization_resources + .remove(&std::any::TypeId::of::()); self.world - .remove_resource::>() + .remove_resource::>() .map(|startup| startup.0) } - /// Check that all startup resources have been consumed, panicing otherwise. - fn check_all_startup_resources_consumed(&self) { - self.startup_resources + /// Check that all initialization resources have been consumed, panicking otherwise. + fn check_all_initialization_resources_consumed(&self) { + self.initialization_resources .values() - .for_each(|v| error!("Startup resource \"{}\" has not been consumed", v)); - if !self.startup_resources.is_empty() { - panic!("All startup resources have not been consumed. This can happen if you inserted a startup resource after the plugin consuming it.") + .for_each(|v| error!("Initialization resource \"{}\" has not been consumed", v)); + if !self.initialization_resources.is_empty() { + panic!("All initialization resources have not been consumed. This can happen if you inserted a initialization resource after the plugin consuming it.") } } diff --git a/crates/bevy_log/src/lib.rs b/crates/bevy_log/src/lib.rs index c60b0bc33ab67..d437d18ed74a2 100644 --- a/crates/bevy_log/src/lib.rs +++ b/crates/bevy_log/src/lib.rs @@ -49,7 +49,7 @@ use tracing_subscriber::{prelude::*, registry::Registry, EnvFilter}; /// # use bevy_utils::tracing::Level; /// fn main() { /// App::new() -/// .insert_startup_resource(LogSettings { +/// .insert_initialization_resource(LogSettings { /// level: Level::DEBUG, /// filter: "wgpu=error,bevy_render=info".to_string(), /// }) @@ -107,7 +107,7 @@ impl Plugin for LogPlugin { fn build(&self, app: &mut App) { let default_filter = { let settings = app - .consume_startup_resource::() + .consume_initialization_resource::() .unwrap_or_default(); format!("{},{}", settings.level, settings.filter) }; diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 18c25d6c9ef78..cf50f6ebb5ee3 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -56,7 +56,7 @@ impl Plugin for WindowPlugin { if self.add_primary_window { let window_descriptor = app - .consume_startup_resource::() + .consume_initialization_resource::() .unwrap_or_default(); let mut create_window_event = app .world diff --git a/examples/app/logs.rs b/examples/app/logs.rs index 8471010f5176f..e90b7ebccecbc 100644 --- a/examples/app/logs.rs +++ b/examples/app/logs.rs @@ -4,7 +4,7 @@ use bevy::prelude::*; fn main() { App::new() // Uncomment this to override the default log settings: - // .insert_startup_resource(bevy::log::LogSettings { + // .insert_initialization_resource(bevy::log::LogSettings { // level: bevy::log::Level::TRACE, // filter: "wgpu=warn,bevy_ecs=info".to_string(), // }) diff --git a/examples/ios/src/lib.rs b/examples/ios/src/lib.rs index 0448fe4ca392f..b474d1e550859 100644 --- a/examples/ios/src/lib.rs +++ b/examples/ios/src/lib.rs @@ -4,7 +4,7 @@ use bevy::{input::touch::TouchPhase, prelude::*, window::WindowMode}; #[bevy_main] fn main() { App::new() - .insert_startup_resource(WindowInitializationDescriptor { + .insert_initialization_resource(WindowInitializationDescriptor { resizable: false, mode: WindowMode::BorderlessFullscreen, ..Default::default() diff --git a/examples/tools/bevymark.rs b/examples/tools/bevymark.rs index b35107a4b4eb6..6d9210fe3d3fe 100644 --- a/examples/tools/bevymark.rs +++ b/examples/tools/bevymark.rs @@ -24,7 +24,7 @@ struct Bird { fn main() { App::new() - .insert_startup_resource(WindowInitializationDescriptor { + .insert_initialization_resource(WindowInitializationDescriptor { title: "BevyMark".to_string(), width: 800., height: 600., diff --git a/examples/ui/text_debug.rs b/examples/ui/text_debug.rs index f8a343fe7ba23..3bb30188c793b 100644 --- a/examples/ui/text_debug.rs +++ b/examples/ui/text_debug.rs @@ -6,7 +6,7 @@ use bevy::{ /// This example is for debugging text layout fn main() { App::new() - .insert_startup_resource(WindowInitializationDescriptor { + .insert_initialization_resource(WindowInitializationDescriptor { present_mode: PresentMode::Immediate, ..Default::default() }) diff --git a/examples/window/scale_factor_override.rs b/examples/window/scale_factor_override.rs index cbc839c82de85..e3eaa1ee79246 100644 --- a/examples/window/scale_factor_override.rs +++ b/examples/window/scale_factor_override.rs @@ -3,7 +3,7 @@ use bevy::prelude::*; /// This example illustrates how to customize the default window settings fn main() { App::new() - .insert_startup_resource(WindowInitializationDescriptor { + .insert_initialization_resource(WindowInitializationDescriptor { width: 500., height: 300., ..Default::default() diff --git a/examples/window/window_settings.rs b/examples/window/window_settings.rs index 3516c88dbfd85..2266d9772aa2c 100644 --- a/examples/window/window_settings.rs +++ b/examples/window/window_settings.rs @@ -3,7 +3,7 @@ use bevy::prelude::*; /// This example illustrates how to customize the default window settings fn main() { App::new() - .insert_startup_resource(WindowInitializationDescriptor { + .insert_initialization_resource(WindowInitializationDescriptor { title: "I am a window!".to_string(), width: 500., height: 300., From 5bfcbb471f11777cdf1ef1d17368f029c1b31d97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 18 Oct 2021 13:59:41 +0200 Subject: [PATCH 12/26] more doc on uniqueness of plugins --- crates/bevy_app/src/plugin.rs | 5 ++++- examples/window/multiple_windows.rs | 2 +- examples/window/transparent_window.rs | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/bevy_app/src/plugin.rs b/crates/bevy_app/src/plugin.rs index f6ad0914bd68e..a57ad17b45f4a 100644 --- a/crates/bevy_app/src/plugin.rs +++ b/crates/bevy_app/src/plugin.rs @@ -4,7 +4,10 @@ use std::any::Any; /// A collection of Bevy App logic and configuration /// /// Plugins configure an [`App`](crate::App). When an [`App`](crate::App) registers -/// a plugin, the plugin's [`Plugin::build`] function is run. +/// a plugin, the plugin's [`Plugin::build`] function is run. By default, a plugin +/// can only be added once to an [`App`](crate::App). If the plugin may need to be +/// added twice or more, the function [`is_unique`](Plugin::is_unique) should be +/// overriden to return `false`. pub trait Plugin: Any + Send + Sync { /// Configures the [`App`] to which this plugin is added. fn build(&self, app: &mut App); diff --git a/examples/window/multiple_windows.rs b/examples/window/multiple_windows.rs index 6932460a20947..267cedc369552 100644 --- a/examples/window/multiple_windows.rs +++ b/examples/window/multiple_windows.rs @@ -54,7 +54,7 @@ fn create_new_window( // sends out a "CreateWindow" event, which will be received by the windowing backend create_window_events.send(CreateWindow { id: window_id, - descriptor: WindowDescriptor { + descriptor: WindowInitializationDescriptor { width: 800., height: 600., vsync: false, diff --git a/examples/window/transparent_window.rs b/examples/window/transparent_window.rs index 422de305c22b2..5a93cc68e8e96 100644 --- a/examples/window/transparent_window.rs +++ b/examples/window/transparent_window.rs @@ -1,12 +1,12 @@ /// An example of how to display a window in transparent mode /// [Documentation & Platform support.](https://docs.rs/bevy/latest/bevy/prelude/struct.WindowDescriptor.html#structfield.transparent) -use bevy::{prelude::*, window::WindowDescriptor}; +use bevy::{prelude::*, window::WindowInitializationDescriptor}; fn main() { App::new() // ClearColor must have 0 alpha, otherwise some color will bleed through .insert_resource(ClearColor(Color::NONE)) - .insert_resource(WindowDescriptor { + .insert_initialization_resource(WindowInitializationDescriptor { // Setting `transparent` allows the `ClearColor`'s alpha value to take effect transparent: true, // Disabling window decorations to make it feel more like a widget than a window From 9ddd8945fe8b83a85a503adb37ff05e1982045ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 18 Oct 2021 14:01:31 +0200 Subject: [PATCH 13/26] typo --- crates/bevy_app/src/app.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 6e0f32def90db..72eb5e1069966 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -666,7 +666,7 @@ impl App { .values() .for_each(|v| error!("Initialization resource \"{}\" has not been consumed", v)); if !self.initialization_resources.is_empty() { - panic!("All initialization resources have not been consumed. This can happen if you inserted a initialization resource after the plugin consuming it.") + panic!("All initialization resources have not been consumed. This can happen if you inserted an initialization resource after the plugin consuming it.") } } From 3908ada7c726f57257a31d64055251366e9e3a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 18 Oct 2021 18:32:12 +0200 Subject: [PATCH 14/26] missed a few rename --- crates/bevy_app/src/app.rs | 2 +- crates/bevy_log/src/lib.rs | 2 +- examples/app/logs.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 72eb5e1069966..a0f31720b83e9 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -647,7 +647,7 @@ impl App { self } - /// Consumes a startup resource, and removes it from the current [App] so that a plugin + /// Consumes an initialization resource, and removes it from the current [App] so that a plugin /// can use it for its configuration. pub fn consume_initialization_resource(&mut self) -> Option where diff --git a/crates/bevy_log/src/lib.rs b/crates/bevy_log/src/lib.rs index d437d18ed74a2..8483fb931abfb 100644 --- a/crates/bevy_log/src/lib.rs +++ b/crates/bevy_log/src/lib.rs @@ -41,7 +41,7 @@ use tracing_subscriber::{prelude::*, registry::Registry, EnvFilter}; /// * Using [`tracing-wasm`](https://crates.io/crates/tracing-wasm) in WASM, logging /// to the browser console. /// -/// You can configure this plugin using the startup resource [`LogSettings`]. +/// You can configure this plugin using the initialization resource [`LogSettings`]. /// ```no_run /// # use bevy_internal::DefaultPlugins; /// # use bevy_app::App; diff --git a/examples/app/logs.rs b/examples/app/logs.rs index e90b7ebccecbc..40cc463fb293c 100644 --- a/examples/app/logs.rs +++ b/examples/app/logs.rs @@ -23,7 +23,7 @@ fn log_system() { error!("something failed"); // by default, trace and debug logs are ignored because they are "noisy" - // you can control what level is logged by adding the LogSettings startup resource + // you can control what level is logged by adding the LogSettings initialization resource // alternatively you can set the log level via the RUST_LOG=LEVEL environment variable // ex: RUST_LOG=trace, RUST_LOG=info,bevy_ecs=warn // the format used here is super flexible. check out this documentation for more info: From 26e51e89f358fc322f134eea5c1864a50a37929a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 18 Oct 2021 18:33:56 +0200 Subject: [PATCH 15/26] and another --- crates/bevy_app/src/app.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index a0f31720b83e9..d830ac9289b13 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -657,7 +657,7 @@ impl App { .remove(&std::any::TypeId::of::()); self.world .remove_resource::>() - .map(|startup| startup.0) + .map(|initialization| initialization.0) } /// Check that all initialization resources have been consumed, panicking otherwise. From 58b70d18b1474a433e82f9db19c32cbf8538be92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Mon, 18 Oct 2021 23:34:17 +0200 Subject: [PATCH 16/26] wording Co-authored-by: Niklas Eicker --- crates/bevy_app/src/app.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index d830ac9289b13..e0c888b8dace7 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -666,7 +666,7 @@ impl App { .values() .for_each(|v| error!("Initialization resource \"{}\" has not been consumed", v)); if !self.initialization_resources.is_empty() { - panic!("All initialization resources have not been consumed. This can happen if you inserted an initialization resource after the plugin consuming it.") + panic!("Not all initialization resources have been consumed. This can happen if you inserted an initialization resource after the plugin consuming it.") } } From 2e42e0bf4e7d00014466e83e9333dac2c6e8f667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Mon, 22 Nov 2021 02:16:08 +0100 Subject: [PATCH 17/26] move AssetServerSettings as an initialization resource --- crates/bevy_asset/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 0067c266ce981..78e10179e9424 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -60,8 +60,8 @@ impl Default for AssetServerSettings { /// delegate to the default `AssetIo` for the platform. pub fn create_platform_default_asset_io(app: &mut App) -> Box { let settings = app - .world - .get_resource_or_insert_with(AssetServerSettings::default); + .consume_initialization_resource::() + .unwrap_or_default(); #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))] let source = FileAssetIo::new(&settings.asset_folder); From 3c41a9692d25efce79c732765b20080258d2a11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= <8672791+mockersf@users.noreply.github.com> Date: Wed, 15 Dec 2021 13:56:35 +0100 Subject: [PATCH 18/26] add method `is_plugin_added` --- crates/bevy_app/src/app.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index e0c888b8dace7..4075c69359dfa 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -802,6 +802,14 @@ impl App { self } + /// Check that a plugin has already been added to the app. + pub fn is_plugin_added(&self) -> bool + where + T: Plugin, + { + self.plugins.contains_key(&std::any::TypeId::of::()) + } + /// Adds a single plugin /// /// One of Bevy's core principles is modularity. All Bevy engine features are implemented From 243ca8c1b78f566e80f8cbbe0520e1875f883cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= <8672791+mockersf@users.noreply.github.com> Date: Fri, 31 Dec 2021 01:23:03 +0100 Subject: [PATCH 19/26] move WgpuOptions as an initialization resource --- crates/bevy_render/src/lib.rs | 4 +--- examples/3d/wireframe.rs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 7ea698db735e1..954cddedabfb9 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -110,9 +110,7 @@ impl Plugin for RenderPlugin { /// Initializes the renderer, sets up the [`RenderStage`](RenderStage) and creates the rendering sub-app. fn build(&self, app: &mut App) { let mut options = app - .world - .get_resource::() - .cloned() + .consume_initialization_resource::() .unwrap_or_default(); app.add_asset::() diff --git a/examples/3d/wireframe.rs b/examples/3d/wireframe.rs index 953b12532cc89..4d446b4741cae 100644 --- a/examples/3d/wireframe.rs +++ b/examples/3d/wireframe.rs @@ -7,7 +7,7 @@ use bevy::{ fn main() { App::new() .insert_resource(Msaa { samples: 4 }) - .insert_resource(WgpuOptions { + .insert_initialization_resource(WgpuOptions { features: WgpuFeatures::POLYGON_MODE_LINE, ..Default::default() }) From aefc996ca28481c3366ff3c835aaeead1bd08039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Mon, 24 Jan 2022 03:14:49 +0100 Subject: [PATCH 20/26] update new examples --- examples/app/headless_defaults.rs | 2 +- examples/shader/compute_shader_game_of_life.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/app/headless_defaults.rs b/examples/app/headless_defaults.rs index e3836be9f54e4..cf14f967e8f62 100644 --- a/examples/app/headless_defaults.rs +++ b/examples/app/headless_defaults.rs @@ -2,7 +2,7 @@ use bevy::{prelude::*, render::options::WgpuOptions}; fn main() { App::new() - .insert_resource(WgpuOptions { + .insert_initialization_resource(WgpuOptions { backends: None, ..Default::default() }) diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index 5ee578fb7b80e..ea1ac0581b808 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -8,7 +8,7 @@ use bevy::{ renderer::{RenderContext, RenderDevice}, RenderApp, RenderStage, }, - window::WindowDescriptor, + window::WindowInitializationDescriptor, }; const SIZE: (u32, u32) = (1280, 720); @@ -17,7 +17,7 @@ const WORKGROUP_SIZE: u32 = 8; fn main() { App::new() .insert_resource(ClearColor(Color::BLACK)) - .insert_resource(WindowDescriptor { + .insert_initialization_resource(WindowInitializationDescriptor { // uncomment for unthrottled FPS // vsync: false, ..Default::default() From 6d298004f387702c9e1567d83f0de2a254a1bc9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Mon, 24 Jan 2022 03:15:15 +0100 Subject: [PATCH 21/26] WinitConfig as an initialization resource --- crates/bevy_winit/src/lib.rs | 3 +-- examples/app/return_after_run.rs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index a833ecf44b57f..0668d43664102 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -231,8 +231,7 @@ pub fn winit_runner_with(mut app: App) { trace!("Entering winit event loop"); let should_return_from_run = app - .world - .get_resource::() + .consume_initialization_resource::() .map_or(false, |config| config.return_from_run); let mut active = true; diff --git a/examples/app/return_after_run.rs b/examples/app/return_after_run.rs index ff73928a11b4a..10930accf547b 100644 --- a/examples/app/return_after_run.rs +++ b/examples/app/return_after_run.rs @@ -3,7 +3,7 @@ use bevy::{prelude::*, winit::WinitConfig}; fn main() { println!("Running first App."); App::new() - .insert_resource(WinitConfig { + .insert_initialization_resource(WinitConfig { return_from_run: true, }) .insert_resource(ClearColor(Color::rgb(0.2, 0.2, 0.8))) @@ -12,7 +12,7 @@ fn main() { .run(); println!("Running another App."); App::new() - .insert_resource(WinitConfig { + .insert_initialization_resource(WinitConfig { return_from_run: true, }) .insert_resource(ClearColor(Color::rgb(0.2, 0.8, 0.2))) From 04a2ea336da33dff0c53a2ca03fa272c2f4f76a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Mon, 24 Jan 2022 03:15:30 +0100 Subject: [PATCH 22/26] DefaultTaskPoolOptions as an initialization resource --- crates/bevy_core/src/lib.rs | 4 +--- examples/app/thread_pool_resources.rs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index f943b281426cf..752a612fe5576 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -42,9 +42,7 @@ pub enum CoreSystem { impl Plugin for CorePlugin { fn build(&self, app: &mut App) { // Setup the default bevy task pools - app.world - .get_resource::() - .cloned() + app.consume_initialization_resource::() .unwrap_or_default() .create_default_pools(&mut app.world); diff --git a/examples/app/thread_pool_resources.rs b/examples/app/thread_pool_resources.rs index b53273b584ac3..6920a5bc45f27 100644 --- a/examples/app/thread_pool_resources.rs +++ b/examples/app/thread_pool_resources.rs @@ -4,7 +4,7 @@ use bevy::prelude::*; /// certain number of threads). fn main() { App::new() - .insert_resource(DefaultTaskPoolOptions::with_num_threads(4)) + .insert_initialization_resource(DefaultTaskPoolOptions::with_num_threads(4)) .add_plugins(DefaultPlugins) .run(); } From a4da44a3758bfe15446472e70c2970654d0e2e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Mon, 24 Jan 2022 23:54:18 +0100 Subject: [PATCH 23/26] initialization ro setup --- crates/bevy_app/src/app.rs | 47 +++++++++---------- crates/bevy_asset/src/lib.rs | 2 +- crates/bevy_core/src/lib.rs | 2 +- crates/bevy_log/src/lib.rs | 6 +-- crates/bevy_render/src/lib.rs | 2 +- crates/bevy_window/src/event.rs | 4 +- crates/bevy_window/src/lib.rs | 4 +- crates/bevy_window/src/window.rs | 8 ++-- crates/bevy_winit/src/lib.rs | 2 +- crates/bevy_winit/src/winit_windows.rs | 6 +-- examples/3d/wireframe.rs | 2 +- examples/app/headless_defaults.rs | 2 +- examples/app/logs.rs | 4 +- examples/app/return_after_run.rs | 4 +- examples/app/thread_pool_resources.rs | 2 +- examples/ios/src/lib.rs | 2 +- .../shader/compute_shader_game_of_life.rs | 4 +- examples/tools/bevymark.rs | 2 +- examples/ui/text_debug.rs | 2 +- examples/window/multiple_windows.rs | 2 +- examples/window/scale_factor_override.rs | 2 +- examples/window/transparent_window.rs | 4 +- examples/window/window_settings.rs | 2 +- 23 files changed, 58 insertions(+), 59 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 4075c69359dfa..c9f3dcdbe0d69 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -19,8 +19,8 @@ use std::{any::TypeId, fmt::Debug}; use bevy_utils::tracing::info_span; bevy_utils::define_label!(AppLabel); -/// Wrapper struct for setting initialization resources aside -struct Init(T); +/// Wrapper struct for setting setup resources aside +struct Setup(T); #[allow(clippy::needless_doctest_main)] /// Containers of app logic and data @@ -62,7 +62,7 @@ pub struct App { pub schedule: Schedule, sub_apps: HashMap, SubApp>, plugins: HashMap>, - initialization_resources: HashMap, + setup_resources: HashMap, } /// Each [`SubApp`] has its own [`Schedule`] and [`World`], enabling a separation of concerns. @@ -107,7 +107,7 @@ impl App { runner: Box::new(run_once), sub_apps: HashMap::default(), plugins: Default::default(), - initialization_resources: Default::default(), + setup_resources: Default::default(), } } @@ -137,7 +137,7 @@ impl App { #[cfg(feature = "trace")] let _bevy_app_run_guard = bevy_app_run_span.enter(); - self.check_all_initialization_resources_consumed(); + self.check_all_setup_resources_consumed(); let mut app = std::mem::replace(self, App::empty()); let runner = std::mem::replace(&mut app.runner, Box::new(run_once)); @@ -631,42 +631,41 @@ impl App { .add_system_to_stage(CoreStage::First, Events::::update_system) } - /// Inserts an initialization resource to the current [App] and overwrites any resource + /// Inserts a setup resource to the current [App] and overwrites any resource /// previously added of the same type. /// - /// An initialization resource is used at startup for plugin initialisation and configuration. - /// All initialization resources inserted must be consumed by a plugin and removed before the + /// A setup resource is used at startup for plugin initialisation and configuration. + /// All setup resources inserted must be consumed by a plugin and removed before the /// application is ran. - pub fn insert_initialization_resource(&mut self, resource: T) -> &mut Self + pub fn insert_setup_resource(&mut self, resource: T) -> &mut Self where T: Resource, { - self.initialization_resources + self.setup_resources .insert(std::any::TypeId::of::(), std::any::type_name::()); - self.insert_resource(Init(resource)); + self.insert_resource(Setup(resource)); self } - /// Consumes an initialization resource, and removes it from the current [App] so that a plugin - /// can use it for its configuration. - pub fn consume_initialization_resource(&mut self) -> Option + /// Consumes a setup resource, and removes it from the current [App] so that a plugin + /// can use it for its setup. + pub fn consume_setup_resource(&mut self) -> Option where T: Resource, { - self.initialization_resources - .remove(&std::any::TypeId::of::()); + self.setup_resources.remove(&std::any::TypeId::of::()); self.world - .remove_resource::>() - .map(|initialization| initialization.0) + .remove_resource::>() + .map(|setup| setup.0) } - /// Check that all initialization resources have been consumed, panicking otherwise. - fn check_all_initialization_resources_consumed(&self) { - self.initialization_resources + /// Check that all setup resources have been consumed, panicking otherwise. + fn check_all_setup_resources_consumed(&self) { + self.setup_resources .values() - .for_each(|v| error!("Initialization resource \"{}\" has not been consumed", v)); - if !self.initialization_resources.is_empty() { - panic!("Not all initialization resources have been consumed. This can happen if you inserted an initialization resource after the plugin consuming it.") + .for_each(|v| error!("Setup resource \"{}\" has not been consumed", v)); + if !self.setup_resources.is_empty() { + panic!("Not all setup resources have been consumed. This can happen if you inserted a setup resource after the plugin consuming it.") } } diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 78e10179e9424..07f0f5cd94a3f 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -60,7 +60,7 @@ impl Default for AssetServerSettings { /// delegate to the default `AssetIo` for the platform. pub fn create_platform_default_asset_io(app: &mut App) -> Box { let settings = app - .consume_initialization_resource::() + .consume_setup_resource::() .unwrap_or_default(); #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))] diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 752a612fe5576..96ce5c0169161 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -42,7 +42,7 @@ pub enum CoreSystem { impl Plugin for CorePlugin { fn build(&self, app: &mut App) { // Setup the default bevy task pools - app.consume_initialization_resource::() + app.consume_setup_resource::() .unwrap_or_default() .create_default_pools(&mut app.world); diff --git a/crates/bevy_log/src/lib.rs b/crates/bevy_log/src/lib.rs index 8483fb931abfb..c27297a24cd7b 100644 --- a/crates/bevy_log/src/lib.rs +++ b/crates/bevy_log/src/lib.rs @@ -41,7 +41,7 @@ use tracing_subscriber::{prelude::*, registry::Registry, EnvFilter}; /// * Using [`tracing-wasm`](https://crates.io/crates/tracing-wasm) in WASM, logging /// to the browser console. /// -/// You can configure this plugin using the initialization resource [`LogSettings`]. +/// You can configure this plugin using the setup resource [`LogSettings`]. /// ```no_run /// # use bevy_internal::DefaultPlugins; /// # use bevy_app::App; @@ -49,7 +49,7 @@ use tracing_subscriber::{prelude::*, registry::Registry, EnvFilter}; /// # use bevy_utils::tracing::Level; /// fn main() { /// App::new() -/// .insert_initialization_resource(LogSettings { +/// .insert_setup_resource(LogSettings { /// level: Level::DEBUG, /// filter: "wgpu=error,bevy_render=info".to_string(), /// }) @@ -107,7 +107,7 @@ impl Plugin for LogPlugin { fn build(&self, app: &mut App) { let default_filter = { let settings = app - .consume_initialization_resource::() + .consume_setup_resource::() .unwrap_or_default(); format!("{},{}", settings.level, settings.filter) }; diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 954cddedabfb9..fe2a9424553e2 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -110,7 +110,7 @@ impl Plugin for RenderPlugin { /// Initializes the renderer, sets up the [`RenderStage`](RenderStage) and creates the rendering sub-app. fn build(&self, app: &mut App) { let mut options = app - .consume_initialization_resource::() + .consume_setup_resource::() .unwrap_or_default(); app.add_asset::() diff --git a/crates/bevy_window/src/event.rs b/crates/bevy_window/src/event.rs index ee017d11d3fd7..8f20b62f44be8 100644 --- a/crates/bevy_window/src/event.rs +++ b/crates/bevy_window/src/event.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use super::{WindowId, WindowInitializationDescriptor}; +use super::{WindowId, WindowSetupDescriptor}; use bevy_math::{IVec2, Vec2}; /// A window event that is sent whenever a windows logical size has changed @@ -17,7 +17,7 @@ pub struct WindowResized { #[derive(Debug, Clone)] pub struct CreateWindow { pub id: WindowId, - pub descriptor: WindowInitializationDescriptor, + pub descriptor: WindowSetupDescriptor, } /// An event that indicates a window should be closed. diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index cf50f6ebb5ee3..208b17d4a1a99 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -16,7 +16,7 @@ pub mod prelude { #[doc(hidden)] pub use crate::{ CursorEntered, CursorIcon, CursorLeft, CursorMoved, FileDragAndDrop, ReceivedCharacter, - Window, WindowInitializationDescriptor, WindowMoved, Windows, + Window, WindowMoved, WindowSetupDescriptor, Windows, }; } @@ -56,7 +56,7 @@ impl Plugin for WindowPlugin { if self.add_primary_window { let window_descriptor = app - .consume_initialization_resource::() + .consume_setup_resource::() .unwrap_or_default(); let mut create_window_event = app .world diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 8c26a003968d0..6a5353714c920 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -207,7 +207,7 @@ pub enum WindowMode { impl Window { pub fn new( id: WindowId, - window_descriptor: &WindowInitializationDescriptor, + window_descriptor: &WindowSetupDescriptor, physical_width: u32, physical_height: u32, scale_factor: f64, @@ -553,7 +553,7 @@ impl Window { } #[derive(Debug, Clone)] -pub struct WindowInitializationDescriptor { +pub struct WindowSetupDescriptor { pub width: f32, pub height: f32, pub position: Option, @@ -578,9 +578,9 @@ pub struct WindowInitializationDescriptor { pub canvas: Option, } -impl Default for WindowInitializationDescriptor { +impl Default for WindowSetupDescriptor { fn default() -> Self { - WindowInitializationDescriptor { + WindowSetupDescriptor { title: "app".to_string(), width: 1280., height: 720., diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 0668d43664102..05390232138d8 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -231,7 +231,7 @@ pub fn winit_runner_with(mut app: App) { trace!("Entering winit event loop"); let should_return_from_run = app - .consume_initialization_resource::() + .consume_setup_resource::() .map_or(false, |config| config.return_from_run); let mut active = true; diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index eedc6a962a9fe..61bf6b5a64c66 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -1,6 +1,6 @@ use bevy_math::IVec2; use bevy_utils::HashMap; -use bevy_window::{Window, WindowId, WindowInitializationDescriptor, WindowMode}; +use bevy_window::{Window, WindowId, WindowMode, WindowSetupDescriptor}; use raw_window_handle::HasRawWindowHandle; use winit::dpi::LogicalSize; @@ -16,7 +16,7 @@ impl WinitWindows { &mut self, event_loop: &winit::event_loop::EventLoopWindowTarget<()>, window_id: WindowId, - window_descriptor: &WindowInitializationDescriptor, + window_descriptor: &WindowSetupDescriptor, ) -> Window { #[cfg(target_os = "windows")] let mut winit_window_builder = { @@ -44,7 +44,7 @@ impl WinitWindows { )), )), _ => { - let WindowInitializationDescriptor { + let WindowSetupDescriptor { width, height, position, diff --git a/examples/3d/wireframe.rs b/examples/3d/wireframe.rs index 4d446b4741cae..69edbfdc72d7e 100644 --- a/examples/3d/wireframe.rs +++ b/examples/3d/wireframe.rs @@ -7,7 +7,7 @@ use bevy::{ fn main() { App::new() .insert_resource(Msaa { samples: 4 }) - .insert_initialization_resource(WgpuOptions { + .insert_setup_resource(WgpuOptions { features: WgpuFeatures::POLYGON_MODE_LINE, ..Default::default() }) diff --git a/examples/app/headless_defaults.rs b/examples/app/headless_defaults.rs index cf14f967e8f62..bfe091715779c 100644 --- a/examples/app/headless_defaults.rs +++ b/examples/app/headless_defaults.rs @@ -2,7 +2,7 @@ use bevy::{prelude::*, render::options::WgpuOptions}; fn main() { App::new() - .insert_initialization_resource(WgpuOptions { + .insert_setup_resource(WgpuOptions { backends: None, ..Default::default() }) diff --git a/examples/app/logs.rs b/examples/app/logs.rs index 40cc463fb293c..ce8537ebb8eb1 100644 --- a/examples/app/logs.rs +++ b/examples/app/logs.rs @@ -4,7 +4,7 @@ use bevy::prelude::*; fn main() { App::new() // Uncomment this to override the default log settings: - // .insert_initialization_resource(bevy::log::LogSettings { + // .insert_setup_resource(bevy::log::LogSettings { // level: bevy::log::Level::TRACE, // filter: "wgpu=warn,bevy_ecs=info".to_string(), // }) @@ -23,7 +23,7 @@ fn log_system() { error!("something failed"); // by default, trace and debug logs are ignored because they are "noisy" - // you can control what level is logged by adding the LogSettings initialization resource + // you can control what level is logged by adding the LogSettings setup resource // alternatively you can set the log level via the RUST_LOG=LEVEL environment variable // ex: RUST_LOG=trace, RUST_LOG=info,bevy_ecs=warn // the format used here is super flexible. check out this documentation for more info: diff --git a/examples/app/return_after_run.rs b/examples/app/return_after_run.rs index 10930accf547b..932be5a98b277 100644 --- a/examples/app/return_after_run.rs +++ b/examples/app/return_after_run.rs @@ -3,7 +3,7 @@ use bevy::{prelude::*, winit::WinitConfig}; fn main() { println!("Running first App."); App::new() - .insert_initialization_resource(WinitConfig { + .insert_setup_resource(WinitConfig { return_from_run: true, }) .insert_resource(ClearColor(Color::rgb(0.2, 0.2, 0.8))) @@ -12,7 +12,7 @@ fn main() { .run(); println!("Running another App."); App::new() - .insert_initialization_resource(WinitConfig { + .insert_setup_resource(WinitConfig { return_from_run: true, }) .insert_resource(ClearColor(Color::rgb(0.2, 0.8, 0.2))) diff --git a/examples/app/thread_pool_resources.rs b/examples/app/thread_pool_resources.rs index 6920a5bc45f27..ddfb2a25bb16c 100644 --- a/examples/app/thread_pool_resources.rs +++ b/examples/app/thread_pool_resources.rs @@ -4,7 +4,7 @@ use bevy::prelude::*; /// certain number of threads). fn main() { App::new() - .insert_initialization_resource(DefaultTaskPoolOptions::with_num_threads(4)) + .insert_setup_resource(DefaultTaskPoolOptions::with_num_threads(4)) .add_plugins(DefaultPlugins) .run(); } diff --git a/examples/ios/src/lib.rs b/examples/ios/src/lib.rs index b474d1e550859..93931bea75f6a 100644 --- a/examples/ios/src/lib.rs +++ b/examples/ios/src/lib.rs @@ -4,7 +4,7 @@ use bevy::{input::touch::TouchPhase, prelude::*, window::WindowMode}; #[bevy_main] fn main() { App::new() - .insert_initialization_resource(WindowInitializationDescriptor { + .insert_setup_resource(WindowSetupDescriptor { resizable: false, mode: WindowMode::BorderlessFullscreen, ..Default::default() diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index ea1ac0581b808..458b0ff6c47a8 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -8,7 +8,7 @@ use bevy::{ renderer::{RenderContext, RenderDevice}, RenderApp, RenderStage, }, - window::WindowInitializationDescriptor, + window::WindowSetupDescriptor, }; const SIZE: (u32, u32) = (1280, 720); @@ -17,7 +17,7 @@ const WORKGROUP_SIZE: u32 = 8; fn main() { App::new() .insert_resource(ClearColor(Color::BLACK)) - .insert_initialization_resource(WindowInitializationDescriptor { + .insert_setup_resource(WindowSetupDescriptor { // uncomment for unthrottled FPS // vsync: false, ..Default::default() diff --git a/examples/tools/bevymark.rs b/examples/tools/bevymark.rs index 6d9210fe3d3fe..66b0e068a1a2a 100644 --- a/examples/tools/bevymark.rs +++ b/examples/tools/bevymark.rs @@ -24,7 +24,7 @@ struct Bird { fn main() { App::new() - .insert_initialization_resource(WindowInitializationDescriptor { + .insert_setup_resource(WindowSetupDescriptor { title: "BevyMark".to_string(), width: 800., height: 600., diff --git a/examples/ui/text_debug.rs b/examples/ui/text_debug.rs index 3bb30188c793b..799b9bb9a2219 100644 --- a/examples/ui/text_debug.rs +++ b/examples/ui/text_debug.rs @@ -6,7 +6,7 @@ use bevy::{ /// This example is for debugging text layout fn main() { App::new() - .insert_initialization_resource(WindowInitializationDescriptor { + .insert_setup_resource(WindowSetupDescriptor { present_mode: PresentMode::Immediate, ..Default::default() }) diff --git a/examples/window/multiple_windows.rs b/examples/window/multiple_windows.rs index 267cedc369552..925ee6969a3dc 100644 --- a/examples/window/multiple_windows.rs +++ b/examples/window/multiple_windows.rs @@ -54,7 +54,7 @@ fn create_new_window( // sends out a "CreateWindow" event, which will be received by the windowing backend create_window_events.send(CreateWindow { id: window_id, - descriptor: WindowInitializationDescriptor { + descriptor: WindowSetupDescriptor { width: 800., height: 600., vsync: false, diff --git a/examples/window/scale_factor_override.rs b/examples/window/scale_factor_override.rs index e3eaa1ee79246..a5f32c00935d2 100644 --- a/examples/window/scale_factor_override.rs +++ b/examples/window/scale_factor_override.rs @@ -3,7 +3,7 @@ use bevy::prelude::*; /// This example illustrates how to customize the default window settings fn main() { App::new() - .insert_initialization_resource(WindowInitializationDescriptor { + .insert_setup_resource(WindowSetupDescriptor { width: 500., height: 300., ..Default::default() diff --git a/examples/window/transparent_window.rs b/examples/window/transparent_window.rs index 5a93cc68e8e96..e24a6aa147f4c 100644 --- a/examples/window/transparent_window.rs +++ b/examples/window/transparent_window.rs @@ -1,12 +1,12 @@ /// An example of how to display a window in transparent mode /// [Documentation & Platform support.](https://docs.rs/bevy/latest/bevy/prelude/struct.WindowDescriptor.html#structfield.transparent) -use bevy::{prelude::*, window::WindowInitializationDescriptor}; +use bevy::{prelude::*, window::WindowSetupDescriptor}; fn main() { App::new() // ClearColor must have 0 alpha, otherwise some color will bleed through .insert_resource(ClearColor(Color::NONE)) - .insert_initialization_resource(WindowInitializationDescriptor { + .insert_setup_resource(WindowSetupDescriptor { // Setting `transparent` allows the `ClearColor`'s alpha value to take effect transparent: true, // Disabling window decorations to make it feel more like a widget than a window diff --git a/examples/window/window_settings.rs b/examples/window/window_settings.rs index 2266d9772aa2c..91bab346a319a 100644 --- a/examples/window/window_settings.rs +++ b/examples/window/window_settings.rs @@ -3,7 +3,7 @@ use bevy::prelude::*; /// This example illustrates how to customize the default window settings fn main() { App::new() - .insert_initialization_resource(WindowInitializationDescriptor { + .insert_setup_resource(WindowSetupDescriptor { title: "I am a window!".to_string(), width: 500., height: 300., From 9863dafa880e5ea787299bcd19774a655fd58445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Fri, 4 Feb 2022 18:27:00 +0100 Subject: [PATCH 24/26] remove duplicate import --- crates/bevy_app/src/app.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index c9f3dcdbe0d69..a831b36abc46b 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -382,6 +382,9 @@ impl App { stage_label: impl StageLabel, system: impl IntoSystemDescriptor, ) -> &mut Self { + if stage_label.type_id() == TypeId::of::() { + panic!("add systems to a startup stage using App::add_startup_system_to_stage"); + } self.schedule.add_system_to_stage(stage_label, system); self } @@ -412,6 +415,9 @@ impl App { stage_label: impl StageLabel, system_set: SystemSet, ) -> &mut Self { + if stage_label.type_id() == TypeId::of::() { + panic!("add system sets to a startup stage using App::add_startup_system_set_to_stage"); + } self.schedule .add_system_set_to_stage(stage_label, system_set); self From 6cec29d06390a58f8c3fc4ca7bf2ac9695b7faec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Fri, 4 Feb 2022 19:11:04 +0100 Subject: [PATCH 25/26] update doc --- crates/bevy_window/src/window.rs | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 6a5353714c920..2aa4ac9c9ca6b 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -5,6 +5,40 @@ use raw_window_handle::RawWindowHandle; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct WindowId(Uuid); +/// Presentation mode for a window. +/// +/// The presentation mode specifies when a frame is presented to the window. The `Fifo` +/// option corresponds to a traditional `VSync`, where the framerate is capped by the +/// display refresh rate. Both `Immediate` and `Mailbox` are low-latency and are not +/// capped by the refresh rate, but may not be available on all platforms. Tearing +/// may be observed with `Immediate` mode, but will not be observed with `Mailbox` or +/// `Fifo`. +/// +/// `Immediate` or `Mailbox` will gracefully fallback to `Fifo` when unavailable. +/// +/// The presentation mode may be declared in the +/// [`WindowSetupDescriptor`](WindowSetupDescriptor::present_mode) or updated on a +/// [`Window`](Window::set_present_mode). +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[doc(alias = "vsync")] +pub enum PresentMode { + /// The presentation engine does **not** wait for a vertical blanking period and + /// the request is presented immediately. This is a low-latency presentation mode, + /// but visible tearing may be observed. Will fallback to `Fifo` if unavailable on the + /// selected platform and backend. Not optimal for mobile. + Immediate = 0, + /// The presentation engine waits for the next vertical blanking period to update + /// the current image, but frames may be submitted without delay. This is a low-latency + /// presentation mode and visible tearing will **not** be observed. Will fallback to `Fifo` + /// if unavailable on the selected platform and backend. Not optimal for mobile. + Mailbox = 1, + /// The presentation engine waits for the next vertical blanking period to update + /// the current image. The framerate will be capped at the display refresh rate, + /// corresponding to the `VSync`. Tearing cannot be observed. Optimal for mobile. + Fifo = 2, // NOTE: The explicit ordinal values mirror wgpu and the vulkan spec. +} + impl WindowId { pub fn new() -> Self { WindowId(Uuid::new_v4()) From 03f63a648f84b3eebe6b8ee40a87da8ebe687d59 Mon Sep 17 00:00:00 2001 From: Kirill Kirillov Date: Sat, 19 Feb 2022 02:04:52 +0500 Subject: [PATCH 26/26] Make UIPlugin work without rendering Achieved by making loading of some functions conditional: if RenderPlugin is absent, they are not added. --- crates/bevy_ui/src/lib.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 2f89f6df9ca85..cd2072a111e34 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -18,6 +18,8 @@ pub use margins::*; pub use render::*; pub use ui_node::*; +use bevy_render::RenderPlugin; + #[doc(hidden)] pub mod prelude { #[doc(hidden)] @@ -76,15 +78,7 @@ impl Plugin for UiPlugin { CoreStage::PreUpdate, ui_focus_system.label(UiSystem::Focus).after(InputSystem), ) - // add these stages to front because these must run before transform update systems - .add_system_to_stage( - CoreStage::PostUpdate, - widget::text_system.before(UiSystem::Flex), - ) - .add_system_to_stage( - CoreStage::PostUpdate, - widget::image_node_system.before(UiSystem::Flex), - ) + // Add these stages to front because these must run before transform update systems .add_system_to_stage( CoreStage::PostUpdate, flex_node_system @@ -102,6 +96,20 @@ impl Plugin for UiPlugin { update_clipping_system.after(TransformSystem::TransformPropagate), ); - crate::render::build_ui_render(app); + // Allows for UIPlugin to be tested without a renderer + // (text_system and image_node_system depend on textures, which are only + // available with RenderPlugin) + if app.is_plugin_added::() { + app.add_system_to_stage( + CoreStage::PostUpdate, + widget::text_system.before(UiSystem::Flex), + ) + .add_system_to_stage( + CoreStage::PostUpdate, + widget::image_node_system.before(UiSystem::Flex), + ); + + crate::render::build_ui_render(app); + } } }