I have been using DOTS and Jobs and have been loving the performance its great there is one thing I don’t think is currently possible (if it is i would love to know how to enable/do it) is the ability to schedule jobs that last multiple frames before competing.
Currently jobs from my knowledge are designed to be scheduled then completed some point later in the same frame. However I have some tasks I would like to compile with Burst and have run for say 10 frames before completing as they are for loading and don’t need to be updated to the player immediately. (if I force them to complete in one frame the player receives a lovely lag spike as the jobs are colossal meshing jobs)
To try and achieve this I created my own JobComponentSystem that works the same way but has two methods:
JobHandle OnTickUpdate(JobHandle inputDeps)
This is for scheduling Jobs to run over multiple frames (one per tick)
and
void OnTickUpdateCompete()
This is called right after the last tick JobHandel.Complete() and is useful for jobs that need to do stuff with the data once its ready. (This happens just before the next OnTickUpdate())
This all seems to work fine… however if I slow the tick rate down to more than 4x slower than the framerate, an internal Job method gives the warning ("deleteing allocation that it’s permitted lifetime of 4 Frames (age = 5+))
And then the job completes fine but some of the data is half process as its native arrays inside the job were disposed this causes some weird effects!
If there a way I can stop this from happening? I Realise why its there as you are trying to avoid memory leaks but if I could somehow assign the jobs native arrays so they don’t get flagged for deletion that would be perfect.
This seems to be such a common request but I never understood why (except if your use case is multiple worlds with different tick rates which is actually a legit problem that Unity needs to solve at some point for multiplayer).
If you want a job to run for multiple frames… just use a Task instead?
You want to use burst in a Task? Pass in a function pointer.
(sorry if I’m coming off a bit rough, but there is literally another post about this half a page down)
Long running jobs is fully supported in 2020.1. The warning most likely comes from using Allocator.TempJob in one of the containers. You can use Persistent for data that lives longer than a frame.
I think it really depends on what you are trying to do… What are those background workers used for.
Is it important that they complete in a predictable amount of time (But not within one frame?)
Or is there no constraint on the latency of the job at all?
Most background work has latency constraints. For things like procedural content generation etc. I would try to aim for a solution that gives you predictable non-spikey performance.
If your game has enough work to constantly fill out one thread. That does mean you have one less core for the rest of the systems. Is this what you want / need in your game?
If so, You can enforce this by making all long running background jobs depend on the previously scheduled background job. Or simply scheduling a new background job only after the previous one completed.
That way at least you will never have two background jobs running at the same time, overwhelming the system.
If you cant afford one core to be used for background work, then I would suggest timeslicing your backgrounds into smaller jobs. So you can schedule a couple of them every frame. Schedule them at a time when there are few other jobs running. I’d aim for timesliced jobs of the size of 1ms or so
The main point is this:
Letting the operating system control timeslicing is a terrible way of taking control of frame budgets…
What is the best approach to handling longer-running jobs which could be done in parallel but complete at different times?
For me I am generating buildings, but since each building is unique they all take different timespans to create depending on floors, walls, and interior complexity. I am looking into maybe splitting the creation jobs into smaller jobs to make sure the jibs at least take about the same time, but at some point the buildings will complete at different times. Should I let the quickest completions await the longest?
If your creating lots of buildings this shouldn’t matter as on average a chunk will probably have a similar mixture of small and large buildings and it will all even out.
If that doesn’t apply or you want it to complete as stuff is ready rater than when all is done you can inside a job have some sort of counter for complexity ad 1 for a small building and 10 for a big one, when the counter gets to say 100 you break from the job. This requires you to mark stuff as clean or dirty so then when the job runs again it skips over the clean stuff.
Does this also apply to native arrays created inside a job? I don’t have any tempjob native arrays but i do have Allocator.Temp arrays created inside a job (and it won’t let me use persistent for this) and I’m getting the older than 4 frames thing.
I can’t create these particular arrays outside the job.