From fbb794d9b8fa79ef769caf59ae1f928af96f0a4e Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Sat, 2 Apr 2022 23:06:10 +0100 Subject: [PATCH 01/16] Setup infrastructure for render app owns extract --- crates/bevy_render/src/extract_param.rs | 82 +++++++++++++++++++ crates/bevy_render/src/lib.rs | 34 ++++---- .../src/render_resource/pipeline_cache.rs | 16 ++-- crates/bevy_render/src/view/window.rs | 11 ++- 4 files changed, 114 insertions(+), 29 deletions(-) create mode 100644 crates/bevy_render/src/extract_param.rs diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs new file mode 100644 index 0000000000000..d55c7c1c7171b --- /dev/null +++ b/crates/bevy_render/src/extract_param.rs @@ -0,0 +1,82 @@ +use std::ops::{Deref, DerefMut}; + +use crate::MainWorld; +use bevy_ecs::{ + prelude::*, + system::{ + ReadOnlySystemParamFetch, SystemParam, SystemParamFetch, SystemParamItem, SystemParamState, + SystemState, + }, +}; + +pub struct ExtractFromMainWorld<'s, P: SystemParam + 'static>(SystemParamItem<'s, 's, P>) +where + P::Fetch: ReadOnlySystemParamFetch; + +impl<'s, P: SystemParam + 'static> Deref for ExtractFromMainWorld<'s, P> +where + P::Fetch: ReadOnlySystemParamFetch, +{ + type Target = SystemParamItem<'s, 's, P>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'s, P: SystemParam + 'static> DerefMut for ExtractFromMainWorld<'s, P> +where + P::Fetch: ReadOnlySystemParamFetch, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<'s, P: SystemParam + 'static> ExtractFromMainWorld<'s, P> +where + P::Fetch: ReadOnlySystemParamFetch, +{ + pub fn into_inner(self) -> SystemParamItem<'s, 's, P> { + self.0 + } +} + +impl<'s, P: SystemParam + 'static> SystemParam for ExtractFromMainWorld<'s, P> +where + P::Fetch: ReadOnlySystemParamFetch, +{ + type Fetch = ExtractFromMainWorldState

; +} + +unsafe impl SystemParamState for ExtractFromMainWorldState

{ + fn init(world: &mut World, system_meta: &mut bevy_ecs::system::SystemMeta) -> Self { + Self { + world: SystemParamState::init(world, system_meta), + state: SystemState::new(&mut (*world.resource_mut::())), + } + } +} + +pub struct ExtractFromMainWorldState { + world: as SystemParam>::Fetch, + state: SystemState

, +} + +impl<'world, 'state, P: SystemParam + 'static> SystemParamFetch<'world, 'state> + for ExtractFromMainWorldState

+where + P::Fetch: ReadOnlySystemParamFetch, +{ + type Item = ExtractFromMainWorld<'state, P>; + + unsafe fn get_param( + state: &'state mut Self, + system_meta: &bevy_ecs::system::SystemMeta, + world: &'world World, + change_tick: u32, + ) -> Self::Item { + let world = SystemParamFetch::get_param(&mut state.world, system_meta, world, change_tick); + ExtractFromMainWorld(state.state.get(&world)) + } +} diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 0caa1a75bc29c..ee2b6f7452ee4 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -3,6 +3,7 @@ extern crate core; pub mod camera; pub mod color; pub mod extract_component; +mod extract_param; pub mod extract_resource; pub mod mesh; pub mod primitives; @@ -14,6 +15,7 @@ pub mod renderer; pub mod settings; pub mod texture; pub mod view; +pub use extract_param::ExtractFromMainWorld; pub mod prelude { #[doc(hidden)] @@ -80,9 +82,9 @@ pub enum RenderStage { /// The Render App World. This is only available as a resource during the Extract step. #[derive(Default)] -pub struct RenderWorld(World); +pub struct MainWorld(World); -impl Deref for RenderWorld { +impl Deref for MainWorld { type Target = World; fn deref(&self) -> &Self::Target { @@ -90,7 +92,7 @@ impl Deref for RenderWorld { } } -impl DerefMut for RenderWorld { +impl DerefMut for MainWorld { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } @@ -109,7 +111,7 @@ pub struct RenderApp; /// A "scratch" world used to avoid allocating new worlds every frame when /// swapping out the [`RenderWorld`]. #[derive(Default)] -struct ScratchRenderWorld(World); +struct ScratchMainWorld(World); impl Plugin for RenderPlugin { /// Initializes the renderer, sets up the [`RenderStage`](RenderStage) and creates the rendering sub-app. @@ -149,7 +151,7 @@ impl Plugin for RenderPlugin { app.insert_resource(device.clone()) .insert_resource(queue.clone()) .insert_resource(adapter_info.clone()) - .init_resource::() + .init_resource::() .register_type::() .register_type::(); @@ -307,17 +309,17 @@ fn extract(app_world: &mut World, render_app: &mut App) { .get_stage_mut::(&RenderStage::Extract) .unwrap(); - // temporarily add the render world to the app world as a resource - let scratch_world = app_world.remove_resource::().unwrap(); - let render_world = std::mem::replace(&mut render_app.world, scratch_world.0); - app_world.insert_resource(RenderWorld(render_world)); + // temporarily add the app world to the render world as a resource + let scratch_world = app_world.remove_resource::().unwrap(); + let inserted_world = std::mem::replace(app_world, scratch_world.0); + let running_world = &mut render_app.world; + running_world.insert_resource(MainWorld(inserted_world)); - extract.run(app_world); + extract.run(&mut running_world); + extract.apply_buffers(&mut running_world); - // add the render world back to the render app - let render_world = app_world.remove_resource::().unwrap(); - let scratch_world = std::mem::replace(&mut render_app.world, render_world.0); - app_world.insert_resource(ScratchRenderWorld(scratch_world)); - - extract.apply_buffers(&mut render_app.world); + // move the app world back, as if nothing happened. + let inserted_world = running_world.remove_resource::().unwrap(); + let scratch_world = std::mem::replace(app_world, inserted_world.0); + inserted_world.insert_resource(ScratchMainWorld(scratch_world)); } diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 71021fbde9cca..a7e0f11187d35 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -7,11 +7,11 @@ use crate::{ ShaderProcessor, ShaderReflectError, }, renderer::RenderDevice, - RenderWorld, + ExtractFromMainWorld, }; use bevy_asset::{AssetEvent, Assets, Handle}; -use bevy_ecs::event::EventReader; -use bevy_ecs::system::{Res, ResMut}; +use bevy_ecs::system::ResMut; +use bevy_ecs::{event::EventReader, system::lifetimeless::SRes}; use bevy_utils::{default, tracing::error, Entry, HashMap, HashSet}; use std::{hash::Hash, iter::FusedIterator, mem, ops::Deref, sync::Arc}; use thiserror::Error; @@ -546,11 +546,13 @@ impl PipelineCache { } pub(crate) fn extract_shaders( - mut world: ResMut, - shaders: Res>, - mut events: EventReader>, + mut cache: ResMut, + extract: ExtractFromMainWorld<( + SRes>, + EventReader<'static, 'static, AssetEvent>, + )>, ) { - let mut cache = world.resource_mut::(); + let (shaders, events) = extract.into_inner(); for event in events.iter() { match event { AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index 305a56ddd115b..7a7bea631bb38 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -2,10 +2,10 @@ use crate::{ render_resource::TextureView, renderer::{RenderDevice, RenderInstance}, texture::BevyDefault, - RenderApp, RenderStage, RenderWorld, + ExtractFromMainWorld, RenderApp, RenderStage, }; use bevy_app::{App, Plugin}; -use bevy_ecs::prelude::*; +use bevy_ecs::{prelude::*, system::lifetimeless::SRes}; use bevy_utils::{tracing::debug, HashMap, HashSet}; use bevy_window::{PresentMode, RawWindowHandleWrapper, WindowClosed, WindowId, Windows}; use std::ops::{Deref, DerefMut}; @@ -68,11 +68,10 @@ impl DerefMut for ExtractedWindows { } fn extract_windows( - mut render_world: ResMut, - mut closed: EventReader, - windows: Res, + mut closed: Extract>, + mut extracted_windows: ResMut, + windows: ExtractFromMainWorld>, ) { - let mut extracted_windows = render_world.get_resource_mut::().unwrap(); for window in windows.iter() { let (new_width, new_height) = ( window.physical_width().max(1), From 36e9b126f3c050d977d3ce2d6674cb55f0a93285 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Sun, 3 Apr 2022 14:55:15 +0100 Subject: [PATCH 02/16] Finish API Cleverly avoid needing the param to be 'static This same trick can't work for `StaticSystemParam` fwiw This is because `StaticSystemParam`'s item needs to exactly reference the original param, whereas this just stores it --- crates/bevy_render/src/extract_param.rs | 76 +++++-------------------- crates/bevy_render/src/lib.rs | 7 ++- 2 files changed, 19 insertions(+), 64 deletions(-) diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index d55c7c1c7171b..f7e5adfdc042c 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -1,82 +1,36 @@ -use std::ops::{Deref, DerefMut}; - use crate::MainWorld; use bevy_ecs::{ prelude::*, system::{ - ReadOnlySystemParamFetch, SystemParam, SystemParamFetch, SystemParamItem, SystemParamState, - SystemState, + ReadOnlySystemParamFetch, SystemParam, SystemParamFetch, SystemParamItem, SystemState, }, }; -pub struct ExtractFromMainWorld<'s, P: SystemParam + 'static>(SystemParamItem<'s, 's, P>) -where - P::Fetch: ReadOnlySystemParamFetch; - -impl<'s, P: SystemParam + 'static> Deref for ExtractFromMainWorld<'s, P> -where - P::Fetch: ReadOnlySystemParamFetch, -{ - type Target = SystemParamItem<'s, 's, P>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'s, P: SystemParam + 'static> DerefMut for ExtractFromMainWorld<'s, P> -where - P::Fetch: ReadOnlySystemParamFetch, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} +pub struct MainWorldState(SystemState

); -impl<'s, P: SystemParam + 'static> ExtractFromMainWorld<'s, P> -where - P::Fetch: ReadOnlySystemParamFetch, -{ - pub fn into_inner(self) -> SystemParamItem<'s, 's, P> { - self.0 +impl FromWorld for MainWorldState

{ + fn from_world(world: &mut World) -> Self { + Self(SystemState::new(&mut world.resource_mut::().0)) } } -impl<'s, P: SystemParam + 'static> SystemParam for ExtractFromMainWorld<'s, P> +#[derive(SystemParam)] +pub struct Extract<'w, 's, P: SystemParam + 'static> where P::Fetch: ReadOnlySystemParamFetch, { - type Fetch = ExtractFromMainWorldState

; + state: Local< + 's, + MainWorldState<<

::Fetch as SystemParamFetch<'static, 'static>>::Item>, + >, + world: Res<'w, World>, } -unsafe impl SystemParamState for ExtractFromMainWorldState

{ - fn init(world: &mut World, system_meta: &mut bevy_ecs::system::SystemMeta) -> Self { - Self { - world: SystemParamState::init(world, system_meta), - state: SystemState::new(&mut (*world.resource_mut::())), - } - } -} - -pub struct ExtractFromMainWorldState { - world: as SystemParam>::Fetch, - state: SystemState

, -} - -impl<'world, 'state, P: SystemParam + 'static> SystemParamFetch<'world, 'state> - for ExtractFromMainWorldState

+impl<'w, 's, P: SystemParam + 'static> Extract<'w, 's, P> where P::Fetch: ReadOnlySystemParamFetch, { - type Item = ExtractFromMainWorld<'state, P>; - - unsafe fn get_param( - state: &'state mut Self, - system_meta: &bevy_ecs::system::SystemMeta, - world: &'world World, - change_tick: u32, - ) -> Self::Item { - let world = SystemParamFetch::get_param(&mut state.world, system_meta, world, change_tick); - ExtractFromMainWorld(state.state.get(&world)) + pub fn value(&mut self) -> SystemParamItem<'_, '_, P> { + self.state.0.get(&self.world) } } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index ee2b6f7452ee4..ca1b282dd50e2 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -15,7 +15,8 @@ pub mod renderer; pub mod settings; pub mod texture; pub mod view; -pub use extract_param::ExtractFromMainWorld; + +pub use extract_param::Extract; pub mod prelude { #[doc(hidden)] @@ -312,7 +313,7 @@ fn extract(app_world: &mut World, render_app: &mut App) { // temporarily add the app world to the render world as a resource let scratch_world = app_world.remove_resource::().unwrap(); let inserted_world = std::mem::replace(app_world, scratch_world.0); - let running_world = &mut render_app.world; + let mut running_world = &mut render_app.world; running_world.insert_resource(MainWorld(inserted_world)); extract.run(&mut running_world); @@ -321,5 +322,5 @@ fn extract(app_world: &mut World, render_app: &mut App) { // move the app world back, as if nothing happened. let inserted_world = running_world.remove_resource::().unwrap(); let scratch_world = std::mem::replace(app_world, inserted_world.0); - inserted_world.insert_resource(ScratchMainWorld(scratch_world)); + app_world.insert_resource(ScratchMainWorld(scratch_world)); } From 80babbfd4fc159c2bc26ae9826f48df3c75f7727 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Sun, 3 Apr 2022 16:21:14 +0100 Subject: [PATCH 03/16] Remove `spawn_and_forget` It's unused, and was only ever needed for the weird extract logic Remove the trick because it's actually stupid --- crates/bevy_ecs/src/system/commands/mod.rs | 6 ------ crates/bevy_render/src/extract_param.rs | 11 +++-------- crates/bevy_render/src/lib.rs | 6 +++--- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index bf40e13fb98b1..e259f4a36b1ba 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -118,12 +118,6 @@ impl<'w, 's> Commands<'w, 's> { } } - /// Spawns a [`Bundle`] without pre-allocating an [`Entity`]. The [`Entity`] will be allocated - /// when this [`Command`] is applied. - pub fn spawn_and_forget(&mut self, bundle: impl Bundle) { - self.queue.push(Spawn { bundle }); - } - /// Creates a new entity with the components contained in `bundle`. /// /// This returns an [`EntityCommands`] builder, which enables inserting more components and diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index f7e5adfdc042c..75346d3fea043 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -1,9 +1,7 @@ use crate::MainWorld; use bevy_ecs::{ prelude::*, - system::{ - ReadOnlySystemParamFetch, SystemParam, SystemParamFetch, SystemParamItem, SystemState, - }, + system::{ReadOnlySystemParamFetch, SystemParam, SystemParamItem, SystemState}, }; pub struct MainWorldState(SystemState

); @@ -19,11 +17,8 @@ pub struct Extract<'w, 's, P: SystemParam + 'static> where P::Fetch: ReadOnlySystemParamFetch, { - state: Local< - 's, - MainWorldState<<

::Fetch as SystemParamFetch<'static, 'static>>::Item>, - >, - world: Res<'w, World>, + state: Local<'s, MainWorldState

>, + world: Res<'w, MainWorld>, } impl<'w, 's, P: SystemParam + 'static> Extract<'w, 's, P> diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index ca1b282dd50e2..418a6b06848e7 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -313,11 +313,11 @@ fn extract(app_world: &mut World, render_app: &mut App) { // temporarily add the app world to the render world as a resource let scratch_world = app_world.remove_resource::().unwrap(); let inserted_world = std::mem::replace(app_world, scratch_world.0); - let mut running_world = &mut render_app.world; + let running_world = &mut render_app.world; running_world.insert_resource(MainWorld(inserted_world)); - extract.run(&mut running_world); - extract.apply_buffers(&mut running_world); + extract.run(running_world); + extract.apply_buffers(running_world); // move the app world back, as if nothing happened. let inserted_world = running_world.remove_resource::().unwrap(); From 90659d4af4d7d7bae9901c787f71c7ee9b569072 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Sun, 3 Apr 2022 20:20:03 +0100 Subject: [PATCH 04/16] Add some docs --- crates/bevy_render/src/extract_param.rs | 37 +++++++++++++++++++++++++ crates/bevy_render/src/lib.rs | 6 ++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index 75346d3fea043..95ba1333ae558 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -4,6 +4,7 @@ use bevy_ecs::{ system::{ReadOnlySystemParamFetch, SystemParam, SystemParamItem, SystemState}, }; +/// Implementation detail of [`Extract`] pub struct MainWorldState(SystemState

); impl FromWorld for MainWorldState

{ @@ -12,6 +13,42 @@ impl FromWorld for MainWorldState

{ } } +/// A helper for accessing [`MainWorld`] content using a system parameter. +/// +/// A [`SystemParam`] adapter which applies the contained `SystemParam` to the [`World`] +/// contained in [`MainWorld`]. This parameter only works for systems run +/// during [`RenderStage::Extract`]. +/// +/// This requires that the contained [`SystemParam`] does not mutate the world, as it +/// uses [`Res`](Res). To get access to the contained `SystemParam`'s item, you +/// must use [`Extract::value`]. This is required because of lifetime limitations in +/// the `SystemParam` api. +/// +/// ## Context +/// +/// [`RenderStage::Extract`] is used to extract (move) data from the simulation world ([`MainWorld`]) to the +/// render world. The render world drives rendering each frame (generally to a [Window]). +/// This design is used to allow performing calculations related to rendering a prior frame at the same +/// time as the next frame is simulated, which increases throughput (FPS). +/// +/// [`Extract`] is used to get data from the main world during [`RenderStage::Extract`]. +/// +/// ## Examples +/// +/// ```rust +/// use bevy_ecs::prelude::*; +/// use bevy_render::Extract; +/// # #[derive(Component)] +/// # struct Cloud; +/// fn extract_clouds(mut commands: Commands, mut clouds: Extract>>) { +/// for cloud in clouds.value().iter() { +/// commands.get_or_spawn(cloud).insert(Cloud); +/// } +/// } +/// ``` +/// +/// [`RenderStage::Extract`]: crate::RenderStage::Extract +/// [Window]: bevy_window::Window #[derive(SystemParam)] pub struct Extract<'w, 's, P: SystemParam + 'static> where diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 418a6b06848e7..744ac8aa022d9 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -81,7 +81,9 @@ pub enum RenderStage { Cleanup, } -/// The Render App World. This is only available as a resource during the Extract step. +/// The simulation [World] of the application, stored as a resource. +/// This resource is only available during [`RenderStage::Extract`]. +/// See [`Extract`] for more details. #[derive(Default)] pub struct MainWorld(World); @@ -110,7 +112,7 @@ pub mod main_graph { pub struct RenderApp; /// A "scratch" world used to avoid allocating new worlds every frame when -/// swapping out the [`RenderWorld`]. +/// swapping out the [`MainWorld`] for [`RenderStage::Extract`]. #[derive(Default)] struct ScratchMainWorld(World); From bcf51e630f506f1677fd1306599a26fa3097c923 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Fri, 24 Jun 2022 16:37:56 +0100 Subject: [PATCH 05/16] Apply buffers after 'extracting' the world See https://github.com/bevyengine/bevy/issues/5082 --- crates/bevy_render/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 744ac8aa022d9..fb86d418b1acc 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -319,10 +319,13 @@ fn extract(app_world: &mut World, render_app: &mut App) { running_world.insert_resource(MainWorld(inserted_world)); extract.run(running_world); - extract.apply_buffers(running_world); - // move the app world back, as if nothing happened. let inserted_world = running_world.remove_resource::().unwrap(); let scratch_world = std::mem::replace(app_world, inserted_world.0); app_world.insert_resource(ScratchMainWorld(scratch_world)); + + // Note: We apply buffers (read, Commands) after the `MainWorld` has been removed from the render app's world + // so that in future, pipelining will be able to do this too without any code relying on it. + // see + extract.apply_buffers(running_world); } From 8a350d2c3242d872d9a06b73d004d676612f5bf4 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Fri, 24 Jun 2022 16:40:43 +0100 Subject: [PATCH 06/16] Fix existing references to `ExtractFromMainWorld` --- .../src/render_resource/pipeline_cache.rs | 13 +++++-------- crates/bevy_render/src/view/window.rs | 8 ++++---- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index a7e0f11187d35..2181974fb17ef 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -7,11 +7,11 @@ use crate::{ ShaderProcessor, ShaderReflectError, }, renderer::RenderDevice, - ExtractFromMainWorld, + Extract, }; use bevy_asset::{AssetEvent, Assets, Handle}; -use bevy_ecs::system::ResMut; -use bevy_ecs::{event::EventReader, system::lifetimeless::SRes}; +use bevy_ecs::event::EventReader; +use bevy_ecs::system::{Res, ResMut}; use bevy_utils::{default, tracing::error, Entry, HashMap, HashSet}; use std::{hash::Hash, iter::FusedIterator, mem, ops::Deref, sync::Arc}; use thiserror::Error; @@ -547,12 +547,9 @@ impl PipelineCache { pub(crate) fn extract_shaders( mut cache: ResMut, - extract: ExtractFromMainWorld<( - SRes>, - EventReader<'static, 'static, AssetEvent>, - )>, + mut extract: Extract<(Res>, EventReader>)>, ) { - let (shaders, events) = extract.into_inner(); + let (shaders, mut events) = extract.value(); for event in events.iter() { match event { AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index 7a7bea631bb38..6b3a0d768bf59 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -2,7 +2,7 @@ use crate::{ render_resource::TextureView, renderer::{RenderDevice, RenderInstance}, texture::BevyDefault, - ExtractFromMainWorld, RenderApp, RenderStage, + Extract, RenderApp, RenderStage, }; use bevy_app::{App, Plugin}; use bevy_ecs::{prelude::*, system::lifetimeless::SRes}; @@ -70,9 +70,9 @@ impl DerefMut for ExtractedWindows { fn extract_windows( mut closed: Extract>, mut extracted_windows: ResMut, - windows: ExtractFromMainWorld>, + mut windows: Extract>, ) { - for window in windows.iter() { + for window in windows.value().iter() { let (new_width, new_height) = ( window.physical_width().max(1), window.physical_height().max(1), @@ -108,7 +108,7 @@ fn extract_windows( extracted_window.physical_height = new_height; } } - for closed_window in closed.iter() { + for closed_window in closed.value().iter() { extracted_windows.remove(&closed_window.id); } } From 620495013278fb07e8e374dafb0eddaec67a3186 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Fri, 24 Jun 2022 17:07:57 +0100 Subject: [PATCH 07/16] Add reporting of incorrect extract systems --- crates/bevy_ecs/src/schedule/stage.rs | 23 +++++++++++++++++++++ crates/bevy_render/src/lib.rs | 29 ++++++++++++++++++++------- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/stage.rs b/crates/bevy_ecs/src/schedule/stage.rs index ff26ced892a6f..b01d0a1247ce9 100644 --- a/crates/bevy_ecs/src/schedule/stage.rs +++ b/crates/bevy_ecs/src/schedule/stage.rs @@ -88,6 +88,7 @@ pub struct SystemStage { last_tick_check: u32, /// If true, buffers will be automatically applied at the end of the stage. If false, buffers must be manually applied. apply_buffers: bool, + must_use_resource: Option, } impl SystemStage { @@ -110,6 +111,7 @@ impl SystemStage { uninitialized_at_end: vec![], last_tick_check: Default::default(), apply_buffers: true, + must_use_resource: None, } } @@ -139,6 +141,10 @@ impl SystemStage { self.executor = executor; } + pub fn set_must_read_resource(&mut self, resource_id: ComponentId) { + self.must_use_resource = Some(resource_id); + } + #[must_use] pub fn with_system(mut self, system: impl IntoSystemDescriptor) -> Self { self.add_system(system); @@ -563,6 +569,20 @@ impl SystemStage { } } + fn check_uses_resource(&self, resource_id: ComponentId, world: &World) { + debug_assert!(!self.systems_modified); + for system in &self.parallel { + let access = system.component_access().unwrap(); + if !access.has_read(resource_id) { + let component_name = world.components().get_info(resource_id).unwrap().name(); + println!( + "System {} doesn't access resource {component_name}, despite being required to", + system.name() + ); + } + } + } + /// All system and component change ticks are scanned once the world counter has incremented /// at least [`CHECK_TICK_THRESHOLD`](crate::change_detection::CHECK_TICK_THRESHOLD) /// times since the previous `check_tick` scan. @@ -782,6 +802,9 @@ impl Stage for SystemStage { if world.contains_resource::() { self.report_ambiguities(world); } + if let Some(resource_id) = self.must_use_resource { + self.check_uses_resource(resource_id, world); + } } else if self.executor_modified { self.executor.rebuild_cached_data(&self.parallel); self.executor_modified = false; diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index fb86d418b1acc..493ec955055fe 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -47,7 +47,10 @@ use bevy_app::{App, AppLabel, Plugin}; use bevy_asset::{AddAsset, AssetServer}; use bevy_ecs::prelude::*; use bevy_utils::tracing::debug; -use std::ops::{Deref, DerefMut}; +use std::{ + any::TypeId, + ops::{Deref, DerefMut}, +}; /// Contains the default Bevy rendering backend based on wgpu. #[derive(Default)] @@ -111,11 +114,6 @@ pub mod main_graph { #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)] pub struct RenderApp; -/// A "scratch" world used to avoid allocating new worlds every frame when -/// swapping out the [`MainWorld`] for [`RenderStage::Extract`]. -#[derive(Default)] -struct ScratchMainWorld(World); - impl Plugin for RenderPlugin { /// Initializes the renderer, sets up the [`RenderStage`](RenderStage) and creates the rendering sub-app. fn build(&self, app: &mut App) { @@ -164,8 +162,20 @@ impl Plugin for RenderPlugin { let mut render_app = App::empty(); let mut extract_stage = SystemStage::parallel().with_system(PipelineCache::extract_shaders); + // Get the ComponentId for MainWorld. This does technically 'waste' a `WorldId`, but that's probably fine + render_app.init_resource::(); + render_app.world.remove_resource::(); + let main_world_in_render = render_app + .world + .components() + .get_resource_id(TypeId::of::()); + // `Extract` systems must read from the main world. We want to emit an error when that doesn't occur + // Safe to unwrap: Ensured it existed just above + extract_stage.set_must_read_resource(main_world_in_render.unwrap()); // don't apply buffers when the stage finishes running - // extract stage runs on the app world, but the buffers are applied to the render world + // extract stage runs on the render world, but buffers are applied + // after losing access to the main world. + // See also https://github.com/bevyengine/bevy/issues/5082 extract_stage.set_apply_buffers(false); render_app .add_stage(RenderStage::Extract, extract_stage) @@ -304,6 +314,11 @@ impl Plugin for RenderPlugin { } } +/// A "scratch" world used to avoid allocating new worlds every frame when +/// swapping out the [`MainWorld`] for [`RenderStage::Extract`]. +#[derive(Default)] +struct ScratchMainWorld(World); + /// 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) { From 0a12e289737fb2a1726ee88f63dee65581327f9b Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Fri, 24 Jun 2022 17:21:02 +0100 Subject: [PATCH 08/16] Fix extractions which weren't compiling --- crates/bevy_sprite/src/render/mod.rs | 35 ++++++------- crates/bevy_text/src/text2d.rs | 20 ++++---- crates/bevy_ui/src/render/mod.rs | 75 +++++++++++++++------------- 3 files changed, 68 insertions(+), 62 deletions(-) diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index c0066482014f7..19aec33dda572 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -23,7 +23,7 @@ use bevy_render::{ renderer::{RenderDevice, RenderQueue}, texture::{BevyDefault, Image}, view::{Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, Visibility}, - RenderWorld, + Extract, }; use bevy_transform::components::GlobalTransform; use bevy_utils::FloatOrd; @@ -198,14 +198,13 @@ pub struct SpriteAssetEvents { } pub fn extract_sprite_events( - mut render_world: ResMut, - mut image_events: EventReader>, + mut events: ResMut, + mut image_events: Extract>>, ) { - let mut events = render_world.resource_mut::(); let SpriteAssetEvents { ref mut images } = *events; images.clear(); - for image in image_events.iter() { + for image in image_events.value().iter() { // AssetEvent: !Clone images.push(match image { AssetEvent::Created { handle } => AssetEvent::Created { @@ -222,19 +221,20 @@ pub fn extract_sprite_events( } pub fn extract_sprites( - mut render_world: ResMut, - texture_atlases: Res>, - sprite_query: Query<(&Visibility, &Sprite, &GlobalTransform, &Handle)>, - atlas_query: Query<( - &Visibility, - &TextureAtlasSprite, - &GlobalTransform, - &Handle, - )>, + mut extracted_sprites: ResMut, + mut texture_atlases: Extract>>, + mut sprite_query: Extract)>>, + mut atlas_query: Extract< + Query<( + &Visibility, + &TextureAtlasSprite, + &GlobalTransform, + &Handle, + )>, + >, ) { - let mut extracted_sprites = render_world.resource_mut::(); extracted_sprites.sprites.clear(); - for (visibility, sprite, transform, handle) in sprite_query.iter() { + for (visibility, sprite, transform, handle) in sprite_query.value().iter() { if !visibility.is_visible { continue; } @@ -252,7 +252,8 @@ pub fn extract_sprites( anchor: sprite.anchor.as_vec(), }); } - for (visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() { + let texture_atlases = texture_atlases.value(); + for (visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.value().iter() { if !visibility.is_visible { continue; } diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index d0bebbad3609a..a0ac0929fe321 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -10,7 +10,7 @@ use bevy_ecs::{ }; use bevy_math::{Vec2, Vec3}; use bevy_reflect::Reflect; -use bevy_render::{texture::Image, view::Visibility, RenderWorld}; +use bevy_render::{texture::Image, view::Visibility, Extract}; use bevy_sprite::{Anchor, ExtractedSprite, ExtractedSprites, TextureAtlas}; use bevy_transform::prelude::{GlobalTransform, Transform}; use bevy_utils::HashSet; @@ -61,17 +61,17 @@ pub struct Text2dBundle { } pub fn extract_text2d_sprite( - mut render_world: ResMut, - texture_atlases: Res>, - text_pipeline: Res, - windows: Res, - text2d_query: Query<(Entity, &Visibility, &Text, &GlobalTransform, &Text2dSize)>, + mut extracted_sprites: ResMut, + mut texture_atlases: Extract>>, + mut text_pipeline: Extract>, + mut windows: Extract>, + mut text2d_query: Extract>, ) { - let mut extracted_sprites = render_world.resource_mut::(); - - let scale_factor = windows.scale_factor(WindowId::primary()) as f32; + let scale_factor = windows.value().scale_factor(WindowId::primary()) as f32; - for (entity, visibility, text, transform, calculated_size) in text2d_query.iter() { + let text_pipeline = text_pipeline.value(); + let texture_atlases = texture_atlases.value(); + for (entity, visibility, text, transform, calculated_size) in text2d_query.value().iter() { if !visibility.is_visible { continue; } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 598dd10eaa866..888fa5885baab 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -21,7 +21,7 @@ use bevy_render::{ renderer::{RenderDevice, RenderQueue}, texture::Image, view::{ExtractedView, ViewUniforms, Visibility}, - RenderApp, RenderStage, RenderWorld, + Extract, RenderApp, RenderStage, }; use bevy_sprite::{Rect, SpriteAssetEvents, TextureAtlas}; use bevy_text::{DefaultTextPipeline, Text}; @@ -174,20 +174,22 @@ pub struct ExtractedUiNodes { } pub fn extract_uinodes( - mut render_world: ResMut, - images: Res>, - uinode_query: Query<( - &Node, - &GlobalTransform, - &UiColor, - &UiImage, - &Visibility, - Option<&CalculatedClip>, - )>, + mut extracted_uinodes: ResMut, + mut images: Extract>>, + mut uinode_query: Extract< + Query<( + &Node, + &GlobalTransform, + &UiColor, + &UiImage, + &Visibility, + Option<&CalculatedClip>, + )>, + >, ) { - let mut extracted_uinodes = render_world.resource_mut::(); extracted_uinodes.uinodes.clear(); - for (uinode, transform, color, image, visibility, clip) in uinode_query.iter() { + let images = images.value(); + for (uinode, transform, color, image, visibility, clip) in uinode_query.value().iter() { if !visibility.is_visible { continue; } @@ -226,10 +228,9 @@ pub struct DefaultCameraView(pub Entity); pub fn extract_default_ui_camera_view( mut commands: Commands, - render_world: Res, - query: Query<(Entity, &Camera, Option<&CameraUi>), With>, + mut query: Extract), With>>, ) { - for (entity, camera, camera_ui) in query.iter() { + for (entity, camera, camera_ui) in query.value().iter() { // ignore cameras with disabled ui if let Some(&CameraUi { is_enabled: false, .. @@ -249,9 +250,9 @@ pub fn extract_default_ui_camera_view( }; projection.update(logical_size.x, logical_size.y); // This roundabout approach is required because spawn().id() won't work in this context - let default_camera_view = render_world.entities().reserve_entity(); - commands - .get_or_spawn(default_camera_view) + + let default_camera_view = commands + .spawn() .insert(ExtractedView { projection: projection.get_projection_matrix(), transform: GlobalTransform::from_xyz( @@ -261,7 +262,8 @@ pub fn extract_default_ui_camera_view( ), width: physical_size.x, height: physical_size.y, - }); + }) + .id(); commands.get_or_spawn(entity).insert_bundle(( DefaultCameraView(default_camera_view), RenderPhase::::default(), @@ -271,24 +273,27 @@ pub fn extract_default_ui_camera_view( } pub fn extract_text_uinodes( - mut render_world: ResMut, - texture_atlases: Res>, - text_pipeline: Res, - windows: Res, - uinode_query: Query<( - Entity, - &Node, - &GlobalTransform, - &Text, - &Visibility, - Option<&CalculatedClip>, - )>, + mut extracted_uinodes: ResMut, + mut texture_atlases: Extract>>, + mut text_pipeline: Extract>, + mut windows: Extract>, + mut uinode_query: Extract< + Query<( + Entity, + &Node, + &GlobalTransform, + &Text, + &Visibility, + Option<&CalculatedClip>, + )>, + >, ) { - let mut extracted_uinodes = render_world.resource_mut::(); + let scale_factor = windows.value().scale_factor(WindowId::primary()) as f32; - let scale_factor = windows.scale_factor(WindowId::primary()) as f32; + let text_pipeline = text_pipeline.value(); + let texture_atlases = texture_atlases.value(); - for (entity, uinode, transform, text, visibility, clip) in uinode_query.iter() { + for (entity, uinode, transform, text, visibility, clip) in uinode_query.value().iter() { if !visibility.is_visible { continue; } From da2dc9f20c6c5074037999eb6e70eff965f0764c Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Fri, 24 Jun 2022 17:40:31 +0100 Subject: [PATCH 09/16] Oops, all `println` --- crates/bevy_ecs/src/schedule/stage.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/stage.rs b/crates/bevy_ecs/src/schedule/stage.rs index b01d0a1247ce9..d46196fe40e7e 100644 --- a/crates/bevy_ecs/src/schedule/stage.rs +++ b/crates/bevy_ecs/src/schedule/stage.rs @@ -12,7 +12,10 @@ use crate::{ }, world::{World, WorldId}, }; -use bevy_utils::{tracing::info, HashMap, HashSet}; +use bevy_utils::{ + tracing::{info, warn}, + HashMap, HashSet, +}; use downcast_rs::{impl_downcast, Downcast}; use fixedbitset::FixedBitSet; use std::fmt::Debug; @@ -575,7 +578,7 @@ impl SystemStage { let access = system.component_access().unwrap(); if !access.has_read(resource_id) { let component_name = world.components().get_info(resource_id).unwrap().name(); - println!( + warn!( "System {} doesn't access resource {component_name}, despite being required to", system.name() ); From ef45123702f07d026f36c7f505d5fa7b483598da Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Fri, 24 Jun 2022 18:48:12 +0100 Subject: [PATCH 10/16] Migrate all extractions to new method --- crates/bevy_core_pipeline/src/core_2d/mod.rs | 6 +- crates/bevy_core_pipeline/src/core_3d/mod.rs | 6 +- crates/bevy_pbr/src/render/light.rs | 50 +++++++++------- crates/bevy_pbr/src/render/mesh.rs | 60 ++++++++++++-------- crates/bevy_render/src/camera/camera.rs | 19 ++++--- crates/bevy_render/src/extract_component.rs | 18 +++--- crates/bevy_render/src/extract_resource.rs | 8 ++- crates/bevy_render/src/render_asset.rs | 15 ++--- crates/bevy_sprite/src/mesh2d/mesh.rs | 6 +- examples/2d/mesh2d_manual.rs | 9 ++- examples/shader/animate_shader.rs | 43 +++++++------- examples/stress_tests/many_lights.rs | 7 ++- 12 files changed, 142 insertions(+), 105 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_2d/mod.rs b/crates/bevy_core_pipeline/src/core_2d/mod.rs index 4e1e8098e93c9..93afcc61ccc2c 100644 --- a/crates/bevy_core_pipeline/src/core_2d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_2d/mod.rs @@ -25,7 +25,7 @@ use bevy_render::{ DrawFunctionId, DrawFunctions, EntityPhaseItem, PhaseItem, RenderPhase, }, render_resource::CachedRenderPipelineId, - RenderApp, RenderStage, + Extract, RenderApp, RenderStage, }; use bevy_utils::FloatOrd; use std::ops::Range; @@ -123,9 +123,9 @@ impl BatchedPhaseItem for Transparent2d { pub fn extract_core_2d_camera_phases( mut commands: Commands, - cameras_2d: Query<(Entity, &Camera), With>, + mut cameras_2d: Extract>>, ) { - for (entity, camera) in cameras_2d.iter() { + for (entity, camera) in cameras_2d.value().iter() { if camera.is_active { commands .get_or_spawn(entity) diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index e3b230a1e7e20..170841be00dbd 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -32,7 +32,7 @@ use bevy_render::{ renderer::RenderDevice, texture::TextureCache, view::ViewDepthTexture, - RenderApp, RenderStage, + Extract, RenderApp, RenderStage, }; use bevy_utils::{FloatOrd, HashMap}; @@ -201,9 +201,9 @@ impl CachedRenderPipelinePhaseItem for Transparent3d { pub fn extract_core_3d_camera_phases( mut commands: Commands, - cameras_3d: Query<(Entity, &Camera), With>, + mut cameras_3d: Extract>>, ) { - for (entity, camera) in cameras_3d.iter() { + for (entity, camera) in cameras_3d.value().iter() { if camera.is_active { commands.get_or_spawn(entity).insert_bundle(( RenderPhase::::default(), diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index bf50d1a8e5fc3..4282da10afa86 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -27,6 +27,7 @@ use bevy_render::{ view::{ ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms, Visibility, VisibleEntities, }, + Extract, }; use bevy_transform::components::GlobalTransform; use bevy_utils::FloatOrd; @@ -381,8 +382,11 @@ pub struct ExtractedClustersPointLights { data: Vec, } -pub fn extract_clusters(mut commands: Commands, views: Query<(Entity, &Clusters), With>) { - for (entity, clusters) in views.iter() { +pub fn extract_clusters( + mut commands: Commands, + mut views: Extract>>, +) { + for (entity, clusters) in views.value().iter() { commands.get_or_spawn(entity).insert_bundle(( ExtractedClustersPointLights { data: clusters.lights.clone(), @@ -399,24 +403,28 @@ pub fn extract_clusters(mut commands: Commands, views: Query<(Entity, &Clusters) #[allow(clippy::too_many_arguments)] pub fn extract_lights( mut commands: Commands, - point_light_shadow_map: Res, - directional_light_shadow_map: Res, - global_point_lights: Res, - mut point_lights: Query<(&PointLight, &mut CubemapVisibleEntities, &GlobalTransform)>, - mut directional_lights: Query<( - Entity, - &DirectionalLight, - &mut VisibleEntities, - &GlobalTransform, - &Visibility, - )>, + mut point_light_shadow_map: Extract>, + mut directional_light_shadow_map: Extract>, + mut global_point_lights: Extract>, + mut point_lights: Extract>, + mut directional_lights: Extract< + Query<( + Entity, + &DirectionalLight, + &VisibleEntities, + &GlobalTransform, + &Visibility, + )>, + >, mut previous_point_lights_len: Local, ) { // NOTE: These shadow map resources are extracted here as they are used here too so this avoids // races between scheduling of ExtractResourceSystems and this system. + let point_light_shadow_map = point_light_shadow_map.value(); if point_light_shadow_map.is_changed() { commands.insert_resource(point_light_shadow_map.clone()); } + let directional_light_shadow_map = directional_light_shadow_map.value(); if directional_light_shadow_map.is_changed() { commands.insert_resource(directional_light_shadow_map.clone()); } @@ -430,11 +438,12 @@ pub fn extract_lights( let point_light_texel_size = 2.0 / point_light_shadow_map.size as f32; let mut point_lights_values = Vec::with_capacity(*previous_point_lights_len); - for entity in global_point_lights.iter().copied() { - if let Ok((point_light, cubemap_visible_entities, transform)) = point_lights.get_mut(entity) - { - let render_cubemap_visible_entities = - std::mem::take(cubemap_visible_entities.into_inner()); + let point_lights = point_lights.value(); + for entity in global_point_lights.value().iter().copied() { + if let Ok((point_light, cubemap_visible_entities, transform)) = point_lights.get(entity) { + // TODO: This is very much not ideal. We should be able to re-use the vector memory. + // However, since exclusive access to the main world in extract is ill-advised, we just clone here. + let render_cubemap_visible_entities = cubemap_visible_entities.clone(); point_lights_values.push(( entity, ( @@ -463,7 +472,7 @@ pub fn extract_lights( commands.insert_or_spawn_batch(point_lights_values); for (entity, directional_light, visible_entities, transform, visibility) in - directional_lights.iter_mut() + directional_lights.value().iter() { if !visibility.is_visible { continue; @@ -481,7 +490,8 @@ pub fn extract_lights( ); let directional_light_texel_size = largest_dimension / directional_light_shadow_map.size as f32; - let render_visible_entities = std::mem::take(visible_entities.into_inner()); + // TODO: As above + let render_visible_entities = visible_entities.clone(); commands.get_or_spawn(entity).insert_bundle(( ExtractedDirectionalLight { color: directional_light.color, diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 950d32d6fba18..a6d6488a09b59 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -25,7 +25,7 @@ use bevy_render::{ BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, }, view::{ComputedVisibility, ViewUniform, ViewUniformOffset, ViewUniforms}, - RenderApp, RenderStage, + Extract, RenderApp, RenderStage, }; use bevy_transform::components::GlobalTransform; use std::num::NonZeroU64; @@ -118,29 +118,35 @@ pub fn extract_meshes( mut commands: Commands, mut previous_caster_len: Local, mut previous_not_caster_len: Local, - caster_query: Query< - ( - Entity, - &ComputedVisibility, - &GlobalTransform, - &Handle, - Option<&NotShadowReceiver>, - ), - Without, + mut caster_query: Extract< + Query< + ( + Entity, + &ComputedVisibility, + &GlobalTransform, + &Handle, + Option<&NotShadowReceiver>, + ), + Without, + >, >, - not_caster_query: Query< - ( - Entity, - &ComputedVisibility, - &GlobalTransform, - &Handle, - Option<&NotShadowReceiver>, - ), - With, + mut not_caster_query: Extract< + Query< + ( + Entity, + &ComputedVisibility, + &GlobalTransform, + &Handle, + Option<&NotShadowReceiver>, + ), + With, + >, >, ) { let mut caster_values = Vec::with_capacity(*previous_caster_len); - for (entity, computed_visibility, transform, handle, not_receiver) in caster_query.iter() { + for (entity, computed_visibility, transform, handle, not_receiver) in + caster_query.value().iter() + { if !computed_visibility.is_visible { continue; } @@ -165,7 +171,9 @@ pub fn extract_meshes( commands.insert_or_spawn_batch(caster_values); let mut not_caster_values = Vec::with_capacity(*previous_not_caster_len); - for (entity, computed_visibility, transform, mesh, not_receiver) in not_caster_query.iter() { + for (entity, computed_visibility, transform, mesh, not_receiver) in + not_caster_query.value().iter() + { if !computed_visibility.is_visible { continue; } @@ -238,9 +246,9 @@ impl SkinnedMeshJoints { } pub fn extract_skinned_meshes( - query: Query<(Entity, &ComputedVisibility, &SkinnedMesh)>, - inverse_bindposes: Res>, - joint_query: Query<&GlobalTransform>, + mut query: Extract>, + mut inverse_bindposes: Extract>>, + mut joint_query: Extract>, mut commands: Commands, mut previous_len: Local, mut previous_joint_len: Local, @@ -249,7 +257,9 @@ pub fn extract_skinned_meshes( let mut joints = Vec::with_capacity(*previous_joint_len); let mut last_start = 0; - for (entity, computed_visibility, skin) in query.iter() { + let inverse_bindposes = inverse_bindposes.value(); + let joint_query = joint_query.value(); + for (entity, computed_visibility, skin) in query.value().iter() { if !computed_visibility.is_visible { continue; } diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 06f8ebf818f7e..8448a7ae1736f 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -4,6 +4,7 @@ use crate::{ render_asset::RenderAssets, render_resource::TextureView, view::{ExtractedView, ExtractedWindows, VisibleEntities}, + Extract, }; use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_derive::{Deref, DerefMut}; @@ -398,15 +399,17 @@ pub struct ExtractedCamera { pub fn extract_cameras( mut commands: Commands, - query: Query<( - Entity, - &Camera, - &CameraRenderGraph, - &GlobalTransform, - &VisibleEntities, - )>, + mut query: Extract< + Query<( + Entity, + &Camera, + &CameraRenderGraph, + &GlobalTransform, + &VisibleEntities, + )>, + >, ) { - for (entity, camera, camera_render_graph, transform, visible_entities) in query.iter() { + for (entity, camera, camera_render_graph, transform, visible_entities) in query.value().iter() { if !camera.is_active { continue; } diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index 673b23c30770e..04dc5dd52c188 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -2,15 +2,15 @@ use crate::{ render_resource::{encase::internal::WriteInto, DynamicUniformBuffer, ShaderType}, renderer::{RenderDevice, RenderQueue}, view::ComputedVisibility, - RenderApp, RenderStage, + Extract, RenderApp, RenderStage, }; use bevy_app::{App, Plugin}; use bevy_asset::{Asset, Handle}; use bevy_ecs::{ component::Component, prelude::*, - query::{QueryItem, WorldQuery}, - system::{lifetimeless::Read, StaticSystemParam}, + query::{QueryItem, ReadOnlyWorldQuery, WorldQuery}, + system::lifetimeless::Read, }; use std::{marker::PhantomData, ops::Deref}; @@ -34,9 +34,9 @@ impl DynamicUniformIndex { /// in the [`RenderStage::Extract`](crate::RenderStage::Extract) step. pub trait ExtractComponent: Component { /// ECS [`WorldQuery`] to fetch the components to extract. - type Query: WorldQuery; + type Query: WorldQuery + ReadOnlyWorldQuery; /// Filters the entities with additional constraints. - type Filter: WorldQuery; + type Filter: WorldQuery + ReadOnlyWorldQuery; /// Defines how the component is transferred into the "render world". fn extract_component(item: QueryItem) -> Self; } @@ -182,10 +182,10 @@ impl ExtractComponent for Handle { fn extract_components( mut commands: Commands, mut previous_len: Local, - mut query: StaticSystemParam>, + mut query: Extract>, ) { let mut values = Vec::with_capacity(*previous_len); - for (entity, query_item) in query.iter_mut() { + for (entity, query_item) in query.value().iter_mut() { values.push((entity, (C::extract_component(query_item),))); } *previous_len = values.len(); @@ -196,10 +196,10 @@ fn extract_components( fn extract_visible_components( mut commands: Commands, mut previous_len: Local, - mut query: StaticSystemParam, C::Query), C::Filter>>, + mut query: Extract>, ) { let mut values = Vec::with_capacity(*previous_len); - for (entity, computed_visibility, query_item) in query.iter_mut() { + for (entity, computed_visibility, query_item) in query.value().iter_mut() { if computed_visibility.is_visible { values.push((entity, (C::extract_component(query_item),))); } diff --git a/crates/bevy_render/src/extract_resource.rs b/crates/bevy_render/src/extract_resource.rs index 5db3be904b263..80a9acb6adb15 100644 --- a/crates/bevy_render/src/extract_resource.rs +++ b/crates/bevy_render/src/extract_resource.rs @@ -4,7 +4,7 @@ use bevy_app::{App, Plugin}; use bevy_ecs::system::{Commands, Res, Resource}; pub use bevy_render_macros::ExtractResource; -use crate::{RenderApp, RenderStage}; +use crate::{Extract, RenderApp, RenderStage}; /// Describes how a resource gets extracted for rendering. /// @@ -39,7 +39,11 @@ impl Plugin for ExtractResourcePlugin { /// This system extracts the resource of the corresponding [`Resource`] type /// by cloning it. -pub fn extract_resource(mut commands: Commands, resource: Res) { +pub fn extract_resource( + mut commands: Commands, + mut resource: Extract>, +) { + let resource = resource.value(); if resource.is_changed() { commands.insert_resource(R::extract_resource(resource.into_inner())); } diff --git a/crates/bevy_render/src/render_asset.rs b/crates/bevy_render/src/render_asset.rs index 22936f3e677be..a0580ed3cee30 100644 --- a/crates/bevy_render/src/render_asset.rs +++ b/crates/bevy_render/src/render_asset.rs @@ -1,4 +1,4 @@ -use crate::{RenderApp, RenderStage}; +use crate::{Extract, RenderApp, RenderStage}; use bevy_app::{App, Plugin}; use bevy_asset::{Asset, AssetEvent, Assets, Handle}; use bevy_ecs::{ @@ -128,15 +128,15 @@ pub type RenderAssets = HashMap, ::PreparedAsset> /// into the "render world". fn extract_render_asset( mut commands: Commands, - mut events: EventReader>, - assets: Res>, + mut events: Extract>>, + mut assets: Extract>>, ) { let mut changed_assets = HashSet::default(); let mut removed = Vec::new(); - for event in events.iter() { + for event in events.value().iter() { match event { AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { - changed_assets.insert(handle); + changed_assets.insert(handle.clone_weak()); } AssetEvent::Removed { handle } => { changed_assets.remove(handle); @@ -146,9 +146,10 @@ fn extract_render_asset( } let mut extracted_assets = Vec::new(); + let assets = assets.value(); for handle in changed_assets.drain() { - if let Some(asset) = assets.get(handle) { - extracted_assets.push((handle.clone_weak(), asset.extract_asset())); + if let Some(asset) = assets.get(&handle) { + extracted_assets.push((handle, asset.extract_asset())); } } diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 7a6ebfeadff93..1db4510f1fd20 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -17,7 +17,7 @@ use bevy_render::{ BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, }, view::{ComputedVisibility, ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms}, - RenderApp, RenderStage, + Extract, RenderApp, RenderStage, }; use bevy_transform::components::GlobalTransform; @@ -116,10 +116,10 @@ bitflags::bitflags! { pub fn extract_mesh2d( mut commands: Commands, mut previous_len: Local, - query: Query<(Entity, &ComputedVisibility, &GlobalTransform, &Mesh2dHandle)>, + mut query: Extract>, ) { let mut values = Vec::with_capacity(*previous_len); - for (entity, computed_visibility, transform, handle) in query.iter() { + for (entity, computed_visibility, transform, handle) in query.value().iter() { if !computed_visibility.is_visible { continue; } diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 2e6c9f6c479a7..88bfc339bbf11 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -19,7 +19,7 @@ use bevy::{ }, texture::BevyDefault, view::VisibleEntities, - RenderApp, RenderStage, + Extract, RenderApp, RenderStage, }, sprite::{ DrawMesh2d, Mesh2dHandle, Mesh2dPipeline, Mesh2dPipelineKey, Mesh2dUniform, @@ -286,10 +286,13 @@ impl Plugin for ColoredMesh2dPlugin { pub fn extract_colored_mesh2d( mut commands: Commands, mut previous_len: Local, - query: Query<(Entity, &ComputedVisibility), With>, + // When extracting, you must use `Extract` to mark the `SystemParam`s + // which should be taken from the main world. + mut query: Extract>>, ) { let mut values = Vec::with_capacity(*previous_len); - for (entity, computed_visibility) in query.iter() { + // The `value` method fetches the `SystemParam` in the `Extract` from the main world + for (entity, computed_visibility) in query.value().iter() { if !computed_visibility.is_visible { continue; } diff --git a/examples/shader/animate_shader.rs b/examples/shader/animate_shader.rs index 048657c3ca9cb..bcc42a2e10afb 100644 --- a/examples/shader/animate_shader.rs +++ b/examples/shader/animate_shader.rs @@ -4,13 +4,18 @@ use bevy::{ core_pipeline::core_3d::Transparent3d, - ecs::system::{lifetimeless::SRes, SystemParamItem}, + ecs::system::{ + lifetimeless::{Read, SRes}, + SystemParamItem, + }, pbr::{ DrawMesh, MeshPipeline, MeshPipelineKey, MeshUniform, SetMeshBindGroup, SetMeshViewBindGroup, }, prelude::*, render::{ + extract_component::{ExtractComponent, ExtractComponentPlugin}, + extract_resource::{extract_resource, ExtractResource}, mesh::MeshVertexBufferLayout, render_asset::RenderAssets, render_phase::{ @@ -64,6 +69,7 @@ impl Plugin for CustomMaterialPlugin { usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, mapped_at_creation: false, }); + app.add_plugin(ExtractComponentPlugin::::default()); app.sub_app_mut(RenderApp) .add_render_command::() @@ -73,26 +79,22 @@ impl Plugin for CustomMaterialPlugin { }) .init_resource::() .init_resource::>() - .add_system_to_stage(RenderStage::Extract, extract_time) - .add_system_to_stage(RenderStage::Extract, extract_custom_material) + .init_resource::() + .add_system_to_stage(RenderStage::Extract, extract_resource::) .add_system_to_stage(RenderStage::Prepare, prepare_time) .add_system_to_stage(RenderStage::Queue, queue_custom) .add_system_to_stage(RenderStage::Queue, queue_time_bind_group); } } -// extract the `CustomMaterial` component into the render world -fn extract_custom_material( - mut commands: Commands, - mut previous_len: Local, - mut query: Query>, -) { - let mut values = Vec::with_capacity(*previous_len); - for entity in query.iter_mut() { - values.push((entity, (CustomMaterial,))); +impl ExtractComponent for CustomMaterial { + type Query = Read; + + type Filter = (); + + fn extract_component(_: bevy::ecs::query::QueryItem) -> Self { + CustomMaterial } - *previous_len = values.len(); - commands.insert_or_spawn_batch(values); } // add each entity with a mesh and a `CustomMaterial` to every view's `Transparent3d` render phase using the `CustomPipeline` @@ -139,11 +141,14 @@ struct ExtractedTime { seconds_since_startup: f32, } -// extract the passed time into a resource in the render world -fn extract_time(mut commands: Commands, time: Res

); - -impl FromWorld for MainWorldState

{ - fn from_world(world: &mut World) -> Self { - Self(SystemState::new(&mut world.resource_mut::().0)) - } -} +use std::ops::{Deref, DerefMut}; /// A helper for accessing [`MainWorld`] content using a system parameter. /// @@ -20,9 +15,7 @@ impl FromWorld for MainWorldState

{ /// during [`RenderStage::Extract`]. /// /// This requires that the contained [`SystemParam`] does not mutate the world, as it -/// uses [`Res`](Res). To get access to the contained `SystemParam`'s item, you -/// must use [`Extract::value`]. This is required because of lifetime limitations in -/// the `SystemParam` api. +/// uses a read-only reference to [`MainWorld`] internally. /// /// ## Context /// @@ -40,8 +33,8 @@ impl FromWorld for MainWorldState

{ /// use bevy_render::Extract; /// # #[derive(Component)] /// # struct Cloud; -/// fn extract_clouds(mut commands: Commands, mut clouds: Extract>>) { -/// for cloud in clouds.value().iter() { +/// fn extract_clouds(mut commands: Commands, clouds: Extract>>) { +/// for cloud in clouds.iter() { /// commands.get_or_spawn(cloud).insert(Cloud); /// } /// } @@ -49,20 +42,79 @@ impl FromWorld for MainWorldState

{ /// /// [`RenderStage::Extract`]: crate::RenderStage::Extract /// [Window]: bevy_window::Window -#[derive(SystemParam)] pub struct Extract<'w, 's, P: SystemParam + 'static> where P::Fetch: ReadOnlySystemParamFetch, { - state: Local<'s, MainWorldState

>, - world: Res<'w, MainWorld>, + item: >::Item, +} + +impl<'w, 's, P: SystemParam> SystemParam for Extract<'w, 's, P> +where + P::Fetch: ReadOnlySystemParamFetch, +{ + type Fetch = ExtractState

; +} + +#[doc(hidden)] +pub struct ExtractState { + state: SystemState

, + main_world_state: ResState, +} + +// SAFETY: only accesses MainWorld resource with read only system params using ResState, +// which is initialized in init() +unsafe impl SystemParamState for ExtractState

{ + fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { + let mut main_world = world.resource_mut::(); + Self { + state: SystemState::new(&mut main_world), + main_world_state: ResState::init(world, system_meta), + } + } +} + +impl<'w, 's, P: SystemParam + 'static> SystemParamFetch<'w, 's> for ExtractState

+where + P::Fetch: ReadOnlySystemParamFetch, +{ + type Item = Extract<'w, 's, P>; + + unsafe fn get_param( + state: &'s mut Self, + system_meta: &bevy_ecs::system::SystemMeta, + world: &'w World, + change_tick: u32, + ) -> Self::Item { + let main_world = ResState::::get_param( + &mut state.main_world_state, + system_meta, + world, + change_tick, + ); + let item = state.state.get(main_world.into_inner()); + Extract { item } + } +} + +impl<'w, 's, P: SystemParam> Deref for Extract<'w, 's, P> +where + P::Fetch: ReadOnlySystemParamFetch, +{ + type Target = >::Item; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.item + } } -impl<'w, 's, P: SystemParam + 'static> Extract<'w, 's, P> +impl<'w, 's, P: SystemParam> DerefMut for Extract<'w, 's, P> where P::Fetch: ReadOnlySystemParamFetch, { - pub fn value(&mut self) -> SystemParamItem<'_, '_, P> { - self.state.0.get(&self.world) + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.item } } diff --git a/crates/bevy_render/src/extract_resource.rs b/crates/bevy_render/src/extract_resource.rs index 80a9acb6adb15..57be9ab7e1c82 100644 --- a/crates/bevy_render/src/extract_resource.rs +++ b/crates/bevy_render/src/extract_resource.rs @@ -41,10 +41,9 @@ impl Plugin for ExtractResourcePlugin { /// by cloning it. pub fn extract_resource( mut commands: Commands, - mut resource: Extract>, + resource: Extract>, ) { - let resource = resource.value(); if resource.is_changed() { - commands.insert_resource(R::extract_resource(resource.into_inner())); + commands.insert_resource(R::extract_resource(&*resource)); } } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 493ec955055fe..89a993a81b2d4 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -84,7 +84,7 @@ pub enum RenderStage { Cleanup, } -/// The simulation [World] of the application, stored as a resource. +/// The simulation [`World`] of the application, stored as a resource. /// This resource is only available during [`RenderStage::Extract`]. /// See [`Extract`] for more details. #[derive(Default)] diff --git a/crates/bevy_render/src/render_asset.rs b/crates/bevy_render/src/render_asset.rs index a0580ed3cee30..4b970180660a6 100644 --- a/crates/bevy_render/src/render_asset.rs +++ b/crates/bevy_render/src/render_asset.rs @@ -129,27 +129,26 @@ pub type RenderAssets = HashMap, ::PreparedAsset> fn extract_render_asset( mut commands: Commands, mut events: Extract>>, - mut assets: Extract>>, + assets: Extract>>, ) { let mut changed_assets = HashSet::default(); let mut removed = Vec::new(); - for event in events.value().iter() { + for event in events.iter() { match event { AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { - changed_assets.insert(handle.clone_weak()); + changed_assets.insert(handle); } AssetEvent::Removed { handle } => { - changed_assets.remove(handle); + changed_assets.remove(&handle); removed.push(handle.clone_weak()); } } } let mut extracted_assets = Vec::new(); - let assets = assets.value(); for handle in changed_assets.drain() { if let Some(asset) = assets.get(&handle) { - extracted_assets.push((handle, asset.extract_asset())); + extracted_assets.push((handle.clone_weak(), asset.extract_asset())); } } diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 2181974fb17ef..e93d2f3008af5 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -547,9 +547,9 @@ impl PipelineCache { pub(crate) fn extract_shaders( mut cache: ResMut, - mut extract: Extract<(Res>, EventReader>)>, + shaders: Extract>>, + mut events: Extract>>, ) { - let (shaders, mut events) = extract.value(); for event in events.iter() { match event { AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index 6b3a0d768bf59..197a90b9191ee 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -68,11 +68,11 @@ impl DerefMut for ExtractedWindows { } fn extract_windows( - mut closed: Extract>, mut extracted_windows: ResMut, - mut windows: Extract>, + mut closed: Extract>, + windows: Extract>, ) { - for window in windows.value().iter() { + for window in windows.iter() { let (new_width, new_height) = ( window.physical_width().max(1), window.physical_height().max(1), @@ -108,7 +108,7 @@ fn extract_windows( extracted_window.physical_height = new_height; } } - for closed_window in closed.value().iter() { + for closed_window in closed.iter() { extracted_windows.remove(&closed_window.id); } } diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 1db4510f1fd20..662b490b2df66 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -116,10 +116,10 @@ bitflags::bitflags! { pub fn extract_mesh2d( mut commands: Commands, mut previous_len: Local, - mut query: Extract>, + query: Extract>, ) { let mut values = Vec::with_capacity(*previous_len); - for (entity, computed_visibility, transform, handle) in query.value().iter() { + for (entity, computed_visibility, transform, handle) in query.iter() { if !computed_visibility.is_visible { continue; } diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 19aec33dda572..c34844c880cd0 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -204,7 +204,7 @@ pub fn extract_sprite_events( let SpriteAssetEvents { ref mut images } = *events; images.clear(); - for image in image_events.value().iter() { + for image in image_events.iter() { // AssetEvent: !Clone images.push(match image { AssetEvent::Created { handle } => AssetEvent::Created { @@ -222,9 +222,9 @@ pub fn extract_sprite_events( pub fn extract_sprites( mut extracted_sprites: ResMut, - mut texture_atlases: Extract>>, - mut sprite_query: Extract)>>, - mut atlas_query: Extract< + texture_atlases: Extract>>, + sprite_query: Extract)>>, + atlas_query: Extract< Query<( &Visibility, &TextureAtlasSprite, @@ -234,7 +234,7 @@ pub fn extract_sprites( >, ) { extracted_sprites.sprites.clear(); - for (visibility, sprite, transform, handle) in sprite_query.value().iter() { + for (visibility, sprite, transform, handle) in sprite_query.iter() { if !visibility.is_visible { continue; } @@ -252,8 +252,7 @@ pub fn extract_sprites( anchor: sprite.anchor.as_vec(), }); } - let texture_atlases = texture_atlases.value(); - for (visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.value().iter() { + for (visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() { if !visibility.is_visible { continue; } diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index a0ac0929fe321..d019bd3137110 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -62,16 +62,13 @@ pub struct Text2dBundle { pub fn extract_text2d_sprite( mut extracted_sprites: ResMut, - mut texture_atlases: Extract>>, - mut text_pipeline: Extract>, - mut windows: Extract>, - mut text2d_query: Extract>, + texture_atlases: Extract>>, + text_pipeline: Extract>, + windows: Extract>, + text2d_query: Extract>, ) { - let scale_factor = windows.value().scale_factor(WindowId::primary()) as f32; - - let text_pipeline = text_pipeline.value(); - let texture_atlases = texture_atlases.value(); - for (entity, visibility, text, transform, calculated_size) in text2d_query.value().iter() { + let scale_factor = windows.scale_factor(WindowId::primary()) as f32; + for (entity, visibility, text, transform, calculated_size) in text2d_query.iter() { if !visibility.is_visible { continue; } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 888fa5885baab..31018401dcbd8 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -175,8 +175,8 @@ pub struct ExtractedUiNodes { pub fn extract_uinodes( mut extracted_uinodes: ResMut, - mut images: Extract>>, - mut uinode_query: Extract< + images: Extract>>, + uinode_query: Extract< Query<( &Node, &GlobalTransform, @@ -188,8 +188,7 @@ pub fn extract_uinodes( >, ) { extracted_uinodes.uinodes.clear(); - let images = images.value(); - for (uinode, transform, color, image, visibility, clip) in uinode_query.value().iter() { + for (uinode, transform, color, image, visibility, clip) in uinode_query.iter() { if !visibility.is_visible { continue; } @@ -228,9 +227,9 @@ pub struct DefaultCameraView(pub Entity); pub fn extract_default_ui_camera_view( mut commands: Commands, - mut query: Extract), With>>, + query: Extract), With>>, ) { - for (entity, camera, camera_ui) in query.value().iter() { + for (entity, camera, camera_ui) in query.iter() { // ignore cameras with disabled ui if let Some(&CameraUi { is_enabled: false, .. @@ -274,10 +273,10 @@ pub fn extract_default_ui_camera_view( pub fn extract_text_uinodes( mut extracted_uinodes: ResMut, - mut texture_atlases: Extract>>, - mut text_pipeline: Extract>, - mut windows: Extract>, - mut uinode_query: Extract< + texture_atlases: Extract>>, + text_pipeline: Extract>, + windows: Extract>, + uinode_query: Extract< Query<( Entity, &Node, @@ -288,12 +287,8 @@ pub fn extract_text_uinodes( )>, >, ) { - let scale_factor = windows.value().scale_factor(WindowId::primary()) as f32; - - let text_pipeline = text_pipeline.value(); - let texture_atlases = texture_atlases.value(); - - for (entity, uinode, transform, text, visibility, clip) in uinode_query.value().iter() { + let scale_factor = windows.scale_factor(WindowId::primary()) as f32; + for (entity, uinode, transform, text, visibility, clip) in uinode_query.iter() { if !visibility.is_visible { continue; } diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 88bfc339bbf11..f9049c16d925b 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -288,11 +288,10 @@ pub fn extract_colored_mesh2d( mut previous_len: Local, // When extracting, you must use `Extract` to mark the `SystemParam`s // which should be taken from the main world. - mut query: Extract>>, + query: Extract>>, ) { let mut values = Vec::with_capacity(*previous_len); - // The `value` method fetches the `SystemParam` in the `Extract` from the main world - for (entity, computed_visibility) in query.value().iter() { + for (entity, computed_visibility) in query.iter() { if !computed_visibility.is_visible { continue; } diff --git a/examples/stress_tests/many_lights.rs b/examples/stress_tests/many_lights.rs index 083d982aa1de5..9ecd46bed9081 100644 --- a/examples/stress_tests/many_lights.rs +++ b/examples/stress_tests/many_lights.rs @@ -170,9 +170,8 @@ fn print_visible_light_count( } } -// TODO: Is this unidiomatic? Oh well, it's a stress test -fn extract_time(mut commands: Commands, mut time: Extract>) { - commands.insert_resource(time.value().into_inner().clone()); +fn extract_time(mut commands: Commands, time: Extract>) { + commands.insert_resource(time.clone()); } struct PrintingTimer(Timer); From ad0ad11498c9920dd37d630ccf3e272891d9db77 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Thu, 30 Jun 2022 15:41:42 -0700 Subject: [PATCH 12/16] clippy --- crates/bevy_render/src/extract_param.rs | 2 +- crates/bevy_render/src/render_asset.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index 9705674738708..37e76a20e2bed 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -82,7 +82,7 @@ where unsafe fn get_param( state: &'s mut Self, - system_meta: &bevy_ecs::system::SystemMeta, + system_meta: &SystemMeta, world: &'w World, change_tick: u32, ) -> Self::Item { diff --git a/crates/bevy_render/src/render_asset.rs b/crates/bevy_render/src/render_asset.rs index 4b970180660a6..9821f1801444d 100644 --- a/crates/bevy_render/src/render_asset.rs +++ b/crates/bevy_render/src/render_asset.rs @@ -136,10 +136,10 @@ fn extract_render_asset( for event in events.iter() { match event { AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { - changed_assets.insert(handle); + changed_assets.insert(handle.clone_weak()); } AssetEvent::Removed { handle } => { - changed_assets.remove(&handle); + changed_assets.remove(handle); removed.push(handle.clone_weak()); } } From 12b17bc9b5378412cf69681e3e528349b469f457 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Thu, 30 Jun 2022 15:44:16 -0700 Subject: [PATCH 13/16] address my comments --- crates/bevy_ecs/src/schedule/stage.rs | 8 ++++---- crates/bevy_ui/src/render/mod.rs | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/stage.rs b/crates/bevy_ecs/src/schedule/stage.rs index d46196fe40e7e..014423e2c4f13 100644 --- a/crates/bevy_ecs/src/schedule/stage.rs +++ b/crates/bevy_ecs/src/schedule/stage.rs @@ -91,7 +91,7 @@ pub struct SystemStage { last_tick_check: u32, /// If true, buffers will be automatically applied at the end of the stage. If false, buffers must be manually applied. apply_buffers: bool, - must_use_resource: Option, + must_read_resource: Option, } impl SystemStage { @@ -114,7 +114,7 @@ impl SystemStage { uninitialized_at_end: vec![], last_tick_check: Default::default(), apply_buffers: true, - must_use_resource: None, + must_read_resource: None, } } @@ -145,7 +145,7 @@ impl SystemStage { } pub fn set_must_read_resource(&mut self, resource_id: ComponentId) { - self.must_use_resource = Some(resource_id); + self.must_read_resource = Some(resource_id); } #[must_use] @@ -805,7 +805,7 @@ impl Stage for SystemStage { if world.contains_resource::() { self.report_ambiguities(world); } - if let Some(resource_id) = self.must_use_resource { + if let Some(resource_id) = self.must_read_resource { self.check_uses_resource(resource_id, world); } } else if self.executor_modified { diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 31018401dcbd8..9d57a87a3b928 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -248,8 +248,6 @@ pub fn extract_default_ui_camera_view( ..Default::default() }; projection.update(logical_size.x, logical_size.y); - // This roundabout approach is required because spawn().id() won't work in this context - let default_camera_view = commands .spawn() .insert(ExtractedView { From 008d7fe37c03317ee8f3a63bdb89a5388681da50 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Thu, 7 Jul 2022 20:53:20 -0700 Subject: [PATCH 14/16] Update crates/bevy_render/src/lib.rs Co-authored-by: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> --- crates/bevy_render/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 89a993a81b2d4..150162ff7a52d 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -174,7 +174,7 @@ impl Plugin for RenderPlugin { extract_stage.set_must_read_resource(main_world_in_render.unwrap()); // don't apply buffers when the stage finishes running // extract stage runs on the render world, but buffers are applied - // after losing access to the main world. + // after access to the main world is removed // See also https://github.com/bevyengine/bevy/issues/5082 extract_stage.set_apply_buffers(false); render_app From 5563e4950e3586162030296a4c9ee3ed85a2cf87 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Fri, 8 Jul 2022 15:35:28 -0700 Subject: [PATCH 15/16] Resolve comments --- crates/bevy_render/src/lib.rs | 3 ++- crates/bevy_render/src/view/window.rs | 4 ++-- examples/shader/animate_shader.rs | 7 +++---- examples/stress_tests/many_lights.rs | 7 +++++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 150162ff7a52d..a2bd0818a9dc2 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -85,7 +85,8 @@ pub enum RenderStage { } /// The simulation [`World`] of the application, stored as a resource. -/// This resource is only available during [`RenderStage::Extract`]. +/// This resource is only available during [`RenderStage::Extract`] and not +/// during command application of that stage. /// See [`Extract`] for more details. #[derive(Default)] pub struct MainWorld(World); diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index a4947ffa78ee2..0104b85e86bd5 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -5,7 +5,7 @@ use crate::{ Extract, RenderApp, RenderStage, }; use bevy_app::{App, Plugin}; -use bevy_ecs::{prelude::*, system::lifetimeless::SRes}; +use bevy_ecs::prelude::*; use bevy_utils::{tracing::debug, HashMap, HashSet}; use bevy_window::{PresentMode, RawWindowHandleWrapper, WindowClosed, WindowId, Windows}; use std::ops::{Deref, DerefMut}; @@ -70,7 +70,7 @@ impl DerefMut for ExtractedWindows { fn extract_windows( mut extracted_windows: ResMut, mut closed: Extract>, - windows: Extract>, + windows: Extract>, ) { for window in windows.iter() { let (new_width, new_height) = ( diff --git a/examples/shader/animate_shader.rs b/examples/shader/animate_shader.rs index bcc42a2e10afb..78aa9f1790f35 100644 --- a/examples/shader/animate_shader.rs +++ b/examples/shader/animate_shader.rs @@ -15,7 +15,7 @@ use bevy::{ prelude::*, render::{ extract_component::{ExtractComponent, ExtractComponentPlugin}, - extract_resource::{extract_resource, ExtractResource}, + extract_resource::{ExtractResource, ExtractResourcePlugin}, mesh::MeshVertexBufferLayout, render_asset::RenderAssets, render_phase::{ @@ -69,7 +69,8 @@ impl Plugin for CustomMaterialPlugin { usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, mapped_at_creation: false, }); - app.add_plugin(ExtractComponentPlugin::::default()); + app.add_plugin(ExtractComponentPlugin::::default()) + .add_plugin(ExtractResourcePlugin::::default()); app.sub_app_mut(RenderApp) .add_render_command::() @@ -79,8 +80,6 @@ impl Plugin for CustomMaterialPlugin { }) .init_resource::() .init_resource::>() - .init_resource::() - .add_system_to_stage(RenderStage::Extract, extract_resource::) .add_system_to_stage(RenderStage::Prepare, prepare_time) .add_system_to_stage(RenderStage::Queue, queue_custom) .add_system_to_stage(RenderStage::Queue, queue_time_bind_group); diff --git a/examples/stress_tests/many_lights.rs b/examples/stress_tests/many_lights.rs index 9ecd46bed9081..59c0536ef32f2 100644 --- a/examples/stress_tests/many_lights.rs +++ b/examples/stress_tests/many_lights.rs @@ -154,7 +154,7 @@ impl Plugin for LogVisibleLights { // System for printing the number of meshes on every tick of the timer fn print_visible_light_count( - time: Res