Jobs.For(count, body) runs body(i) for every i below count on the engine’s worker threads and the calling thread together, and returns when every one has run:
Jobs.For(enemies.Count, scope (index) => enemies[index].Think());- Each body must only touch its own item’s data. Reading shared data that nothing changes during the batch is fine; writing it is not (no adding to a shared list, no moving objects in the scene, no graphics calls).
chunkhands out that many items at a time, for cheap items (Jobs.For(100000, body, 256)), so the threads take turns less often.minimum: fewer items than this run on the calling thread alone (waking the workers costs a few microseconds).Jobs.ThreadIndexsays which thread a body is on (0 the caller, 1 and up the workers, all belowJobs.ThreadCount), for scratch memory kept per thread.- A
Jobs.Forstarted inside another (or from a second thread while one runs) runs on the calling thread.
There is one worker for each core but two (one is the calling thread’s; the rest leave room for the physics and audio threads), up to 15. They start the first time a batch is big enough to share and sleep between batches. Web builds and one-core machines have none: everything runs on the calling thread.
What uses them
- Models bent on the CPU (Skinned Mesh Renderers without GPU skinning): the frame’s models are bent together at the end of
Scene.Update, once animation, scripts and IK are done. 100 characters of 5,184 vertices: 13 ms to 4.2 ms. - Navigation paths: what Navigation Agents ask for during
Updateis found together after it (NavigationData.FindPaths), each thread with its own search memory. Reading an agent’sPath,HasArrived,PathFailedorRemainingDistancebefore then finds that agent’s path at once, so scripts see the same thing as before.
Both wait for the batch only inside Scene.Update: called on their own (an editor tool calling OnFrame, a test calling Update), they do their work at once.
This guide is docs/jobs.md in the engine’s repository. Spotted a mistake? Open an issue.