private void BuildBatches()
        {
            JobHandle[] handles = new JobHandle[cellToProcess.Count];
            for (int i = 0; i < cellToProcess.Count; ++i)
            {
                int cellIndex = cellToProcess[i];
                GGrassPatchNativeData nativeData = cellsNativeData[cellIndex];

                //GGrassPatch cell = cellToProcess[i];
                GBuildInstancedBatchJob job = new GBuildInstancedBatchJob()
                {
                    instances     = nativeData.instances,
                    batchMetadata = nativeData.metadata,
                    maxLength     = BATCH_MAX_INSTANCE_COUNT
                };
                handles[i] = job.Schedule();
            }

            GJobUtilities.CompleteAll(handles);

            for (int i = 0; i < cellToProcess.Count; ++i)
            {
                int cellIndex = cellToProcess[i];
                CreateInstancedBatches(cellIndex);
            }
        }
        private void CreateInstancedBatches(int cellIndex)
        {
            GGrassPatchNativeData nativeData = cellsNativeData[cellIndex];

            NativeArray <Matrix4x4>  trs      = nativeData.trs;
            NativeArray <Vector3Int> metadata = nativeData.metadata;

            GInstancedBatch[] batches = new GInstancedBatch[metadata[0].z];
            for (int i = 0; i < batches.Length; ++i)
            {
                int prototypeIndex = metadata[i + 1].x;
                int startIndex     = metadata[i + 1].y;
                int length         = metadata[i + 1].z;
                int indexLimit     = startIndex + length;

                GInstancedBatch batch = new GInstancedBatch(BATCH_MAX_INSTANCE_COUNT);
                batch.prototypeIndex = prototypeIndex;
                for (int j = startIndex; j < indexLimit; ++j)
                {
                    batch.AddTransform(trs[j]);
                }

                batches[i] = batch;
            }

            cellsData[cellIndex].instancedBatches = batches;
        }
        private void CalculateAndCacheTransforms()
        {
            if (cellToProcess.Count == 0)
            {
                return;
            }
            bool willSkipFrame = false;

            try
            {
                NativeArray <float>   prototypePivotOffset = new NativeArray <float>(prototypes.Count, Allocator.TempJob);
                NativeArray <Vector3> prototypeSize        = new NativeArray <Vector3>(prototypes.Count, Allocator.TempJob);
                for (int i = 0; i < prototypes.Count; ++i)
                {
                    prototypePivotOffset[i] = prototypes[i].pivotOffset;
                    prototypeSize[i]        = prototypes[i].size;
                }

                JobHandle[] handles = new JobHandle[cellToProcess.Count];
                for (int i = 0; i < cellToProcess.Count; ++i)
                {
                    int                   cellIndex  = cellToProcess[i];
                    GGrassPatch           cell       = cells[cellIndex];
                    GGrassPatchNativeData nativeData = new GGrassPatchNativeData(cell.Instances);
                    cellsNativeData[cellIndex] = nativeData;

                    GCalculateGrassTransformJob job = new GCalculateGrassTransformJob()
                    {
                        instances            = nativeData.instances,
                        transforms           = nativeData.trs,
                        prototypePivotOffset = prototypePivotOffset,
                        prototypeSize        = prototypeSize,
                        terrainSize          = terrainSize,
                        terrainPos           = terrainPosition
                    };
                    //handles[i] = job.Schedule();
                    handles[i] = job.Schedule(nativeData.instances.Length, 100);
                }

                GJobUtilities.CompleteAll(handles);

                prototypePivotOffset.Dispose();
                prototypeSize.Dispose();
            }
            catch (System.InvalidOperationException)
            {
                willSkipFrame = true;
            }
            catch (System.Exception e)
            {
                Debug.LogException(e);
            }

            if (willSkipFrame)
            {
                throw new GSkipFrameException();
            }
        }