bevy/release-content/migration-guides/stop_storing_system_access.md
Chris Russell bb4ea9c28b
Stop storing access for all systems (#19477)
# Objective

Reduce memory usage by storing fewer copies of
`FilteredAccessSet<ComponentId>`.

Currently, the `System` trait exposes the `component_access_set` for the
system, which is used by the multi-threaded executor to determine which
systems can run concurrently. But because it is available on the trait,
it needs to be stored for *every* system, even ones that are not run by
the executor! In particular, it is never needed for observers, or for
the inner systems in a `PipeSystem` or `CombinatorSystem`.


## Solution

Instead of exposing the access from a method on `System`, return it from
`System::initialize`. Since it is still needed during scheduling, store
the access alongside the boxed system in the schedule.

That's not quite enough for systems built using `SystemParamBuilder`s,
though. Those calculate the access in `SystemParamBuilder::build`, which
happens earlier than `System::initialize`. To handle those, we separate
`SystemParam::init_state` into `init_state`, which creates the state
value, and `init_access`, which calculates the access. This lets
`System::initialize` call `init_access` on a state that was provided by
the builder.

An additional benefit of that separation is that it removes the need to
duplicate access checks between `SystemParamBuilder::build` and
`SystemParam::init_state`.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-13 17:56:09 +00:00

2.1 KiB

title pull_requests
Stop storing access in systems
19496
19477

Bevy used to store component access in all systems, even though it was only used for top-level systems in schedules. To reduce memory usage, the component access is now stored in the schedule instead.

The trait methods System::component_access and System::component_access_set have been removed. Instead, the access is returned from System::initialize. If you were implementing System manually, the initialize method should return the access instead of storing it. If you were calling component_access or component_access_set on a system that you initialized yourself, you will need to store the access yourself.

let system = IntoSystem::into_system(your_system);
// 0.16
system.initialize(&mut world);
let access = system.component_access();
// 0.17
let component_access_set = system.initialize(&mut world);
let access = component_access_set.combined_access();

SystemMeta no longer stores FilteredAccessSet<ComponentId>. It is instead passed as a separate parameter when initializing a SystemParam.

To better share logic between SystemParam and SystemParamBuilder, SystemParam::init_state has been split into init_state, which creates the state value, and init_access, which calculates the access. SystemParamBuilder::build now only creates the state, and SystemParam::init_access will be called to calculate the access for built parameters.

If you were implementing SystemParam manually, you will need to separate the logic into two methods and change any uses of system_meta.component_access_set(_mut) to the new component_access_set parameter. Note that init_state no longer has access to SystemMeta or component_access_set, and init_access only has &state, so the state can no longer depend on the system.

If you were calling init_state manually, you will need to call init_access afterwards.

// 0.16
let param_state = P::init_state(world, &mut meta);
// 0.17
let param_state = P::init_state(world);
let mut component_access_set = FilteredAccessSet::new();
P::init_access(&param_state, &mut meta, &mut component_access_set, world);