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