From a7e9330af9915bef32e762389e158e1a91590997 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 13 Oct 2024 16:58:46 -0400 Subject: [PATCH] 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 --- .../src/auto_exposure/buffers.rs | 4 +- crates/bevy_core_pipeline/src/core_2d/mod.rs | 3 +- crates/bevy_core_pipeline/src/core_3d/mod.rs | 18 +- crates/bevy_core_pipeline/src/dof/mod.rs | 3 +- crates/bevy_core_pipeline/src/taa/mod.rs | 4 +- crates/bevy_ecs/src/query/fetch.rs | 4 +- crates/bevy_pbr/src/cluster/mod.rs | 8 +- crates/bevy_pbr/src/light_probe/mod.rs | 11 +- crates/bevy_pbr/src/prepass/mod.rs | 3 +- crates/bevy_pbr/src/render/light.rs | 23 +- crates/bevy_pbr/src/ssao/mod.rs | 4 +- crates/bevy_pbr/src/volumetric_fog/render.rs | 12 +- crates/bevy_render/src/camera/camera.rs | 4 +- crates/bevy_render/src/extract_component.rs | 8 +- crates/bevy_render/src/extract_param.rs | 4 +- crates/bevy_render/src/sync_world.rs | 220 +++++++++++++++++- crates/bevy_sprite/src/render/mod.rs | 4 +- crates/bevy_ui/src/render/box_shadow.rs | 8 +- crates/bevy_ui/src/render/mod.rs | 21 +- .../src/render/ui_material_pipeline.rs | 6 +- .../src/render/ui_texture_slice_pipeline.rs | 6 +- 21 files changed, 291 insertions(+), 87 deletions(-) diff --git a/crates/bevy_core_pipeline/src/auto_exposure/buffers.rs b/crates/bevy_core_pipeline/src/auto_exposure/buffers.rs index 459a072406c3b..84b4011d2c557 100644 --- a/crates/bevy_core_pipeline/src/auto_exposure/buffers.rs +++ b/crates/bevy_core_pipeline/src/auto_exposure/buffers.rs @@ -27,13 +27,13 @@ pub(super) struct ExtractedStateBuffers { pub(super) fn extract_buffers( mut commands: Commands, - changed: Extract>>, + changed: Extract>>, mut removed: Extract>, ) { 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(), }); diff --git a/crates/bevy_core_pipeline/src/core_2d/mod.rs b/crates/bevy_core_pipeline/src/core_2d/mod.rs index e40bad7239c2c..c9883ce441cb6 100644 --- a/crates/bevy_core_pipeline/src/core_2d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_2d/mod.rs @@ -375,7 +375,7 @@ pub fn extract_core_2d_camera_phases( mut transparent_2d_phases: ResMut>, mut opaque_2d_phases: ResMut>, mut alpha_mask_2d_phases: ResMut>, - cameras_2d: Extract>>, + cameras_2d: Extract>>, mut live_entities: Local, ) { 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); diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 559e757d71d51..ae88905bacd49 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -528,17 +528,16 @@ pub fn extract_core_3d_camera_phases( mut alpha_mask_3d_phases: ResMut>, mut transmissive_3d_phases: ResMut>, mut transparent_3d_phases: ResMut>, - cameras_3d: Extract>>, + cameras_3d: Extract>>, mut live_entities: Local, ) { 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, Has, @@ -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); diff --git a/crates/bevy_core_pipeline/src/dof/mod.rs b/crates/bevy_core_pipeline/src/dof/mod.rs index c09373ee3ab10..628fc63075e9b 100644 --- a/crates/bevy_core_pipeline/src/dof/mod.rs +++ b/crates/bevy_core_pipeline/src/dof/mod.rs @@ -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>, + mut query: Extract>, ) { 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; diff --git a/crates/bevy_core_pipeline/src/taa/mod.rs b/crates/bevy_core_pipeline/src/taa/mod.rs index af7aaea58889f..d2f84f5de7f2f 100644 --- a/crates/bevy_core_pipeline/src/taa/mod.rs +++ b/crates/bevy_core_pipeline/src/taa/mod.rs @@ -358,7 +358,7 @@ impl SpecializedRenderPipeline for TaaPipeline { fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut) { 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 { 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, diff --git a/crates/bevy_pbr/src/cluster/mod.rs b/crates/bevy_pbr/src/cluster/mod.rs index ce54c20a8acd2..2972f4e116fbb 100644 --- a/crates/bevy_pbr/src/cluster/mod.rs +++ b/crates/bevy_pbr/src/cluster/mod.rs @@ -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>, - mapper: Extract>, + views: Extract>, + mapper: Extract>, ) { 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 }, diff --git a/crates/bevy_pbr/src/light_probe/mod.rs b/crates/bevy_pbr/src/light_probe/mod.rs index dca5d7209e9b5..0cda05f055eae 100644 --- a/crates/bevy_pbr/src/light_probe/mod.rs +++ b/crates/bevy_pbr/src/light_probe/mod.rs @@ -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), With>>, + view_query: Extract), With>>, 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( image_assets: Res>, light_probe_query: Extract>>, view_query: Extract< - Query<(&RenderEntity, &GlobalTransform, &Frustum, Option<&C>), With>, + Query<(RenderEntity, &GlobalTransform, &Frustum, Option<&C>), With>, >, mut reflection_probes: Local>>, mut view_reflection_probes: Local>>, @@ -437,16 +437,15 @@ fn gather_light_probes( // 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::>(); } else { commands - .get_entity(entity) + .get_entity(view_entity) .expect("View entity wasn't synced.") .insert(render_view_light_probes); } diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 5bbe90b37feea..bcaef630686e2 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -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), With>>, + cameras_3d: Extract), With>>, ) { 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."); diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 6ddb265885ab4..315563752aa48 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -193,7 +193,7 @@ pub fn extract_lights( global_point_lights: Extract>, 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, >, >, - mapper: Extract>, + mapper: Extract>, mut previous_point_lights_len: Local, mut previous_spot_lights_len: Local, ) { @@ -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>, + mapper: &Extract>, 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)) }) diff --git a/crates/bevy_pbr/src/ssao/mod.rs b/crates/bevy_pbr/src/ssao/mod.rs index eb9357278c72c..445589099c33a 100644 --- a/crates/bevy_pbr/src/ssao/mod.rs +++ b/crates/bevy_pbr/src/ssao/mod.rs @@ -522,7 +522,7 @@ fn extract_ssao_settings( mut commands: Commands, cameras: Extract< Query< - (&RenderEntity, &Camera, &ScreenSpaceAmbientOcclusion, &Msaa), + (RenderEntity, &Camera, &ScreenSpaceAmbientOcclusion, &Msaa), (With, With, With), >, >, @@ -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()); } diff --git a/crates/bevy_pbr/src/volumetric_fog/render.rs b/crates/bevy_pbr/src/volumetric_fog/render.rs index d6b151fd95b91..f3f8f26cf2015 100644 --- a/crates/bevy_pbr/src/volumetric_fog/render.rs +++ b/crates/bevy_pbr/src/volumetric_fog/render.rs @@ -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>, - fog_volumes: Extract>, - volumetric_lights: Extract>, + view_targets: Extract>, + fog_volumes: Extract>, + volumetric_lights: Extract>, ) { 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); } diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 4c22cf93ff9b3..dfd395ed7916d 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -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), diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index 2000046548760..a2cd3b1655a8a 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -199,12 +199,12 @@ impl Plugin for ExtractComponentPlugin { fn extract_components( mut commands: Commands, mut previous_len: Local, - query: Extract>, + query: Extract>, ) { 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( fn extract_visible_components( mut commands: Commands, mut previous_len: Local, - query: Extract>, + query: Extract>, ) { 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)); } } } diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index ca86ba3a40fe2..6ac7079bc5f49 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -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>>) { +/// fn extract_clouds(mut commands: Commands, clouds: Extract>>) { /// for cloud in &clouds { -/// commands.entity(cloud.id()).insert(Cloud); +/// commands.entity(cloud).insert(Cloud); /// } /// } /// ``` diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index ed6414ece2b24..eefe7753ffd19 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -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, + ) { + <&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 { + <&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, + ) { + <&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 { + <&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::{ diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 300a16ce637c6..ac55d47cceca4 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -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, diff --git a/crates/bevy_ui/src/render/box_shadow.rs b/crates/bevy_ui/src/render/box_shadow.rs index b874a93919e64..209344f068aad 100644 --- a/crates/bevy_ui/src/render/box_shadow.rs +++ b/crates/bevy_ui/src/render/box_shadow.rs @@ -247,7 +247,7 @@ pub fn extract_shadows( Option<&TargetCamera>, )>, >, - mapping: Extract>, + mapping: Extract>, ) { 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, diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index b07bd748ac9cb..e3be4a5bdb7dc 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -235,7 +235,7 @@ pub fn extract_uinode_background_colors( &BackgroundColor, )>, >, - mapping: Extract>, + mapping: Extract>, ) { 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, >, >, - mapping: Extract>, + mapping: Extract>, ) { 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>, + mapping: Extract>, ) { let image = AssetId::::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, diff --git a/crates/bevy_ui/src/render/ui_material_pipeline.rs b/crates/bevy_ui/src/render/ui_material_pipeline.rs index 990f48555c912..e9a3803ebb48b 100644 --- a/crates/bevy_ui/src/render/ui_material_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_material_pipeline.rs @@ -376,7 +376,7 @@ pub fn extract_ui_material_nodes( Without, >, >, - render_entity_lookup: Extract>, + render_entity_lookup: Extract>, ) { // 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( 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( }, border, clip: clip.map(|clip| clip.clip), - camera_entity: camera_entity.id(), + camera_entity, main_entity: entity.into(), }, ); diff --git a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs index b33d2707d4522..b9d5cbb61ff58 100644 --- a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs @@ -261,7 +261,7 @@ pub fn extract_ui_texture_slices( Option<&TextureAtlas>, )>, >, - mapping: Extract>, + mapping: Extract>, ) { 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,