From caa2353cc44a531791c3d561f8499bfd736774e6 Mon Sep 17 00:00:00 2001 From: Trashtalk Date: Sat, 28 Feb 2026 23:49:58 +0100 Subject: [PATCH 1/3] speedup resource lookup --- crates/bevy_ecs/src/resource.rs | 2 +- crates/bevy_ecs/src/system/system_param.rs | 50 ++++++++++++------- .../bevy_ecs/src/world/unsafe_world_cell.rs | 33 ++++++++++++ 3 files changed, 66 insertions(+), 19 deletions(-) diff --git a/crates/bevy_ecs/src/resource.rs b/crates/bevy_ecs/src/resource.rs index a94fa13277efc..62ed1a9a18d51 100644 --- a/crates/bevy_ecs/src/resource.rs +++ b/crates/bevy_ecs/src/resource.rs @@ -114,7 +114,7 @@ impl DerefMut for ResourceEntities { #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Debug))] #[derive(Component, Debug)] #[component(on_insert, on_discard, on_despawn)] -pub struct IsResource(ComponentId); +pub struct IsResource(pub ComponentId); impl IsResource { /// Creates a new instance with the given `component_id` diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 3663087815cbb..d99c910708320 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -9,12 +9,12 @@ use crate::{ bundle::Bundles, change_detection::{ComponentTicksMut, ComponentTicksRef, Tick}, component::{ComponentId, Components}, - entity::{Entities, EntityAllocator}, + entity::{Entities, Entity, EntityAllocator}, query::{ Access, FilteredAccess, FilteredAccessSet, IterQueryData, QueryData, QueryFilter, QuerySingleError, QueryState, ReadOnlyQueryData, }, - resource::{Resource, IS_RESOURCE}, + resource::{IsResource, Resource, IS_RESOURCE}, storage::NonSendData, system::{Query, Single, SystemMeta}, world::{ @@ -738,15 +738,21 @@ unsafe impl<'a, T: Resource> ReadOnlySystemParam for Res<'a, T> {} // SAFETY: Res ComponentId access is applied to SystemMeta. If this Res // conflicts with any prior access, a panic will occur. unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { - type State = ComponentId; + type State = (ComponentId, Entity); type Item<'w, 's> = Res<'w, T>; fn init_state(world: &mut World) -> Self::State { - world.components_registrator().register_component::() + let component_id = world.components_registrator().register_component::(); + if let Some(entity) = world.resource_entities().get(component_id) { + return (component_id, *entity); + } else { + let entity = world.spawn(IsResource(component_id)).id(); + return (component_id, entity); + } } fn init_access( - &component_id: &Self::State, + &(component_id, _): &Self::State, system_meta: &mut SystemMeta, component_access_set: &mut FilteredAccessSet, _world: &mut World, @@ -777,13 +783,12 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { #[inline] unsafe fn validate_param( - &mut component_id: &mut Self::State, + &mut (component_id, entity): &mut Self::State, _system_meta: &SystemMeta, world: UnsafeWorldCell, ) -> Result<(), SystemParamValidationError> { // SAFETY: Read-only access to the resource - if let Some(entity) = unsafe { world.resource_entities() }.get(component_id) - && let Ok(entity_ref) = world.get_entity(*entity) + if let Ok(entity_ref) = world.get_entity(entity) && entity_ref.contains_id(component_id) { Ok(()) @@ -796,13 +801,15 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { #[inline] unsafe fn get_param<'w, 's>( - &mut component_id: &'s mut Self::State, + &mut (component_id, entity): &'s mut Self::State, system_meta: &SystemMeta, world: UnsafeWorldCell<'w>, change_tick: Tick, ) -> Self::Item<'w, 's> { let (ptr, ticks) = world - .get_resource_with_ticks(component_id) + .get_entity(entity) + .ok() + .and_then(|entity| entity.get_with_ticks(component_id)) .unwrap_or_else(|| { panic!( "Resource requested by {} does not exist: {}", @@ -826,15 +833,21 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { // SAFETY: Res ComponentId access is applied to SystemMeta. If this Res // conflicts with any prior access, a panic will occur. unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { - type State = ComponentId; + type State = (ComponentId, Entity); type Item<'w, 's> = ResMut<'w, T>; fn init_state(world: &mut World) -> Self::State { - world.components_registrator().register_component::() + let component_id = world.components_registrator().register_component::(); + if let Some(entity) = world.resource_entities().get(component_id) { + return (component_id, *entity); + } else { + let entity = world.spawn(IsResource(component_id)).id(); + return (component_id, entity); + } } fn init_access( - &component_id: &Self::State, + &(component_id, _): &Self::State, system_meta: &mut SystemMeta, component_access_set: &mut FilteredAccessSet, _world: &mut World, @@ -868,13 +881,12 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { #[inline] unsafe fn validate_param( - &mut component_id: &mut Self::State, + &mut (component_id, entity): &mut Self::State, _system_meta: &SystemMeta, world: UnsafeWorldCell, ) -> Result<(), SystemParamValidationError> { // SAFETY: Read-only access to the resource. - if let Some(entity) = unsafe { world.resource_entities() }.get(component_id) - && let Ok(entity_ref) = world.get_entity(*entity) + if let Ok(entity_ref) = world.get_entity(entity) && entity_ref.contains_id(component_id) { Ok(()) @@ -887,13 +899,15 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { #[inline] unsafe fn get_param<'w, 's>( - &mut component_id: &'s mut Self::State, + &mut (component_id, entity): &'s mut Self::State, system_meta: &SystemMeta, world: UnsafeWorldCell<'w>, change_tick: Tick, ) -> Self::Item<'w, 's> { let value = world - .get_resource_mut_by_id(component_id) + .get_entity(entity) + .ok() + .and_then(|entity| entity.get_mut_by_id(component_id).ok()) .unwrap_or_else(|| { panic!( "Resource requested by {} does not exist: {}", diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 7452d545bd456..75c8cc89d7061 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -1121,6 +1121,39 @@ impl<'w> UnsafeEntityCell<'w> { } } + /// Gets the component of the given [`ComponentId`] from the entity with the tick information. + /// + /// **You should prefer to use the typed API where possible and only + /// use this in cases where the actual component types are not known at + /// compile time.** + /// + /// Unlike [`UnsafeEntityCell::get`], this returns a raw pointer to the component, + /// which is only valid while the `'w` borrow of the lifetime is active. + /// + /// # Safety + /// It is the caller's responsibility to ensure that + /// - the [`UnsafeEntityCell`] has permission to access the component + /// - no other mutable references to the component exist at the same time + #[inline] + pub unsafe fn get_with_ticks( + self, + component_id: ComponentId, + ) -> Option<(Ptr<'w>, ComponentTickCells<'w>)> { + let info = self.world.components().get_info(component_id)?; + // SAFETY: + // - caller ensures there is no `&mut World` + // - caller ensures there are no mutable borrows of this resource + // - caller ensures that we have permission to access this resource + // - storage_type and location are valid + get_component_and_ticks( + self.world, + component_id, + info.storage_type(), + self.entity, + self.location, + ) + } + /// Retrieves a mutable untyped reference to the given `entity`'s [`Component`] of the given [`ComponentId`]. /// Returns `None` if the `entity` does not have a [`Component`] of the given type. /// From fed5730022142b8b032e7377a7a10f278cbaa3a8 Mon Sep 17 00:00:00 2001 From: Trashtalk Date: Sun, 1 Mar 2026 00:15:43 +0100 Subject: [PATCH 2/3] fix --- crates/bevy_render/src/extract_param.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index 4d81fb9eb1903..d945854ebf1b3 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -101,7 +101,7 @@ where world: UnsafeWorldCell, ) -> Result<(), SystemParamValidationError> { // SAFETY: Read-only access to world data registered in `init_state`. - let result = unsafe { world.get_resource_by_id(state.main_world_state) }; + let result = unsafe { world.get_resource_by_id(state.main_world_state.0) }; let Some(main_world) = result else { return Err(SystemParamValidationError::invalid::( "`MainWorld` resource does not exist", From 5aac54fdf4b68de56b1da679e3c6087024599d70 Mon Sep 17 00:00:00 2001 From: Trashtalk Date: Sun, 1 Mar 2026 00:28:41 +0100 Subject: [PATCH 3/3] clippy --- crates/bevy_ecs/src/system/system_param.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index d99c910708320..f26e3637f2a63 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -745,10 +745,9 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { let component_id = world.components_registrator().register_component::(); if let Some(entity) = world.resource_entities().get(component_id) { return (component_id, *entity); - } else { - let entity = world.spawn(IsResource(component_id)).id(); - return (component_id, entity); } + let entity = world.spawn(IsResource(component_id)).id(); + (component_id, entity) } fn init_access( @@ -840,10 +839,9 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { let component_id = world.components_registrator().register_component::(); if let Some(entity) = world.resource_entities().get(component_id) { return (component_id, *entity); - } else { - let entity = world.spawn(IsResource(component_id)).id(); - return (component_id, entity); } + let entity = world.spawn(IsResource(component_id)).id(); + (component_id, entity) } fn init_access(