Search Unity

Question InvalidOperationException: all contained must be valid when scheduling a job

Discussion in 'Burst' started by laurentlavigne, May 28, 2021.

  1. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,329
    job+ burst is hella fast when it works but it's super complicated to get right.
    I'm getting these and have no idea why or how to fix them.
    InvalidOperationException: The UNKNOWN_OBJECT_TYPE RecalculateBoostJob.newTextureMapHalfPointer_INOUT has been deallocated. All containers must be valid when scheduling a job.

    then later on in the console

    A Native Collection has not been disposed, resulting in a memory leak. Enable Full StackTraces to get more details.


    then
    InvalidOperationException: The UNKNOWN_OBJECT_TYPE RecalculateBoostJob.newTextureMapHalfPointer_INOUT has been deallocated. All containers must be valid when scheduling a job.


    and finally a bouquet of those

    A Native Collection has not been disposed, resulting in a memory leak. Enable Full StackTraces to get more details.

    and they go back and forth

    that's the code
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using System.Collections.Generic;
    4. using System.Runtime.Remoting.Messaging;
    5. using Unity.Burst;
    6. using Unity.Collections;
    7. using Unity.Jobs;
    8. using Unity.Mathematics;
    9. using Unity.Profiling;
    10. using Random = UnityEngine.Random;
    11.  
    12. [DefaultExecutionOrder(-9001)]
    13. public class BlissManager : MonoBehaviour
    14. {
    15.     public static BlissManager _instance;
    16.     public static BlissManager instance
    17.     {
    18.         get
    19.         {
    20.             if (!_instance)
    21.             {
    22.                 _instance = FindObjectOfType<BlissManager>();
    23.                 #if UNITY_EDITOR
    24.                 Debug.LogWarning($"Expensive find object in {_instance?.gameObject.name} ... scene: {_instance?.gameObject.scene.name}");
    25.                 #endif
    26.             }
    27.             return _instance;
    28.         }
    29.     }
    30.     List<Booster> _boosters = new List<Booster>();
    31.     Texture2D _tex = null;
    32.     public float boostEffectMultiplier = 2f;
    33.     List<Rect> _recalculate = new List<Rect>();
    34.     const int HALF_GRID_SIZE = 100;
    35.     public static int[] _grid;
    36.     public int baseBliss = 0;
    37.     float[] _lastMap = null;
    38.     NativeArray<half> _newTextureMapHalfPointer;
    39.     int _changeUMin = 0;
    40.     int _changeUMax = 0;
    41.     int _changeVMin = 0;
    42.     int _changeVMax = 0;
    43.     public RenderTexture blissRT;
    44.     readonly half _good = (half) 1;
    45.     readonly half _neutral = (half) 0.5;
    46.     readonly half _bad = (half) 0;
    47.     [Space] public bool testBlit = false;
    48.     // Beautification
    49.     public bool performanceBeautifyEnabled;
    50.     public GameObject beautifyBliss;
    51.     public ParticleSystem beautyCorrupted;
    52.     public LayerMask beautyLayer;
    53.     //debug
    54.     #if UNITY_EDITOR
    55.     [Header("DEBUG")] [SerializeField] bool forceFullRecompute;
    56.     #endif
    57.  
    58.     bool Paused => Time.timeScale < 0.1f;
    59.     //job inout variables
    60.     JobHandle recalculateBoostJobHandle;
    61.     NativeArray<int> _gridNative;
    62.  
    63.     public void Awake()
    64.     {
    65.         if (_instance)
    66.         {
    67.             Destroy(gameObject);
    68.             return;
    69.         }
    70.         _instance = this;
    71.         transform.parent = null;
    72.         DontDestroyOnLoad(gameObject);
    73.         // _newTextureMapHalfPointer = new NativeArray<half>(HALF_GRID_SIZE * HALF_GRID_SIZE * 4, Allocator.Persistent);
    74.         _lastMap = new float[HALF_GRID_SIZE * HALF_GRID_SIZE * 4];
    75.         for (int i = 0; i < _lastMap.Length; i++) { _lastMap[i] = 0.5f; }
    76.         _tex = new Texture2D(HALF_GRID_SIZE * 2, HALF_GRID_SIZE * 2, TextureFormat.RHalf, mipChain: true);
    77.         //set the bliss map to 0.5 which is neutral
    78.         var newMap = _tex.GetRawTextureData<half>();
    79.         for (int i = 0; i < newMap.Length; i++) { newMap[i] = _neutral; }
    80.         _tex.Apply(true);
    81.         if (testBlit)
    82.             Graphics.Blit(_tex, blissRT);
    83.         else
    84.             Graphics.CopyTexture(_tex, blissRT);
    85.     }
    86.  
    87.     void OnEnable()
    88.     {
    89.         Shader.SetGlobalFloat("_BlissMapSize", HALF_GRID_SIZE * 2);
    90.     }
    91.  
    92.     ParticleSystem _corruptionParticle;
    93.  
    94.     public void Start()
    95.     {
    96.         if (beautyCorrupted)
    97.         {
    98.             _corruptionParticle = (Instantiate(beautyCorrupted.gameObject, Vector3.zero, Quaternion.identity) as GameObject).GetComponent<ParticleSystem>();
    99.             _corruptionParticle.enableEmission = false;
    100.             _corruptionParticle.transform.parent = transform;
    101.         }
    102.         _changeVMax = HALF_GRID_SIZE * 2;
    103.         _changeUMax = HALF_GRID_SIZE * 2;
    104.         CalculateEntireBlissMap();
    105.     }
    106.  
    107.     // emit particles
    108.     void FixedUpdate()
    109.     {
    110.         if (performanceBeautifyEnabled)
    111.         {
    112.             for (int u = 0; u < HALF_GRID_SIZE * 2; u++)
    113.             {
    114.                 for (int v = 0; v < HALF_GRID_SIZE * 2; v++)
    115.                 {
    116.                     if (_beautyGrid[u, v] != null)
    117.                     {
    118.                         BeautyGrid beauty = _beautyGrid[u, v];
    119.                         if (beauty.bliss <= .3f)
    120.                         {
    121.                             if (Random.value > CORRUPTIONPARTICLEPROBABILITY)
    122.                             {
    123.                                 Vector3 randomCircle = Random.insideUnitSphere;
    124.                                 randomCircle.y = 0;
    125.                                 _corruptionParticle.Emit(beauty.position + randomCircle, Vector3.zero, 1, Random.Range(0.5f, 1.5f), Color.white);
    126.                             }
    127.                         }
    128.                     }
    129.                 }
    130.             }
    131.         }
    132.     }
    133.  
    134.     //recalculate bliss
    135.     public void Update()
    136.     {
    137.         if (Paused)
    138.             return;
    139.         #if UNITY_EDITOR
    140.         if (forceFullRecompute)
    141.         {
    142.             CalculateEntireBlissMap();
    143.             _recalculate.Clear();
    144.             return;
    145.         }
    146.         #endif
    147.         if (_recalculate.Count > 0)
    148.         {
    149.             Rect rmax = _recalculate[0];
    150.             for (int i = 1; i < _recalculate.Count; i++)
    151.             {
    152.                 Rect r = _recalculate[i];
    153.                 rmax.xMin = Mathf.Min(rmax.xMin, r.xMin);
    154.                 rmax.xMax = Mathf.Max(rmax.xMax, r.xMax);
    155.                 rmax.yMin = Mathf.Min(rmax.yMin, r.yMin);
    156.                 rmax.yMax = Mathf.Max(rmax.yMax, r.yMax);
    157.             }
    158.             RecalculateBoostJobified(rmax);
    159.             _recalculate.Clear();
    160.         }
    161.     }
    162.  
    163.     public void RegisterBooster(Booster b)
    164.     {
    165.         _boosters.Add(b);
    166.     }
    167.  
    168.     public void RemoveBooster(Booster b)
    169.     {
    170.         _boosters.Remove(b);
    171.     }
    172.  
    173.     public float GetBoostMultiplier(Vector3 pos, bool negative)
    174.     {
    175.         int boost = GetBoost(pos);
    176.  
    177.         // are we boosted by corruption? if so -ve the boost
    178.         if (negative) boost = -boost;
    179.  
    180.         // if boosted (i.e. positive bliss for good guys, negative for bad guys) return the multiplier
    181.         if (boost <= 0) return 1;
    182.         return boostEffectMultiplier;
    183.     }
    184.  
    185.     public void QueueRecalculateBoost(Rect r)
    186.     {
    187.         _recalculate.Add(r);
    188.     }
    189.  
    190.     public void CalculateEntireBlissMap()
    191.     {
    192.         RecalculateBoostJobified(new Rect(-HALF_GRID_SIZE, -HALF_GRID_SIZE, HALF_GRID_SIZE * 2, HALF_GRID_SIZE * 2));
    193.     }
    194.  
    195.     struct Booster4Job
    196.     {
    197.         public Vector3 pos;
    198.         public int range;
    199.         public int boostAmount;
    200.     }
    201.     public struct NativeUnit<T> where T : struct
    202.     {
    203.         NativeArray<T> internalContainer;
    204.  
    205.         public NativeUnit(Allocator allocator)
    206.         {
    207.             internalContainer = new NativeArray<T>(1, allocator);
    208.         }
    209.  
    210.         public void Dispose()
    211.         {
    212.             internalContainer.Dispose();
    213.         }
    214.  
    215.         public T Value
    216.         {
    217.             get { return internalContainer[0]; }
    218.             set { internalContainer[0] = value; }
    219.         }
    220.     }
    221.     List<Booster4Job> _b4j = new List<Booster4Job>(1000);
    222.     bool jobStarted = false;
    223.  
    224.     static readonly ProfilerMarker ___recalculateBliss = new ProfilerMarker ("-Recalculate Bliss");
    225.     static readonly ProfilerMarker ___applyTexture = new ProfilerMarker("--apply Texture");
    226.  
    227.     public void RecalculateBoostJobified(Rect r)
    228.     {
    229.         ___recalculateBliss.Begin();
    230.         if (_grid == null)
    231.             _grid = new int[HALF_GRID_SIZE * 2 * HALF_GRID_SIZE * 2];
    232.         // prep native data for the job
    233.         _newTextureMapHalfPointer = _tex.GetPixelData<half>(0);
    234.         _b4j.Clear();
    235.         foreach (Booster boost in _boosters)
    236.         {
    237.             if (!boost || !boost.gameObject.activeInHierarchy || !boost.boostEnabled)
    238.                 continue;
    239.             _b4j.Add(new Booster4Job() {pos = boost.GetIntPosition(), range = boost.range, boostAmount = boost.boostAmount});
    240.         }
    241.         _gridNative = new NativeArray<int>(_grid, Allocator.TempJob);
    242.         var recalculateBoostJob = new RecalculateBoostJob
    243.         {
    244.             r_IN = r,
    245.             boosters4job_IN = new NativeArray<Booster4Job>(_b4j.ToArray(), Allocator.TempJob),
    246.             baseBliss_IN = baseBliss,
    247.             grid_OUT = _gridNative,
    248.             newTextureMapHalfPointer_INOUT = _newTextureMapHalfPointer
    249.         };
    250.         recalculateBoostJob.Run();
    251.         //unpack
    252.         _gridNative.CopyTo(_grid);
    253.         //clean up
    254.         _gridNative.Dispose();
    255.         // texture
    256.         ___applyTexture.Begin();
    257.         _tex.Apply(true);
    258.         if (testBlit)
    259.             Graphics.Blit(_tex, blissRT);
    260.         else
    261.             Graphics.CopyTexture(_tex, blissRT);
    262.         ___applyTexture.End();
    263.         ___recalculateBliss.End();
    264.     }
    265.  
    266.     [BurstCompile]
    267.     struct RecalculateBoostJob : IJob
    268.     {
    269.         [ReadOnly] public Rect r_IN;
    270.         [DeallocateOnJobCompletion]
    271.         [ReadOnly] public NativeArray<Booster4Job> boosters4job_IN;
    272.         [ReadOnly] public int baseBliss_IN;
    273.         public NativeArray<int> grid_OUT;
    274.         public NativeArray<half> newTextureMapHalfPointer_INOUT;
    275.  
    276.         public void Execute()
    277.         {
    278.             // init the blissmap
    279.             var blissmap_OUT = new NativeArray<int>(HALF_GRID_SIZE * 2 * HALF_GRID_SIZE * 2, Allocator.Temp, NativeArrayOptions.ClearMemory);
    280.             for (int i = 0; i < blissmap_OUT.Length; i++)
    281.                 blissmap_OUT[i] = 0;
    282.             // convert to int and clamp
    283.             int xMin = Mathf.Max((int) r_IN.xMin, -HALF_GRID_SIZE);
    284.             int xMax = Mathf.Min((int) r_IN.xMax, HALF_GRID_SIZE);
    285.             int zMin = Mathf.Max((int) r_IN.yMin, -HALF_GRID_SIZE);
    286.             int zMax = Mathf.Min((int) r_IN.yMax, HALF_GRID_SIZE);
    287.             // go through each booster
    288.             int b;
    289.             for (int i = 0; i < boosters4job_IN.Length; i++)
    290.             {
    291.                 var boost = boosters4job_IN[i];
    292.                 var pos = boost.pos;
    293.                 int cx = (int) pos.x;
    294.                 int dx2 = cx + boost.range + 2;
    295.                 int dx1 = cx - boost.range - 1;
    296.                 int cz = (int) pos.z;
    297.                 int dz2 = cz + boost.range + 2;
    298.                 int dz1 = cz - boost.range - 1;
    299.                 // early exit if the booster isn't within the changed rect
    300.                 if (dx2 < xMin || dz2 < zMin || dx1 > xMax || dz1 > zMax)
    301.                     continue;
    302.                 dx1 = Mathf.Max(dx1, xMin);
    303.                 dx2 = Mathf.Min(dx2, xMax);
    304.                 dz1 = Mathf.Max(dz1, zMin);
    305.                 dz2 = Mathf.Min(dz2, zMax);
    306.                 // convert current boost [-1,+1] to binary mask
    307.                 b = Boost2BlissBitmask(boost.boostAmount);
    308.                 int rangeSqr = boost.range * boost.range;
    309.                 for (int z = dz1; z < dz2; z++)
    310.                 {
    311.                     for (int x = dx1; x < dx2; x++)
    312.                     {
    313.                         int distSqr = (x - cx) * (x - cx) + (z - cz) * (z - cz);
    314.                         if (distSqr < rangeSqr)
    315.                         {
    316.                             var boostCoord = BoostCoord(x, z);
    317.                             // bliss bitwise math
    318.                             blissmap_OUT[boostCoord] = blissmap_OUT[boostCoord] | b;
    319.                         }
    320.                     }
    321.                 }
    322.             }
    323.             // composite pass with base bliss
    324.             b = Boost2BlissBitmask(baseBliss_IN);
    325.             for (int x = xMin; x < xMax; x++)
    326.             {
    327.                 for (int z = zMin; z < zMax; z++)
    328.                 {
    329.                     var boostCoord = BoostCoord(x, z);
    330.                     grid_OUT[boostCoord] = blissmap_OUT[boostCoord] | b;
    331.                 }
    332.             }
    333.             // expand change rect for rendering
    334.             int4 uv = new int4(Mathf.Min(HALF_GRID_SIZE * 2, HALF_GRID_SIZE + xMin), Mathf.Max(0, HALF_GRID_SIZE + xMax), Mathf.Min(HALF_GRID_SIZE * 2, HALF_GRID_SIZE + zMin), Mathf.Max(0, HALF_GRID_SIZE + zMax));
    335.             // texture update
    336.             for (int u = uv.x; u < uv.y; u++)
    337.             {
    338.                 for (int v = uv.z; v < uv.w; v++)
    339.                 {
    340.                     int coord = u + v * HALF_GRID_SIZE * 2;
    341.                     int bliss = BlissBitmask2Boost(grid_OUT[coord]);
    342.                     half newf = (half) (bliss == -1 ? 0 : bliss == 0 ? 0.5 : 1);
    343.                     newTextureMapHalfPointer_INOUT[coord] = newf;
    344.                 }
    345.             }
    346.         }
    347.     }
    348.  
    349.     static int Boost2BlissBitmask(int b)
    350.     {
    351.         if (b > 0)
    352.             return 1;
    353.         if (b < 0)
    354.             return 2;
    355.         return 0;
    356.     }
    357.  
    358.     public static int BoostCoord(int x, int z)
    359.     {
    360.         if (x > HALF_GRID_SIZE - 1) x = HALF_GRID_SIZE - 1;
    361.         if (z > HALF_GRID_SIZE - 1) z = HALF_GRID_SIZE - 1;
    362.         if (x < -(HALF_GRID_SIZE - 1)) x = -(HALF_GRID_SIZE - 1);
    363.         if (z < -(HALF_GRID_SIZE - 1)) z = -(HALF_GRID_SIZE - 1);
    364.         return (x + HALF_GRID_SIZE) + (z + HALF_GRID_SIZE) * HALF_GRID_SIZE * 2;
    365.     }
    366.  
    367.     public int GetBoost(Vector3 pos)
    368.     {
    369.         int x = Mathf.FloorToInt(pos.x + 0.001f);
    370.         int z = Mathf.FloorToInt(pos.z + 0.001f);
    371.         return GetBoost(x, z);
    372.     }
    373.  
    374.     public static int GetBoost(int coord)
    375.     {
    376.         return BlissBitmask2Boost(_grid[coord]);
    377.     }
    378.  
    379.     public int GetBoost(int x, int z)
    380.     {
    381.         if (_grid == null) return 0;
    382.         return BlissBitmask2Boost(_grid[BoostCoord(x, z)]);
    383.     }
    384.  
    385.     static int BlissBitmask2Boost(int b)
    386.     {
    387.         if (b == 1) return 1;
    388.         if (b == 2) return -1;
    389.         return 0;
    390.     }
    391.  
    392.     int ConvertWorldToUVCoordinates(int2 worldCoordinates)
    393.     {
    394.         int result = 0;
    395.         Physics.Raycast(new Vector3(worldCoordinates.x, 1, worldCoordinates.y), Vector3.down, out RaycastHit hit, 2);
    396.         var uv = hit.textureCoord;
    397.         return (int) uv.x + (int) uv.y * HALF_GRID_SIZE * 2;
    398.     }
    399.  
    400.     public void OnDrawGizmosSelected()
    401.     {
    402.         Gizmos.color = Color.yellow;
    403.         Gizmos.DrawWireCube(Vector3.zero, new Vector3(HALF_GRID_SIZE * 2, 1, HALF_GRID_SIZE * 2));
    404.     }
    405.    
    406. // what's on the terrain, optimization
    407.     class BeautyGrid
    408.     {
    409.         public Vector3 position;
    410.         public Vector3 normal;
    411.         public float bliss;
    412.         public GameObject blissObject;
    413.         public ParticleSystem corruptionParticle;
    414.     }
    415.     BeautyGrid[,] _beautyGrid = new BeautyGrid[HALF_GRID_SIZE * 2, HALF_GRID_SIZE * 2];
    416.     RaycastHit _hit = new RaycastHit();
    417.  
    418.     public void ForceRecalculateBoostAndUpdateFoliage()
    419.     {
    420.         RecalculateBoostJobified(new Rect(-HALF_GRID_SIZE, -HALF_GRID_SIZE, HALF_GRID_SIZE * 2, HALF_GRID_SIZE * 2));
    421.     }
    422.  
    423.     void InstantiateBlissObject(int u, int v)
    424.     {
    425.         Vector3 n = _beautyGrid[u, v].normal;
    426.         Quaternion rotation = Quaternion.AngleAxis(Random.value * 360, n);
    427.         Vector3 randomOffset = Vector3.Cross(n, Random.insideUnitSphere);
    428.         GameObject blissObjectInstance = Instantiate(beautifyBliss, _beautyGrid[u, v].position + randomOffset * .3f, rotation) as GameObject;
    429.         //                    Color randomGrassColor = Random.Range(.5f,1)*Color.white;
    430.         //                    randomGrassColor.a = 1;
    431.         //                    blissObjectInstance.renderer.material.color = randomGrassColor;
    432.         blissObjectInstance.transform.parent = transform;
    433.         blissObjectInstance.SetActive(false);
    434.         _beautyGrid[u, v].blissObject = blissObjectInstance;
    435.     }
    436.  
    437.     void InstantiateCorruptionParticle(int u, int v)
    438.     {
    439.         _beautyGrid[u, v].corruptionParticle = (Instantiate(beautyCorrupted.gameObject, _beautyGrid[u, v].position, Quaternion.identity) as GameObject).GetComponent<ParticleSystem>();
    440.         _beautyGrid[u, v].corruptionParticle.enableEmission = false;
    441.         _beautyGrid[u, v].corruptionParticle.gameObject.SetActive(false);
    442.         _beautyGrid[u, v].corruptionParticle.transform.parent = transform;
    443.     }
    444.  
    445. //add or animate grass or lava to match bliss
    446.     void Beautify(int u, int v, float bliss)
    447.     {
    448.         // turn off all particles and grass and skip if beautify is off
    449.         if (!performanceBeautifyEnabled)
    450.         {
    451.             if (_beautyGrid[u, v] != null)
    452.             {
    453.                 if (_beautyGrid[u, v].blissObject)
    454.                     _beautyGrid[u, v].blissObject.SetActive(false);
    455.                 if (_beautyGrid[u, v].corruptionParticle)
    456.                     _beautyGrid[u, v].corruptionParticle.enableEmission = false;
    457.                 _beautyGrid[u, v].bliss = 0;
    458.             }
    459.             return;
    460.         }
    461.         UnityEngine.Profiling.Profiler.BeginSample("beautify");
    462.         //init beautify grid element
    463.         if (_beautyGrid[u, v] == null)
    464.         {
    465.             if (Physics.Raycast(new Vector3(u - HALF_GRID_SIZE + .5f, 30, v - HALF_GRID_SIZE + .5f), -Vector3.up, out _hit, 40, beautyLayer))
    466.             {
    467.                 _beautyGrid[u, v] = new BeautyGrid();
    468.                 _beautyGrid[u, v].position = _hit.point;
    469.                 _beautyGrid[u, v].normal = _hit.normal;
    470.             }
    471.         }
    472.         if (_beautyGrid[u, v] == null)
    473.             return;
    474.         _beautyGrid[u, v].bliss = bliss;
    475.         // calculate scale of bliss grass
    476.         #if UNITY_IOS || UNITY_SWITCH
    477.         // skip every 2 beautification
    478.         if (u % 2 == 0 || v % 2 == 0)
    479.             return;
    480.         #endif
    481.         if (bliss >= 0.7f)
    482.         {
    483.             //bliss
    484.             if (!_beautyGrid[u, v].blissObject)
    485.                 InstantiateBlissObject(u, v);
    486.             _beautyGrid[u, v].blissObject.SetActive(true);
    487.             _beautyGrid[u, v].blissObject.transform.localScale = 2 * (bliss - .5f) * Vector3.one;
    488.         } else
    489.         {
    490.             if (_beautyGrid[u, v].blissObject)
    491.                 _beautyGrid[u, v].blissObject.SetActive(false);
    492.         }
    493.         UnityEngine.Profiling.Profiler.EndSample();
    494.     }
    495.     #if UNITY_IOS || unity_
    496.     const float CORRUPTIONPARTICLEPROBABILITY = 0.990f;
    497.     #else
    498.     const float CORRUPTIONPARTICLEPROBABILITY = 0.95f;
    499.     #endif
    500. }
     
  2. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,329
    what's super weird is that it used to work, i didn't change that code
    the erros happen only when i load async inactive then activate the scene at a later date.