 Now, if we want to use ECS and the job system together, well, first off, in the updates of our systems, we can create schedule and complete jobs because updates run within the main thread. And here I'm creating a job, which is using a component data array, because that is a valid kind of field for a job, a component data arrays and entity arrays. Those are allowed. I don't know if they're technically blittable or if an exception is made, but they are allowed. Component groups are not allowed because those are reference types. They're class instances. But anyway, with component data arrays, we can get at the components as they exist in our chunks. And so here in the update from the component group, we're getting the component data array, creating a job and plugging in that component data array, scheduling the job, kicking off its execution, and then completing it. And then it's going to iterate through all those components. And as we iterate through them, we can both read them and mutate them as they exist in chunks. And very much like with native containers, there are safety checks wherein if we schedule two jobs that are accessing the same iterator, we're going to get an error. So here if I create another instance of the job, job two, we'll call it, schedule it here. Now we'll get an exception on the second schedule because it's trying to access the same iterator, just like if we had a job trying to access a native container already and used by a scheduled job. And so one of these two jobs, if they want to touch the same iterator, one of them should complete before the other is scheduled, or one should be a dependency of the other. Also we'll get a safety check exception if we access an iterator while it's in use by a scheduled job. So here because this job has not yet been completed, when we're trying to access this iterator, this will throw an exception. Now here I'm trying to have this job be completed not in the same frame where it's scheduled, but completed in the next frame. So what I've done is I've made the handle a field, and we call complete on that handle at the start of the update and then schedule the next job. So yes, the first time the update is performed, this call to complete is on a handle that represents no actual job, but that's fine, that's harmless. But then we schedule a job and the next time we call update, it'll complete that first scheduled job. So let's see if this works. I come over here, hit play, and we're getting a bunch of exceptions. We have that first my update call, but then we get a bunch of exceptions. What happened? Well, when an update of a system is called, it is known to that system which component groups it accesses. And it looks at all the scheduled jobs and says, well, are there any scheduled jobs which touch those same component groups? And the answer is yes. In this case, we have a job created in the same system, which is touching a component group. And so there's a conflict between the job created in the previous update and this subsequent call to update because that outstanding job hasn't yet been completed. And so we get an exception and that's why you never see my update printed again. It's not even doing the second call of update before it throws the exception. And the reason the safety checks are this conservative is because with component groups, the potential is that you can have two separate jobs accessing different component groups, but there can be overlap in those component groups of how they access the actual entities and components in memory in the chunks. And so unlike with native containers where you have to have jobs accessing the very same native container to have a conflict with their entity components, you can have totally separate iterators, but they're touching the same data and memory. And so in this case, even though the logic would be fine, we wouldn't really have a conflict. The safety check is conservative and says, well, you have an outstanding job when this on update is called that is using a component group used by this system. So this raises the question, what if I want to create jobs and systems that aren't immediately completed in those same updates and yet have them all access component groups that make conflict with those of other systems? Well, that's where we need what are called job component systems. Here, I've made the system a job component system rather than just a regular component system. And the difference now is that the on update method takes in a job handle and returns a job handle. And the idea is that all the jobs I create within this on update should either be the handle returned from the method or a dependency thereof. So I can create multiple jobs in here, but they all have to form a chain where at the end, there's one handle returned and all the other jobs are dependencies directly or indirectly of that job. And then what happens with that handle, it's retained by the job component system. And the next time on update is called immediately before it's going to complete that handle. So this job I'm creating in the update and then scheduling and returning the job handle that returns, that is automatically going to be completed immediately before the next on update. And so now if I run this code, it'll run without errors. We don't get those exceptions from the safety checks. Now, what about though, this input dependency? Well, you have potentially many job component systems. And again, it's known to each system which component groups it accesses because the way we get a component group is by calling the get component group method of the system. And so we know which component groups each job component system accesses. And so in a sense, it could be known which job component systems are touching the same component groups. And so the job handle passed into this on update is a combination of all the returned handles from the other job component systems, which conflict with this one. So say I have five job component systems, A, B, C, D, and E. And we're updating D, but E and A, let's say, have component groups that conflict with the component groups of D. And so in the update of D, the job handle passed in as a combination of the handles returned by the updates of A and E. Such that if all the jobs we create within D, if they use that input handle as a dependency, either directly or indirectly, then we can be guaranteed there aren't going to be any conflicts between D and the other job component systems. So as a rule, all the jobs you create within your job component systems, they form a single chain where they all directly or indirectly are dependencies of the input. And then there's a single job handle at the tail of this chain, and that's what you return from the update. And if you follow these rules, then it'll work out that all of the jobs you create in all of your job component systems, they can touch component group iterators, and there aren't going to be any conflicts between those jobs. We'll have set up effectively the chain of dependencies between all the jobs we create. And so properly, what I should be doing in this example is I should be passing in input depths as a dependency for this one job I'm creating. And that way, if there are any other job component systems which are creating jobs that access the same component group so there's potentially conflicting access, then this job will have those as dependencies. And be clear, all these jobs are being completed at the start of the next update of that same system. So they're running for a full frame. And when the input dependencies are passed in, that's combining the handles of all the other conflicting job component systems, their last returned handles, which in some cases will be from updates earlier in that same frame, but in some cases it'll be from job component systems that their last updates in the previous frame. So the input dependencies aren't necessarily from jobs created previously in this frame. Some of them can be jobs created in the last frame. For every job component system, it's just always keeping track of the last returned job handle. And that's what's used when we combine the dependencies and pass them in as the input dependencies. Now in some cases you want these jobs to complete sooner than after a full frame elapses. And so what you can do is into a job component system you can inject a barrier system. And when you do automatically then, the handle returned by that job component system will be completed in the next update of that barrier system. And those jobs of course are completed before the entity command buffers are flushed. So here for example, we have the my barrier barrier system and we've made this job component system update before my barrier. So it's always gonna run in the frame before. And then we're injecting my barrier into this job component system such that from it we can create a command buffer which is being passed to the job. The job is now expecting a command buffer. And then we can use that in the job code where we're getting the entity and then say destroying it but not doing so immediately. We want it to be done later after the job completes. And when my barrier updates, it's first going to complete the job returned from this update and then it's gonna flush the buffer that was created here. It's of course very important that we complete the jobs before you flush the buffers because in the execution of jobs, that's where we queue up the commands in the buffer and then we want them to be executed after. So the barrier will complete the jobs first. So that's how we can use a barrier in a job component system. And you can imagine that we might have many job component systems all using the same barrier. So we arrange them to update before the barrier and they all create command buffers. They'll have jobs that then queue up commands. And then when that barrier system updates, it completes all the jobs from those job component systems and then flushes all of their command buffers. And there's actually one detail I forgot here. The entity array in your job should be marked as read only because entity arrays are always meant to be read only. So I got an exception when I ran the code beforehand without this attribute. And also understand that what we're doing effectively in this code is we're destroying all the entities such that they are no longer any entities which match the component group of this job component system. And that means on update will not be run a second time. I'm not sure I mentioned this before, but the on update methods of a system, it's known to that system which component groups it touches and if there aren't any entities which match those component groups, any of those component groups, then obviously the update doesn't need to be run because it wouldn't do anything. It's presumed that the purpose of an update is to do something with the component groups. And so if there aren't any entities matching those component groups, the on update is simply skipped, it's not executed. And so if I come here and run this program, you see my update printed once, but it's not being printed again because after that first update, the jobs are clearing all the entities used by this job component system and so there aren't any subsequent updates.