So far, I’ve made my systems using inject and manually looping them in OnUpdate(). I want to optimize some systems by “jobifying” them. If I understand correctly, when your making a job that implements from IJobParallelFor, the NativeArrays in it should have the same length. However, ComponentDataFromEntity does not have the same length as the injected data. How is it fed in an IJobParallelFor struct then? Do I temporarily create an array for it and assign it to a NativeArray?
private struct Data {
public int Length;
public ComponentDataArray<Target> Targets;
}
[Inject]
private ComponentDataFromEntity<Position> allPositions;
private struct Job : IJobParallelFor {
...
public NativeArray<Position> Positions;
...
}
// Note sure if this is even correct
protected override JobHandle OnUpdate(JobHandle inputDeps) {
// Prepare input positions
Position[] alignedPositions = new Position[this.data.Length];
for(int i = 0; i < this.data.Length; ++i) {
alignedPositions = this.allPositions[this.data.Targets[i].targetEntity];
}
Job job = new Job();
job.Positions = new NativeArray(alignedPositions, Allocator.TempJob);
return job.Schedule(this.data.Length, 64, inputDeps);
}
if you are only reading from CDFE, add the [ReadOnly] attribute.
if you must write to it and you know what you are doing, you can add [NativeDisableContainerSafetyRestriction], but that will disable the safety system and error checking, potentially crashing Unity.
yes but beware that if you write to the same entity from multiple threads, you will get race conditions (your results will be different depending on the timing of the threads, and your data can be corrupted, potentially causing crashes or undefined behavior later).
also the cache will be invalidated from multiple threads, slowing down things.
try to avoid that if you can (there is EntityCommandBuffer.Concurrent or NativeQueue.Concurrent if you need them. you can write to them from multiple threads but not read)
for your example, you can do
struct Job : IJobParallelFor {
[ReadOnly] public ComponentDataFromEntity<Position> allPositions;
public ComponentDataArray<Target> Targets;
void Execute(int index) {
var position = allPositions[Targets[index].entity];
// do your stuff
// assign your result to provided index of output array, or use a concurrent ECB or native queue to schedule commands to be executed later in the main thread
}
}
I do need to write to ComponentDataFromEntity. How do I use EntityCommandBuffer.Concurrent or NativeQueue.Concurrent? An EntityCommandBuffer can only be ReadOnly inside a job.
To be clear, the only issue is if you need to write to components referenced by the same entity. If your logic makes this impossible (i.e. you’ve divided up the data in to exclusive groups based on some other types or values), you can write to the components directly without any issue wrt data consistency.
struct Job : IJobParallelFor {
public ComponentDataFromEntity<Position> allPositions;
public ComponentDataArray<Target> Targets;
void Execute(int index) {
Position position = allPositions[Targets[index].entity];
// Compute new position
allPositions[Targets[index].entity] = position; // Modify the position
}
}