mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Make RenderStage::Extract
run on the render world (#4402)
# Objective - Currently, the `Extract` `RenderStage` is executed on the main world, with the render world available as a resource. - However, when needing access to resources in the render world (e.g. to mutate them), the only way to do so was to get exclusive access to the whole `RenderWorld` resource. - This meant that effectively only one extract which wrote to resources could run at a time. - We didn't previously make `Extract`ing writing to the world a non-happy path, even though we want to discourage that. ## Solution - Move the extract stage to run on the render world. - Add the main world as a `MainWorld` resource. - Add an `Extract` `SystemParam` as a convenience to access a (read only) `SystemParam` in the main world during `Extract`. ## Future work It should be possible to avoid needing to use `get_or_spawn` for the render commands, since now the `Commands`' `Entities` matches up with the world being executed on. We need to determine how this interacts with https://github.com/bevyengine/bevy/pull/3519 It's theoretically possible to remove the need for the `value` method on `Extract`. However, that requires slightly changing the `SystemParam` interface, which would make it more complicated. That would probably mess up the `SystemState` api too. ## Todo I still need to add doc comments to `Extract`. --- ## Changelog ### Changed - The `Extract` `RenderStage` now runs on the render world (instead of the main world as before). You must use the `Extract` `SystemParam` to access the main world during the extract phase. Resources on the render world can now be accessed using `ResMut` during extract. ### Removed - `Commands::spawn_and_forget`. Use `Commands::get_or_spawn(e).insert_bundle(bundle)` instead ## Migration Guide The `Extract` `RenderStage` now runs on the render world (instead of the main world as before). You must use the `Extract` `SystemParam` to access the main world during the extract phase. `Extract` takes a single type parameter, which is any system parameter (such as `Res`, `Query` etc.). It will extract this from the main world, and returns the result of this extraction when `value` is called on it. For example, if previously your extract system looked like: ```rust fn extract_clouds(mut commands: Commands, clouds: Query<Entity, With<Cloud>>) { for cloud in clouds.iter() { commands.get_or_spawn(cloud).insert(Cloud); } } ``` the new version would be: ```rust fn extract_clouds(mut commands: Commands, mut clouds: Extract<Query<Entity, With<Cloud>>>) { for cloud in clouds.value().iter() { commands.get_or_spawn(cloud).insert(Cloud); } } ``` The diff is: ```diff --- a/src/clouds.rs +++ b/src/clouds.rs @@ -1,5 +1,5 @@ -fn extract_clouds(mut commands: Commands, clouds: Query<Entity, With<Cloud>>) { - for cloud in clouds.iter() { +fn extract_clouds(mut commands: Commands, mut clouds: Extract<Query<Entity, With<Cloud>>>) { + for cloud in clouds.value().iter() { commands.get_or_spawn(cloud).insert(Cloud); } } ``` You can now also access resources from the render world using the normal system parameters during `Extract`: ```rust fn extract_assets(mut render_assets: ResMut<MyAssets>, source_assets: Extract<Res<MyAssets>>) { *render_assets = source_assets.clone(); } ``` Please note that all existing extract systems need to be updated to match this new style; even if they currently compile they will not run as expected. A warning will be emitted on a best-effort basis if this is not met. Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
e6faf993b0
commit
7b2cf98896
22 changed files with 375 additions and 192 deletions
|
@ -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,7 +123,7 @@ impl BatchedPhaseItem for Transparent2d {
|
|||
|
||||
pub fn extract_core_2d_camera_phases(
|
||||
mut commands: Commands,
|
||||
cameras_2d: Query<(Entity, &Camera), With<Camera2d>>,
|
||||
cameras_2d: Extract<Query<(Entity, &Camera), With<Camera2d>>>,
|
||||
) {
|
||||
for (entity, camera) in cameras_2d.iter() {
|
||||
if camera.is_active {
|
||||
|
|
|
@ -34,7 +34,7 @@ use bevy_render::{
|
|||
renderer::RenderDevice,
|
||||
texture::TextureCache,
|
||||
view::ViewDepthTexture,
|
||||
RenderApp, RenderStage,
|
||||
Extract, RenderApp, RenderStage,
|
||||
};
|
||||
use bevy_utils::{FloatOrd, HashMap};
|
||||
|
||||
|
@ -208,7 +208,7 @@ impl CachedRenderPipelinePhaseItem for Transparent3d {
|
|||
|
||||
pub fn extract_core_3d_camera_phases(
|
||||
mut commands: Commands,
|
||||
cameras_3d: Query<(Entity, &Camera), With<Camera3d>>,
|
||||
cameras_3d: Extract<Query<(Entity, &Camera), With<Camera3d>>>,
|
||||
) {
|
||||
for (entity, camera) in cameras_3d.iter() {
|
||||
if camera.is_active {
|
||||
|
|
|
@ -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;
|
||||
|
@ -88,6 +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_read_resource: Option<ComponentId>,
|
||||
}
|
||||
|
||||
impl SystemStage {
|
||||
|
@ -110,6 +114,7 @@ impl SystemStage {
|
|||
uninitialized_at_end: vec![],
|
||||
last_tick_check: Default::default(),
|
||||
apply_buffers: true,
|
||||
must_read_resource: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,6 +144,10 @@ impl SystemStage {
|
|||
self.executor = executor;
|
||||
}
|
||||
|
||||
pub fn set_must_read_resource(&mut self, resource_id: ComponentId) {
|
||||
self.must_read_resource = Some(resource_id);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_system<Params>(mut self, system: impl IntoSystemDescriptor<Params>) -> Self {
|
||||
self.add_system(system);
|
||||
|
@ -563,6 +572,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();
|
||||
warn!(
|
||||
"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 +805,9 @@ impl Stage for SystemStage {
|
|||
if world.contains_resource::<ReportExecutionOrderAmbiguities>() {
|
||||
self.report_ambiguities(world);
|
||||
}
|
||||
if let Some(resource_id) = self.must_read_resource {
|
||||
self.check_uses_resource(resource_id, world);
|
||||
}
|
||||
} else if self.executor_modified {
|
||||
self.executor.rebuild_cached_data(&self.parallel);
|
||||
self.executor_modified = false;
|
||||
|
|
|
@ -145,12 +145,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
|
||||
|
|
|
@ -34,7 +34,7 @@ use bevy_render::{
|
|||
renderer::RenderDevice,
|
||||
texture::FallbackImage,
|
||||
view::{ExtractedView, Msaa, VisibleEntities},
|
||||
RenderApp, RenderStage,
|
||||
Extract, RenderApp, RenderStage,
|
||||
};
|
||||
use bevy_utils::{tracing::error, HashMap, HashSet};
|
||||
use std::hash::Hash;
|
||||
|
@ -455,15 +455,15 @@ pub type RenderMaterials<T> = HashMap<Handle<T>, PreparedMaterial<T>>;
|
|||
/// into the "render world".
|
||||
fn extract_materials<M: Material>(
|
||||
mut commands: Commands,
|
||||
mut events: EventReader<AssetEvent<M>>,
|
||||
assets: Res<Assets<M>>,
|
||||
mut events: Extract<EventReader<AssetEvent<M>>>,
|
||||
assets: Extract<Res<Assets<M>>>,
|
||||
) {
|
||||
let mut changed_assets = HashSet::default();
|
||||
let mut removed = Vec::new();
|
||||
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);
|
||||
|
@ -474,8 +474,8 @@ fn extract_materials<M: Material>(
|
|||
|
||||
let mut extracted_assets = Vec::new();
|
||||
for handle in changed_assets.drain() {
|
||||
if let Some(asset) = assets.get(handle) {
|
||||
extracted_assets.push((handle.clone_weak(), asset.clone()));
|
||||
if let Some(asset) = assets.get(&handle) {
|
||||
extracted_assets.push((handle, asset.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ use bevy_render::{
|
|||
view::{
|
||||
ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms, Visibility, VisibleEntities,
|
||||
},
|
||||
Extract,
|
||||
};
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
use bevy_utils::FloatOrd;
|
||||
|
@ -386,7 +387,10 @@ pub struct ExtractedClustersPointLights {
|
|||
data: Vec<VisiblePointLights>,
|
||||
}
|
||||
|
||||
pub fn extract_clusters(mut commands: Commands, views: Query<(Entity, &Clusters), With<Camera>>) {
|
||||
pub fn extract_clusters(
|
||||
mut commands: Commands,
|
||||
views: Extract<Query<(Entity, &Clusters), With<Camera>>>,
|
||||
) {
|
||||
for (entity, clusters) in views.iter() {
|
||||
commands.get_or_spawn(entity).insert_bundle((
|
||||
ExtractedClustersPointLights {
|
||||
|
@ -404,20 +408,22 @@ 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<PointLightShadowMap>,
|
||||
directional_light_shadow_map: Res<DirectionalLightShadowMap>,
|
||||
global_point_lights: Res<GlobalVisiblePointLights>,
|
||||
mut point_lights: Query<(&PointLight, &mut CubemapVisibleEntities, &GlobalTransform)>,
|
||||
mut spot_lights: Query<(&SpotLight, &mut VisibleEntities, &GlobalTransform)>,
|
||||
mut directional_lights: Query<
|
||||
(
|
||||
Entity,
|
||||
&DirectionalLight,
|
||||
&mut VisibleEntities,
|
||||
&GlobalTransform,
|
||||
&Visibility,
|
||||
),
|
||||
Without<SpotLight>,
|
||||
point_light_shadow_map: Extract<Res<PointLightShadowMap>>,
|
||||
directional_light_shadow_map: Extract<Res<DirectionalLightShadowMap>>,
|
||||
global_point_lights: Extract<Res<GlobalVisiblePointLights>>,
|
||||
point_lights: Extract<Query<(&PointLight, &CubemapVisibleEntities, &GlobalTransform)>>,
|
||||
spot_lights: Extract<Query<(&SpotLight, &VisibleEntities, &GlobalTransform)>>,
|
||||
directional_lights: Extract<
|
||||
Query<
|
||||
(
|
||||
Entity,
|
||||
&DirectionalLight,
|
||||
&VisibleEntities,
|
||||
&GlobalTransform,
|
||||
&Visibility,
|
||||
),
|
||||
Without<SpotLight>,
|
||||
>,
|
||||
>,
|
||||
mut previous_point_lights_len: Local<usize>,
|
||||
mut previous_spot_lights_len: Local<usize>,
|
||||
|
@ -441,10 +447,10 @@ pub fn extract_lights(
|
|||
|
||||
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());
|
||||
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,
|
||||
(
|
||||
|
@ -475,8 +481,10 @@ pub fn extract_lights(
|
|||
|
||||
let mut spot_lights_values = Vec::with_capacity(*previous_spot_lights_len);
|
||||
for entity in global_point_lights.iter().copied() {
|
||||
if let Ok((spot_light, visible_entities, transform)) = spot_lights.get_mut(entity) {
|
||||
let render_visible_entities = std::mem::take(visible_entities.into_inner());
|
||||
if let Ok((spot_light, visible_entities, transform)) = spot_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_visible_entities = visible_entities.clone();
|
||||
let texel_size =
|
||||
2.0 * spot_light.outer_angle.tan() / directional_light_shadow_map.size as f32;
|
||||
|
||||
|
@ -512,7 +520,7 @@ pub fn extract_lights(
|
|||
commands.insert_or_spawn_batch(spot_lights_values);
|
||||
|
||||
for (entity, directional_light, visible_entities, transform, visibility) in
|
||||
directional_lights.iter_mut()
|
||||
directional_lights.iter()
|
||||
{
|
||||
if !visibility.is_visible {
|
||||
continue;
|
||||
|
@ -530,7 +538,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,
|
||||
|
|
|
@ -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,14 +118,16 @@ pub fn extract_meshes(
|
|||
mut commands: Commands,
|
||||
mut prev_caster_commands_len: Local<usize>,
|
||||
mut prev_not_caster_commands_len: Local<usize>,
|
||||
meshes_query: Query<(
|
||||
Entity,
|
||||
&ComputedVisibility,
|
||||
&GlobalTransform,
|
||||
&Handle<Mesh>,
|
||||
Option<With<NotShadowReceiver>>,
|
||||
Option<With<NotShadowCaster>>,
|
||||
)>,
|
||||
meshes_query: Extract<
|
||||
Query<(
|
||||
Entity,
|
||||
&ComputedVisibility,
|
||||
&GlobalTransform,
|
||||
&Handle<Mesh>,
|
||||
Option<With<NotShadowReceiver>>,
|
||||
Option<With<NotShadowCaster>>,
|
||||
)>,
|
||||
>,
|
||||
) {
|
||||
let mut caster_commands = Vec::with_capacity(*prev_caster_commands_len);
|
||||
let mut not_caster_commands = Vec::with_capacity(*prev_not_caster_commands_len);
|
||||
|
@ -202,12 +204,12 @@ impl SkinnedMeshJoints {
|
|||
}
|
||||
|
||||
pub fn extract_skinned_meshes(
|
||||
query: Query<(Entity, &ComputedVisibility, &SkinnedMesh)>,
|
||||
inverse_bindposes: Res<Assets<SkinnedMeshInverseBindposes>>,
|
||||
joint_query: Query<&GlobalTransform>,
|
||||
mut commands: Commands,
|
||||
mut previous_len: Local<usize>,
|
||||
mut previous_joint_len: Local<usize>,
|
||||
query: Extract<Query<(Entity, &ComputedVisibility, &SkinnedMesh)>>,
|
||||
inverse_bindposes: Extract<Res<Assets<SkinnedMeshInverseBindposes>>>,
|
||||
joint_query: Extract<Query<&GlobalTransform>>,
|
||||
) {
|
||||
let mut values = Vec::with_capacity(*previous_len);
|
||||
let mut joints = Vec::with_capacity(*previous_joint_len);
|
||||
|
|
|
@ -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};
|
||||
|
@ -393,13 +394,15 @@ pub struct ExtractedCamera {
|
|||
|
||||
pub fn extract_cameras(
|
||||
mut commands: Commands,
|
||||
query: Query<(
|
||||
Entity,
|
||||
&Camera,
|
||||
&CameraRenderGraph,
|
||||
&GlobalTransform,
|
||||
&VisibleEntities,
|
||||
)>,
|
||||
query: Extract<
|
||||
Query<(
|
||||
Entity,
|
||||
&Camera,
|
||||
&CameraRenderGraph,
|
||||
&GlobalTransform,
|
||||
&VisibleEntities,
|
||||
)>,
|
||||
>,
|
||||
) {
|
||||
for (entity, camera, camera_render_graph, transform, visible_entities) in query.iter() {
|
||||
if !camera.is_active {
|
||||
|
|
|
@ -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<C: Component> DynamicUniformIndex<C> {
|
|||
/// 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::Query>) -> Self;
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ impl<T: Asset> ExtractComponent for Handle<T> {
|
|||
fn extract_components<C: ExtractComponent>(
|
||||
mut commands: Commands,
|
||||
mut previous_len: Local<usize>,
|
||||
mut query: StaticSystemParam<Query<(Entity, C::Query), C::Filter>>,
|
||||
mut query: Extract<Query<(Entity, C::Query), C::Filter>>,
|
||||
) {
|
||||
let mut values = Vec::with_capacity(*previous_len);
|
||||
for (entity, query_item) in query.iter_mut() {
|
||||
|
@ -196,7 +196,7 @@ fn extract_components<C: ExtractComponent>(
|
|||
fn extract_visible_components<C: ExtractComponent>(
|
||||
mut commands: Commands,
|
||||
mut previous_len: Local<usize>,
|
||||
mut query: StaticSystemParam<Query<(Entity, Read<ComputedVisibility>, C::Query), C::Filter>>,
|
||||
mut query: Extract<Query<(Entity, &ComputedVisibility, C::Query), C::Filter>>,
|
||||
) {
|
||||
let mut values = Vec::with_capacity(*previous_len);
|
||||
for (entity, computed_visibility, query_item) in query.iter_mut() {
|
||||
|
|
120
crates/bevy_render/src/extract_param.rs
Normal file
120
crates/bevy_render/src/extract_param.rs
Normal file
|
@ -0,0 +1,120 @@
|
|||
use crate::MainWorld;
|
||||
use bevy_ecs::{
|
||||
prelude::*,
|
||||
system::{
|
||||
ReadOnlySystemParamFetch, ResState, SystemMeta, SystemParam, SystemParamFetch,
|
||||
SystemParamState, SystemState,
|
||||
},
|
||||
};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// 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 a read-only reference to [`MainWorld`] internally.
|
||||
///
|
||||
/// ## 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, clouds: Extract<Query<Entity, With<Cloud>>>) {
|
||||
/// for cloud in clouds.iter() {
|
||||
/// commands.get_or_spawn(cloud).insert(Cloud);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`RenderStage::Extract`]: crate::RenderStage::Extract
|
||||
/// [Window]: bevy_window::Window
|
||||
pub struct Extract<'w, 's, P: SystemParam + 'static>
|
||||
where
|
||||
P::Fetch: ReadOnlySystemParamFetch,
|
||||
{
|
||||
item: <P::Fetch as SystemParamFetch<'w, 's>>::Item,
|
||||
}
|
||||
|
||||
impl<'w, 's, P: SystemParam> SystemParam for Extract<'w, 's, P>
|
||||
where
|
||||
P::Fetch: ReadOnlySystemParamFetch,
|
||||
{
|
||||
type Fetch = ExtractState<P>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct ExtractState<P: SystemParam> {
|
||||
state: SystemState<P>,
|
||||
main_world_state: ResState<MainWorld>,
|
||||
}
|
||||
|
||||
// SAFETY: only accesses MainWorld resource with read only system params using ResState,
|
||||
// which is initialized in init()
|
||||
unsafe impl<P: SystemParam + 'static> SystemParamState for ExtractState<P> {
|
||||
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
||||
let mut main_world = world.resource_mut::<MainWorld>();
|
||||
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<P>
|
||||
where
|
||||
P::Fetch: ReadOnlySystemParamFetch,
|
||||
{
|
||||
type Item = Extract<'w, 's, P>;
|
||||
|
||||
unsafe fn get_param(
|
||||
state: &'s mut Self,
|
||||
system_meta: &SystemMeta,
|
||||
world: &'w World,
|
||||
change_tick: u32,
|
||||
) -> Self::Item {
|
||||
let main_world = ResState::<MainWorld>::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 = <P::Fetch as SystemParamFetch<'w, 's>>::Item;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.item
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w, 's, P: SystemParam> DerefMut for Extract<'w, 's, P>
|
||||
where
|
||||
P::Fetch: ReadOnlySystemParamFetch,
|
||||
{
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.item
|
||||
}
|
||||
}
|
|
@ -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,8 +39,11 @@ impl<R: ExtractResource> Plugin for ExtractResourcePlugin<R> {
|
|||
|
||||
/// This system extracts the resource of the corresponding [`Resource`] type
|
||||
/// by cloning it.
|
||||
pub fn extract_resource<R: ExtractResource>(mut commands: Commands, resource: Res<R::Source>) {
|
||||
pub fn extract_resource<R: ExtractResource>(
|
||||
mut commands: Commands,
|
||||
resource: Extract<Res<R::Source>>,
|
||||
) {
|
||||
if resource.is_changed() {
|
||||
commands.insert_resource(R::extract_resource(resource.into_inner()));
|
||||
commands.insert_resource(R::extract_resource(&*resource));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -16,6 +17,8 @@ pub mod settings;
|
|||
pub mod texture;
|
||||
pub mod view;
|
||||
|
||||
pub use extract_param::Extract;
|
||||
|
||||
pub mod prelude {
|
||||
#[doc(hidden)]
|
||||
pub use crate::{
|
||||
|
@ -45,7 +48,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)]
|
||||
|
@ -79,11 +85,14 @@ 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`] and not
|
||||
/// during command application of that stage.
|
||||
/// See [`Extract`] for more details.
|
||||
#[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 {
|
||||
|
@ -91,7 +100,7 @@ impl Deref for RenderWorld {
|
|||
}
|
||||
}
|
||||
|
||||
impl DerefMut for RenderWorld {
|
||||
impl DerefMut for MainWorld {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
|
@ -107,11 +116,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 [`RenderWorld`].
|
||||
#[derive(Default)]
|
||||
struct ScratchRenderWorld(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) {
|
||||
|
@ -150,7 +154,7 @@ impl Plugin for RenderPlugin {
|
|||
app.insert_resource(device.clone())
|
||||
.insert_resource(queue.clone())
|
||||
.insert_resource(adapter_info.clone())
|
||||
.init_resource::<ScratchRenderWorld>()
|
||||
.init_resource::<ScratchMainWorld>()
|
||||
.register_type::<Frustum>()
|
||||
.register_type::<CubemapFrusta>();
|
||||
|
||||
|
@ -160,8 +164,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::<MainWorld>();
|
||||
render_app.world.remove_resource::<MainWorld>();
|
||||
let main_world_in_render = render_app
|
||||
.world
|
||||
.components()
|
||||
.get_resource_id(TypeId::of::<MainWorld>());
|
||||
// `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 access to the main world is removed
|
||||
// See also https://github.com/bevyengine/bevy/issues/5082
|
||||
extract_stage.set_apply_buffers(false);
|
||||
render_app
|
||||
.add_stage(RenderStage::Extract, extract_stage)
|
||||
|
@ -300,6 +316,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) {
|
||||
|
@ -308,17 +329,20 @@ fn extract(app_world: &mut World, render_app: &mut App) {
|
|||
.get_stage_mut::<SystemStage>(&RenderStage::Extract)
|
||||
.unwrap();
|
||||
|
||||
// temporarily add the render world to the app world as a resource
|
||||
let scratch_world = app_world.remove_resource::<ScratchRenderWorld>().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::<ScratchMainWorld>().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(running_world);
|
||||
// move the app world back, as if nothing happened.
|
||||
let inserted_world = running_world.remove_resource::<MainWorld>().unwrap();
|
||||
let scratch_world = std::mem::replace(app_world, inserted_world.0);
|
||||
app_world.insert_resource(ScratchMainWorld(scratch_world));
|
||||
|
||||
// add the render world back to the render app
|
||||
let render_world = app_world.remove_resource::<RenderWorld>().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);
|
||||
// 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 <https://github.com/bevyengine/bevy/issues/5082>
|
||||
extract.apply_buffers(running_world);
|
||||
}
|
||||
|
|
|
@ -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::{
|
||||
|
@ -123,15 +123,15 @@ pub type RenderAssets<A> = HashMap<Handle<A>, <A as RenderAsset>::PreparedAsset>
|
|||
/// into the "render world".
|
||||
fn extract_render_asset<A: RenderAsset>(
|
||||
mut commands: Commands,
|
||||
mut events: EventReader<AssetEvent<A>>,
|
||||
assets: Res<Assets<A>>,
|
||||
mut events: Extract<EventReader<AssetEvent<A>>>,
|
||||
assets: Extract<Res<Assets<A>>>,
|
||||
) {
|
||||
let mut changed_assets = HashSet::default();
|
||||
let mut removed = Vec::new();
|
||||
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);
|
||||
|
@ -142,8 +142,8 @@ fn extract_render_asset<A: RenderAsset>(
|
|||
|
||||
let mut extracted_assets = Vec::new();
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
|||
ShaderProcessor, ShaderReflectError,
|
||||
},
|
||||
renderer::RenderDevice,
|
||||
RenderWorld,
|
||||
Extract,
|
||||
};
|
||||
use bevy_asset::{AssetEvent, Assets, Handle};
|
||||
use bevy_ecs::event::EventReader;
|
||||
|
@ -546,11 +546,10 @@ impl PipelineCache {
|
|||
}
|
||||
|
||||
pub(crate) fn extract_shaders(
|
||||
mut world: ResMut<RenderWorld>,
|
||||
shaders: Res<Assets<Shader>>,
|
||||
mut events: EventReader<AssetEvent<Shader>>,
|
||||
mut cache: ResMut<Self>,
|
||||
shaders: Extract<Res<Assets<Shader>>>,
|
||||
mut events: Extract<EventReader<AssetEvent<Shader>>>,
|
||||
) {
|
||||
let mut cache = world.resource_mut::<Self>();
|
||||
for event in events.iter() {
|
||||
match event {
|
||||
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
render_resource::TextureView,
|
||||
renderer::{RenderDevice, RenderInstance},
|
||||
texture::BevyDefault,
|
||||
RenderApp, RenderStage, RenderWorld,
|
||||
Extract, RenderApp, RenderStage,
|
||||
};
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_ecs::prelude::*;
|
||||
|
@ -68,11 +68,10 @@ impl DerefMut for ExtractedWindows {
|
|||
}
|
||||
|
||||
fn extract_windows(
|
||||
mut render_world: ResMut<RenderWorld>,
|
||||
mut closed: EventReader<WindowClosed>,
|
||||
windows: Res<Windows>,
|
||||
mut extracted_windows: ResMut<ExtractedWindows>,
|
||||
mut closed: Extract<EventReader<WindowClosed>>,
|
||||
windows: Extract<Res<Windows>>,
|
||||
) {
|
||||
let mut extracted_windows = render_world.get_resource_mut::<ExtractedWindows>().unwrap();
|
||||
for window in windows.iter() {
|
||||
let (new_width, new_height) = (
|
||||
window.physical_width().max(1),
|
||||
|
|
|
@ -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,7 +116,7 @@ bitflags::bitflags! {
|
|||
pub fn extract_mesh2d(
|
||||
mut commands: Commands,
|
||||
mut previous_len: Local<usize>,
|
||||
query: Query<(Entity, &ComputedVisibility, &GlobalTransform, &Mesh2dHandle)>,
|
||||
query: Extract<Query<(Entity, &ComputedVisibility, &GlobalTransform, &Mesh2dHandle)>>,
|
||||
) {
|
||||
let mut values = Vec::with_capacity(*previous_len);
|
||||
for (entity, computed_visibility, transform, handle) in query.iter() {
|
||||
|
|
|
@ -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;
|
||||
|
@ -197,10 +197,9 @@ pub struct SpriteAssetEvents {
|
|||
}
|
||||
|
||||
pub fn extract_sprite_events(
|
||||
mut render_world: ResMut<RenderWorld>,
|
||||
mut image_events: EventReader<AssetEvent<Image>>,
|
||||
mut events: ResMut<SpriteAssetEvents>,
|
||||
mut image_events: Extract<EventReader<AssetEvent<Image>>>,
|
||||
) {
|
||||
let mut events = render_world.resource_mut::<SpriteAssetEvents>();
|
||||
let SpriteAssetEvents { ref mut images } = *events;
|
||||
images.clear();
|
||||
|
||||
|
@ -221,17 +220,18 @@ pub fn extract_sprite_events(
|
|||
}
|
||||
|
||||
pub fn extract_sprites(
|
||||
mut render_world: ResMut<RenderWorld>,
|
||||
texture_atlases: Res<Assets<TextureAtlas>>,
|
||||
sprite_query: Query<(&Visibility, &Sprite, &GlobalTransform, &Handle<Image>)>,
|
||||
atlas_query: Query<(
|
||||
&Visibility,
|
||||
&TextureAtlasSprite,
|
||||
&GlobalTransform,
|
||||
&Handle<TextureAtlas>,
|
||||
)>,
|
||||
mut extracted_sprites: ResMut<ExtractedSprites>,
|
||||
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
|
||||
sprite_query: Extract<Query<(&Visibility, &Sprite, &GlobalTransform, &Handle<Image>)>>,
|
||||
atlas_query: Extract<
|
||||
Query<(
|
||||
&Visibility,
|
||||
&TextureAtlasSprite,
|
||||
&GlobalTransform,
|
||||
&Handle<TextureAtlas>,
|
||||
)>,
|
||||
>,
|
||||
) {
|
||||
let mut extracted_sprites = render_world.resource_mut::<ExtractedSprites>();
|
||||
extracted_sprites.sprites.clear();
|
||||
for (visibility, sprite, transform, handle) in sprite_query.iter() {
|
||||
if !visibility.is_visible {
|
||||
|
|
|
@ -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,16 +61,13 @@ pub struct Text2dBundle {
|
|||
}
|
||||
|
||||
pub fn extract_text2d_sprite(
|
||||
mut render_world: ResMut<RenderWorld>,
|
||||
texture_atlases: Res<Assets<TextureAtlas>>,
|
||||
text_pipeline: Res<DefaultTextPipeline>,
|
||||
windows: Res<Windows>,
|
||||
text2d_query: Query<(Entity, &Visibility, &Text, &GlobalTransform, &Text2dSize)>,
|
||||
mut extracted_sprites: ResMut<ExtractedSprites>,
|
||||
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
|
||||
text_pipeline: Extract<Res<DefaultTextPipeline>>,
|
||||
windows: Extract<Res<Windows>>,
|
||||
text2d_query: Extract<Query<(Entity, &Visibility, &Text, &GlobalTransform, &Text2dSize)>>,
|
||||
) {
|
||||
let mut extracted_sprites = render_world.resource_mut::<ExtractedSprites>();
|
||||
|
||||
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;
|
||||
|
|
|
@ -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,18 +174,19 @@ pub struct ExtractedUiNodes {
|
|||
}
|
||||
|
||||
pub fn extract_uinodes(
|
||||
mut render_world: ResMut<RenderWorld>,
|
||||
images: Res<Assets<Image>>,
|
||||
uinode_query: Query<(
|
||||
&Node,
|
||||
&GlobalTransform,
|
||||
&UiColor,
|
||||
&UiImage,
|
||||
&Visibility,
|
||||
Option<&CalculatedClip>,
|
||||
)>,
|
||||
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
|
||||
images: Extract<Res<Assets<Image>>>,
|
||||
uinode_query: Extract<
|
||||
Query<(
|
||||
&Node,
|
||||
&GlobalTransform,
|
||||
&UiColor,
|
||||
&UiImage,
|
||||
&Visibility,
|
||||
Option<&CalculatedClip>,
|
||||
)>,
|
||||
>,
|
||||
) {
|
||||
let mut extracted_uinodes = render_world.resource_mut::<ExtractedUiNodes>();
|
||||
extracted_uinodes.uinodes.clear();
|
||||
for (uinode, transform, color, image, visibility, clip) in uinode_query.iter() {
|
||||
if !visibility.is_visible {
|
||||
|
@ -226,8 +227,7 @@ pub struct DefaultCameraView(pub Entity);
|
|||
|
||||
pub fn extract_default_ui_camera_view<T: Component>(
|
||||
mut commands: Commands,
|
||||
render_world: Res<RenderWorld>,
|
||||
query: Query<(Entity, &Camera, Option<&UiCameraConfig>), With<T>>,
|
||||
query: Extract<Query<(Entity, &Camera, Option<&UiCameraConfig>), With<T>>>,
|
||||
) {
|
||||
for (entity, camera, camera_ui) in query.iter() {
|
||||
// ignore cameras with disabled ui
|
||||
|
@ -245,10 +245,8 @@ pub fn extract_default_ui_camera_view<T: Component>(
|
|||
..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 = 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(
|
||||
|
@ -258,7 +256,8 @@ pub fn extract_default_ui_camera_view<T: Component>(
|
|||
),
|
||||
width: physical_size.x,
|
||||
height: physical_size.y,
|
||||
});
|
||||
})
|
||||
.id();
|
||||
commands.get_or_spawn(entity).insert_bundle((
|
||||
DefaultCameraView(default_camera_view),
|
||||
RenderPhase::<TransparentUi>::default(),
|
||||
|
@ -268,23 +267,22 @@ pub fn extract_default_ui_camera_view<T: Component>(
|
|||
}
|
||||
|
||||
pub fn extract_text_uinodes(
|
||||
mut render_world: ResMut<RenderWorld>,
|
||||
texture_atlases: Res<Assets<TextureAtlas>>,
|
||||
text_pipeline: Res<DefaultTextPipeline>,
|
||||
windows: Res<Windows>,
|
||||
uinode_query: Query<(
|
||||
Entity,
|
||||
&Node,
|
||||
&GlobalTransform,
|
||||
&Text,
|
||||
&Visibility,
|
||||
Option<&CalculatedClip>,
|
||||
)>,
|
||||
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
|
||||
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
|
||||
text_pipeline: Extract<Res<DefaultTextPipeline>>,
|
||||
windows: Extract<Res<Windows>>,
|
||||
uinode_query: Extract<
|
||||
Query<(
|
||||
Entity,
|
||||
&Node,
|
||||
&GlobalTransform,
|
||||
&Text,
|
||||
&Visibility,
|
||||
Option<&CalculatedClip>,
|
||||
)>,
|
||||
>,
|
||||
) {
|
||||
let mut extracted_uinodes = render_world.resource_mut::<ExtractedUiNodes>();
|
||||
|
||||
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;
|
||||
|
|
|
@ -19,7 +19,7 @@ use bevy::{
|
|||
},
|
||||
texture::BevyDefault,
|
||||
view::VisibleEntities,
|
||||
RenderApp, RenderStage,
|
||||
Extract, RenderApp, RenderStage,
|
||||
},
|
||||
sprite::{
|
||||
DrawMesh2d, Mesh2dHandle, Mesh2dPipeline, Mesh2dPipelineKey, Mesh2dUniform,
|
||||
|
@ -286,7 +286,9 @@ impl Plugin for ColoredMesh2dPlugin {
|
|||
pub fn extract_colored_mesh2d(
|
||||
mut commands: Commands,
|
||||
mut previous_len: Local<usize>,
|
||||
query: Query<(Entity, &ComputedVisibility), With<ColoredMesh2d>>,
|
||||
// When extracting, you must use `Extract` to mark the `SystemParam`s
|
||||
// which should be taken from the main world.
|
||||
query: Extract<Query<(Entity, &ComputedVisibility), With<ColoredMesh2d>>>,
|
||||
) {
|
||||
let mut values = Vec::with_capacity(*previous_len);
|
||||
for (entity, computed_visibility) in query.iter() {
|
||||
|
|
|
@ -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::{ExtractResource, ExtractResourcePlugin},
|
||||
mesh::MeshVertexBufferLayout,
|
||||
render_asset::RenderAssets,
|
||||
render_phase::{
|
||||
|
@ -64,6 +69,8 @@ impl Plugin for CustomMaterialPlugin {
|
|||
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
app.add_plugin(ExtractComponentPlugin::<CustomMaterial>::default())
|
||||
.add_plugin(ExtractResourcePlugin::<ExtractedTime>::default());
|
||||
|
||||
app.sub_app_mut(RenderApp)
|
||||
.add_render_command::<Transparent3d, DrawCustom>()
|
||||
|
@ -73,26 +80,20 @@ impl Plugin for CustomMaterialPlugin {
|
|||
})
|
||||
.init_resource::<CustomPipeline>()
|
||||
.init_resource::<SpecializedMeshPipelines<CustomPipeline>>()
|
||||
.add_system_to_stage(RenderStage::Extract, extract_time)
|
||||
.add_system_to_stage(RenderStage::Extract, extract_custom_material)
|
||||
.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<usize>,
|
||||
mut query: Query<Entity, With<CustomMaterial>>,
|
||||
) {
|
||||
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<CustomMaterial>;
|
||||
|
||||
type Filter = ();
|
||||
|
||||
fn extract_component(_: bevy::ecs::query::QueryItem<Self::Query>) -> 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`
|
||||
|
@ -138,11 +139,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<Time>) {
|
||||
commands.insert_resource(ExtractedTime {
|
||||
seconds_since_startup: time.seconds_since_startup() as f32,
|
||||
});
|
||||
impl ExtractResource for ExtractedTime {
|
||||
type Source = Time;
|
||||
|
||||
fn extract_resource(time: &Self::Source) -> Self {
|
||||
ExtractedTime {
|
||||
seconds_since_startup: time.seconds_since_startup() as f32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TimeMeta {
|
||||
|
|
|
@ -6,7 +6,7 @@ use bevy::{
|
|||
math::{DVec2, DVec3},
|
||||
pbr::{ExtractedPointLight, GlobalLightMeta},
|
||||
prelude::*,
|
||||
render::{camera::ScalingMode, RenderApp, RenderStage},
|
||||
render::{camera::ScalingMode, Extract, RenderApp, RenderStage},
|
||||
window::PresentMode,
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
|
@ -156,7 +156,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<Time>,
|
||||
time: Res<ExtractedTime>,
|
||||
mut timer: Local<PrintingTimer>,
|
||||
visible: Query<&ExtractedPointLight>,
|
||||
global_light_meta: Res<GlobalLightMeta>,
|
||||
|
@ -172,8 +172,11 @@ fn print_visible_light_count(
|
|||
}
|
||||
}
|
||||
|
||||
fn extract_time(mut commands: Commands, time: Res<Time>) {
|
||||
commands.insert_resource(time.into_inner().clone());
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct ExtractedTime(Time);
|
||||
|
||||
fn extract_time(mut commands: Commands, time: Extract<Res<Time>>) {
|
||||
commands.insert_resource(ExtractedTime(time.clone()));
|
||||
}
|
||||
|
||||
struct PrintingTimer(Timer);
|
||||
|
|
Loading…
Reference in a new issue