Search Unity

Resolved my game is Bursting to flame

Discussion in 'Burst' started by laurentlavigne, Apr 17, 2021.

  1. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,325
    I kept getting these and they don't seem to crash the game so I ignored them
    upload_2021-4-16_16-3-15.png
    but today I got those and since there are more words I think it's got more destructive potential
    upload_2021-4-16_16-3-0.png

    Since I converted this thing to burst without really understand all these allocation stories I have no idea how to fix that :cool:, wait wrong emoji, :oops:

    I don't really have the time to understand all the intricacies of the job system or good non managed programming because, well, everything's on fire and the deadline is almost there, so I was wondering if anyone of you kind souls could sift through this code (aka beauty) and tell me how it's done.

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

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,683
    You never dispose this array and thus have not disposed native array and thus memory leak.

    upload_2021-4-17_2-27-25.png

    You allocate persistent memory for
    _newTextureMapHalfPointer 
    and not use it but just override it by another native array from
    _tex.GetPixelData<half>(0);
    and thus wave not disposed initial native array and thus memory leak.

    upload_2021-4-17_2-29-10.png
     
    tmonestudio and laurentlavigne like this.
  3. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,325
    @eizenhorn you're the superhero of DOTS!! And in 20 minutes! Dude I cannot wait to play your game and see what *that*'s like.

    For record keeping:
    Is solved with that
    upload_2021-4-16_17-29-27.png
    I initially did a boosters4job_IN.Dispose() at the end of the job but the console told me to use that attribute instead - good job on the error messages!

    Is solved by nuking the "new NativeArray" line.
    I was copy pasting this from the doc, aren't pointers memory address? Weird that they can be allocated.
     
  4. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,683
    The issue here not in "can or can not" it's just
    half
    value, problem is - you allocated array, you reserved a block of memory for data. But on
    _newTextureMapHalfPointer = _tex.GetPixelData<half>(0);
    you set to this variable different value - different native array, thus it variable not stores reference to initial native array, an your reserved initially block of memory just stuck, no one uses it, no one dispose it, thus safety system saves you from having a memory leak.
     
  5. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,325
    I still don't understand, maybe that's the difference from managed memory where that sort of stuff would get discarded by the garbage collector.
     
  6. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,683
    Yes it is. This memory developer should manage by himself, this is why safety system was made, for safe user from himself.