But forget that lofty goal! Specifically I’d like to ask for an EntityCommandBuffer.SetArchetype() function (and concurrent version), so that we can change an entity’s archetype once, and avoid adding components individually.
I assume that when I do that (add them individually), the ECB makes each of those structural changes in sequence, Potentially changing the entity’s archetype many times.
is this true, or is ECB smart enough to combine the component Adds and Removes into a single archetype change, and move the entity to a new chunk only once?
I don’t think it’s a good idea on the system level. If you don’t want archetype changes all you need to do is don’t change the components. Throw in booleans to turn on or off certain components.
The archetype by definition is a set of components if you are allowed to set an archetype without all the components, there is no meaning of archetypes. And they have to implement a bunch of slow checks and assurances that you only get back data that actually exist in the context.
In simpler words: archetypes wouldn’t exist at the first place if they could do that efficiently.
Interesting. I use EntityManager.SetArchetype() in my conversion code to avoid changing an entity’s archetype multiple times in a rapid succession. It works great - I can figure out the components I need ahead of time, and then call SetArchetype() to add them all in one go. If I need to write specific component data, I can do that in parallel later, without needing to make any more structural changes.
That’s the same reason I would want to do it with an ECB.Concurrent. There’s a place in my code where I reliably need to add or remove several component types. Right now, I add or remove those types individually. It’s already fast, but I expect I could speed that up considerably by using the same technique:
Figure out the new archetype first.
Call an ECB.Concurrent version of SetArchetype() to make the necessary structural change once.
Follow up with parallel jobs to assign the actual component data values.
I imagine that would be significantly faster, and cleaner, assuming there isn’t already an ECB optimization for this.
I may misunderstood you.
If you’re talking about creation of new entities, AFAIK there is no need of this, you can create the entity and fill up the components before the playback is called.
Entities basically don’t exist until the playback called, so archetype changes does not matter.
The only case archetype change matters for sure is when you actually add/remove components to/from entities runtime. Which you should avoid as much as possible in tight loops because it results archetype changes.
If you have archetype change once in a while (couple of seconds or something), then it does not matter. If it changes in every or every couple of frames, then it worth to take a look and prevent the frequent change.
Just want to comment that this was my first approach to writing ECS code. I ran into a few bottlenecks:
having an entity start with every component they might ever need reduced their chunk utilization by a lot. That slowed down the jobs that were working on them. It ended up being much faster to bite the bullet and adopt an approach where they are added/removed on demand through ECBs. I was surprised, but the numbers didn’t lie.
Having a bool in a ComponentData which determines if it should be worked upon may still require a System to be run, and even a job to be scheduled, just to find out it should be skipped. That got untenably expensive for me. After a I had a few dozen systems in my project, I realized that the cost (at least at the time) of just updating a system was too high if it wasn’t actually needed. The early-out approach just wasn’t performant enough. And also, I realized over time that it was fighting the way Unity was optimizing their ECS design to work. Systems and queries are built around the idea of filtering by archetype. And in the end, that was proven to be much faster.
Adding entity/archetype pairs to a NativeStream from my jobs.
After those jobs are complete, reading from that stream on the main thread and calling EntityManager.SetArchetype() for each pair.
But I imagine that approach might be quite slow. Without picking through the ECB code, I expect it may have some optimizations to make it’s main-thread playback faster.
If you want to say that “Creating entity with predefined Archetype is efficient, but changing archetype of existing entity is inefficient, so EntityCommandBuffer.SetArchetype is a bad idea.”, you should think about why EntityManager.SetArchetype exists.
Maybe EntityCommandBuffer.AddComponent(Entity, ComponentTypes) and EntityCommandBuffer.RemoveComponent(Entity, ComponentTypes) could be helpful to you. They allow you to add or remove multiple components at once.
You may have to first calculate which components to add/remove if you want to set the archetype, so these to methods are not ideal.
Another problem with this approach is that I could not find a way to create ComponentTypes in a burst job, because typeof is managed code.