Shared components
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.rscontains tests with basic shared components functionality.- all
fragmenting_value.rstests now useSharedcomponents.
Questions
- At this point
Sharedcomponents andFragmentingValue's are pretty much the same. Theoretically these concepts are separate - it is possible to create a shared component that isn't aFragmentingValueas long as thekeyfor that component is a fragmenting value. Likewise,FragmentingValue's don't necessarily need to beSharedcomponents - 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
addedtick 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?
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.
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.
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.