diff --git a/crates/bevy_audio/src/audio_output.rs b/crates/bevy_audio/src/audio_output.rs index 385b7d5fdc580..c118147127ca9 100644 --- a/crates/bevy_audio/src/audio_output.rs +++ b/crates/bevy_audio/src/audio_output.rs @@ -1,6 +1,6 @@ use crate::{Audio, AudioSource, Decodable}; use bevy_asset::{Asset, Assets}; -use bevy_ecs::world::World; +use bevy_ecs::system::{NonSend, Res, ResMut}; use bevy_utils::tracing::warn; use rodio::{OutputStream, OutputStreamHandle, Sink}; use std::marker::PhantomData; @@ -67,15 +67,14 @@ where } /// Plays audio currently queued in the [`Audio`] resource through the [`AudioOutput`] resource -pub fn play_queued_audio_system(world: &mut World) -where +pub fn play_queued_audio_system( + audio_output: NonSend>, + mut audio: ResMut>, + audio_assets: Option>>, +) where Source: Decodable, { - let world = world.cell(); - let audio_output = world.get_non_send::>().unwrap(); - let mut audio = world.get_resource_mut::>().unwrap(); - - if let Some(audio_sources) = world.get_resource::>() { + if let Some(audio_sources) = audio_assets { audio_output.try_play_queued(&*audio_sources, &mut *audio); }; } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index ad037819e89f7..5082122244d62 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1,11 +1,9 @@ mod entity_ref; mod spawn_batch; -mod world_cell; pub use crate::change_detection::Mut; pub use entity_ref::*; pub use spawn_batch::*; -pub use world_cell::*; use crate::{ archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes}, @@ -77,8 +75,6 @@ pub struct World { pub(crate) storages: Storages, pub(crate) bundles: Bundles, pub(crate) removed_components: SparseSet>, - /// Access cache used by [WorldCell]. - pub(crate) archetype_component_access: ArchetypeComponentAccess, main_thread_validator: MainThreadValidator, pub(crate) change_tick: AtomicU32, pub(crate) last_change_tick: u32, @@ -94,7 +90,6 @@ impl Default for World { storages: Default::default(), bundles: Default::default(), removed_components: Default::default(), - archetype_component_access: Default::default(), main_thread_validator: Default::default(), // Default value is `1`, and `last_change_tick`s default to `0`, such that changes // are detected on first system runs and for direct world queries. @@ -164,13 +159,6 @@ impl World { &self.bundles } - /// Retrieves a [`WorldCell`], which safely enables multiple mutable World accesses at the same - /// time, provided those accesses do not conflict with each other. - #[inline] - pub fn cell(&mut self) -> WorldCell<'_> { - WorldCell::new(self) - } - pub fn init_component(&mut self) -> ComponentId { self.components.init_component::(&mut self.storages) } diff --git a/crates/bevy_ecs/src/world/world_cell.rs b/crates/bevy_ecs/src/world/world_cell.rs deleted file mode 100644 index af7858d256b74..0000000000000 --- a/crates/bevy_ecs/src/world/world_cell.rs +++ /dev/null @@ -1,351 +0,0 @@ -use crate::{ - archetype::ArchetypeComponentId, - storage::SparseSet, - system::Resource, - world::{Mut, World}, -}; -use std::{ - any::TypeId, - cell::RefCell, - ops::{Deref, DerefMut}, - rc::Rc, -}; - -/// Exposes safe mutable access to multiple resources at a time in a World. Attempting to access -/// World in a way that violates Rust's mutability rules will panic thanks to runtime checks. -pub struct WorldCell<'w> { - pub(crate) world: &'w mut World, - pub(crate) access: Rc>, -} - -pub(crate) struct ArchetypeComponentAccess { - access: SparseSet, -} - -impl Default for ArchetypeComponentAccess { - fn default() -> Self { - Self { - access: SparseSet::new(), - } - } -} - -const UNIQUE_ACCESS: usize = 0; -const BASE_ACCESS: usize = 1; -impl ArchetypeComponentAccess { - const fn new() -> Self { - Self { - access: SparseSet::new(), - } - } - - fn read(&mut self, id: ArchetypeComponentId) -> bool { - let id_access = self.access.get_or_insert_with(id, || BASE_ACCESS); - if *id_access == UNIQUE_ACCESS { - false - } else { - *id_access += 1; - true - } - } - - fn drop_read(&mut self, id: ArchetypeComponentId) { - let id_access = self.access.get_or_insert_with(id, || BASE_ACCESS); - *id_access -= 1; - } - - fn write(&mut self, id: ArchetypeComponentId) -> bool { - let id_access = self.access.get_or_insert_with(id, || BASE_ACCESS); - if *id_access == BASE_ACCESS { - *id_access = UNIQUE_ACCESS; - true - } else { - false - } - } - - fn drop_write(&mut self, id: ArchetypeComponentId) { - let id_access = self.access.get_or_insert_with(id, || BASE_ACCESS); - *id_access = BASE_ACCESS; - } -} - -impl<'w> Drop for WorldCell<'w> { - fn drop(&mut self) { - let mut access = self.access.borrow_mut(); - // give world ArchetypeComponentAccess back to reuse allocations - let _ = std::mem::swap(&mut self.world.archetype_component_access, &mut *access); - } -} - -pub struct WorldBorrow<'w, T> { - value: &'w T, - archetype_component_id: ArchetypeComponentId, - access: Rc>, -} - -impl<'w, T> WorldBorrow<'w, T> { - fn new( - value: &'w T, - archetype_component_id: ArchetypeComponentId, - access: Rc>, - ) -> Self { - assert!( - access.borrow_mut().read(archetype_component_id), - "Attempted to immutably access {}, but it is already mutably borrowed", - std::any::type_name::(), - ); - Self { - value, - archetype_component_id, - access, - } - } -} - -impl<'w, T> Deref for WorldBorrow<'w, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &Self::Target { - self.value - } -} - -impl<'w, T> Drop for WorldBorrow<'w, T> { - fn drop(&mut self) { - let mut access = self.access.borrow_mut(); - access.drop_read(self.archetype_component_id); - } -} - -pub struct WorldBorrowMut<'w, T> { - value: Mut<'w, T>, - archetype_component_id: ArchetypeComponentId, - access: Rc>, -} - -impl<'w, T> WorldBorrowMut<'w, T> { - fn new( - value: Mut<'w, T>, - archetype_component_id: ArchetypeComponentId, - access: Rc>, - ) -> Self { - assert!( - access.borrow_mut().write(archetype_component_id), - "Attempted to mutably access {}, but it is already mutably borrowed", - std::any::type_name::(), - ); - Self { - value, - archetype_component_id, - access, - } - } -} - -impl<'w, T> Deref for WorldBorrowMut<'w, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &Self::Target { - self.value.deref() - } -} - -impl<'w, T> DerefMut for WorldBorrowMut<'w, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut *self.value - } -} - -impl<'w, T> Drop for WorldBorrowMut<'w, T> { - fn drop(&mut self) { - let mut access = self.access.borrow_mut(); - access.drop_write(self.archetype_component_id); - } -} - -impl<'w> WorldCell<'w> { - pub(crate) fn new(world: &'w mut World) -> Self { - // this is cheap because ArchetypeComponentAccess::new() is const / allocation free - let access = std::mem::replace( - &mut world.archetype_component_access, - ArchetypeComponentAccess::new(), - ); - // world's ArchetypeComponentAccess is recycled to cut down on allocations - Self { - world, - access: Rc::new(RefCell::new(access)), - } - } - - pub fn get_resource(&self) -> Option> { - let component_id = self.world.components.get_resource_id(TypeId::of::())?; - let resource_archetype = self.world.archetypes.resource(); - let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?; - Some(WorldBorrow::new( - // SAFE: ComponentId matches TypeId - unsafe { self.world.get_resource_with_id(component_id)? }, - archetype_component_id, - self.access.clone(), - )) - } - - pub fn get_resource_mut(&self) -> Option> { - let component_id = self.world.components.get_resource_id(TypeId::of::())?; - let resource_archetype = self.world.archetypes.resource(); - let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?; - Some(WorldBorrowMut::new( - // SAFE: ComponentId matches TypeId and access is checked by WorldBorrowMut - unsafe { - self.world - .get_resource_unchecked_mut_with_id(component_id)? - }, - archetype_component_id, - self.access.clone(), - )) - } - - pub fn get_non_send(&self) -> Option> { - let component_id = self.world.components.get_resource_id(TypeId::of::())?; - let resource_archetype = self.world.archetypes.resource(); - let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?; - Some(WorldBorrow::new( - // SAFE: ComponentId matches TypeId - unsafe { self.world.get_non_send_with_id(component_id)? }, - archetype_component_id, - self.access.clone(), - )) - } - - pub fn get_non_send_mut(&self) -> Option> { - let component_id = self.world.components.get_resource_id(TypeId::of::())?; - let resource_archetype = self.world.archetypes.resource(); - let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?; - Some(WorldBorrowMut::new( - // SAFE: ComponentId matches TypeId and access is checked by WorldBorrowMut - unsafe { - self.world - .get_non_send_unchecked_mut_with_id(component_id)? - }, - archetype_component_id, - self.access.clone(), - )) - } -} - -#[cfg(test)] -mod tests { - use super::BASE_ACCESS; - use crate::{archetype::ArchetypeId, world::World}; - use std::any::TypeId; - - #[test] - fn world_cell() { - let mut world = World::default(); - world.insert_resource(1u32); - world.insert_resource(1u64); - let cell = world.cell(); - { - let mut a = cell.get_resource_mut::().unwrap(); - assert_eq!(1, *a); - *a = 2; - } - { - let a = cell.get_resource::().unwrap(); - assert_eq!(2, *a, "ensure access is dropped"); - - let b = cell.get_resource::().unwrap(); - assert_eq!( - 2, *b, - "ensure multiple immutable accesses can occur at the same time" - ); - } - { - let a = cell.get_resource_mut::().unwrap(); - assert_eq!( - 2, *a, - "ensure both immutable accesses are dropped, enabling a new mutable access" - ); - - let b = cell.get_resource::().unwrap(); - assert_eq!( - 1, *b, - "ensure multiple non-conflicting mutable accesses can occur at the same time" - ); - } - } - - #[test] - fn world_access_reused() { - let mut world = World::default(); - world.insert_resource(1u32); - { - let cell = world.cell(); - { - let mut a = cell.get_resource_mut::().unwrap(); - assert_eq!(1, *a); - *a = 2; - } - } - - let u32_component_id = world - .components - .get_resource_id(TypeId::of::()) - .unwrap(); - let resource_archetype = world.archetypes.get(ArchetypeId::RESOURCE).unwrap(); - let u32_archetype_component_id = resource_archetype - .get_archetype_component_id(u32_component_id) - .unwrap(); - assert_eq!(world.archetype_component_access.access.len(), 1); - assert_eq!( - world - .archetype_component_access - .access - .get(u32_archetype_component_id), - Some(&BASE_ACCESS), - "reused access count is 'base'" - ); - } - - #[test] - #[should_panic] - fn world_cell_double_mut() { - let mut world = World::default(); - world.insert_resource(1u32); - let cell = world.cell(); - let _value_a = cell.get_resource_mut::().unwrap(); - let _value_b = cell.get_resource_mut::().unwrap(); - } - - #[test] - #[should_panic] - fn world_cell_ref_and_mut() { - let mut world = World::default(); - world.insert_resource(1u32); - let cell = world.cell(); - let _value_a = cell.get_resource::().unwrap(); - let _value_b = cell.get_resource_mut::().unwrap(); - } - - #[test] - #[should_panic] - fn world_cell_mut_and_ref() { - let mut world = World::default(); - world.insert_resource(1u32); - let cell = world.cell(); - let _value_a = cell.get_resource_mut::().unwrap(); - let _value_b = cell.get_resource::().unwrap(); - } - - #[test] - #[should_panic] - fn world_cell_ref_and_ref() { - let mut world = World::default(); - world.insert_resource(1u32); - let cell = world.cell(); - let _value_a = cell.get_resource_mut::().unwrap(); - let _value_b = cell.get_resource::().unwrap(); - } -} diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 4d2b05b47a622..724e7c9aa4dd2 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -162,52 +162,53 @@ pub struct ShadowPipeline { // TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system impl FromWorld for ShadowPipeline { fn from_world(world: &mut World) -> Self { - let world = world.cell(); - let render_device = world.get_resource::().unwrap(); - - let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - entries: &[ - // View - BindGroupLayoutEntry { - binding: 0, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: BufferSize::new(ViewUniform::std140_size_static() as u64), + world.resource_scope(|world: &mut World, render_device: Mut| { + let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { + entries: &[ + // View + BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: BufferSize::new( + ViewUniform::std140_size_static() as u64 + ), + }, + count: None, }, - count: None, - }, - ], - label: Some("shadow_view_layout"), - }); - - let mesh_pipeline = world.get_resource::().unwrap(); - - ShadowPipeline { - view_layout, - mesh_layout: mesh_pipeline.mesh_layout.clone(), - point_light_sampler: render_device.create_sampler(&SamplerDescriptor { - address_mode_u: AddressMode::ClampToEdge, - address_mode_v: AddressMode::ClampToEdge, - address_mode_w: AddressMode::ClampToEdge, - mag_filter: FilterMode::Linear, - min_filter: FilterMode::Linear, - mipmap_filter: FilterMode::Nearest, - compare: Some(CompareFunction::GreaterEqual), - ..Default::default() - }), - directional_light_sampler: render_device.create_sampler(&SamplerDescriptor { - address_mode_u: AddressMode::ClampToEdge, - address_mode_v: AddressMode::ClampToEdge, - address_mode_w: AddressMode::ClampToEdge, - mag_filter: FilterMode::Linear, - min_filter: FilterMode::Linear, - mipmap_filter: FilterMode::Nearest, - compare: Some(CompareFunction::GreaterEqual), - ..Default::default() - }), - } + ], + label: Some("shadow_view_layout"), + }); + + let mesh_pipeline = world.resource::(); + + ShadowPipeline { + view_layout, + mesh_layout: mesh_pipeline.mesh_layout.clone(), + point_light_sampler: render_device.create_sampler(&SamplerDescriptor { + address_mode_u: AddressMode::ClampToEdge, + address_mode_v: AddressMode::ClampToEdge, + address_mode_w: AddressMode::ClampToEdge, + mag_filter: FilterMode::Linear, + min_filter: FilterMode::Linear, + mipmap_filter: FilterMode::Nearest, + compare: Some(CompareFunction::GreaterEqual), + ..Default::default() + }), + directional_light_sampler: render_device.create_sampler(&SamplerDescriptor { + address_mode_u: AddressMode::ClampToEdge, + address_mode_v: AddressMode::ClampToEdge, + address_mode_w: AddressMode::ClampToEdge, + mag_filter: FilterMode::Linear, + min_filter: FilterMode::Linear, + mipmap_filter: FilterMode::Nearest, + compare: Some(CompareFunction::GreaterEqual), + ..Default::default() + }), + } + }) } } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 788dd9365b48f..83b2f0b6fa607 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -121,8 +121,9 @@ 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 windows = app + .world + .resource_mut::(); let raw_handle = windows.get_primary().map(|window| unsafe { let handle = window.raw_window_handle().get_handle(); instance.create_surface(&handle) diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 4eceaf2bdfc87..5cc186d29b324 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -38,7 +38,6 @@ pub struct SpritePipeline { impl FromWorld for SpritePipeline { fn from_world(world: &mut World) -> Self { - let world = world.cell(); let render_device = world.get_resource::().unwrap(); let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { diff --git a/crates/bevy_ui/src/render/pipeline.rs b/crates/bevy_ui/src/render/pipeline.rs index 4538dc922b131..45d0447bcc127 100644 --- a/crates/bevy_ui/src/render/pipeline.rs +++ b/crates/bevy_ui/src/render/pipeline.rs @@ -13,7 +13,6 @@ pub struct UiPipeline { impl FromWorld for UiPipeline { fn from_world(world: &mut World) -> Self { - let world = world.cell(); let render_device = world.get_resource::().unwrap(); let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index c58c9e4fc8906..38e8fd95116fa 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -11,7 +11,11 @@ pub use winit_config::*; pub use winit_windows::*; use bevy_app::{App, AppExit, CoreStage, Events, ManualEventReader, Plugin}; -use bevy_ecs::{system::IntoExclusiveSystem, world::World}; +use bevy_ecs::{ + event::EventWriter, + system::{Res, ResMut, SystemParam, SystemState}, + world::World, +}; use bevy_math::{ivec2, DVec2, Vec2}; use bevy_utils::tracing::{error, trace, warn}; use bevy_window::{ @@ -34,137 +38,13 @@ impl Plugin for WinitPlugin { fn build(&self, app: &mut App) { app.init_resource::() .set_runner(winit_runner) - .add_system_to_stage(CoreStage::PostUpdate, change_window.exclusive_system()); + .add_system_to_stage(CoreStage::PostUpdate, change_window); let event_loop = EventLoop::new(); handle_initial_window_events(&mut app.world, &event_loop); app.insert_non_send_resource(event_loop); } } -fn change_window(world: &mut World) { - let world = world.cell(); - let winit_windows = world.get_resource::().unwrap(); - let mut windows = world.get_resource_mut::().unwrap(); - - for bevy_window in windows.iter_mut() { - let id = bevy_window.id(); - for command in bevy_window.drain_commands() { - match command { - bevy_window::WindowCommand::SetWindowMode { - mode, - resolution: (width, height), - } => { - let window = winit_windows.get_window(id).unwrap(); - match mode { - bevy_window::WindowMode::BorderlessFullscreen => { - window - .set_fullscreen(Some(winit::window::Fullscreen::Borderless(None))); - } - bevy_window::WindowMode::Fullscreen => { - window.set_fullscreen(Some(winit::window::Fullscreen::Exclusive( - get_best_videomode(&window.current_monitor().unwrap()), - ))); - } - bevy_window::WindowMode::SizedFullscreen => window.set_fullscreen(Some( - winit::window::Fullscreen::Exclusive(get_fitting_videomode( - &window.current_monitor().unwrap(), - width, - height, - )), - )), - bevy_window::WindowMode::Windowed => window.set_fullscreen(None), - } - } - bevy_window::WindowCommand::SetTitle { title } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_title(&title); - } - bevy_window::WindowCommand::SetScaleFactor { scale_factor } => { - let mut window_dpi_changed_events = world - .get_resource_mut::>() - .unwrap(); - window_dpi_changed_events.send(WindowScaleFactorChanged { id, scale_factor }); - } - bevy_window::WindowCommand::SetResolution { - logical_resolution: (width, height), - scale_factor, - } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_inner_size( - winit::dpi::LogicalSize::new(width, height) - .to_physical::(scale_factor), - ); - } - bevy_window::WindowCommand::SetPresentMode { .. } => (), - bevy_window::WindowCommand::SetResizable { resizable } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_resizable(resizable); - } - bevy_window::WindowCommand::SetDecorations { decorations } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_decorations(decorations); - } - bevy_window::WindowCommand::SetCursorIcon { icon } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_cursor_icon(converters::convert_cursor_icon(icon)); - } - bevy_window::WindowCommand::SetCursorLockMode { locked } => { - let window = winit_windows.get_window(id).unwrap(); - window - .set_cursor_grab(locked) - .unwrap_or_else(|e| error!("Unable to un/grab cursor: {}", e)); - } - bevy_window::WindowCommand::SetCursorVisibility { visible } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_cursor_visible(visible); - } - bevy_window::WindowCommand::SetCursorPosition { position } => { - let window = winit_windows.get_window(id).unwrap(); - let inner_size = window.inner_size().to_logical::(window.scale_factor()); - window - .set_cursor_position(winit::dpi::LogicalPosition::new( - position.x, - inner_size.height - position.y, - )) - .unwrap_or_else(|e| error!("Unable to set cursor position: {}", e)); - } - bevy_window::WindowCommand::SetMaximized { maximized } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_maximized(maximized); - } - bevy_window::WindowCommand::SetMinimized { minimized } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_minimized(minimized); - } - bevy_window::WindowCommand::SetPosition { position } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_outer_position(PhysicalPosition { - x: position[0], - y: position[1], - }); - } - bevy_window::WindowCommand::SetResizeConstraints { resize_constraints } => { - let window = winit_windows.get_window(id).unwrap(); - let constraints = resize_constraints.check_constraints(); - let min_inner_size = LogicalSize { - width: constraints.min_width, - height: constraints.min_height, - }; - let max_inner_size = LogicalSize { - width: constraints.max_width, - height: constraints.max_height, - }; - - window.set_min_inner_size(Some(min_inner_size)); - if constraints.max_width.is_finite() && constraints.max_height.is_finite() { - window.set_max_inner_size(Some(max_inner_size)); - } - } - } - } - } -} - fn run(event_loop: EventLoop<()>, event_handler: F) -> ! where F: 'static + FnMut(Event<'_, ()>, &EventLoopWindowTarget<()>, &mut ControlFlow), @@ -223,6 +103,32 @@ pub fn winit_runner(app: App) { // winit_runner_with(app, EventLoop::new_any_thread()); // } +#[derive(SystemParam)] +struct WindowEvents<'w, 's> { + window_resized: EventWriter<'w, 's, WindowResized>, + window_close_requested: EventWriter<'w, 's, WindowCloseRequested>, + window_scale_factor_changed: EventWriter<'w, 's, WindowScaleFactorChanged>, + window_backend_scale_factor_changed: EventWriter<'w, 's, WindowBackendScaleFactorChanged>, + window_focused: EventWriter<'w, 's, WindowFocused>, + window_moved: EventWriter<'w, 's, WindowMoved>, +} + +#[derive(SystemParam)] +struct InputEvents<'w, 's> { + keyboard_input: EventWriter<'w, 's, KeyboardInput>, + character_input: EventWriter<'w, 's, ReceivedCharacter>, + mouse_button_input: EventWriter<'w, 's, MouseButtonInput>, + mouse_wheel_input: EventWriter<'w, 's, MouseWheel>, + touch_input: EventWriter<'w, 's, TouchInput>, +} + +#[derive(SystemParam)] +struct CursorEvents<'w, 's> { + cursor_moved: EventWriter<'w, 's, CursorMoved>, + cursor_entered: EventWriter<'w, 's, CursorEntered>, + cursor_left: EventWriter<'w, 's, CursorLeft>, +} + pub fn winit_runner_with(mut app: App) { let mut event_loop = app .world @@ -263,9 +169,24 @@ pub fn winit_runner_with(mut app: App) { window_id: winit_window_id, .. } => { - let world = app.world.cell(); - let winit_windows = world.get_resource_mut::().unwrap(); - let mut windows = world.get_resource_mut::().unwrap(); + let mut system_state: SystemState<( + Res, + ResMut, + WindowEvents, + InputEvents, + CursorEvents, + EventWriter, + )> = SystemState::new(&mut app.world); + + let ( + winit_windows, + mut windows, + mut window_events, + mut input_events, + mut cursor_events, + mut file_drag_and_drop_events, + ) = system_state.get_mut(&mut app.world); + let window_id = if let Some(window_id) = winit_windows.get_window_id(winit_window_id) { window_id @@ -287,28 +208,23 @@ pub fn winit_runner_with(mut app: App) { match event { WindowEvent::Resized(size) => { window.update_actual_size_from_backend(size.width, size.height); - let mut resize_events = - world.get_resource_mut::>().unwrap(); - resize_events.send(WindowResized { + window_events.window_resized.send(WindowResized { id: window_id, width: window.width(), height: window.height(), }); } WindowEvent::CloseRequested => { - let mut window_close_requested_events = world - .get_resource_mut::>() - .unwrap(); - window_close_requested_events.send(WindowCloseRequested { id: window_id }); + window_events + .window_close_requested + .send(WindowCloseRequested { id: window_id }); } WindowEvent::KeyboardInput { ref input, .. } => { - let mut keyboard_input_events = - world.get_resource_mut::>().unwrap(); - keyboard_input_events.send(converters::convert_keyboard_input(input)); + input_events + .keyboard_input + .send(converters::convert_keyboard_input(input)); } WindowEvent::CursorMoved { position, .. } => { - let mut cursor_moved_events = - world.get_resource_mut::>().unwrap(); let winit_window = winit_windows.get_window(window_id).unwrap(); let inner_size = winit_window.inner_size(); @@ -319,45 +235,36 @@ pub fn winit_runner_with(mut app: App) { window .update_cursor_physical_position_from_backend(Some(physical_position)); - cursor_moved_events.send(CursorMoved { + cursor_events.cursor_moved.send(CursorMoved { id: window_id, position: (physical_position / window.scale_factor()).as_vec2(), }); } WindowEvent::CursorEntered { .. } => { - let mut cursor_entered_events = - world.get_resource_mut::>().unwrap(); - cursor_entered_events.send(CursorEntered { id: window_id }); + cursor_events + .cursor_entered + .send(CursorEntered { id: window_id }); } WindowEvent::CursorLeft { .. } => { - let mut cursor_left_events = - world.get_resource_mut::>().unwrap(); window.update_cursor_physical_position_from_backend(None); - cursor_left_events.send(CursorLeft { id: window_id }); + cursor_events.cursor_left.send(CursorLeft { id: window_id }); } WindowEvent::MouseInput { state, button, .. } => { - let mut mouse_button_input_events = world - .get_resource_mut::>() - .unwrap(); - mouse_button_input_events.send(MouseButtonInput { + input_events.mouse_button_input.send(MouseButtonInput { button: converters::convert_mouse_button(button), state: converters::convert_element_state(state), }); } WindowEvent::MouseWheel { delta, .. } => match delta { event::MouseScrollDelta::LineDelta(x, y) => { - let mut mouse_wheel_input_events = - world.get_resource_mut::>().unwrap(); - mouse_wheel_input_events.send(MouseWheel { + input_events.mouse_wheel_input.send(MouseWheel { unit: MouseScrollUnit::Line, x, y, }); } event::MouseScrollDelta::PixelDelta(p) => { - let mut mouse_wheel_input_events = - world.get_resource_mut::>().unwrap(); - mouse_wheel_input_events.send(MouseWheel { + input_events.mouse_wheel_input.send(MouseWheel { unit: MouseScrollUnit::Pixel, x: p.x as f32, y: p.y as f32, @@ -365,9 +272,6 @@ pub fn winit_runner_with(mut app: App) { } }, WindowEvent::Touch(touch) => { - let mut touch_input_events = - world.get_resource_mut::>().unwrap(); - let mut location = touch.location.to_logical(window.scale_factor()); // On a mobile window, the start is from the top while on PC/Linux/OSX from @@ -376,14 +280,12 @@ pub fn winit_runner_with(mut app: App) { let window_height = windows.get_primary().unwrap().height(); location.y = window_height - location.y; } - touch_input_events.send(converters::convert_touch_input(touch, location)); + input_events + .touch_input + .send(converters::convert_touch_input(touch, location)); } WindowEvent::ReceivedCharacter(c) => { - let mut char_input_events = world - .get_resource_mut::>() - .unwrap(); - - char_input_events.send(ReceivedCharacter { + input_events.character_input.send(ReceivedCharacter { id: window_id, char: c, }); @@ -392,13 +294,12 @@ pub fn winit_runner_with(mut app: App) { scale_factor, new_inner_size, } => { - let mut backend_scale_factor_change_events = world - .get_resource_mut::>() - .unwrap(); - backend_scale_factor_change_events.send(WindowBackendScaleFactorChanged { - id: window_id, - scale_factor, - }); + window_events.window_backend_scale_factor_changed.send( + WindowBackendScaleFactorChanged { + id: window_id, + scale_factor, + }, + ); let prior_factor = window.scale_factor(); window.update_scale_factor_from_backend(scale_factor); let new_factor = window.scale_factor(); @@ -413,14 +314,12 @@ pub fn winit_runner_with(mut app: App) { ) .to_physical::(forced_factor); } else if approx::relative_ne!(new_factor, prior_factor) { - let mut scale_factor_change_events = world - .get_resource_mut::>() - .unwrap(); - - scale_factor_change_events.send(WindowScaleFactorChanged { - id: window_id, - scale_factor, - }); + window_events.window_scale_factor_changed.send( + WindowScaleFactorChanged { + id: window_id, + scale_factor, + }, + ); } let new_logical_width = new_inner_size.width as f64 / new_factor; @@ -428,9 +327,7 @@ pub fn winit_runner_with(mut app: App) { if approx::relative_ne!(window.width() as f64, new_logical_width) || approx::relative_ne!(window.height() as f64, new_logical_height) { - let mut resize_events = - world.get_resource_mut::>().unwrap(); - resize_events.send(WindowResized { + window_events.window_resized.send(WindowResized { id: window_id, width: new_logical_width as f32, height: new_logical_height as f32, @@ -443,39 +340,31 @@ pub fn winit_runner_with(mut app: App) { } WindowEvent::Focused(focused) => { window.update_focused_status_from_backend(focused); - let mut focused_events = - world.get_resource_mut::>().unwrap(); - focused_events.send(WindowFocused { + window_events.window_focused.send(WindowFocused { id: window_id, focused, }); } WindowEvent::DroppedFile(path_buf) => { - let mut events = - world.get_resource_mut::>().unwrap(); - events.send(FileDragAndDrop::DroppedFile { + file_drag_and_drop_events.send(FileDragAndDrop::DroppedFile { id: window_id, path_buf, }); } WindowEvent::HoveredFile(path_buf) => { - let mut events = - world.get_resource_mut::>().unwrap(); - events.send(FileDragAndDrop::HoveredFile { + file_drag_and_drop_events.send(FileDragAndDrop::HoveredFile { id: window_id, path_buf, }); } WindowEvent::HoveredFileCancelled => { - let mut events = - world.get_resource_mut::>().unwrap(); - events.send(FileDragAndDrop::HoveredFileCancelled { id: window_id }); + file_drag_and_drop_events + .send(FileDragAndDrop::HoveredFileCancelled { id: window_id }); } WindowEvent::Moved(position) => { let position = ivec2(position.x, position.y); window.update_actual_position_from_backend(position); - let mut events = world.get_resource_mut::>().unwrap(); - events.send(WindowMoved { + window_events.window_moved.send(WindowMoved { id: window_id, position, }); @@ -524,12 +413,16 @@ fn handle_create_window_events( event_loop: &EventLoopWindowTarget<()>, create_window_event_reader: &mut ManualEventReader, ) { - let world = world.cell(); - let mut winit_windows = world.get_resource_mut::().unwrap(); - let mut windows = world.get_resource_mut::().unwrap(); - let create_window_events = world.get_resource::>().unwrap(); - let mut window_created_events = world.get_resource_mut::>().unwrap(); - for create_window_event in create_window_event_reader.iter(&create_window_events) { + let mut system_state: SystemState<( + ResMut, + ResMut, + Res>, + EventWriter, + )> = SystemState::new(world); + let (mut winit_windows, mut windows, create_window_events, mut window_created_events) = + system_state.get_mut(world); + + for create_window_event in create_window_event_reader.iter(&*create_window_events) { let window = winit_windows.create_window( event_loop, create_window_event.id, @@ -543,11 +436,15 @@ fn handle_create_window_events( } fn handle_initial_window_events(world: &mut World, event_loop: &EventLoop<()>) { - let world = world.cell(); - let mut winit_windows = world.get_resource_mut::().unwrap(); - let mut windows = world.get_resource_mut::().unwrap(); - let mut create_window_events = world.get_resource_mut::>().unwrap(); - let mut window_created_events = world.get_resource_mut::>().unwrap(); + let mut system_state: SystemState<( + ResMut, + ResMut, + ResMut>, + EventWriter, + )> = SystemState::new(world); + let (mut winit_windows, mut windows, mut create_window_events, mut window_created_events) = + system_state.get_mut(world); + for create_window_event in create_window_events.drain() { let window = winit_windows.create_window( event_loop, @@ -560,3 +457,124 @@ fn handle_initial_window_events(world: &mut World, event_loop: &EventLoop<()>) { }); } } + +fn change_window( + winit_windows: Res, + mut windows: ResMut, + mut window_dpi_changed_events: EventWriter, +) { + for bevy_window in windows.iter_mut() { + let id = bevy_window.id(); + for command in bevy_window.drain_commands() { + match command { + bevy_window::WindowCommand::SetWindowMode { + mode, + resolution: (width, height), + } => { + let window = winit_windows.get_window(id).unwrap(); + match mode { + bevy_window::WindowMode::BorderlessFullscreen => { + window + .set_fullscreen(Some(winit::window::Fullscreen::Borderless(None))); + } + bevy_window::WindowMode::Fullscreen => { + window.set_fullscreen(Some(winit::window::Fullscreen::Exclusive( + get_best_videomode(&window.current_monitor().unwrap()), + ))); + } + bevy_window::WindowMode::SizedFullscreen => window.set_fullscreen(Some( + winit::window::Fullscreen::Exclusive(get_fitting_videomode( + &window.current_monitor().unwrap(), + width, + height, + )), + )), + bevy_window::WindowMode::Windowed => window.set_fullscreen(None), + } + } + bevy_window::WindowCommand::SetTitle { title } => { + let window = winit_windows.get_window(id).unwrap(); + window.set_title(&title); + } + bevy_window::WindowCommand::SetScaleFactor { scale_factor } => { + window_dpi_changed_events.send(WindowScaleFactorChanged { id, scale_factor }); + } + bevy_window::WindowCommand::SetResolution { + logical_resolution: (width, height), + scale_factor, + } => { + let window = winit_windows.get_window(id).unwrap(); + window.set_inner_size( + winit::dpi::LogicalSize::new(width, height) + .to_physical::(scale_factor), + ); + } + bevy_window::WindowCommand::SetPresentMode { .. } => (), + bevy_window::WindowCommand::SetResizable { resizable } => { + let window = winit_windows.get_window(id).unwrap(); + window.set_resizable(resizable); + } + bevy_window::WindowCommand::SetDecorations { decorations } => { + let window = winit_windows.get_window(id).unwrap(); + window.set_decorations(decorations); + } + bevy_window::WindowCommand::SetCursorIcon { icon } => { + let window = winit_windows.get_window(id).unwrap(); + window.set_cursor_icon(converters::convert_cursor_icon(icon)); + } + bevy_window::WindowCommand::SetCursorLockMode { locked } => { + let window = winit_windows.get_window(id).unwrap(); + window + .set_cursor_grab(locked) + .unwrap_or_else(|e| error!("Unable to un/grab cursor: {}", e)); + } + bevy_window::WindowCommand::SetCursorVisibility { visible } => { + let window = winit_windows.get_window(id).unwrap(); + window.set_cursor_visible(visible); + } + bevy_window::WindowCommand::SetCursorPosition { position } => { + let window = winit_windows.get_window(id).unwrap(); + let inner_size = window.inner_size().to_logical::(window.scale_factor()); + window + .set_cursor_position(winit::dpi::LogicalPosition::new( + position.x, + inner_size.height - position.y, + )) + .unwrap_or_else(|e| error!("Unable to set cursor position: {}", e)); + } + bevy_window::WindowCommand::SetMaximized { maximized } => { + let window = winit_windows.get_window(id).unwrap(); + window.set_maximized(maximized); + } + bevy_window::WindowCommand::SetMinimized { minimized } => { + let window = winit_windows.get_window(id).unwrap(); + window.set_minimized(minimized); + } + bevy_window::WindowCommand::SetPosition { position } => { + let window = winit_windows.get_window(id).unwrap(); + window.set_outer_position(PhysicalPosition { + x: position[0], + y: position[1], + }); + } + bevy_window::WindowCommand::SetResizeConstraints { resize_constraints } => { + let window = winit_windows.get_window(id).unwrap(); + let constraints = resize_constraints.check_constraints(); + let min_inner_size = LogicalSize { + width: constraints.min_width, + height: constraints.min_height, + }; + let max_inner_size = LogicalSize { + width: constraints.max_width, + height: constraints.max_height, + }; + + window.set_min_inner_size(Some(min_inner_size)); + if constraints.max_width.is_finite() && constraints.max_height.is_finite() { + window.set_max_inner_size(Some(max_inner_size)); + } + } + } + } + } +} diff --git a/examples/shader/animate_shader.rs b/examples/shader/animate_shader.rs index 6e8f4b00af0fc..9f5edd426d759 100644 --- a/examples/shader/animate_shader.rs +++ b/examples/shader/animate_shader.rs @@ -177,7 +177,6 @@ pub struct CustomPipeline { impl FromWorld for CustomPipeline { fn from_world(world: &mut World) -> Self { - let world = world.cell(); let asset_server = world.get_resource::().unwrap(); let shader = asset_server.load("shaders/animate_shader.wgsl"); diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 4c88cd17481e4..baaa39068488c 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -161,7 +161,6 @@ pub struct CustomPipeline { impl FromWorld for CustomPipeline { fn from_world(world: &mut World) -> Self { - let world = world.cell(); let asset_server = world.get_resource::().unwrap(); asset_server.watch_for_changes().unwrap(); let shader = asset_server.load("shaders/instancing.wgsl");