How to define length of output array (DeferredJobArray)

Hi,

I have two jobs I’m running, which are part of a game world initialisation. The first one is an IJobParallelForFilter and checks a large tile map array for a specific tile type and saves these indices to a list. The second job iterates over this list (DeferredJobArray) and writes to an output array to define corresponding positions for the tile type prefab. The problem is I can’t initialise the output array (without a sync point) as its dependant on the previous job.

Is there a workaround for this? For this particular case I could actually run it all in a single job, but there could be situations later on where it would be good to run several jobs that use the index list from the initial job.

var streetIndices = new NativeList<int>(Allocator.TempJob);
GetTypeIndicesJob getTypeIndicesJob = new GetTypeIndicesJob
{
   tileType = TileType.Street,
   tiles = tiles
};
JobHandle getTypeIndicesJobHandle = getTypeIndicesJob.ScheduleAppend(streetIndices, tiles.Length, 128);

var localToWorlds = new NativeArray<LocalToWorld>(streetIndices.AsDeferredJobArray().Length, Allocator.TempJob); // gives 0 length as list is empty when this is scheduled
GetLocalToWorldJob getLocalToWorldJob = new GetLocalToWorldJob
{
   nx = mapGeneratorCity.nX,
   indices = streetIndices.AsDeferredJobArray(),
   localToWorlds = localToWorlds
};
getLocalToWorldJob.Schedule(streetIndices, 128, getTypeIndicesJobHandle).Complete();


[BurstCompile]
public struct GetLocalToWorldJob : IJobParallelForDefer
{
   public int nx;
   [ReadOnly] public NativeArray<int> indices;
   [NativeDisableParallelForRestriction] public NativeArray<LocalToWorld> localToWorlds;

   public void Execute(int index)
   {
       GetTileIndexPos(indices[index], out int x, out int z);
       localToWorlds[index] = (new LocalToWorld { Value = float4x4.Translate(new float3(x, 0, z) * 20f) });
   }

   private void GetTileIndexPos(int index, out int x, out int z)
   {
       z = index / nx;
       x = index - (z * nx);
   }
}

For this use case I have been using a NativeList and a quick IJob to resize it. I agree it would be useful to have an API for deferred allocation of a NativeArray.

1 Like

I guess just use another NativeList, and resize it in the job?

1 Like

Ah, hadn’t thought of that. Thanks!

Just getting started with IJobParallelForDefer so this is part of what is confusing me.

I thought this was the point of doing something like

edgeDeformationTarget = new NativeList<float2>(
    Allocator.TempJob
).AsDeferredJobArray();

But my edgeDeformationTarget.Length is still 0. Even inside of the Execute method.

If you set it like this, I believe it will always be zero, as it is a brand new array and always being deferred. The AsDeferred should be used where you want it deferred.

To be more clear, before the job that is to wait on the information, it should be used as a normal NativeList. When scheduling the jobs that are deferred, THAT is where you use AsDeferred.

It seems as if you should only defer one array. Even if your job uses multiple arrays.

So, the list that is deferred is the control loop count. As normally when you schedule a ForJob, it would need to know the count ahead of time. The IJobParallelForDefer allows you to defer, at some cost, the length of the loop 'til later. Your example though you are both creating, an array and passing it in immediately to the job, so there is in theory nothing writing to it for that job to utilize, nor any way for the job to pull that data out.
The schedule still needs the original list.

See this example: com.unity.jobs/Unity.Jobs.Tests/NativeListDeferredArrayTests.cs at b040be936dac172bf8d64a6b7db24d3469c3910f · needle-mirror/com.unity.jobs · GitHub

(NOTE: They complete immediately to perform the test. You should not be doing so in your code :wink: )

Also a critical note, most of the documentation is in the source code that you can view with most IDEs or by finding the package in your file browser and viewing it that way.

1 Like