From 660553b121e4223ec9b142223d51cf8db556e857 Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Sat, 3 May 2025 09:13:26 -0300 Subject: [PATCH 1/9] Guard systems that need `WindowPlugin` on `PointerInputPlugin` --- crates/bevy_picking/src/input.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/bevy_picking/src/input.rs b/crates/bevy_picking/src/input.rs index 712e612224c7b..2e91e5f1872f2 100644 --- a/crates/bevy_picking/src/input.rs +++ b/crates/bevy_picking/src/input.rs @@ -23,7 +23,7 @@ use bevy_math::Vec2; use bevy_platform::collections::{HashMap, HashSet}; use bevy_reflect::prelude::*; use bevy_render::camera::RenderTarget; -use bevy_window::{PrimaryWindow, WindowEvent, WindowRef}; +use bevy_window::{PrimaryWindow, WindowEvent, WindowPlugin, WindowRef}; use tracing::debug; use crate::pointer::{ @@ -80,6 +80,14 @@ impl Plugin for PointerInputPlugin { app.insert_resource(*self) .add_systems(Startup, spawn_mouse_pointer) .add_systems( + Last, + deactivate_touch_pointers.run_if(PointerInputPlugin::is_touch_enabled), + ) + .register_type::() + .register_type::(); + + if app.is_plugin_added::() { + app.add_systems( First, ( mouse_pick_events.run_if(PointerInputPlugin::is_mouse_enabled), @@ -87,13 +95,8 @@ impl Plugin for PointerInputPlugin { ) .chain() .in_set(PickSet::Input), - ) - .add_systems( - Last, - deactivate_touch_pointers.run_if(PointerInputPlugin::is_touch_enabled), - ) - .register_type::() - .register_type::(); + ); + } } } From 1b394b14e0ea7fa6ba90e0e3b764500371ac2924 Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Sat, 3 May 2025 09:14:25 -0300 Subject: [PATCH 2/9] Guard methods that rely on `WindowPlugin` or `WindowRenderPlugin` on `CameraDriverNode` --- .../src/camera/camera_driver_node.rs | 78 ++++++++++--------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/crates/bevy_render/src/camera/camera_driver_node.rs b/crates/bevy_render/src/camera/camera_driver_node.rs index 8be5a345b4c4a..6ff4cb5bf1db6 100644 --- a/crates/bevy_render/src/camera/camera_driver_node.rs +++ b/crates/bevy_render/src/camera/camera_driver_node.rs @@ -31,7 +31,6 @@ impl Node for CameraDriverNode { world: &World, ) -> Result<(), NodeRunError> { let sorted_cameras = world.resource::(); - let windows = world.resource::(); let mut camera_windows = >::default(); for sorted_camera in &sorted_cameras.0 { let Ok(camera) = self.cameras.get_manual(world, sorted_camera.entity) else { @@ -41,15 +40,22 @@ impl Node for CameraDriverNode { let mut run_graph = true; if let Some(NormalizedRenderTarget::Window(window_ref)) = camera.target { let window_entity = window_ref.entity(); - if windows - .windows - .get(&window_entity) - .is_some_and(|w| w.physical_width > 0 && w.physical_height > 0) - { - camera_windows.insert(window_entity); + if let Some(windows) = world.get_resource::() { + if windows + .windows + .get(&window_entity) + .is_some_and(|w| w.physical_width > 0 && w.physical_height > 0) + { + camera_windows.insert(window_entity); + } else { + // The window doesn't exist anymore or zero-sized so we don't need to run the graph + run_graph = false; + } } else { - // The window doesn't exist anymore or zero-sized so we don't need to run the graph - run_graph = false; + tracing::warn!( + "Camera is targeting Window {}, but ExtractedWindows is not available.", + window_entity + ); } } if run_graph { @@ -61,35 +67,37 @@ impl Node for CameraDriverNode { // wgpu (and some backends) require doing work for swap chains if you call `get_current_texture()` and `present()` // This ensures that Bevy doesn't crash, even when there are no cameras (and therefore no work submitted). - for (id, window) in world.resource::().iter() { - if camera_windows.contains(id) { - continue; - } + if let Some(windows) = world.get_resource::() { + for (id, window) in windows.iter() { + if camera_windows.contains(id) { + continue; + } - let Some(swap_chain_texture) = &window.swap_chain_texture_view else { - continue; - }; + let Some(swap_chain_texture) = &window.swap_chain_texture_view else { + continue; + }; - #[cfg(feature = "trace")] - let _span = tracing::info_span!("no_camera_clear_pass").entered(); - let pass_descriptor = RenderPassDescriptor { - label: Some("no_camera_clear_pass"), - color_attachments: &[Some(RenderPassColorAttachment { - view: swap_chain_texture, - resolve_target: None, - ops: Operations { - load: LoadOp::Clear(clear_color_global.to_linear().into()), - store: StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }; + #[cfg(feature = "trace")] + let _span = tracing::info_span!("no_camera_clear_pass").entered(); + let pass_descriptor = RenderPassDescriptor { + label: Some("no_camera_clear_pass"), + color_attachments: &[Some(RenderPassColorAttachment { + view: swap_chain_texture, + resolve_target: None, + ops: Operations { + load: LoadOp::Clear(clear_color_global.to_linear().into()), + store: StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }; - render_context - .command_encoder() - .begin_render_pass(&pass_descriptor); + render_context + .command_encoder() + .begin_render_pass(&pass_descriptor); + } } Ok(()) From cbd8070a5aca6afec5ea6fa13e2e7deb8ebef605 Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Sat, 3 May 2025 09:15:01 -0300 Subject: [PATCH 3/9] Guard systems that rely on `WindowPlugin` on `CameraProjectionPlugin` --- crates/bevy_render/src/camera/projection.rs | 23 +++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/crates/bevy_render/src/camera/projection.rs b/crates/bevy_render/src/camera/projection.rs index e3f95cb0361e5..71ddeb1b14251 100644 --- a/crates/bevy_render/src/camera/projection.rs +++ b/crates/bevy_render/src/camera/projection.rs @@ -8,6 +8,7 @@ use bevy_ecs::prelude::*; use bevy_math::{ops, AspectRatio, Mat4, Rect, Vec2, Vec3A, Vec4}; use bevy_reflect::{std_traits::ReflectDefault, Reflect, ReflectDeserialize, ReflectSerialize}; use bevy_transform::{components::GlobalTransform, TransformSystem}; +use bevy_window::WindowPlugin; use derive_more::derive::From; use serde::{Deserialize, Serialize}; @@ -24,21 +25,25 @@ impl Plugin for CameraProjectionPlugin { .register_type::() .register_type::() .add_systems( + PostUpdate, + crate::view::update_frusta + .in_set(VisibilitySystems::UpdateFrusta) + .after(crate::camera::camera_system) + .after(TransformSystem::TransformPropagate), + ); + + if app.is_plugin_added::() { + app.add_systems( PostStartup, crate::camera::camera_system.in_set(CameraUpdateSystem), ) .add_systems( PostUpdate, - ( - crate::camera::camera_system - .in_set(CameraUpdateSystem) - .before(AssetEvents), - crate::view::update_frusta - .in_set(VisibilitySystems::UpdateFrusta) - .after(crate::camera::camera_system) - .after(TransformSystem::TransformPropagate), - ), + (crate::camera::camera_system + .in_set(CameraUpdateSystem) + .before(AssetEvents),), ); + } } } From 9b313a2a350a233b0afdf2590579ef21fef82521 Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Sat, 3 May 2025 09:16:02 -0300 Subject: [PATCH 4/9] Guard `render_system` that rely on `WindowPlugin` or `WindowRenderPlugin` --- crates/bevy_render/src/renderer/mod.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 1691911c2cbbe..262f862bccbf8 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -85,14 +85,15 @@ pub fn render_system(world: &mut World, state: &mut SystemState(); } - let mut windows = world.resource_mut::(); - for window in windows.values_mut() { - if let Some(surface_texture) = window.swap_chain_texture.take() { - // TODO(clean): winit docs recommends calling pre_present_notify before this. - // though `present()` doesn't present the frame, it schedules it to be presented - // by wgpu. - // https://docs.rs/winit/0.29.9/wasm32-unknown-unknown/winit/window/struct.Window.html#method.pre_present_notify - surface_texture.present(); + if let Some(mut windows) = world.get_resource_mut::() { + for window in windows.values_mut() { + if let Some(surface_texture) = window.swap_chain_texture.take() { + // TODO(clean): winit docs recommends calling pre_present_notify before this. + // though `present()` doesn't present the frame, it schedules it to be presented + // by wgpu. + // https://docs.rs/winit/0.29.9/wasm32-unknown-unknown/winit/window/struct.Window.html#method.pre_present_notify + surface_texture.present(); + } } } From fc586edb0cc67430d25f061b709f477b50f10f41 Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Sat, 3 May 2025 09:16:33 -0300 Subject: [PATCH 5/9] Guard screenshot methods that rely on `WindowPlugin` or `WindowRenderPlugin` --- .../bevy_render/src/view/window/screenshot.rs | 283 +++++++++--------- 1 file changed, 144 insertions(+), 139 deletions(-) diff --git a/crates/bevy_render/src/view/window/screenshot.rs b/crates/bevy_render/src/view/window/screenshot.rs index 6e223eedaf047..7e652a549f55d 100644 --- a/crates/bevy_render/src/view/window/screenshot.rs +++ b/crates/bevy_render/src/view/window/screenshot.rs @@ -494,84 +494,85 @@ impl SpecializedRenderPipeline for ScreenshotToScreenPipeline { } pub(crate) fn submit_screenshot_commands(world: &World, encoder: &mut CommandEncoder) { - let targets = world.resource::(); - let prepared = world.resource::(); - let pipelines = world.resource::(); - let gpu_images = world.resource::>(); - let windows = world.resource::(); - let manual_texture_views = world.resource::(); - - for (entity, render_target) in targets.iter() { - match render_target { - NormalizedRenderTarget::Window(window) => { - let window = window.entity(); - let Some(window) = windows.get(&window) else { - continue; - }; - let width = window.physical_width; - let height = window.physical_height; - let Some(texture_format) = window.swap_chain_texture_format else { - continue; - }; - let Some(swap_chain_texture) = window.swap_chain_texture.as_ref() else { - continue; - }; - let texture_view = swap_chain_texture.texture.create_view(&Default::default()); - render_screenshot( - encoder, - prepared, - pipelines, - entity, - width, - height, - texture_format, - &texture_view, - ); - } - NormalizedRenderTarget::Image(image) => { - let Some(gpu_image) = gpu_images.get(&image.handle) else { - warn!("Unknown image for screenshot, skipping: {:?}", image); - continue; - }; - let width = gpu_image.size.width; - let height = gpu_image.size.height; - let texture_format = gpu_image.texture_format; - let texture_view = gpu_image.texture_view.deref(); - render_screenshot( - encoder, - prepared, - pipelines, - entity, - width, - height, - texture_format, - texture_view, - ); - } - NormalizedRenderTarget::TextureView(texture_view) => { - let Some(texture_view) = manual_texture_views.get(texture_view) else { - warn!( - "Unknown manual texture view for screenshot, skipping: {:?}", - texture_view + if let Some(targets) = world.get_resource::() { + let prepared = world.resource::(); + let pipelines = world.resource::(); + let gpu_images = world.resource::>(); + let windows = world.resource::(); + let manual_texture_views = world.resource::(); + + for (entity, render_target) in targets.iter() { + match render_target { + NormalizedRenderTarget::Window(window) => { + let window = window.entity(); + let Some(window) = windows.get(&window) else { + continue; + }; + let width = window.physical_width; + let height = window.physical_height; + let Some(texture_format) = window.swap_chain_texture_format else { + continue; + }; + let Some(swap_chain_texture) = window.swap_chain_texture.as_ref() else { + continue; + }; + let texture_view = swap_chain_texture.texture.create_view(&Default::default()); + render_screenshot( + encoder, + prepared, + pipelines, + entity, + width, + height, + texture_format, + &texture_view, ); - continue; - }; - let width = texture_view.size.x; - let height = texture_view.size.y; - let texture_format = texture_view.format; - let texture_view = texture_view.texture_view.deref(); - render_screenshot( - encoder, - prepared, - pipelines, - entity, - width, - height, - texture_format, - texture_view, - ); - } - }; + } + NormalizedRenderTarget::Image(image) => { + let Some(gpu_image) = gpu_images.get(&image.handle) else { + warn!("Unknown image for screenshot, skipping: {:?}", image); + continue; + }; + let width = gpu_image.size.width; + let height = gpu_image.size.height; + let texture_format = gpu_image.texture_format; + let texture_view = gpu_image.texture_view.deref(); + render_screenshot( + encoder, + prepared, + pipelines, + entity, + width, + height, + texture_format, + texture_view, + ); + } + NormalizedRenderTarget::TextureView(texture_view) => { + let Some(texture_view) = manual_texture_views.get(texture_view) else { + warn!( + "Unknown manual texture view for screenshot, skipping: {:?}", + texture_view + ); + continue; + }; + let width = texture_view.size.x; + let height = texture_view.size.y; + let texture_format = texture_view.format; + let texture_view = texture_view.texture_view.deref(); + render_screenshot( + encoder, + prepared, + pipelines, + entity, + width, + height, + texture_format, + texture_view, + ); + } + }; + } } } @@ -626,70 +627,74 @@ pub(crate) fn collect_screenshots(world: &mut World) { #[cfg(feature = "trace")] let _span = tracing::info_span!("collect_screenshots").entered(); - let sender = world.resource::().deref().clone(); - let prepared = world.resource::(); - - for (entity, prepared) in prepared.iter() { - let entity = *entity; - let sender = sender.clone(); - let width = prepared.size.width; - let height = prepared.size.height; - let texture_format = prepared.texture.format(); - let pixel_size = texture_format.pixel_size(); - let buffer = prepared.buffer.clone(); - - let finish = async move { - let (tx, rx) = async_channel::bounded(1); - let buffer_slice = buffer.slice(..); - // The polling for this map call is done every frame when the command queue is submitted. - buffer_slice.map_async(wgpu::MapMode::Read, move |result| { - let err = result.err(); - if err.is_some() { - panic!("{}", err.unwrap().to_string()); - } - tx.try_send(()).unwrap(); - }); - rx.recv().await.unwrap(); - let data = buffer_slice.get_mapped_range(); - // we immediately move the data to CPU memory to avoid holding the mapped view for long - let mut result = Vec::from(&*data); - drop(data); - - if result.len() != ((width * height) as usize * pixel_size) { - // Our buffer has been padded because we needed to align to a multiple of 256. - // We remove this padding here - let initial_row_bytes = width as usize * pixel_size; - let buffered_row_bytes = - gpu_readback::align_byte_size(width * pixel_size as u32) as usize; - - let mut take_offset = buffered_row_bytes; - let mut place_offset = initial_row_bytes; - for _ in 1..height { - result.copy_within(take_offset..take_offset + buffered_row_bytes, place_offset); - take_offset += buffered_row_bytes; - place_offset += initial_row_bytes; + if let Some(prepared) = world.get_resource::() { + let sender = world.resource::().deref().clone(); + + for (entity, prepared) in prepared.iter() { + let entity = *entity; + let sender = sender.clone(); + let width = prepared.size.width; + let height = prepared.size.height; + let texture_format = prepared.texture.format(); + let pixel_size = texture_format.pixel_size(); + let buffer = prepared.buffer.clone(); + + let finish = async move { + let (tx, rx) = async_channel::bounded(1); + let buffer_slice = buffer.slice(..); + // The polling for this map call is done every frame when the command queue is submitted. + buffer_slice.map_async(wgpu::MapMode::Read, move |result| { + let err = result.err(); + if err.is_some() { + panic!("{}", err.unwrap().to_string()); + } + tx.try_send(()).unwrap(); + }); + rx.recv().await.unwrap(); + let data = buffer_slice.get_mapped_range(); + // we immediately move the data to CPU memory to avoid holding the mapped view for long + let mut result = Vec::from(&*data); + drop(data); + + if result.len() != ((width * height) as usize * pixel_size) { + // Our buffer has been padded because we needed to align to a multiple of 256. + // We remove this padding here + let initial_row_bytes = width as usize * pixel_size; + let buffered_row_bytes = + gpu_readback::align_byte_size(width * pixel_size as u32) as usize; + + let mut take_offset = buffered_row_bytes; + let mut place_offset = initial_row_bytes; + for _ in 1..height { + result.copy_within( + take_offset..take_offset + buffered_row_bytes, + place_offset, + ); + take_offset += buffered_row_bytes; + place_offset += initial_row_bytes; + } + result.truncate(initial_row_bytes * height as usize); } - result.truncate(initial_row_bytes * height as usize); - } - if let Err(e) = sender.send(( - entity, - Image::new( - Extent3d { - width, - height, - depth_or_array_layers: 1, - }, - wgpu::TextureDimension::D2, - result, - texture_format, - RenderAssetUsages::RENDER_WORLD, - ), - )) { - error!("Failed to send screenshot: {}", e); - } - }; + if let Err(e) = sender.send(( + entity, + Image::new( + Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + wgpu::TextureDimension::D2, + result, + texture_format, + RenderAssetUsages::RENDER_WORLD, + ), + )) { + error!("Failed to send screenshot: {}", e); + } + }; - AsyncComputeTaskPool::get().spawn(finish).detach(); + AsyncComputeTaskPool::get().spawn(finish).detach(); + } } } From c66f85fbc60aa5f6c87613d2c231dc8bbacbde8a Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Sat, 3 May 2025 09:17:07 -0300 Subject: [PATCH 6/9] Guard insertion of `WindowRenderPlugin` on presence of `WindowPlugin` --- crates/bevy_render/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 843bb6828457c..c953a75cea8ef 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -76,7 +76,7 @@ use bevy_ecs::schedule::ScheduleBuildSettings; use bevy_utils::prelude::default; pub use extract_param::Extract; -use bevy_window::{PrimaryWindow, RawHandleWrapperHolder}; +use bevy_window::{PrimaryWindow, RawHandleWrapperHolder, WindowPlugin}; use experimental::occlusion_culling::OcclusionCullingPlugin; use globals::GlobalsPlugin; use render_asset::{ @@ -392,8 +392,10 @@ impl Plugin for RenderPlugin { } }; + if app.is_plugin_added::() { + app.add_plugins(WindowRenderPlugin); + } app.add_plugins(( - WindowRenderPlugin, CameraPlugin, ViewPlugin, MeshPlugin, From c0fa23a45374ddeaf9e6dc5f1771e68fd880d09e Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Sat, 3 May 2025 09:17:31 -0300 Subject: [PATCH 7/9] Guard methods of `ViewPlugin` that rely on `WindowPlugin` --- crates/bevy_render/src/view/mod.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index c392dcaaebe76..5688bce130a72 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -120,6 +120,8 @@ impl Plugin for ViewPlugin { VisibilityRangePlugin, )); + let has_windows_render_plugin = app.is_plugin_added::(); + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.add_systems( Render, @@ -128,18 +130,26 @@ impl Plugin for ViewPlugin { clear_view_attachments .in_set(RenderSet::ManageViews) .before(create_surfaces), - prepare_view_attachments - .in_set(RenderSet::ManageViews) - .before(prepare_view_targets) - .after(prepare_windows), - prepare_view_targets - .in_set(RenderSet::ManageViews) - .after(prepare_windows) - .after(crate::render_asset::prepare_assets::) - .ambiguous_with(crate::camera::sort_cameras), // doesn't use `sorted_camera_index_for_target` prepare_view_uniforms.in_set(RenderSet::PrepareResources), ), ); + + if has_windows_render_plugin { + render_app.add_systems( + Render, + ( + prepare_view_attachments + .in_set(RenderSet::ManageViews) + .before(prepare_view_targets) + .after(prepare_windows), + prepare_view_targets + .in_set(RenderSet::ManageViews) + .after(prepare_windows) + .after(crate::render_asset::prepare_assets::) + .ambiguous_with(crate::camera::sort_cameras), // doesn't use `sorted_camera_index_for_target` + ), + ); + } } } From f2faef2e78e90a07ea672eaef9549648f84e37ca Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Sat, 3 May 2025 10:23:26 -0300 Subject: [PATCH 8/9] Guard `bevy_winit` plugins, methods and systems thar rely on `WindowPlugin` --- crates/bevy_winit/src/lib.rs | 15 +++++++++------ crates/bevy_winit/src/state.rs | 24 +++++++++++++++++------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 97943cc14a98d..faa8e165f6d92 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -17,7 +17,7 @@ extern crate alloc; use bevy_derive::Deref; use bevy_reflect::prelude::ReflectDefault; use bevy_reflect::Reflect; -use bevy_window::{RawHandleWrapperHolder, WindowEvent}; +use bevy_window::{RawHandleWrapperHolder, WindowEvent, WindowPlugin}; use core::marker::PhantomData; use winit::{event_loop::EventLoop, window::WindowId}; @@ -129,8 +129,12 @@ impl Plugin for WinitPlugin { .init_resource::() .insert_resource(DisplayHandleWrapper(event_loop.owned_display_handle())) .add_event::() - .set_runner(|app| winit_runner(app, event_loop)) - .add_systems( + .set_runner(|app| winit_runner(app, event_loop)); + + app.add_plugins(cursor::CursorPlugin); + + if app.is_plugin_added::() { + app.add_systems( Last, ( // `exit_on_all_closed` only checks if windows exist but doesn't access data, @@ -141,9 +145,8 @@ impl Plugin for WinitPlugin { ) .chain(), ); - - app.add_plugins(AccessKitPlugin); - app.add_plugins(cursor::CursorPlugin); + app.add_plugins(AccessKitPlugin); + } } } diff --git a/crates/bevy_winit/src/state.rs b/crates/bevy_winit/src/state.rs index 33ad693c5ab4a..67022e7e16d71 100644 --- a/crates/bevy_winit/src/state.rs +++ b/crates/bevy_winit/src/state.rs @@ -18,7 +18,7 @@ use bevy_input::{ }; #[cfg(any(not(target_arch = "wasm32"), feature = "custom_cursor"))] use bevy_log::error; -use bevy_log::{trace, warn}; +use bevy_log::{once, trace, warn}; #[cfg(feature = "custom_cursor")] use bevy_math::URect; use bevy_math::{ivec2, DVec2, Vec2}; @@ -41,7 +41,7 @@ use winit::{ use bevy_window::{ AppLifecycle, CursorEntered, CursorLeft, CursorMoved, FileDragAndDrop, Ime, RequestRedraw, - Window, WindowBackendScaleFactorChanged, WindowCloseRequested, WindowDestroyed, + Window, WindowBackendScaleFactorChanged, WindowCloseRequested, WindowCreated, WindowDestroyed, WindowEvent as BevyWindowEvent, WindowFocused, WindowMoved, WindowOccluded, WindowResized, WindowScaleFactorChanged, WindowThemeChanged, }; @@ -493,8 +493,14 @@ impl ApplicationHandler for WinitAppRunnerState { SystemState::>>::from_world(self.world_mut()); create_monitors(event_loop, create_monitor.get_mut(self.world_mut())); create_monitor.apply(self.world_mut()); - create_windows(event_loop, create_window.get_mut(self.world_mut())); - create_window.apply(self.world_mut()); + if self + .world_mut() + .get_resource::>() + .is_some() + { + create_windows(event_loop, create_window.get_mut(self.world_mut())); + create_window.apply(self.world_mut()); + } // TODO: This is a workaround for https://github.com/bevyengine/bevy/issues/17488 // while preserving the iOS fix in https://github.com/bevyengine/bevy/pull/11245 @@ -862,9 +868,13 @@ impl WinitAppRunnerState { } if !buffered_events.is_empty() { - world - .resource_mut::>() - .send_batch(buffered_events); + if let Some(mut events) = world.get_resource_mut::>() { + events.send_batch(buffered_events); + } else { + once!(warn!( + "Lost windows events because `Events` is not available." + )); + } } } From 7b16d403b6ac9ff385de51b29b7d835354722803 Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Sat, 10 May 2025 11:42:50 -0300 Subject: [PATCH 9/9] Remove duplicated `update_frusta` system --- crates/bevy_render/src/camera/projection.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/crates/bevy_render/src/camera/projection.rs b/crates/bevy_render/src/camera/projection.rs index b95b72b7da153..ce1c2baa5b21c 100644 --- a/crates/bevy_render/src/camera/projection.rs +++ b/crates/bevy_render/src/camera/projection.rs @@ -39,15 +39,9 @@ impl Plugin for CameraProjectionPlugin { ) .add_systems( PostUpdate, - ( - crate::camera::camera_system - .in_set(CameraUpdateSystems) - .before(AssetEventSystems), - crate::view::update_frusta - .in_set(VisibilitySystems::UpdateFrusta) - .after(crate::camera::camera_system) - .after(TransformSystems::Propagate), - ), + crate::camera::camera_system + .in_set(CameraUpdateSystems) + .before(AssetEventSystems), ); } }