-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
base: main
Are you sure you want to change the base?
Shared components #19456
Conversation
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
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. |
Ah, that's an interesting rationale. This seems interesting for ZST components more broadly. |
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:Shared
components are always fragmenting values, which means they'reimmutable
and havekey=Self
.This also means that making relationships fragmenting is as simple as adding
#[component(storage = "Shared")]
to the targeting component.Testing
storage/shared.rs
contains tests with basic shared components functionality.fragmenting_value.rs
tests now useShared
components.Questions
Shared
components andFragmentingValue
's are pretty much the same. Theoretically these concepts are separate - it is possible to create a shared component that isn't aFragmentingValue
as long as thekey
for that component is a fragmenting value. Likewise,FragmentingValue
's don't necessarily need to beShared
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.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?