SanderMertens / edict

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

crates docs actions MIT/Apache loc

Edict

is experimental ECS with ref-counted entities and built-in change detection written in Rust by your fellow 🦀

Features

Counting references to individual entities adds few desirable properties.

For one, strong entity reference guarantees that enitity it referes to is alive. This allows providing non-fallible API to fetch data attached to entities.

Another one is automatic entity despawn when no one references the entity. This may seem as a step backward, as most ECS tend to require manual entity despawn, allowing entities to just sit in the World and be queried by systems. This may lead to problems when encoding ownership of an entity by another. If references to owned entities are stored in owner's component, then despawning the owner will break the relationship, code that despawns the owner may be unaware about component that holds refrences to owned entitites. This will leave previously owned entities orphaned. Hence owned entity must store a reference to its owner and check periodically if owner was despawned.

With edict owner stores strong references (Entity) to owned entities. When strong reference is dropped - possibly together with the component on despawn - the entity will be despawned if no other strong references left.

edict provides EntityId reference type which works as entity references in traditional ECS.

Another feature of edict is integrated change detection. It tracks when components are accessed mutably and may efficiently iterate through modified components. "Modified when?" Careful reader may inquire. Imagine a game loop, where a set of systems run on each cycle. If system has a query over modified components, it probably wants to see all modifications since it ran this query last time. edict offeers Tracks type. Created simply with World::tracks(), this type is used in all queries that checks for components modification. Tracks instance inform the query, that that modifications occured since the last use of this Tracks instance should be returned by query. On the first use of Tracks returnd from World::tracks() all components are considered to be modified. World::tracks_now() returns Tracks instance for which all modifications happened prior World::tracks_now() call to be obsolete.

no_std support

edict supports no_std environment, but requires alloc. With "std" feature error types implement Error trait, apart from that only few internal pieces depend on "std" feature. "std" feature is enabled by default and must be turned off for no_std environemnt. Dependent crates that also support no_std should use default-features = false for edict dependency, and optionally enable "std" if needed.

Benching

Results gathered from ecs_bench_suite Tested on Apple's M1 cpu. 100% is the fastest one in the column.

ECS simple_insert simple_iter fragmented_iter add_remove_component
legion 169.49 us [100%] 7.3514 us [106%] 259.69 ns [521%] 1.9975 ms [3047%]
hecs 367.39 us [217%] 6.9587 us [100%] 451.25 ns [905%] 589.64 us [1112%]
bevy 414.22 us [244%] 8.8831 us [128%] 1.2211 us [2448%] 1.4385 ms [2194%]
planck_ecs 335.36 us [198%] 24.519 us [352%] 517.86 ns [1038%] 67.039 us [102%]
shipyard 324.81 us [192%] 15.837 us [227%] 49.883 ns [100%] 65.877 us [100%]
specs 875.03 us [516%] 34.117 us [490%] 1.8565 us [3722%] 65.551 us [100%]
edict 697.68 us [412%] 7.5105 us [108%] 170.77 ns [342%] 395.12 us [603%]

Analysis

edict suffers expected penalty on entity spawn in simple_insert, it has to allocate storage for refcounts. There's room for improvement.

Iteration is relatively similar to hecs (the fastest in simple_iter), although edict tracks modifications, while hecs doesn't.

In fragmented_iter benchmark edict shows its low perf cost for iteration on each archetype and so performs great iterating on many archetypes with fewer entities.

In add_remove_component benchmark edict performs surprisingly better than hecs. legion and bevy can't keep up. Sparse set based ECSs are unchallenged winners here.

License

Licensed under either of

at your option.

Contributions

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

License:Other


Languages

Language:Rust 100.0%