Skip to content
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

Bevy ECS migration #49

Merged
merged 1 commit into from
Jul 10, 2020
Merged

Bevy ECS migration #49

merged 1 commit into from
Jul 10, 2020

Conversation

cart
Copy link
Member

@cart cart commented Jul 10, 2020

After much deliberation and experimentation, I have decided that forking hecs is a better fit for Bevy's ECS needs. Legion is a top tier ECS by all measures, but hecs has a number of properties that make it desirable for our project:

  • System Functions: Its types are much more compatible with Bevy's "system functions". A recent legion refactor is incompatible with our system function implementation. Additionally, legion's lifetimes were already incompatible with system functions. We had to fork legion (and make it less safe) in order to make them work at all.

  • Compile Times / Code Complexity: hecs is much simpler and compiles much faster. On my machine legion (with all default features disabled to be fair) clean builds in ~12 seconds, whereas default hecs compiles in ~3. With equivalent "bevy system function" implementations (and serde enabled in both libs) hecs compiles in ~7 seconds whereas legion compiles in ~45.

  • Performance: default hecs is faster than legion according to ecs_bench. bevy's optimized hecs fork w/ system functions is way faster than legion (or any other rust ecs as far as I know). The "pos_vel" update benchmark results:

    bevy_ecs hecs legion (experimental) specs shipyard (packed) shipyard (unpacked)
    700ns 1880ns 1540ns 1900ns 3050ns 7870ns
  • Less Opinionated: hecs' simplicity allows us to take it in our own direction / makes me more comfortable forking it. it means we need to do more work (ex: serialization + multithreading + scheduling), but it also means we are more free to mold it into the best ecs for bevy.

Here are the details of the hecs fork:

  • Added "system functions" and IntoSystem traits as the primary way to write systems. Currently there are "query systems" and "foreach systems". Each system variant can optionally specify resource dependencies and command buffers.
  • Added a Resources collection and resource queries
  • Add Local resources as a supplement to normal "global" resources. Local resources are unique to specific system instances and are auto-initialized. This allows there to be multiple instances of the same system, each with their own unique state (we use this in bevy's Render Graph implementation). Its also slighty more ergonomic because you dont need to manually initialize global resources.
  • Added a Schedule with a useable single-threaded runner and an initial naive / extremely broken multi-threaded runner.
  • Entity indices are now queryable and are not returned in queries by default. This both improves ergonomics and significantly boosts performance in some cases.
  • Entity indices are now UUIDs and are no longer generational. This allows apps to use Entity ids as stable ids during serialization and networking. This also improves query performance because we don't need to look up entity generation information while iterating. It also removes ~300 lines of code dedicated to entity index maintenance. However this new model does come at a small cost: entity creation + world.get::<Component>(entity) lookup now requires hashing, which by our benchmarks is about 5x slower than the previous array indexing implementation. Given that this is an uncommon pattern and the major benefits the new design yields, we consider this small corner-case performance cost worth it.
  • Expose more hecs interfaces as public so that we can build higher-level apis on top of the core hecs codebase (multithreading, functions-as-systems, world builders, schedules, etc)

Its worth pointing out that almost none of these changes were 100% my idea. In many ways bevy_ecs is a consolidation of my favorite ideas from Legion (boxed system traits + schedules + resources), Shipyard (system functions + using archetypes to store resources + queryable entities), and Hecs (core ECS implementation .. which is actually also inspired by Legion). If any of the developers of those projects ever read this, I want to give a huge thanks to all of you. I'm really standing on the shoulders of giants here.

@cart
Copy link
Member Author

cart commented Jul 10, 2020

Here are some system function examples to illustrate what this looks like in practice:

fn move_system(mut query: Query<(&mut Position, &Velocity)>) {
    for (pos, vel) in &mut query.iter() {
        pos.x += vel.dx;
        pos.y += vel.dy;
    }
}

fn move_system_entity(mut query: Query<(Entity, &mut Position, &Velocity)>) {
    for (entity, pos, vel) in &mut query.iter() {
        pos.x += vel.dx;
        pos.y += vel.dy;
    }
}

fn move_system_with_resource(resource: Res<T>, mut query: Query<(&mut Position, &Velocity)>) {
    for (pos, vel) in &mut query.iter() {
        pos.x += vel.dx;
        pos.y += vel.dy;
    }
}

fn move_system_foreach(pos: &mut Position, vel: &Velocity) {
    pos.x += vel.dx;
    pos.y += vel.dy;
}

fn move_system_foreach_entity(entity: Entity, pos: &mut Position, vel: &Velocity) {
    pos.x += vel.dx;
    pos.y += vel.dy;
}

fn move_system_foreach_with_resource(resource: Res<T>, pos: &mut Position, vel: &Velocity) {
    pos.x += vel.dx;
    pos.y += vel.dy;
}

fn hello_world_system() {
  println!("hello world!");
}

fn commands_system(mut commands: Commands) {
  commands.spawn((Postion(0), Velocity(1)));
}

@cart cart merged commit db3191e into master Jul 10, 2020
@karroffel karroffel added A-ECS Entities, components, systems, and events C-Performance A change motivated by improving speed, memory usage or compile times C-Dependencies A change to the crates that Bevy depends on labels Aug 13, 2020
bors bot pushed a commit that referenced this pull request Apr 27, 2022
@cart cart deleted the bevy_ecs branch October 31, 2022 20:55
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-Dependencies A change to the crates that Bevy depends on C-Performance A change motivated by improving speed, memory usage or compile times
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants