So there’s this technique called Delayed Result Gathering where you can delay the gathering of Jobs’ result, to prevent stalling the main thread in waiting.
In normal method, a Job related loop would look like this:
Loop()
{
DispatchJob();
GatherResult();
DoOtherStuffs();
}
Right? But then this whole sequence is executed within a frame, which means GatherResult
had to stall everything else and wait for the Job to finish first. Which means the whole process essentially looked more akin to this:
Loop()
{
DispatchJob();
// Wait until Job done
GatherResult();
DoOtherStuffs();
}
With Delayed Result Gathering, we essentially collect the result at the start of next frame, as in:
Loop()
{
// Hopefully the Job is done
GatherResult();
DispatchJob();
// No need to wait for Job done
DoOtherStuffs();
}
With a single frame of delay, the Job can be done asynchronously, ideally. One can even push it further, to collect the result only when the Job is fully complete, resulting in zero waiting:
Loop()
{
if(jobDone) // Wait for the Job completely asynchronously
GatherResult();
if(!jobDispatched)
DispatchJob();
// No need to wait for Job done
DoOtherStuffs()
}
Though for some reason, back when I tried to implement it when I’m researching how to write a scalable AI, Unity gave me some error preventing me to do so. I figure the performance improvement is already good enough, so I just leave it as is.
Recently I read an example on how to generate combined mesh asynchronously using Jobs. And I realized that there’s a much intuitive way of implementing Delayed Result Gathering, which allows me to write it in the much intuitive DispatchJob -> GatherResult order.
So I’ve done it using Coroutine:
Coroutine()
{
JobHandle handle = DispatchJob();
while(!handle.IsComplete)
yield return null;
handle.Complete();
GatherResult();
}
And so now the EnemyManager can update 100 enemies easily without getting stalled by the Job.