Megafunk / MassSample

My understanding of Unreal Engine 5's experimental ECS plugin with a small sample project.
MIT License
680 stars 112 forks source link

DestroyEntity performance #64

Open JanVogelsang opened 4 months ago

JanVogelsang commented 4 months ago

I was wondering about the performance of removing and adding entities, as it feels like these might be quite expensive operations. If destroying an entity simply punches multiple holes in a chunk (one per fragment), this might make iterating over all entities in a chunk non-trivial. It might also affect cache efficiency as skipping holes will most likely mean that you will load cache lines with information you discard again. Then again, preventing holes by moving lots of entities is quite inefficient as well. I can't think of any other way of dealing with the aforementioned issues, do you know how Epic implemented it?
Have you done any benchmarks so far or have any advice on how to deal with scenarios where you want to constantly destroy and create new entities? I thought about simply adding some sort of "IsDead" tag instead of destroying entities and when creating new entities first querying all dead entities and "revive" them by removing the tag again (and setting some fragment values). But this would be quite inefficient as well as each time a new entity should be created, one must first iterate over all entities (or at least a subset) and look for a specific tag. Furthermore, how does the Engine even handle scenarios where people add tags at arbitrary locations in the chunk, as this would constantly "invalidate" existing queries, which massively impacts cache efficiency.
It's quite difficult to grasp how this whole Mass system can be efficient, as it has so few restrictions that it's hard to believe it still manages to perform well even if people add tags or fragments at arbitrary places, which constantly creates new archetypes (potentially), remove and create new entities, and just mess with entities all over the place.

JanVogelsang commented 3 months ago

I didn't have time to dig into engine code yet, but I think the most clever way of handling removed entities must be to immediately fill any holes. After one simulation step, when all deferred commands are called, one can easily remove entities that should be destroyed and fill the hole using the very last entity in the last chunk of the same archetype. Doing so comes at very little cost, as the last chunk can be cached quite easily and even filling multiple holes would be quite cheap.
If you know how Epic actually implemented it in Mass, feel free to post it here, but I think this might be the way.