Notifications
Article
Part 4: Unity ECS - ECS and Jobs
Published 9 days ago
97
0
The jobs in Entity Component System
Part 1: Unity ECS - briefly about ecs Part 2: Unity ECS - project design Part 3: Unity ECS - operations on Entities Part 4: Unity ECS - ECS and Jobs
I'll talk about Jobs usage from ECS, if you're not familiar with Jobs itself, take a look at Joachim's presentation or official documentation. Briefly, C# Job system allows us to write more efficient, multithread code in safe and easy way. Unlike in ECS approach, you need to manage dependency on jobs. With JobComponentSystem it's done automatically for you.

JobComponentSystem - what is it?

JobComponentSystem is pretty similar to ComponentSystem. It derives from ComponentSystem, so you can also use functions like OnStartRunning, OnStopRunning, OnDestroyManager, OnCreateManager or OnUpdate - but this is a bit different. Let's take a look how it can be made:
public class DamageSystem : JobComponentSystem { public struct Data { public int Length; public ComponentDataArray<Health> Health; } [Inject] private Data m_data; private struct Job : IJob { public Data data; public void Execute() { for (int i = 0; i < data.Length; i++) { var health = data.Health[i]; health.Value--; data.Health[i] = health; } } } protected override JobHandle OnUpdate(JobHandle inputDeps) { Job job = new Job() { data = m_data }; return job.Schedule(inputDeps); } }
But this can be simplified to:
public class DamageSystem : JobComponentSystem { private struct Job : IJobProcessComponentData<Health> // can take up to three components <T0, T1, T2> { public void Execute(ref Health health) { health.Value--; } } protected override JobHandle OnUpdate(JobHandle inputDeps) { Job job = new Job(); return job.Schedule(this, inputDeps); } }
Much less code and actually more efficient since IJobProcessComponentData works in parallel, IJob not. There are more jobs like IJobParallelForBatch, IJobParallelForFilter and IJobParallelFor. The last mentioned also works parallel and it's more similar to IJob. Usage example:
public class DamageSystem : JobComponentSystem { public struct Data { public int Length; public ComponentDataArray<Health> Health; } [Inject] private Data m_data; private struct Job : IJobParallelFor { public Data data; public void Execute(int i) { var health = data.Health[i]; health.Value--; data.Health[i] = health; } } protected override JobHandle OnUpdate(JobHandle inputDeps) { Job job = new Job() { data = m_data }; return job.Schedule(m_data.Length, 64, inputDeps); } }
Jobs don't work in the main thread, so all data you need to be passed to the job from main thread - using an OnUpdate function as you can see in above examples.

Burst Compiler

We can use Burst compiler for even better performance with the attribute [BurstCompile] ([ComputeJobOptimization] is obsolete now) above our job structure. Remember that in the editor it will be slower than in build version, because Unity've done a great job with safety checks to prevent any unexpected behaviors and bugs, but naturally it has a cost. However, you can disable it in the editor there:
But don't do this unless you measure performance. The cheks can really help you in understanding what you shouldn't do and why.
To write code even more optimized to Burst Compiler we should use Unity Mathematics package which is math library with shader syntax. We've got there a lot of new types like float2, float3, int2, int3, bool2, bool3 etc. and mathematics function like in Mathf.

EntityCommandBuffer - how to get them in jobs

Using EntityCommandBuffer in the job is a bit different because it's not automatically created. We need to create BarrierSystem and create EntityCommandBuffer in the main thread (OnUpdate) then pass it into a Job. BarrierSystem is just public class YourBarrier : BarrierSystem { } as you could read from my previous articles, you know that you can inject other systems just with [Inject] YourBarrier barrier; and finally, pass it to the job.
public class YourBarrier : BarrierSystem { } public class YourJobSystem: JobComponentSystem { [Inject] private YourBarrier barrier; struct Job : IJob { public EntityCommandBuffer CommandBuffer; public void Execute() { CommandBuffer.RemoveComponent(..); } } protected override JobHandle OnUpdate(JobHandle inputDeps) { Job job = new Job() { CommandBuffer = barrier.CreateCommandBuffer() }; return job.Schedule(inputDeps); } }
In new preview (0.0.8), you can use concurrent buffer in parallel jobs, just make sure you use EntityCommandBuffer.Concurrent
public class YourBarrier : BarrierSystem { } public class SomeJobSystem : JobComponentSystem { [Inject] private YourBarrier barrier; [BurstCompile] struct Job : IJobProcessComponentData<SomeComponent> { public EntityCommandBuffer.Concurrent CommandBuffer; public void Execute(ref SomeComponent comp) { CommandBuffer.DestroyEntity(comp.Entity); } } protected override JobHandle OnUpdate(JobHandle inputDeps) { Job job = new Job() { CommandBuffer = barrier.CreateCommandBuffer() }; return job.Schedule(this, 64, inputDeps); } }

Some useful information

Remember that BarrierSystem is updated system and command will be performed after YourBarrierSystem is updated. Keep in mind that you can still manage update order with [UpdateAfter/Before(..)] attribute.
You can still add extra dependencies to jobs before they're scheduled in OnUpdate() function.
JobComponentSystem like ComponentSystem also tracks needed entities, so it's not called when it's not needed. You can use jobs even from ComponentSystem, it's not required to use JobComponentSystem, but it's much better to use it in it since it has a lot work behind in dependency management.
Using C# Job System in ECS is really simple since all hard work is done for us (dependency management), so feel free to write fast code easy and safely :)

That's all I wanted to share with you with this series. Follow to not miss any new content.

You can take a look at my Github profile and see some complete game prototypes using ECS.
Give me some feedback in comment section, don't worry I won't hate you if you show me some "anomaly" in my article. If you enjoy my article - like it and follow me. It'll motivate me to write more articles. See you.

Tomasz Piowczyk
Student Unity Developer - Programmer
6
Comments