RaycastCommand offset positions

We are constructing a flattened 2D native array of raycast commands. We start from the top left corner, define the first start position of every row, then assign the remaining positions of the rays in a parallel job where on each loop of the job, an inner loop is run to assign one whole row. Our results are like so:

The orange points are where our start positions for each row are assigned and the left side of the image is supposed to be where all the rays are. Instead, we get a pattern where one row would overflow to be more than double the width it should be, causing the following row to be empty. So we have the correct total number of rays and they all fire in the same direction but they sometimes start in odd places.

Our code is as follows:

/// <summary>
    /// Schedules a Raycast Job in a grid defined by a rect of a landmark, then generates a silhouette based on
    /// all the returned hit points
    /// </summary>
    private void RaycastSilhouetteGrid()
    {
        float3 topRightCorner = GetFloat3FromPoint(rectPoints[0]);
        float3 topLeftCorner = GetFloat3FromPoint(rectPoints[1]);
        float3 bottomLeftCorner = GetFloat3FromPoint(rectPoints[2]);

        float width = math.distance(topRightCorner, topLeftCorner);
        float height = math.distance(bottomLeftCorner, topLeftCorner);

        int raycastsPerRow = (int)(width / distanceBetweenRaycasts);
        int raycastsPerColumn = (int)(height / distanceBetweenRaycasts);

        int raycastAmount = raycastsPerRow * raycastsPerColumn;

        Debug.Log(raycastAmount);

        NativeArray<RaycastCommand> commands = new NativeArray<RaycastCommand>(raycastAmount, Allocator.TempJob);
        NativeArray<RaycastHit> hits = new NativeArray<RaycastHit>(raycastAmount, Allocator.TempJob);
        NativeArray<float3> startPositons = new NativeArray<float3>(raycastsPerRow, Allocator.TempJob);

        float3 referencePointUp = referencePoint.up;

        startPositons[0] = topLeftCorner;

        for (int i = 1; i < raycastsPerRow; i++)
        {
            startPositons[i] = startPositons[i - 1] + (-referencePointUp * distanceBetweenRaycasts);
            Instantiate(hitPointPrefab, startPositons[i], Quaternion.identity);
        }

        float3 relativeForward = math.normalize(landmark.position - referencePoint.position);
        float3 relativeRight = math.cross(new float3(0, 1, 0), relativeForward);

        SilhouetteRaycastJob raycastJob = new SilhouetteRaycastJob(commands, startPositons, relativeForward, relativeRight, raycastsPerColumn, raycastsPerRow, distanceBetweenRaycasts);

        JobHandle commandHandle = raycastJob.Schedule(raycastsPerRow, 1);
        JobHandle handle = RaycastCommand.ScheduleBatch(commands, hits, 1, commandHandle);
        handle.Complete();

        GenerateSilhouette(hits);
        //ShowHitDebugs(hits);
        onSilhouetteFinishedGenerating.RaiseEvent();

        for (int i = 0; i < commands.Length; i++)
        {
            Debug.DrawRay(commands[i].from, commands[i].direction, Color.red, 1000);
        }

        startPositons.Dispose();
        commands.Dispose();
        hits.Dispose();
    }

And here is our job code which calculates each position within a row:

[BurstCompile]
public struct SilhouetteRaycastJob : IJobParallelFor
{
    [WriteOnly] [NativeDisableParallelForRestriction] public NativeArray<RaycastCommand> commands;
    [ReadOnly] public NativeArray<float3> startPositons;
    [ReadOnly] public float3 direction;
    [ReadOnly] public float3 right;
    [ReadOnly] public int columnAmount;
    [ReadOnly] public int rowAmount;
    [ReadOnly] public float distanceBetween;

    public SilhouetteRaycastJob(NativeArray<RaycastCommand> commands, NativeArray<float3> startPositons, float3 direction, float3 right, int columnAmount, int rowAmount, float distanceBetween)
    {
        this.commands = commands;
        this.startPositons = startPositons;
        this.direction = direction;
        this.right = right;
        this.columnAmount = columnAmount;
        this.rowAmount = rowAmount;
        this.distanceBetween = distanceBetween;
    }

    public void Execute(int index)
    {
        float3 position = startPositons[index];

        for (int j = 0; j < columnAmount - 1; j++)
        {
            commands[(rowAmount * index) + j] = new RaycastCommand(position, direction);
            position += (right * distanceBetween);
        }

        commands[(rowAmount * index) + columnAmount - 1] = new RaycastCommand(position, direction);
    }
}

Has anyone come across a similar issue?

for (int j = 0; j < columnAmount - 1; j++)
        {
            commands[(rowAmount * index) + j] =

if columnAmount > rowAmount, you are writing to the same indices from multiple jobs => race condition

what you want to di is scheduling the IJobParallelFor (or IJobParallelForBatch) so that it only writes to a single index / it’s allowed indices, and calculate the positions inside the job with

x = (i % rowAmount) * width;
y = (i / rowAmount) * height;