Start Job AFTER other code?

I have a job that relies on entities created, however that job is started before the other code runs.

An entity query within the OnCreate on that job returns 0 records because the other code has not created the entities yet. Is there a way to control when the job starts?

Thanks

Show system code

You again? Do you have a big “S” on your shirt?

Anyway:

    protected override void OnCreate()
    {
       
        EntityQuery allRoadsQuery = GetEntityQuery(
            typeof(ERRoadTag),
            ComponentType.ReadOnly<RoadDetails>(),
            ComponentType.ReadOnly<LanePoints>());

        roadEntities = allRoadsQuery.ToEntityArray(Allocator.Persistent);

        EntityQuery allConnectionsQuery = GetEntityQuery(
            typeof(ERConnectionTag),
            ComponentType.ReadOnly<ConnectionDetails>(),
            ComponentType.ReadOnly<LanePoints>());

        connectionEntities = allConnectionsQuery.ToEntityArray(Allocator.Persistent);
    }

The problem is that I would LIKE to avoid recreating these two queries each time the Job’s OnUpdate runs. So I would like this to run when the job starts, however the roads and connections have not been made yet. I just tried to disable the system: World.Active.GetExistingSystem<ER3D_TrafficSystem>().Enabled = false;

However, the job starts before the line above can be run. Is there a way to disable the job until I explicitly run it?

Update: I moved the query code to OnStartRunning within the job, and that seems to work. Is this the correct method?

  1. ToEntityArray copies the data in a new array. you need to call it each frame to get the right data
  2. GetEntityQuery should be called in OnCreate and cached
  3. do you actually need the entity arrays? you can use IJobForEachWithEntity to iterate them without copying
1 Like

I’ll start with #3. Yes, I need these two arrays as the Job iterates through Auto/Cars to move them. When a car gets to the end of it’s current list of points, it needs to find the next Road and it’s Connection to get the next set of points. But thanks anyway.

#1. The roadEntities and connectionEntities never change, so calling it each frame is a waste of time.

#2 I can’t call this within OnCreate because the code that is creating the entities has not run, or not completed so I end up with no Road or Connection entities to fill those arrays. I ended up disabling the Job System, creating the entities, then re-enabling the Job System which creates the arrays within OnStartRunning

World.Active.GetExistingSystem<ER3D_TrafficSystem>().Enabled = false;

CreateAutoEntities();

CreateRoadEntities();

World.Active.GetExistingSystem<ER3D_TrafficSystem>().Enabled = true;

So this is working for me.

Your system should run only if both EQ have entities?

I’m sorry, but I do not understand what you mean by “EQ”

This might help, here is the code: https://github.com/Blissgig/Easy-Road-3D-ECS-Traffic Specifically the Job System https://github.com/Blissgig/Easy-Road-3D-ECS-Traffic/blob/master/ER3D_TrafficSystem.cs

Basically this is about moving “autos” around using data from Easy Roads 3D (great asset, fantastic support) The roads have lanes, these lanes have points along them. There are Connections (aka Intersections) that have additional data. The Autos have their CURRENT set of points. When the Job System completes an auto’s set of points, I have to query the next road, and the next connection to get the next series of points.

So, yea, I need both sets of Road and Connection entities in order to get the next set of points.

The good news is that I got it working by disabling the Job and then re-enabling it after all entities are created and then populating the queries within OnStartRunning within the Job System. If this design is wrong, if there is a better way to do ANY of this, I am all ears. (I’ve been wrong before)

Have a wonderful day.

Entity query. Seems weird to be enabling/disabling entire systems when your system shoudn’t run if it doesnt the entity query isn’t met(im sure someone else more enlightened can chime in on if all queries must be met or just one etc). Also you are always making a query whenever you enable/disable that system(which may have an impact on your performance) rather doing it once in OnCreate.
If its a case of the system running when just one query is met but you want both, you could move the array creation to OnUpdate, use [DeallocateOnJobCompletion] inside the job and also add

if(queryA.CalculateEntityCount()==0 && queryB.CalculateEntityCount()==0)
         return default;

Thanks, however creating the query in the OnUpdate would be expensive as I would have to create the queries every frame, or at least check if they exist on every frame. (If you review the history on this code; https://github.com/Blissgig/Easy-Road-3D-ECS-Traffic/blob/master/ER3D_TrafficSystem.cs you will see that is exactly what I did, however doing so had Unity stating that a memory leak was caused by this.

I very much hear you about turning the Job on and off, however, this happens only once. The Road, Connection and Auto entities are created and then the Job is turned on. Only that one time.

The OnCreate can’t be used since the Roads, Connection, Auto entities are not created when the JobStarts… hence my original question.

So, for the moment, this works. Not perfect, but considering the issues I can’t think of another pattern.

Thank you again for the info.

Have a great evening.

What I said was not to put the query creation in OnUpdate, but to put a check whether the query entity count met a criteria, and run the job based on that. It shouldn’t matter that the entities aren’t created, because there are many instances where a system waits and does nothing if it has no matching entity query to work on. That’s one of the core features of ecs in dots.
That said I would guess the job runs because your IJobForEach uses a query different from what is declared in your system and it starts based on that query(BCCCC) rather than what the system has(again someone else feel free to chime in and correct me).

1 Like

Right, and I mentioned that I would prefer to avoid the check for the existence of these records on each Update. Your answer will work, absolutely. I just HOPE that doing in OnStartRunning, AND since I am only stopping and starting the Job the one time that it should be the most optimal.

The code is here: https://github.com/Blissgig/Easy-Road-3D-ECS-Traffic/blob/master/ER3D_TrafficSystem.cs

So the differences I am doing vs what you mention are minor, but since I am attempting to create a VR game, I am looking for EVERY. TINY. LITTLE. BIT. of performance boost I can get. So not checking to see if the query has values each frame SEEMS to be the best option. Like you said, feel free to prove me wrong. (I got NO problem being wrong)

I’ll prove you wrong by telling you that as soon as you do a GetEntityQuery in a system, the system will automatically do a check on the query every frame. [AlwaysUpdateSystem] will disable that check, so you can put that EntityQuery fetch back in OnCreate where it belongs.

Also, it is a terrible idea to be micro-optimizing at this level until you have a real performance problem, because later down the line you are going to wish you still had that time you spent micro-optimizing to optimize your heavy loops using the Burst inspector.

I’ll look into the [AlwaysUpdateSystem] attribute, thanks. I still can’t put it in OnCreate since the entities are not created before that runs.

Yes, as a business developer I tend to avoid optimization until it becomes insanely slow… but I am working this for a VR project and attempting to squeeze every little bit is still a decent idea.

I do not understand what you mean by; “I’ll prove you wrong by telling you that as soon as you do a GetEntityQuery in a system, the system will automatically do a check on the query every frame” when the query is only run on the OnStartRunning, why the f! would Unity run that query every frame? I FEEL that I am missing your point. Do you have some documentation that elaborates on this?

You can see my entire code for this at: https://github.com/Blissgig/Easy-Road-3D-ECS-Traffic

Thank you very much for your time, and have a great evening

I think you misunderstand how EntityQueries work.

EntityQueries are like a shopping list. They tell Unity which chunks to fetch when Unity decides it needs to fetch chunks. The shopping list can be created before the store opens for the day. In the same way, you can create an EntityQuery before the entities exist. Every system update before OnUpdate() gets called, the system calls ShouldRunSystem() which calls the IsEmptyIgnoreFilter property on every EntityQuery it has saved (unless you use that attribute I mentioned). That function then checks at that moment if any entities match the query. It does not matter when the entities were created nor when the query was created. All that matters is that there is a match when the check is performed, and that check is performed every update in the ShouldRunSystem() and also the matching part is done every time you schedule a job that uses an EntityQuery (implicitly or explicitly).

So in your code, you are “creating” the EntityQuery in OnStartRunning(). You are not “running” the EntityQuery then. You only need to create it once, so it belongs in OnCreate().

3 Likes

This is why I think the API need to be up front about of what is “SetReaderWriter” hidden in each method. GetEntityQuery has one. GetSingleton which looks like a one-off method you can use in OnUpdate instead of OnCreate, is also one. The thing to realize is that some methods apply or modify permanent state of the system just by calling it in the back. You can suspect this on all methods that are on this. (protected methods)

1 Like

I just moved my code from OnStartRunning to OnCreate, cause your statement seems RIDICULOUS. …and yet, well, you already know the answer. This is SO contrary to what I know about events and database queries.

Thank you for taking the time to enlighten me. Have a great day

1 Like