Commit graph

11 commits

Author SHA1 Message Date
dweiller
2d923ea9b7 ecs: fix memory corruption in set/removeComponent 2022-07-01 16:48:35 -07:00
dweiller
fa5afee5bc ecs: allow components to have type void 2022-06-18 20:56:36 -07:00
dweiller
dd4d741aed ecs: use std.mem.alignForward to calculate padding 2022-06-12 08:02:53 -07:00
dweiller
f6d29e7669 ecs: fix padding and data copy in setCapacity() 2022-06-12 08:02:53 -07:00
dweiller
390b8bd922 ecs: fix appendUndefined() off-by-one error 2022-06-12 08:02:53 -07:00
Stephen Gutekanst
70283bfcb4 ecs: zig fmt
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
2022-06-10 16:56:12 -07:00
Stephen Gutekanst
334ed5c25f ecs: switch from ArrayList per-table-per-component-set -> single-[]u8-per-table
This switches our ECS over to manually managed memory (1 `[]u8` per archetype table,
with multiple column arrays packed into it - dealing with padding/alignment ourselves)
rather than the prior 1 `ArrayList(Component)` per component in an archetype table.

This idea was discussed in depth in [#ecs:matrix.org](https://matrix.to/#/#ecs:matrix.org)
(thanks Levy!) Notable advantages from my POV:

1. This means we don't need an `ErasedComponentStorage` interface, which is nice.
2. It means component storage does not have to have a Zig type. It could e.g. in theory enable
   the ECS to be usable from other languages (C, WebAssembly plugins, etc.) with component types
   defined in those languages in the future.
3. It reduces some overhead `ArrayList` has: slice ptr+len+capacity integers per component
   array per table
4. It guarantees component arrays are contiguous memory, rather than relying on the allocator
   to hopefully provide that (may not hold true in multi-threaded large-allocation situations.)
5. It means we could easily optimize for tables that have very few components by allocating exact
   memory for them (could've done this with `ArrayList` too, but now it's more likely the
   allocation are larger and thus more reusable by future archetype tables.) This could be quite
   important because one can imagine ending up with many small archetype tables.

Overall seems like the right thing to do, so we're doing it.

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
2022-06-10 13:24:27 -07:00
Stephen Gutekanst
14ec786c62 ecs: remove incomplete sparse storage implementation
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
2022-06-10 13:24:27 -07:00
dweiller
3234b6c0dd ecs: fix pointer invalidation in set/removeComponent 2022-06-07 19:26:40 -07:00
dweiller
997cf7d446 ecs: fix argument order in copy() calls 2022-06-07 19:26:40 -07:00
Stephen Gutekanst
0ef13eb1cc ecs: third major redesign/rethink of implementation
In the past:

* hexops/mach#156 was the initial ECS implementation detailed in https://devlog.hexops.com/2022/lets-build-ecs-part-1
* hexops/mach#157 was the second major redesign in which we:
    * Eliminated major limitations (e.g. inability to add/remove components at runtime)
    * Investigated sparse sets
    * Began thinking in terms of databases
    * Enabled runtime introspection

Our second revision of the ECS, however, still had _archetypes_ exposed as a public-facing
user concern. When a new component was added to an entity, say a weapon, the table storing
entities of that archetype changed to effectively have a new column `?Weapon` with a null
value for _all existing entities of that archetype_. We can say that our ECS had archetypes
as a user-facing concern AND this made performance worse: when iterating all entities with
a weapon, we needed to check if the component value was `null` or not because every column
was `?Weapon` instead of a guaranteed non-null value like `Weapon`. This was a key learning
that I got from [discussing ECS tradeoffs with the Bevy team](https://github.com/hexops/mach/pull/157#issuecomment-1022916117).

This third revision of our ECS has some big benefits:

* Entities are now just IDs proper, you can add/remove arbitrary components at runtime.
    * You don't have an "entity which always belongs to one archetype table which changes"
    * Rather, you have an "entity of one archetype" and adding a component means that entity _moves_ from one archetype table to another.
    * Archetypes are now an implementation detail, not something you worry about as a consumer of the API.
* Performance
    * We benefit from the fact that we no longer need check if a component on an entity is `null` or not.
* Introspection
    * Previously iterating the component names/values an entity had was not possible, now it is.
* Querying & multi-threading
    * Very very early stages into this, but we now have a general plan for how querying will work and multi-threading.
    * Effectively, it will look much like interfacing with a database: you have a connection (we call it an adapter)
      and you can ask for information through that. More work to be done here.
* Systems, we now have a (very) basic starting point for how systems will work.

Some examples of how the API looks today:

* 979240135b/ecs/src/main.zig (L49)
* 979240135b/ecs/src/entities.zig (L625-L656)

Much more work to do, I will do a blog post detailing this step-by-step first though.

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
2022-03-19 10:59:26 -07:00