After using DOTS for a few weeks now, I’m still confused about certain aspects and best practices, and I’d love if you folks could answer these questions so I can have a better understanding. Thanks!
When to use
var ecb = new EntityCommandBuffer(Allocator.Temp);
// ...
ecb.Playback(state.EntityManager);
ecb.Dispose();
instead of:
var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
?
Which is better, a IJobEntity with IJobEntity or a batched modification using entity manager and passing it an array of entities?
How to handle job dependencies between systems? Let’s say system B has to run job Y after job X in system A has finished.
When is an IJobEntity not worth it and is better to use SystemAPI.Query in the main thread?
Are one frame-lived components really that bad idea? What do you suggest instead?
In jobs, do I always have to declare ReadOnly in properties and in/ref in Execute params?
When to use SystemAPI.QueryBuilder instead of SystemAPI.Query?
SystemAPI vs EntityManager vs EntityCommandBuffer? Which is better for the same operation (e.g modifying component data)?
Use the first when you want the sync point immediately after iterating entities. Otherwise use the second.
I think you mistyped the question.
It is automatically tracked by component type. See SystemState.Dependency.
When you have few entities and the work is not dependent on any currently running jobs.
Depends on their frequency of occurrence and the function they serve. It isn’t as black and white as some may lead you to believe.
If I take your question literally, no. But if I try to reinterpret it into what I am thinking you are asking, then yes.
When you need an EntityQuery instance.
Largely depends. SystemAPI will sync on SystemState.Dependency, but cache consecutive lookups. EntityManager will only sync on the particular type. EntityCommandBuffer won’t sync immediately at all but defers the sync and the processing until playback.
Thanks for your fast reply @DreamingImLatios ! I have some follow-up questions if you don’t mind.
Yeah, sorry, the question was: which is better, an IJobEntity with ECB or a batched update using entity manager and passing it an array of entities?
Could you expand a little more? The jobs don’t necessarily share the same components. I understand how to add jobs dependencies in the same system, but not sure how to do it when jobs are in different systems? How do I get the job handler of a different system?
So maybe it’s not necessary, but would you say it’s a good practice to do it?
I assume that’s for scenarios where I want ToEntityArray or to ToComponentEntityArray. In that case, is it better to use a query builder and batch updates with EntityMager, or a foreach with SystemAPI.Query?
Thanks, it’s a little clearer now, but I’m still quite unsure of when I would want to use each. Do you have some examples? It seems for most of the time, I’d want to use SystemAPI, since it caches consecutive lookups.
Both of these are ways to make structural changes, which always require a sync point. The former makes the sync point and makes the structural changes at the moment that Playback is called. The latter will wait until a certain point in the frame (in your example its at the start of the simulation system group) to trigger the sync point and make the changes. The advantage of the second option is that several different systems can share a single sync point to make changes which is generally better for performance. But sometimes you just need to make changes immediately so the first option is good for that.
There is also a third option for structural changes which is to just call EntityManager.AddComponent and other similar methods. These trigger a sync point immediately when the method is called. So if you want to make a lot of structural changes, you generally want to use a command buffer.
Yeah I think this is a typo. If you are using Schedule or ScheduleParallel with the IJE, then it can execute on other thread(s) which allows for other systems to start running on the main thread sooner and opens the door for more parallelism. If you are using Run with IJE, it is similar to EntityManager methods that take an array of entities in that they both execute and make those changes immediately. I’m actually not sure which option has better performance but I’d assume the EntityManager since it avoids some of the overhead of running a job on the main thread.
Yup, like @DreamingImLatios said, this is done automatically. For example, if system B runs after system A and one writes to a component that the other reads from, the dependency is automatically handled for you. If the order of the systems is important, use [UpdateAfter] or [UpdateBefore] on your systems to enforce the system ordering.
I assume you mean IJE using Schedule or ScheduleParallel. Using IJE with Run is basically the same as SystemAPI.Query. Scheduling jobs is good for when you want to iterate a lot of entities and you have other work that can be done concurrently.
The best option really depends on the use case. Enableable components are a decent option for one frame type of events. E.g. enable the component to trigger some system, then disable the component. Avoids structural changes and lets you filter for the events using queries.
Yeah you should be in the habit of always specifying these. This is what tells the job system if a given job needs read of write dependencies for a given component or collection.
I fine the naming of these confusing and misleading. SystemAPI.Query is for main thread iteration of entities, very similar to the old Entities.ForEach. SystemAPI.QueryBuilder is for creating new EntityQuerys.
The answer depends on the kind of operation. Modifying component through the use of SetComponent is something that all three can do. SystemAPI and EntityManager make the change immediately and ECB makes the change during playback, either when Playback is called or when the corresponding ECB system runs if you are using something like BeginSimulationEntityCommandBufferSystem.Singleton. Both SystemAPI and EntityManager are really similar in this regard so it doesn’t really matter which you use.
Both SystemAPI and EntityManager provide GetComponent* methods for reading data on the main thread. They work pretty much the same way so again, it doesn’t really matter which you use. But an ECB has no way to read component data. This is because all of the ECB commands are deferred until playback.
Structural changes like AddComponent or RemoveComponent can be done by the EntityManager or EntityCommandBuffer. Like I talked about in some previous answers, the EntityManager does these immediately while the ECB does these during playback. The right choice depends on the use case. SystemAPI does not provide any ways to make structural changes.
Edit: It sounds like there are some differences between SystemAPI and EntityManager for reading and writing component data according to @DreamingImLatios
What do you mean by “batch updates”? Are you doing structural changes, like adding and removing queries (if so, prefer EntityQuery) or do you mean changing component values (if so, prefer SystemAPI.Query of IJobEntity)?
Indeed, if you intend to do all your work on the main thread. However, I think most people prefer to use IJobEntity instead.
If your system mostly schedules jobs but you need to read or write a component on the main thread to set up those jobs, you want to use EntityManager for that particular access, otherwise you’ll create a sync point on all the input dependencies for your jobs. It is a fairly obscure but practical detail.
@DreamingImLatios , this is a neat detail I didn’t know, and I realized I’m doing this in several places. But most of the time I’m accessing singletons, so I assume this doesn’t count, since EM doesn’t have GetSingleton, right?