mirror of
https://github.com/bevyengine/bevy
synced 2024-11-24 21:53:07 +00:00
Implement WorldQuery
for MainWorld
and RenderWorld
components (#15745)
# Objective #15320 is a particularly painful breaking change, and the new `RenderEntity` in particular is very noisy, with a lot of `let entity = entity.id()` spam. ## Solution Implement `WorldQuery`, `QueryData` and `ReadOnlyQueryData` for `RenderEntity` and `WorldEntity`. These work the same as the `Entity` impls from a user-facing perspective: they simply return an owned (copied) `Entity` identifier. This dramatically reduces noise and eases migration. Under the hood, these impls defer to the implementations for `&T` for everything other than the "call .id() for the user" bit, as they involve read-only access to component data. Doing it this way (as opposed to implementing a custom fetch, as tried in the first commit) dramatically reduces the maintenance risk of complex unsafe code outside of `bevy_ecs`. To make this easier (and encourage users to do this themselves!), I've made `ReadFetch` and `WriteFetch` slightly more public: they're no longer `doc(hidden)`. This is a good change, since trying to vendor the logic is much worse than just deferring to the existing tested impls. ## Testing I've run a handful of rendering examples (breakout, alien_cake_addict, auto_exposure, fog_volumes, box_shadow) and nothing broke. ## Follow-up We should lint for the uses of `&RenderEntity` and `&MainEntity` in queries: this is just less nice for no reason. --------- Co-authored-by: Trashtalk217 <trashtalk217@gmail.com>
This commit is contained in:
parent
d96a9d15f6
commit
a7e9330af9
21 changed files with 291 additions and 87 deletions
|
@ -27,13 +27,13 @@ pub(super) struct ExtractedStateBuffers {
|
|||
|
||||
pub(super) fn extract_buffers(
|
||||
mut commands: Commands,
|
||||
changed: Extract<Query<(&RenderEntity, &AutoExposure), Changed<AutoExposure>>>,
|
||||
changed: Extract<Query<(RenderEntity, &AutoExposure), Changed<AutoExposure>>>,
|
||||
mut removed: Extract<RemovedComponents<AutoExposure>>,
|
||||
) {
|
||||
commands.insert_resource(ExtractedStateBuffers {
|
||||
changed: changed
|
||||
.iter()
|
||||
.map(|(entity, settings)| (entity.id(), settings.clone()))
|
||||
.map(|(entity, settings)| (entity, settings.clone()))
|
||||
.collect(),
|
||||
removed: removed.read().collect(),
|
||||
});
|
||||
|
|
|
@ -375,7 +375,7 @@ pub fn extract_core_2d_camera_phases(
|
|||
mut transparent_2d_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
|
||||
mut opaque_2d_phases: ResMut<ViewBinnedRenderPhases<Opaque2d>>,
|
||||
mut alpha_mask_2d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask2d>>,
|
||||
cameras_2d: Extract<Query<(&RenderEntity, &Camera), With<Camera2d>>>,
|
||||
cameras_2d: Extract<Query<(RenderEntity, &Camera), With<Camera2d>>>,
|
||||
mut live_entities: Local<EntityHashSet>,
|
||||
) {
|
||||
live_entities.clear();
|
||||
|
@ -384,7 +384,6 @@ pub fn extract_core_2d_camera_phases(
|
|||
if !camera.is_active {
|
||||
continue;
|
||||
}
|
||||
let entity = entity.id();
|
||||
transparent_2d_phases.insert_or_clear(entity);
|
||||
opaque_2d_phases.insert_or_clear(entity);
|
||||
alpha_mask_2d_phases.insert_or_clear(entity);
|
||||
|
|
|
@ -528,17 +528,16 @@ pub fn extract_core_3d_camera_phases(
|
|||
mut alpha_mask_3d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
|
||||
mut transmissive_3d_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
|
||||
mut transparent_3d_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
|
||||
cameras_3d: Extract<Query<(&RenderEntity, &Camera), With<Camera3d>>>,
|
||||
cameras_3d: Extract<Query<(RenderEntity, &Camera), With<Camera3d>>>,
|
||||
mut live_entities: Local<EntityHashSet>,
|
||||
) {
|
||||
live_entities.clear();
|
||||
|
||||
for (render_entity, camera) in &cameras_3d {
|
||||
for (entity, camera) in &cameras_3d {
|
||||
if !camera.is_active {
|
||||
continue;
|
||||
}
|
||||
|
||||
let entity = render_entity.id();
|
||||
opaque_3d_phases.insert_or_clear(entity);
|
||||
alpha_mask_3d_phases.insert_or_clear(entity);
|
||||
transmissive_3d_phases.insert_or_clear(entity);
|
||||
|
@ -563,7 +562,7 @@ pub fn extract_camera_prepass_phase(
|
|||
cameras_3d: Extract<
|
||||
Query<
|
||||
(
|
||||
&RenderEntity,
|
||||
RenderEntity,
|
||||
&Camera,
|
||||
Has<DepthPrepass>,
|
||||
Has<NormalPrepass>,
|
||||
|
@ -577,20 +576,13 @@ pub fn extract_camera_prepass_phase(
|
|||
) {
|
||||
live_entities.clear();
|
||||
|
||||
for (
|
||||
render_entity,
|
||||
camera,
|
||||
depth_prepass,
|
||||
normal_prepass,
|
||||
motion_vector_prepass,
|
||||
deferred_prepass,
|
||||
) in cameras_3d.iter()
|
||||
for (entity, camera, depth_prepass, normal_prepass, motion_vector_prepass, deferred_prepass) in
|
||||
cameras_3d.iter()
|
||||
{
|
||||
if !camera.is_active {
|
||||
continue;
|
||||
}
|
||||
|
||||
let entity = render_entity.id();
|
||||
if depth_prepass || normal_prepass || motion_vector_prepass {
|
||||
opaque_3d_prepass_phases.insert_or_clear(entity);
|
||||
alpha_mask_3d_prepass_phases.insert_or_clear(entity);
|
||||
|
|
|
@ -810,7 +810,7 @@ impl SpecializedRenderPipeline for DepthOfFieldPipeline {
|
|||
/// Extracts all [`DepthOfField`] components into the render world.
|
||||
fn extract_depth_of_field_settings(
|
||||
mut commands: Commands,
|
||||
mut query: Extract<Query<(&RenderEntity, &DepthOfField, &Projection)>>,
|
||||
mut query: Extract<Query<(RenderEntity, &DepthOfField, &Projection)>>,
|
||||
) {
|
||||
if !DEPTH_TEXTURE_SAMPLING_SUPPORTED {
|
||||
info_once!(
|
||||
|
@ -820,7 +820,6 @@ fn extract_depth_of_field_settings(
|
|||
}
|
||||
|
||||
for (entity, depth_of_field, projection) in query.iter_mut() {
|
||||
let entity = entity.id();
|
||||
// Depth of field is nonsensical without a perspective projection.
|
||||
let Projection::Perspective(ref perspective_projection) = *projection else {
|
||||
continue;
|
||||
|
|
|
@ -358,7 +358,7 @@ impl SpecializedRenderPipeline for TaaPipeline {
|
|||
|
||||
fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut<MainWorld>) {
|
||||
let mut cameras_3d = main_world.query_filtered::<(
|
||||
&RenderEntity,
|
||||
RenderEntity,
|
||||
&Camera,
|
||||
&Projection,
|
||||
&mut TemporalAntiAliasing,
|
||||
|
@ -375,7 +375,7 @@ fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut<MainWorld
|
|||
let has_perspective_projection = matches!(camera_projection, Projection::Perspective(_));
|
||||
if camera.is_active && has_perspective_projection {
|
||||
commands
|
||||
.get_entity(entity.id())
|
||||
.get_entity(entity)
|
||||
.expect("Camera entity wasn't synced.")
|
||||
.insert(taa_settings.clone());
|
||||
taa_settings.reset = false;
|
||||
|
|
|
@ -1057,7 +1057,7 @@ unsafe impl QueryData for &Archetype {
|
|||
/// SAFETY: access is read only
|
||||
unsafe impl ReadOnlyQueryData for &Archetype {}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// The [`WorldQuery::Fetch`] type for `& T`.
|
||||
pub struct ReadFetch<'w, T: Component> {
|
||||
components: StorageSwitch<
|
||||
T,
|
||||
|
@ -1415,7 +1415,7 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> {
|
|||
/// SAFETY: access is read only
|
||||
unsafe impl<'__w, T: Component> ReadOnlyQueryData for Ref<'__w, T> {}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// The [`WorldQuery::Fetch`] type for `&mut T`.
|
||||
pub struct WriteFetch<'w, T: Component> {
|
||||
components: StorageSwitch<
|
||||
T,
|
||||
|
|
|
@ -526,8 +526,8 @@ pub(crate) fn clusterable_object_order(
|
|||
/// Extracts clusters from the main world from the render world.
|
||||
pub fn extract_clusters(
|
||||
mut commands: Commands,
|
||||
views: Extract<Query<(&RenderEntity, &Clusters, &Camera)>>,
|
||||
mapper: Extract<Query<&RenderEntity>>,
|
||||
views: Extract<Query<(RenderEntity, &Clusters, &Camera)>>,
|
||||
mapper: Extract<Query<RenderEntity>>,
|
||||
) {
|
||||
for (entity, clusters, camera) in &views {
|
||||
if !camera.is_active {
|
||||
|
@ -548,14 +548,14 @@ pub fn extract_clusters(
|
|||
for clusterable_entity in &cluster_objects.entities {
|
||||
if let Ok(entity) = mapper.get(*clusterable_entity) {
|
||||
data.push(ExtractedClusterableObjectElement::ClusterableObjectEntity(
|
||||
entity.id(),
|
||||
entity,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commands
|
||||
.get_entity(entity.id())
|
||||
.get_entity(entity)
|
||||
.expect("Clusters entity wasn't synced.")
|
||||
.insert((
|
||||
ExtractedClusterableObjects { data },
|
||||
|
|
|
@ -372,7 +372,7 @@ impl Plugin for LightProbePlugin {
|
|||
/// Compared to the `ExtractComponentPlugin`, this implementation will create a default instance
|
||||
/// if one does not already exist.
|
||||
fn gather_environment_map_uniform(
|
||||
view_query: Extract<Query<(&RenderEntity, Option<&EnvironmentMapLight>), With<Camera3d>>>,
|
||||
view_query: Extract<Query<(RenderEntity, Option<&EnvironmentMapLight>), With<Camera3d>>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
for (view_entity, environment_map_light) in view_query.iter() {
|
||||
|
@ -386,7 +386,7 @@ fn gather_environment_map_uniform(
|
|||
EnvironmentMapUniform::default()
|
||||
};
|
||||
commands
|
||||
.get_entity(view_entity.id())
|
||||
.get_entity(view_entity)
|
||||
.expect("Environment map light entity wasn't synced.")
|
||||
.insert(environment_map_uniform);
|
||||
}
|
||||
|
@ -398,7 +398,7 @@ fn gather_light_probes<C>(
|
|||
image_assets: Res<RenderAssets<GpuImage>>,
|
||||
light_probe_query: Extract<Query<(&GlobalTransform, &C), With<LightProbe>>>,
|
||||
view_query: Extract<
|
||||
Query<(&RenderEntity, &GlobalTransform, &Frustum, Option<&C>), With<Camera3d>>,
|
||||
Query<(RenderEntity, &GlobalTransform, &Frustum, Option<&C>), With<Camera3d>>,
|
||||
>,
|
||||
mut reflection_probes: Local<Vec<LightProbeInfo<C>>>,
|
||||
mut view_reflection_probes: Local<Vec<LightProbeInfo<C>>>,
|
||||
|
@ -437,16 +437,15 @@ fn gather_light_probes<C>(
|
|||
// Gather up the light probes in the list.
|
||||
render_view_light_probes.maybe_gather_light_probes(&view_reflection_probes);
|
||||
|
||||
let entity = view_entity.id();
|
||||
// Record the per-view light probes.
|
||||
if render_view_light_probes.is_empty() {
|
||||
commands
|
||||
.get_entity(entity)
|
||||
.get_entity(view_entity)
|
||||
.expect("View entity wasn't synced.")
|
||||
.remove::<RenderViewLightProbes<C>>();
|
||||
} else {
|
||||
commands
|
||||
.get_entity(entity)
|
||||
.get_entity(view_entity)
|
||||
.expect("View entity wasn't synced.")
|
||||
.insert(render_view_light_probes);
|
||||
}
|
||||
|
|
|
@ -581,11 +581,10 @@ where
|
|||
// Extract the render phases for the prepass
|
||||
pub fn extract_camera_previous_view_data(
|
||||
mut commands: Commands,
|
||||
cameras_3d: Extract<Query<(&RenderEntity, &Camera, Option<&PreviousViewData>), With<Camera3d>>>,
|
||||
cameras_3d: Extract<Query<(RenderEntity, &Camera, Option<&PreviousViewData>), With<Camera3d>>>,
|
||||
) {
|
||||
for (entity, camera, maybe_previous_view_data) in cameras_3d.iter() {
|
||||
if camera.is_active {
|
||||
let entity = entity.id();
|
||||
let mut entity = commands
|
||||
.get_entity(entity)
|
||||
.expect("Camera entity wasn't synced.");
|
||||
|
|
|
@ -193,7 +193,7 @@ pub fn extract_lights(
|
|||
global_point_lights: Extract<Res<GlobalVisibleClusterableObjects>>,
|
||||
point_lights: Extract<
|
||||
Query<(
|
||||
&RenderEntity,
|
||||
RenderEntity,
|
||||
&PointLight,
|
||||
&CubemapVisibleEntities,
|
||||
&GlobalTransform,
|
||||
|
@ -204,7 +204,7 @@ pub fn extract_lights(
|
|||
>,
|
||||
spot_lights: Extract<
|
||||
Query<(
|
||||
&RenderEntity,
|
||||
RenderEntity,
|
||||
&SpotLight,
|
||||
&VisibleMeshEntities,
|
||||
&GlobalTransform,
|
||||
|
@ -216,7 +216,7 @@ pub fn extract_lights(
|
|||
directional_lights: Extract<
|
||||
Query<
|
||||
(
|
||||
&RenderEntity,
|
||||
RenderEntity,
|
||||
&DirectionalLight,
|
||||
&CascadesVisibleEntities,
|
||||
&Cascades,
|
||||
|
@ -230,7 +230,7 @@ pub fn extract_lights(
|
|||
Without<SpotLight>,
|
||||
>,
|
||||
>,
|
||||
mapper: Extract<Query<&RenderEntity>>,
|
||||
mapper: Extract<Query<RenderEntity>>,
|
||||
mut previous_point_lights_len: Local<usize>,
|
||||
mut previous_spot_lights_len: Local<usize>,
|
||||
) {
|
||||
|
@ -298,7 +298,7 @@ pub fn extract_lights(
|
|||
volumetric: volumetric_light.is_some(),
|
||||
};
|
||||
point_lights_values.push((
|
||||
render_entity.id(),
|
||||
render_entity,
|
||||
(
|
||||
extracted_point_light,
|
||||
render_cubemap_visible_entities,
|
||||
|
@ -331,7 +331,7 @@ pub fn extract_lights(
|
|||
2.0 * ops::tan(spot_light.outer_angle) / directional_light_shadow_map.size as f32;
|
||||
|
||||
spot_lights_values.push((
|
||||
render_entity.id(),
|
||||
render_entity,
|
||||
(
|
||||
ExtractedPointLight {
|
||||
color: spot_light.color.into(),
|
||||
|
@ -388,14 +388,14 @@ pub fn extract_lights(
|
|||
let mut cascade_visible_entities = EntityHashMap::default();
|
||||
for (e, v) in cascades.cascades.iter() {
|
||||
if let Ok(entity) = mapper.get(*e) {
|
||||
extracted_cascades.insert(entity.id(), v.clone());
|
||||
extracted_cascades.insert(entity, v.clone());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (e, v) in frusta.frusta.iter() {
|
||||
if let Ok(entity) = mapper.get(*e) {
|
||||
extracted_frusta.insert(entity.id(), v.clone());
|
||||
extracted_frusta.insert(entity, v.clone());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -403,7 +403,7 @@ pub fn extract_lights(
|
|||
for (e, v) in visible_entities.entities.iter() {
|
||||
if let Ok(entity) = mapper.get(*e) {
|
||||
cascade_visible_entities.insert(
|
||||
entity.id(),
|
||||
entity,
|
||||
v.iter()
|
||||
.map(|v| create_render_visible_mesh_entities(&mut commands, &mapper, v))
|
||||
.collect(),
|
||||
|
@ -414,7 +414,7 @@ pub fn extract_lights(
|
|||
}
|
||||
|
||||
commands
|
||||
.get_entity(entity.id())
|
||||
.get_entity(entity)
|
||||
.expect("Light entity wasn't synced.")
|
||||
.insert((
|
||||
ExtractedDirectionalLight {
|
||||
|
@ -442,7 +442,7 @@ pub fn extract_lights(
|
|||
|
||||
fn create_render_visible_mesh_entities(
|
||||
commands: &mut Commands,
|
||||
mapper: &Extract<Query<&RenderEntity>>,
|
||||
mapper: &Extract<Query<RenderEntity>>,
|
||||
visible_entities: &VisibleMeshEntities,
|
||||
) -> RenderVisibleMeshEntities {
|
||||
RenderVisibleMeshEntities {
|
||||
|
@ -451,7 +451,6 @@ fn create_render_visible_mesh_entities(
|
|||
.map(|e| {
|
||||
let render_entity = mapper
|
||||
.get(*e)
|
||||
.map(RenderEntity::id)
|
||||
.unwrap_or_else(|_| commands.spawn(TemporaryRenderEntity).id());
|
||||
(render_entity, MainEntity::from(*e))
|
||||
})
|
||||
|
|
|
@ -522,7 +522,7 @@ fn extract_ssao_settings(
|
|||
mut commands: Commands,
|
||||
cameras: Extract<
|
||||
Query<
|
||||
(&RenderEntity, &Camera, &ScreenSpaceAmbientOcclusion, &Msaa),
|
||||
(RenderEntity, &Camera, &ScreenSpaceAmbientOcclusion, &Msaa),
|
||||
(With<Camera3d>, With<DepthPrepass>, With<NormalPrepass>),
|
||||
>,
|
||||
>,
|
||||
|
@ -537,7 +537,7 @@ fn extract_ssao_settings(
|
|||
}
|
||||
if camera.is_active {
|
||||
commands
|
||||
.get_entity(entity.id())
|
||||
.get_entity(entity)
|
||||
.expect("SSAO entity wasn't synced.")
|
||||
.insert(ssao_settings.clone());
|
||||
}
|
||||
|
|
|
@ -271,9 +271,9 @@ impl FromWorld for VolumetricFogPipeline {
|
|||
/// from the main world to the render world.
|
||||
pub fn extract_volumetric_fog(
|
||||
mut commands: Commands,
|
||||
view_targets: Extract<Query<(&RenderEntity, &VolumetricFog)>>,
|
||||
fog_volumes: Extract<Query<(&RenderEntity, &FogVolume, &GlobalTransform)>>,
|
||||
volumetric_lights: Extract<Query<(&RenderEntity, &VolumetricLight)>>,
|
||||
view_targets: Extract<Query<(RenderEntity, &VolumetricFog)>>,
|
||||
fog_volumes: Extract<Query<(RenderEntity, &FogVolume, &GlobalTransform)>>,
|
||||
volumetric_lights: Extract<Query<(RenderEntity, &VolumetricLight)>>,
|
||||
) {
|
||||
if volumetric_lights.is_empty() {
|
||||
return;
|
||||
|
@ -281,14 +281,14 @@ pub fn extract_volumetric_fog(
|
|||
|
||||
for (entity, volumetric_fog) in view_targets.iter() {
|
||||
commands
|
||||
.get_entity(entity.id())
|
||||
.get_entity(entity)
|
||||
.expect("Volumetric fog entity wasn't synced.")
|
||||
.insert(*volumetric_fog);
|
||||
}
|
||||
|
||||
for (entity, fog_volume, fog_transform) in fog_volumes.iter() {
|
||||
commands
|
||||
.get_entity(entity.id())
|
||||
.get_entity(entity)
|
||||
.expect("Fog volume entity wasn't synced.")
|
||||
.insert((*fog_volume).clone())
|
||||
.insert(*fog_transform);
|
||||
|
@ -296,7 +296,7 @@ pub fn extract_volumetric_fog(
|
|||
|
||||
for (entity, volumetric_light) in volumetric_lights.iter() {
|
||||
commands
|
||||
.get_entity(entity.id())
|
||||
.get_entity(entity)
|
||||
.expect("Volumetric light entity wasn't synced.")
|
||||
.insert(*volumetric_light);
|
||||
}
|
||||
|
|
|
@ -1019,7 +1019,7 @@ pub fn extract_cameras(
|
|||
mut commands: Commands,
|
||||
query: Extract<
|
||||
Query<(
|
||||
&RenderEntity,
|
||||
RenderEntity,
|
||||
&Camera,
|
||||
&CameraRenderGraph,
|
||||
&GlobalTransform,
|
||||
|
@ -1096,7 +1096,7 @@ pub fn extract_cameras(
|
|||
})
|
||||
.collect(),
|
||||
};
|
||||
let mut commands = commands.entity(render_entity.id());
|
||||
let mut commands = commands.entity(render_entity);
|
||||
commands.insert((
|
||||
ExtractedCamera {
|
||||
target: camera.target.normalize(primary_window),
|
||||
|
|
|
@ -199,12 +199,12 @@ impl<C: ExtractComponent> Plugin for ExtractComponentPlugin<C> {
|
|||
fn extract_components<C: ExtractComponent>(
|
||||
mut commands: Commands,
|
||||
mut previous_len: Local<usize>,
|
||||
query: Extract<Query<(&RenderEntity, C::QueryData), C::QueryFilter>>,
|
||||
query: Extract<Query<(RenderEntity, C::QueryData), C::QueryFilter>>,
|
||||
) {
|
||||
let mut values = Vec::with_capacity(*previous_len);
|
||||
for (entity, query_item) in &query {
|
||||
if let Some(component) = C::extract_component(query_item) {
|
||||
values.push((entity.id(), component));
|
||||
values.push((entity, component));
|
||||
}
|
||||
}
|
||||
*previous_len = values.len();
|
||||
|
@ -215,13 +215,13 @@ fn extract_components<C: ExtractComponent>(
|
|||
fn extract_visible_components<C: ExtractComponent>(
|
||||
mut commands: Commands,
|
||||
mut previous_len: Local<usize>,
|
||||
query: Extract<Query<(&RenderEntity, &ViewVisibility, C::QueryData), C::QueryFilter>>,
|
||||
query: Extract<Query<(RenderEntity, &ViewVisibility, C::QueryData), C::QueryFilter>>,
|
||||
) {
|
||||
let mut values = Vec::with_capacity(*previous_len);
|
||||
for (entity, view_visibility, query_item) in &query {
|
||||
if view_visibility.get() {
|
||||
if let Some(component) = C::extract_component(query_item) {
|
||||
values.push((entity.id(), component));
|
||||
values.push((entity, component));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,9 +34,9 @@ use core::ops::{Deref, DerefMut};
|
|||
/// # #[derive(Component)]
|
||||
/// // Do make sure to sync the cloud entities before extracting them.
|
||||
/// # struct Cloud;
|
||||
/// fn extract_clouds(mut commands: Commands, clouds: Extract<Query<&RenderEntity, With<Cloud>>>) {
|
||||
/// fn extract_clouds(mut commands: Commands, clouds: Extract<Query<RenderEntity, With<Cloud>>>) {
|
||||
/// for cloud in &clouds {
|
||||
/// commands.entity(cloud.id()).insert(Cloud);
|
||||
/// commands.entity(cloud).insert(Cloud);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
|
|
|
@ -30,9 +30,13 @@ use bevy_utils::hashbrown;
|
|||
/// between the main world and the render world.
|
||||
/// It does so by spawning and despawning entities in the render world, to match spawned and despawned entities in the main world.
|
||||
/// The link between synced entities is maintained by the [`RenderEntity`] and [`MainEntity`] components.
|
||||
///
|
||||
/// The [`RenderEntity`] contains the corresponding render world entity of a main world entity, while [`MainEntity`] contains
|
||||
/// the corresponding main world entity of a render world entity.
|
||||
/// The entities can be accessed by calling `.id()` on either component.
|
||||
/// For convenience, [`QueryData`](bevy_ecs::query::QueryData) implementations are provided for both components:
|
||||
/// adding [`MainEntity`] to a query (without a `&`) will return the corresponding main world [`Entity`],
|
||||
/// and adding [`RenderEntity`] will return the corresponding render world [`Entity`].
|
||||
/// If you have access to the component itself, the underlying entities can be accessed by calling `.id()`.
|
||||
///
|
||||
/// Synchronization is necessary preparation for extraction ([`ExtractSchedule`](crate::ExtractSchedule)), which copies over component data from the main
|
||||
/// to the render world for these entities.
|
||||
|
@ -243,6 +247,220 @@ pub(crate) fn despawn_temporary_render_entities(
|
|||
}
|
||||
}
|
||||
|
||||
/// This module exists to keep the complex unsafe code out of the main module.
|
||||
///
|
||||
/// The implementations for both [`MainEntity`] and [`RenderEntity`] should stay in sync,
|
||||
/// and are based off of the `&T` implementation in `bevy_ecs`.
|
||||
mod render_entities_world_query_impls {
|
||||
use super::{MainEntity, RenderEntity};
|
||||
|
||||
use bevy_ecs::{
|
||||
archetype::Archetype,
|
||||
component::{ComponentId, Components, Tick},
|
||||
entity::Entity,
|
||||
query::{FilteredAccess, QueryData, ReadOnlyQueryData, WorldQuery},
|
||||
storage::{Table, TableRow},
|
||||
world::{unsafe_world_cell::UnsafeWorldCell, World},
|
||||
};
|
||||
|
||||
/// SAFETY: defers completely to `&RenderEntity` implementation,
|
||||
/// and then only modifies the output safely.
|
||||
unsafe impl WorldQuery for RenderEntity {
|
||||
type Item<'w> = Entity;
|
||||
type Fetch<'w> = <&'static RenderEntity as WorldQuery>::Fetch<'w>;
|
||||
type State = <&'static RenderEntity as WorldQuery>::State;
|
||||
|
||||
fn shrink<'wlong: 'wshort, 'wshort>(item: Entity) -> Entity {
|
||||
item
|
||||
}
|
||||
|
||||
fn shrink_fetch<'wlong: 'wshort, 'wshort>(
|
||||
fetch: Self::Fetch<'wlong>,
|
||||
) -> Self::Fetch<'wshort> {
|
||||
fetch
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn init_fetch<'w>(
|
||||
world: UnsafeWorldCell<'w>,
|
||||
component_id: &ComponentId,
|
||||
last_run: Tick,
|
||||
this_run: Tick,
|
||||
) -> Self::Fetch<'w> {
|
||||
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
|
||||
unsafe {
|
||||
<&RenderEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
|
||||
}
|
||||
}
|
||||
|
||||
const IS_DENSE: bool = <&'static RenderEntity as WorldQuery>::IS_DENSE;
|
||||
|
||||
#[inline]
|
||||
unsafe fn set_archetype<'w>(
|
||||
fetch: &mut Self::Fetch<'w>,
|
||||
component_id: &ComponentId,
|
||||
archetype: &'w Archetype,
|
||||
table: &'w Table,
|
||||
) {
|
||||
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
|
||||
unsafe {
|
||||
<&RenderEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn set_table<'w>(
|
||||
fetch: &mut Self::Fetch<'w>,
|
||||
&component_id: &ComponentId,
|
||||
table: &'w Table,
|
||||
) {
|
||||
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
|
||||
unsafe { <&RenderEntity as WorldQuery>::set_table(fetch, &component_id, table) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn fetch<'w>(
|
||||
fetch: &mut Self::Fetch<'w>,
|
||||
entity: Entity,
|
||||
table_row: TableRow,
|
||||
) -> Self::Item<'w> {
|
||||
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
|
||||
let component =
|
||||
unsafe { <&RenderEntity as WorldQuery>::fetch(fetch, entity, table_row) };
|
||||
component.id()
|
||||
}
|
||||
|
||||
fn update_component_access(
|
||||
&component_id: &ComponentId,
|
||||
access: &mut FilteredAccess<ComponentId>,
|
||||
) {
|
||||
<&RenderEntity as WorldQuery>::update_component_access(&component_id, access);
|
||||
}
|
||||
|
||||
fn init_state(world: &mut World) -> ComponentId {
|
||||
<&RenderEntity as WorldQuery>::init_state(world)
|
||||
}
|
||||
|
||||
fn get_state(components: &Components) -> Option<Self::State> {
|
||||
<&RenderEntity as WorldQuery>::get_state(components)
|
||||
}
|
||||
|
||||
fn matches_component_set(
|
||||
&state: &ComponentId,
|
||||
set_contains_id: &impl Fn(ComponentId) -> bool,
|
||||
) -> bool {
|
||||
<&RenderEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: Component access of Self::ReadOnly is a subset of Self.
|
||||
// Self::ReadOnly matches exactly the same archetypes/tables as Self.
|
||||
unsafe impl QueryData for RenderEntity {
|
||||
type ReadOnly = RenderEntity;
|
||||
}
|
||||
|
||||
// SAFETY: the underlying `Entity` is copied, and no mutable access is provided.
|
||||
unsafe impl ReadOnlyQueryData for RenderEntity {}
|
||||
|
||||
/// SAFETY: defers completely to `&RenderEntity` implementation,
|
||||
/// and then only modifies the output safely.
|
||||
unsafe impl WorldQuery for MainEntity {
|
||||
type Item<'w> = Entity;
|
||||
type Fetch<'w> = <&'static MainEntity as WorldQuery>::Fetch<'w>;
|
||||
type State = <&'static MainEntity as WorldQuery>::State;
|
||||
|
||||
fn shrink<'wlong: 'wshort, 'wshort>(item: Entity) -> Entity {
|
||||
item
|
||||
}
|
||||
|
||||
fn shrink_fetch<'wlong: 'wshort, 'wshort>(
|
||||
fetch: Self::Fetch<'wlong>,
|
||||
) -> Self::Fetch<'wshort> {
|
||||
fetch
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn init_fetch<'w>(
|
||||
world: UnsafeWorldCell<'w>,
|
||||
component_id: &ComponentId,
|
||||
last_run: Tick,
|
||||
this_run: Tick,
|
||||
) -> Self::Fetch<'w> {
|
||||
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
|
||||
unsafe {
|
||||
<&MainEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
|
||||
}
|
||||
}
|
||||
|
||||
const IS_DENSE: bool = <&'static MainEntity as WorldQuery>::IS_DENSE;
|
||||
|
||||
#[inline]
|
||||
unsafe fn set_archetype<'w>(
|
||||
fetch: &mut Self::Fetch<'w>,
|
||||
component_id: &ComponentId,
|
||||
archetype: &'w Archetype,
|
||||
table: &'w Table,
|
||||
) {
|
||||
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
|
||||
unsafe {
|
||||
<&MainEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn set_table<'w>(
|
||||
fetch: &mut Self::Fetch<'w>,
|
||||
&component_id: &ComponentId,
|
||||
table: &'w Table,
|
||||
) {
|
||||
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
|
||||
unsafe { <&MainEntity as WorldQuery>::set_table(fetch, &component_id, table) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn fetch<'w>(
|
||||
fetch: &mut Self::Fetch<'w>,
|
||||
entity: Entity,
|
||||
table_row: TableRow,
|
||||
) -> Self::Item<'w> {
|
||||
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
|
||||
let component = unsafe { <&MainEntity as WorldQuery>::fetch(fetch, entity, table_row) };
|
||||
component.id()
|
||||
}
|
||||
|
||||
fn update_component_access(
|
||||
&component_id: &ComponentId,
|
||||
access: &mut FilteredAccess<ComponentId>,
|
||||
) {
|
||||
<&MainEntity as WorldQuery>::update_component_access(&component_id, access);
|
||||
}
|
||||
|
||||
fn init_state(world: &mut World) -> ComponentId {
|
||||
<&MainEntity as WorldQuery>::init_state(world)
|
||||
}
|
||||
|
||||
fn get_state(components: &Components) -> Option<Self::State> {
|
||||
<&MainEntity as WorldQuery>::get_state(components)
|
||||
}
|
||||
|
||||
fn matches_component_set(
|
||||
&state: &ComponentId,
|
||||
set_contains_id: &impl Fn(ComponentId) -> bool,
|
||||
) -> bool {
|
||||
<&MainEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: Component access of Self::ReadOnly is a subset of Self.
|
||||
// Self::ReadOnly matches exactly the same archetypes/tables as Self.
|
||||
unsafe impl QueryData for MainEntity {
|
||||
type ReadOnly = MainEntity;
|
||||
}
|
||||
|
||||
// SAFETY: the underlying `Entity` is copied, and no mutable access is provided.
|
||||
unsafe impl ReadOnlyQueryData for MainEntity {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bevy_ecs::{
|
||||
|
|
|
@ -374,7 +374,7 @@ pub fn extract_sprites(
|
|||
sprite_query: Extract<
|
||||
Query<(
|
||||
Entity,
|
||||
&RenderEntity,
|
||||
RenderEntity,
|
||||
&ViewVisibility,
|
||||
&Sprite,
|
||||
&GlobalTransform,
|
||||
|
@ -422,7 +422,7 @@ pub fn extract_sprites(
|
|||
|
||||
// PERF: we don't check in this function that the `Image` asset is ready, since it should be in most cases and hashing the handle is expensive
|
||||
extracted_sprites.sprites.insert(
|
||||
(entity.id(), original_entity.into()),
|
||||
(entity, original_entity.into()),
|
||||
ExtractedSprite {
|
||||
color: sprite.color.into(),
|
||||
transform: *transform,
|
||||
|
|
|
@ -247,7 +247,7 @@ pub fn extract_shadows(
|
|||
Option<&TargetCamera>,
|
||||
)>,
|
||||
>,
|
||||
mapping: Extract<Query<&RenderEntity>>,
|
||||
mapping: Extract<Query<RenderEntity>>,
|
||||
) {
|
||||
for (entity, uinode, transform, view_visibility, box_shadow, clip, camera) in &box_shadow_query
|
||||
{
|
||||
|
@ -256,7 +256,7 @@ pub fn extract_shadows(
|
|||
continue;
|
||||
};
|
||||
|
||||
let Ok(&camera_entity) = mapping.get(camera_entity) else {
|
||||
let Ok(camera_entity) = mapping.get(camera_entity) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
@ -266,7 +266,7 @@ pub fn extract_shadows(
|
|||
}
|
||||
|
||||
let ui_logical_viewport_size = camera_query
|
||||
.get(camera_entity.id())
|
||||
.get(camera_entity)
|
||||
.ok()
|
||||
.and_then(|(_, c)| c.logical_viewport_size())
|
||||
.unwrap_or(Vec2::ZERO)
|
||||
|
@ -321,7 +321,7 @@ pub fn extract_shadows(
|
|||
max: shadow_size + 6. * blur_radius,
|
||||
},
|
||||
clip: clip.map(|clip| clip.clip),
|
||||
camera_entity: camera_entity.id(),
|
||||
camera_entity,
|
||||
radius,
|
||||
blur_radius,
|
||||
size: shadow_size,
|
||||
|
|
|
@ -235,7 +235,7 @@ pub fn extract_uinode_background_colors(
|
|||
&BackgroundColor,
|
||||
)>,
|
||||
>,
|
||||
mapping: Extract<Query<&RenderEntity>>,
|
||||
mapping: Extract<Query<RenderEntity>>,
|
||||
) {
|
||||
for (entity, uinode, transform, view_visibility, clip, camera, background_color) in
|
||||
&uinode_query
|
||||
|
@ -245,7 +245,7 @@ pub fn extract_uinode_background_colors(
|
|||
continue;
|
||||
};
|
||||
|
||||
let Ok(&render_camera_entity) = mapping.get(camera_entity) else {
|
||||
let Ok(render_camera_entity) = mapping.get(camera_entity) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
@ -265,7 +265,7 @@ pub fn extract_uinode_background_colors(
|
|||
},
|
||||
clip: clip.map(|clip| clip.clip),
|
||||
image: AssetId::default(),
|
||||
camera_entity: render_camera_entity.id(),
|
||||
camera_entity: render_camera_entity,
|
||||
item: ExtractedUiItem::Node {
|
||||
atlas_scaling: None,
|
||||
transform: transform.compute_matrix(),
|
||||
|
@ -302,7 +302,7 @@ pub fn extract_uinode_images(
|
|||
Without<ImageScaleMode>,
|
||||
>,
|
||||
>,
|
||||
mapping: Extract<Query<&RenderEntity>>,
|
||||
mapping: Extract<Query<RenderEntity>>,
|
||||
) {
|
||||
for (entity, uinode, transform, view_visibility, clip, camera, image, atlas) in &uinode_query {
|
||||
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
||||
|
@ -357,7 +357,7 @@ pub fn extract_uinode_images(
|
|||
rect,
|
||||
clip: clip.map(|clip| clip.clip),
|
||||
image: image.texture.id(),
|
||||
camera_entity: render_camera_entity.id(),
|
||||
camera_entity: render_camera_entity,
|
||||
item: ExtractedUiItem::Node {
|
||||
atlas_scaling,
|
||||
transform: transform.compute_matrix(),
|
||||
|
@ -388,7 +388,7 @@ pub fn extract_uinode_borders(
|
|||
AnyOf<(&BorderColor, &Outline)>,
|
||||
)>,
|
||||
>,
|
||||
mapping: Extract<Query<&RenderEntity>>,
|
||||
mapping: Extract<Query<RenderEntity>>,
|
||||
) {
|
||||
let image = AssetId::<Image>::default();
|
||||
|
||||
|
@ -409,7 +409,7 @@ pub fn extract_uinode_borders(
|
|||
continue;
|
||||
};
|
||||
|
||||
let Ok(&render_camera_entity) = mapping.get(camera_entity) else {
|
||||
let Ok(render_camera_entity) = mapping.get(camera_entity) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
@ -435,7 +435,7 @@ pub fn extract_uinode_borders(
|
|||
},
|
||||
image,
|
||||
clip: maybe_clip.map(|clip| clip.clip),
|
||||
camera_entity: render_camera_entity.id(),
|
||||
camera_entity: render_camera_entity,
|
||||
item: ExtractedUiItem::Node {
|
||||
atlas_scaling: None,
|
||||
transform: global_transform.compute_matrix(),
|
||||
|
@ -464,7 +464,7 @@ pub fn extract_uinode_borders(
|
|||
},
|
||||
image,
|
||||
clip: maybe_clip.map(|clip| clip.clip),
|
||||
camera_entity: render_camera_entity.id(),
|
||||
camera_entity: render_camera_entity,
|
||||
item: ExtractedUiItem::Node {
|
||||
transform: global_transform.compute_matrix(),
|
||||
atlas_scaling: None,
|
||||
|
@ -503,7 +503,7 @@ pub fn extract_default_ui_camera_view(
|
|||
query: Extract<
|
||||
Query<
|
||||
(
|
||||
&RenderEntity,
|
||||
RenderEntity,
|
||||
&Camera,
|
||||
Option<&UiAntiAlias>,
|
||||
Option<&UiBoxShadowSamples>,
|
||||
|
@ -534,7 +534,6 @@ pub fn extract_default_ui_camera_view(
|
|||
camera.physical_viewport_rect(),
|
||||
camera.physical_viewport_size(),
|
||||
) {
|
||||
let entity = entity.id();
|
||||
// use a projection matrix with the origin in the top left instead of the bottom left that comes with OrthographicProjection
|
||||
let projection_matrix = Mat4::orthographic_rh(
|
||||
0.0,
|
||||
|
|
|
@ -376,7 +376,7 @@ pub fn extract_ui_material_nodes<M: UiMaterial>(
|
|||
Without<BackgroundColor>,
|
||||
>,
|
||||
>,
|
||||
render_entity_lookup: Extract<Query<&RenderEntity>>,
|
||||
render_entity_lookup: Extract<Query<RenderEntity>>,
|
||||
) {
|
||||
// If there is only one camera, we use it as default
|
||||
let default_single_camera = default_ui_camera.get();
|
||||
|
@ -386,7 +386,7 @@ pub fn extract_ui_material_nodes<M: UiMaterial>(
|
|||
continue;
|
||||
};
|
||||
|
||||
let Ok(&camera_entity) = render_entity_lookup.get(camera_entity) else {
|
||||
let Ok(camera_entity) = render_entity_lookup.get(camera_entity) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
@ -419,7 +419,7 @@ pub fn extract_ui_material_nodes<M: UiMaterial>(
|
|||
},
|
||||
border,
|
||||
clip: clip.map(|clip| clip.clip),
|
||||
camera_entity: camera_entity.id(),
|
||||
camera_entity,
|
||||
main_entity: entity.into(),
|
||||
},
|
||||
);
|
||||
|
|
|
@ -261,7 +261,7 @@ pub fn extract_ui_texture_slices(
|
|||
Option<&TextureAtlas>,
|
||||
)>,
|
||||
>,
|
||||
mapping: Extract<Query<&RenderEntity>>,
|
||||
mapping: Extract<Query<RenderEntity>>,
|
||||
) {
|
||||
for (
|
||||
entity,
|
||||
|
@ -280,7 +280,7 @@ pub fn extract_ui_texture_slices(
|
|||
continue;
|
||||
};
|
||||
|
||||
let Ok(&camera_entity) = mapping.get(camera_entity) else {
|
||||
let Ok(camera_entity) = mapping.get(camera_entity) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
@ -319,7 +319,7 @@ pub fn extract_ui_texture_slices(
|
|||
},
|
||||
clip: clip.map(|clip| clip.clip),
|
||||
image: image.texture.id(),
|
||||
camera_entity: camera_entity.id(),
|
||||
camera_entity,
|
||||
image_scale_mode: image_scale_mode.clone(),
|
||||
atlas_rect,
|
||||
flip_x: image.flip_x,
|
||||
|
|
Loading…
Reference in a new issue