Would it be considered good practice to construct a blob asset at runtime and using a reference to it in a (shared)component?
For example, I have several waypoint graphs as blob assets (properly constructed using only BlobArrays).
Entities could then have a SharedComponent that contains a BlobAssetReference which they can use to perform calculations on this graph.
Now, what if occasionally I need to generate a new graph of waypoints. I could do this at runtime and change the reference on the entities that need the new graph. But how would I go about disposing of the ‘old’ graphs that aren’t used anymore by any entity?
I thought about using some kind of SystemStateSharedComponent to track if any entity is still using a graph. But it seems like this is still on a per-entity basis. Tracking if no entity uses a graph would still require some mumbo jumbo with GetAllUniqueSharedComponentData and storing some internal system state. Just disposing some old graph on the main thread poses the risk that there would still be some entities using it.
Several other questions pop to mind:
is constructing blob assets at runtime even a good idea? How about disposing them?
would memory become fragmented as I keep allocating and deallocating blob assets from time to time
any other approaches to this? I need something that gives entities access to large structures inside a job, where these structures might differ for different entities. BlobAssetReference seemed like the perfect fit.
There is no reason you can’t do both. In some cases it’s required, for example in order to change properties of some of the physics components you must replace the blob and dispose of the old. But you must track the ones you create at runtime yourself.
Links to 2 more threads that should help understand them better.
Thanks. The solution proposed to use a system state component to track unused blobs would work if the blob is only tied to a single entity. But in my situation, multiple entities could use the same reference and I need to know when no entity references the blob anymore.
I’ve also thought about storing the blob asset reference once on a single entity and let other entities reference that entity. That way I could use a system component to know when the entity that owns the blob is removed and it would be safe to dispose. However, it will be difficult (and slow?) for other entities that want to use the blob as I would always need to retrieve the entity that has the blob reference first…
If anyone else has some thoughts on a good approach to this, I’d love to hear.
I’m not super well read on this, since it hasn’t come up quite yet. But another option is to count references in a NativeHashMap. It could be a reactive system with systemstate/chunk/shared components. I’ve done something like this to count entities with a that have each component value.
I’ve also thought about storing the blob asset reference once on a single entity and let other entities reference that entity. That way I could use a system component to know when the entity that owns the blob is removed and it would be safe to dispose. However, it will be difficult (and slow?) for other entities that want to use the blob as I would always need to retrieve the entity that has the blob reference first.
This would work, SystemBase.GetComponent is slower relative to direct refs. But it shouldn’t matter too much unless you have massive throughput.
Thought about something like this but I keep reading everywhere a system shouldn’t maintain a persistent state. You’d need to keep track of the previous frame’s references in order to determine if something isn’t referenced anymore. Or am I missing something here?
You should try to avoid storing native containers on your system. That’s what I did until now to cache some information but now that ISystemBase is out it’s no longer an option if you want to benefit from it. (That’s why I’m looking into blobmap )
You could still use the counting way.
For each shared reference create an entity that has the reference and a count component.
On every update query for all entity with the shared component reference (only) if the result is 1 it means only the counting entity still reference the blob so you can dispose of the blob and destroy the counting entity.
I see, but you can’t get the actual data from a shared component in a job right? Or are you referring to
EntityManager.GetAllUniqueSharedComponentData (and just do it in the systems OnUpdate)?
Yes, use EntityManager.GetAllUniqueSharedComponentData on the main thread to iterate over all currently possible values. (except if you now all values at compile time and they won’t change which is not the case here since you make them at runtime).
I’m no expert in SharedComponent but I think it will involve creating an entity query for for each in OnUpdate which I uderstand is a big performace hit…
Plus I think there is a limit to the number of SharedComponent types possible in ECS (8 if I’m not mistaken).
The reason is that it could lead to chunk fragmentation that would hurt performance more than it would help, so you should use SharedComponent only if you really need to group entities together to perform a very specific task that involved those related entities.
I’m not sure you use case is suited for that kind of component.
Do you plan to make new reference often ?
What is the chance for a reference to have no applicatble entity?
Would it be a big deal to keep that reference even if there is no entity using it ?
Could it hapen to have a “yoyo” effect (Frame 1 blob is refrence by 1 entity, Frame 2, no one refrence it so destroy, Frame 3 now someone want that refrence again, so you have to rebuild it ) ?
Depending on those answer, you may be better of using a buffer than a blob.