Error: IndexOutOfRangeException: Index x is out of restricted IJobParallelFor

Hi all,

I’m trying to write Terrain generator which uses new IJob system but I’m getting stuck at this error:

IndexOutOfRangeException: Index 369 is out of restricted IJobParallelFor range [128...191] in ReadWriteBuffer.

I assume this issue is caused when Unity is trying to write on the same variable on multiple threads. Or it is caused by something else?
Is there save way to pass ~60k size array to IJob system?

example code:

struct GenereteJob : IJobParallelFor
    {
        [WriteOnly] public NativeArray<float> jNoiseMap;
        [ReadOnly] public NativeArray<Vector2> jOctaveOffstes;

        public int jWidth;
        public int jHeight;
        public float jScale;
        public int jOctaves;
        public float jPersistence;
        public float jLacunarity;

        public float jMaxNoiseHeight;
        public float jMinNoiseHeight;
        public float jHalfWidth;
        public float jHalfHeight;

        public int jY;

        public void Execute(int i)
        {
            float amplitude = 1;
            float frequence = 1;
            float noiseHeight = 0;

            for (int ii = 0; ii < jOctaves; ii++)
            {
                float sampleX = (i - jHalfWidth) / jScale * frequence + jOctaveOffstes[ii].x;
                float sampleY = (jY - jHalfHeight) / jScale * frequence + jOctaveOffstes[ii].y;

                float perlinValue = Mathf.PerlinNoise(sampleX, sampleY) * 2 - 1;
                noiseHeight += perlinValue * amplitude;

                amplitude *= jPersistence;
                frequence *= jLacunarity;
            }

            if (noiseHeight > jMaxNoiseHeight)
            {
                jMaxNoiseHeight = noiseHeight;
            }
            else if (noiseHeight < jMinNoiseHeight)
            {
                jMinNoiseHeight = noiseHeight;
            }
            jNoiseMap[i+jWidth*jY] = noiseHeight;
        }
    }

    public static float[,] GenerateNoiseMap (int width, int height, float scale, int octaves, float persistence, float lacunarity, Vector2 offset, int seed, bool bJob = false)
    {

        System.Random rnd = new System.Random(seed);
        Vector2[] octaveOffstes = new Vector2[octaves];

        for(int i=0; i<octaves; i++)
        {
            float offsetX = rnd.Next(-1000, 1000) + offset.x;
            float offsetY = rnd.Next(-1000, 1000) + offset.y;
            octaveOffstes[i] = new Vector2(offsetX, offsetY);
        }

        float[,] noiseMap = new float[width, height];

        float maxNoiseHeight = float.MinValue;
        float minNoiseHeight = float.MaxValue;

        float halfWidth = width / 2f;
        float halfHeight = height / 2f;
        if (bJob)
        {
            float[] tempNoiseMap = new float[noiseMap.Length];
            var JobNoiseMap = new NativeArray<float>(noiseMap.Length, Allocator.Persistent);
            var JobOctaveOffstes = new NativeArray<Vector2>(octaveOffstes.Length, Allocator.Persistent);

            var jobHandle = new List<JobHandle>();

            for (int i = 0; i < octaveOffstes.Length; i++)
            {
                JobOctaveOffstes[i] = octaveOffstes[i];
            }
            for (int y = 0; y < height; y++)
            {
                var job = new GenereteJob()
                {
                    jNoiseMap = JobNoiseMap,
                    jOctaveOffstes = JobOctaveOffstes,

                    jWidth = width,
                    jHeight = height,
                    jScale = scale,
                    jOctaves = octaves,
                    jPersistence = persistence,
                    jLacunarity = lacunarity,

                    jMaxNoiseHeight = maxNoiseHeight,
                    jMinNoiseHeight = minNoiseHeight,
                    jHalfWidth = halfWidth,
                    jHalfHeight = halfHeight,

                    jY = y
                };

                if (y == 0)
                {
                    jobHandle.Add(job.Schedule(width, 64));
                }
                else
                {
                    jobHandle.Add(job.Schedule(width, 64, jobHandle[y - 1]));
                }
            }

            jobHandle.Last().Complete();
           
            JobNoiseMap.CopyTo(tempNoiseMap);
            JobNoiseMap.Dispose();
            JobOctaveOffstes.Dispose();

            for(int i=0; i<width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    noiseMap[i, j] = tempNoiseMap[i + width * j];
                }
            }
        }
        else
        {
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    float amplitude = 1;
                    float frequence = 1;
                    float noiseHeight = 0;

                    for (int i = 0; i < octaves; i++)
                    {
                        float sampleX = (x - halfWidth) / scale * frequence + octaveOffstes[i].x;
                        float sampleY = (y - halfHeight) / scale * frequence + octaveOffstes[i].y;

                        float perlinValue = Mathf.PerlinNoise(sampleX, sampleY) * 2 - 1;
                        noiseHeight += perlinValue * amplitude;

                        amplitude *= persistence;
                        frequence *= lacunarity;
                    }

                    if (noiseHeight > maxNoiseHeight)
                    {
                        maxNoiseHeight = noiseHeight;
                    }
                    else if (noiseHeight < minNoiseHeight)
                    {
                        minNoiseHeight = noiseHeight;
                    }
                    noiseMap[x, y] = noiseHeight;
                }
            }
        }

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                noiseMap[x, y] = Mathf.InverseLerp(minNoiseHeight, maxNoiseHeight, noiseMap[x, y]);
            }
        }

        return noiseMap;
    }

You can use [NativeDisableParallelForRestriction] on the NativeArray if you are certain that in your specific case there is no actual race condition.

5 Likes

Adding a [NativeDisableParallelForRestriction] solved my issue. Thank you.

1 Like

Do we have better restriction? Such as restrict for a block size

I have use IJobParallelFor for making triangle indexbuffer. I would like to restrict a chunk of 6 ints for making a quad. And so it would still error if I touch any block outside those 6 ints

2 Likes

But then you could make an error in your debug code, which would become even more confusing… I guess this would be good if you’re working in a team though. You don’t want somebody else coming and changing something in the Job 6months later unknowingly creating a race condition. You could just detect this yourself though using a #if UNITY_EDITOR: do bounds checking manually, block.

@Joachim_Ante_1 @S1mpleQ

How are you guys getting this to work? I have put [NativeDisableParallelForRestriction] on my NativeArray and Im still getting this error: System.IndexOutOfRangeException: Index {0} is out of range of ‘{1}’ Length.

Has this changed? I’m in Unity 2019.4.29

This is the code in my IJobParallelFor:

public void Execute(int index) {
    distance = Vector3.Distance(positionData[index], currentCamPos);    // Compute distance to main cam
          
    for (int i = 0; i < LODDistances.Length; i++) {                     // Iterate over LOD distances
        if (distance < LODDistances[i]) {                                   // Find applicable LOD Level
            lodDataOutput[index] = i;                                           // Set LOD Level

            return;
        }
    }

    lodDataOutput[index] = -1;                                              // Out of range, set -1 to disable rendering
}

LODDistances contains the distances to test against when setting the active LOD level for each entity.

1 Like