Skip to content

Shared components #19456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open

Conversation

eugineerd
Copy link
Contributor

@eugineerd eugineerd commented Jun 1, 2025

Objective

Introduce new component storage type to allow deduplication of component values.

Solution

This PR build on top of #19153 to introduce a new storage type that can only contain fragmenting values, which allows to store only one component value for all archetypes. This reduces memory usage and improves iteration speed for components that have small variation in values and exist on many entities (enums, grid positions, etc.).

The main addition is the Shared storage type:

#[derive(Component, Clone, Hash, PartialEq, Eq)]
#[component(storage = "Shared")]
enum Faction {
  Player,
  Enemy,
  Custom(u32),
}

Shared components are always fragmenting values, which means they're immutable and have key=Self.

This also means that making relationships fragmenting is as simple as adding #[component(storage = "Shared")] to the targeting component.

#[derive(Component, Debug, Eq, PartialEq, Clone, Hash)]
#[relationship(relationship_target = TargetedBy)]
#[component(storage = "Shared")]
struct Targeting(Entity);

#[derive(Component, Debug)]
#[relationship_target(relationship = Targeting)]
struct TargetedBy(Vec<Entity>);

Testing

  • storage/shared.rs contains tests with basic shared components functionality.
  • all fragmenting_value.rs tests now use Shared components.

Questions

  • At this point Shared components and FragmentingValue's are pretty much the same. Theoretically these concepts are separate - it is possible to create a shared component that isn't a FragmentingValue as long as the key for that component is a fragmenting value. Likewise, FragmentingValue's don't necessarily need to be Shared components - table and sparse set components are also supported, even though practically there are no upsides for storing it anywhere other than shared storage. The separation between these concepts is somewhat blurry in this PR, so the question is - does it make sense to keep them as separate? Unifying both of these concepts as one might make it easier to reason about, although I don't think it would simplify the code much as it already makes this assumption.
  • What should be the added tick for the shared component? Should it be when the value was first encountered? Or should it be different for every archetype (which would be the same as the tick archetype was first created). This PR implements the first version, but is that the correct way to handle this?

eugineerd added 24 commits May 5, 2025 11:01
It was just example code for this feature and wasn't intended to be usable anyway.
This restriction isn't really required for current implementation.
`cache_archetype_after_bundle_insert` resets `fragmenting_values_map` when caching new edges.
Now this is skipped if base archetype already exists and the only thing changed is the fragmenting value components.
Also replace `FragmentingValuesOwned` with `FragmentingValuesShared` to reduce memory overhead and make efficient batch fragmenting value comparison possible
@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-ECS Entities, components, systems, and events X-Controversial There is active debate or serious implications around merging this PR M-Needs-Release-Note Work that should be called out in the blog due to impact S-Needs-Review Needs reviewer attention (from anyone!) to move forward and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jun 2, 2025
@alice-i-cecile alice-i-cecile added the S-Needs-Design This issue requires design work to think about how it would best be accomplished label Jun 2, 2025
@alice-i-cecile
Copy link
Member

From my perspective as an SME-ECS, I want to see very strong motivation for why Bevy and Bevy users need this. I have no doubt that it's well-implemented, but storage types are hard to maintain and add another optimization knob for users to tune.

@eugineerd
Copy link
Contributor Author

eugineerd commented Jun 2, 2025

From my perspective as an SME-ECS, I want to see very strong motivation for why Bevy and Bevy users need this. I have no doubt that it's well-implemented, but storage types are hard to maintain and add another optimization knob for users to tune.

Fragmenting value components in general fragment archetypes by values. This means that all entities within their archetype have the same value for the fragmenting component, so there's an unnecessary memory overhead for storing all of these component values even though we know they're the same. I think this custom storage type is essential if we want an efficient implementation of fragmenting value components and fits nicely with all other available storage types.

For users this allows a simple way to opt component into fragmentation by value.

In general separate storage type isn't required for these optimizations, maybe they can be merged into some other storage type, but I'm not sure that'll lead to cleaner internal implementation.

Also, in case code size is a problem, most of the code in this PR is from the #19153, which isn't currently merged. This PR isn't that big by itself.

@alice-i-cecile
Copy link
Member

Fragmenting value components in general fragment archetypes by values. This means that all entities within their archetype have the same value for the fragmenting component, so there's an unnecessary memory overhead for storing all of these component values even though we know they're the same.

Ah, that's an interesting rationale. This seems interesting for ZST components more broadly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Feature A new feature, making something new possible M-Needs-Release-Note Work that should be called out in the blog due to impact S-Needs-Design This issue requires design work to think about how it would best be accomplished X-Controversial There is active debate or serious implications around merging this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants