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:
Alice Cecile 2024-10-13 16:58:46 -04:00 committed by GitHub
parent d96a9d15f6
commit a7e9330af9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 291 additions and 87 deletions

View file

@ -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(),
});

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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,

View file

@ -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 },

View file

@ -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);
}

View file

@ -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.");

View file

@ -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))
})

View file

@ -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());
}

View file

@ -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);
}

View file

@ -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),

View file

@ -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));
}
}
}

View file

@ -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);
/// }
/// }
/// ```

View file

@ -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::{

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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(),
},
);

View file

@ -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,