Search Unity

Bug The DebugStream system doesn't clean up properly when the system is destroyed

Discussion in 'Physics for ECS' started by Zec_, Nov 19, 2019.

  1. Zec_

    Zec_

    Joined:
    Feb 9, 2017
    Posts:
    148
    The DebugStream system spawns a GameObject and attaches a DrawComponent MonoBehaviour that points back to the system. This GameObject doesn't get properly cleaned up/destroyed in OnDestroy on the system. It's quite easy to reproduce this by just spawning and destroying worlds that contain the dots physics systems.

    I'm not looking for a solution to this problem, as I know how I would handle cleaning up this object myself. Merely reporting a small and simple bug.
     
    jdtec and steveeHavok like this.
  2. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    857
    Do you know what triggers this? I occasionally get it too but I am unsure what specifically causes it.
     
  3. filod

    filod

    Joined:
    Oct 3, 2015
    Posts:
    224
    i found a simple "solution" to this, just disable "Enter Play Mode Settings", but it's definely a bug
     
  4. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    Thanks, I've logged this issue. It's been bugging me for a while as well.
     
  5. Zec_

    Zec_

    Joined:
    Feb 9, 2017
    Posts:
    148
    This doesn't seem to be handled properly quite yet, so I thought I'd post a suggestion to how you can change your DebugStream system to handle it better. I see it as there being a few issues:
    • The GameObject created isn't properly tagged with HideFlags.DontSave. The created object is therefore often saved inside scenes when running physics systems in the Editor World (outside of Playmode).
    • When destroying the system, the object created by the system isn't cleaned up. We create and destroy worlds occasionally, which can lead to an abundance of these gameobjects.
    I've modified OnUpdate and OnDestroy in my DebugStream.cs in this way:
    upload_2020-9-5_14-41-4.png

    And here's the whole file with the diffs made.
    Code (CSharp):
    1.  
    2. using System.Collections.Generic;
    3. using Unity.Collections;
    4. using Unity.Entities;
    5. using Unity.Mathematics;
    6. using Unity.Physics;
    7. using Unity.Physics.Systems;
    8. using UnityEditor;
    9. using UnityEngine;
    10.  
    11. [UpdateBefore(typeof(BuildPhysicsWorld))]
    12. public class DebugStream : SystemBase
    13. {
    14.     readonly List<NativeStream> m_DebugStreams = new List<NativeStream>();
    15.     DrawComponent m_DrawComponent;
    16.     EndFramePhysicsSystem m_EndFramePhysicsSystem;
    17.  
    18.     protected override void OnCreate()
    19.     {
    20.         m_EndFramePhysicsSystem = World.GetOrCreateSystem<EndFramePhysicsSystem>();
    21.     }
    22.  
    23.     public struct Context
    24.     {
    25.         public void Begin(int index)
    26.         {
    27.             Writer.BeginForEachIndex(index);
    28.         }
    29.  
    30.         public void End()
    31.         {
    32.             Writer.EndForEachIndex();
    33.         }
    34.  
    35.         public void Point(float3 x, float size, Color color)
    36.         {
    37.             Writer.Write(Type.Point);
    38.             Writer.Write(new Point { X = x, Size = size, Color = color });
    39.         }
    40.  
    41.         public void Line(float3 x0, float3 x1, Color color)
    42.         {
    43.             Writer.Write(Type.Line);
    44.             Writer.Write(new Line { X0 = x0, X1 = x1, Color = color });
    45.         }
    46.  
    47.         public void Arrow(float3 x, float3 v, Color color)
    48.         {
    49.             Writer.Write(Type.Arrow);
    50.             Writer.Write(new Line { X0 = x, X1 = x + v, Color = color });
    51.         }
    52.  
    53.         public void Plane(float3 x, float3 v, Color color)
    54.         {
    55.             Writer.Write(Type.Plane);
    56.             Writer.Write(new Line { X0 = x, X1 = x + v, Color = color });
    57.         }
    58.  
    59.         public void Circle(float3 x, float3 v, Color color)
    60.         {
    61.             Writer.Write(Type.Circle);
    62.             Writer.Write(new Line { X0 = x, X1 = x + v, Color = color });
    63.         }
    64.  
    65.         public void Arc(float3 center, float3 normal, float3 arm, float angle, Color color)
    66.         {
    67.             Writer.Write(Type.Arc);
    68.             Writer.Write(new Arc { Center = center, Normal = normal, Arm = arm, Angle = angle, Color = color });
    69.         }
    70.  
    71.         public void Cone(float3 point, float3 axis, float angle, Color color)
    72.         {
    73.             Writer.Write(Type.Cone);
    74.             Writer.Write(new Cone { Point = point, Axis = axis, Angle = angle, Color = color });
    75.         }
    76.  
    77.         public void Box(float3 size, float3 center, quaternion orientation, Color color)
    78.         {
    79.             Writer.Write(Type.Box);
    80.             Writer.Write(new Box { Size = size, Center = center, Orientation = orientation, Color = color });
    81.         }
    82.  
    83.         public void Text(char[] text, float3 x, Color color)
    84.         {
    85.             Writer.Write(Type.Text);
    86.             Writer.Write(new Text { X = x, Color = color, Length = text.Length });
    87.  
    88.             foreach (char c in text)
    89.             {
    90.                 Writer.Write(c);
    91.             }
    92.         }
    93.  
    94.         internal NativeStream.Writer Writer;
    95.     }
    96.  
    97.     public Context GetContext(int foreachCount)
    98.     {
    99.         var stream = new NativeStream(foreachCount, Allocator.TempJob);
    100.         m_DebugStreams.Add(stream);
    101.         return new Context { Writer = stream.AsWriter() };
    102.     }
    103.  
    104.     public enum Type
    105.     {
    106.         Point,
    107.         Line,
    108.         Arrow,
    109.         Plane,
    110.         Circle,
    111.         Arc,
    112.         Cone,
    113.         Text,
    114.         Box
    115.     }
    116.  
    117.     public struct Point
    118.     {
    119.         public float3 X;
    120.         public float Size;
    121.         public Color Color;
    122.  
    123.         public void Draw()
    124.         {
    125. #if UNITY_EDITOR
    126.             Handles.color = Color;
    127.             Handles.DrawLine(X - new float3(Size, 0, 0), X + new float3(Size, 0, 0));
    128.             Handles.DrawLine(X - new float3(0, Size, 0), X + new float3(0, Size, 0));
    129.             Handles.DrawLine(X - new float3(0, 0, Size), X + new float3(0, 0, Size));
    130. #endif
    131.         }
    132.     }
    133.  
    134.     public struct Line
    135.     {
    136.         public float3 X0;
    137.         public float3 X1;
    138.         public Color Color;
    139.  
    140.         public void Draw()
    141.         {
    142. #if UNITY_EDITOR
    143.             Handles.color = Color;
    144.             Handles.DrawLine(X0, X1);
    145. #endif
    146.         }
    147.  
    148.         public void DrawArrow()
    149.         {
    150.             if (!math.all(X0 == X1))
    151.             {
    152. #if UNITY_EDITOR
    153.                 Handles.color = Color;
    154.  
    155.                 Handles.DrawLine(X0, X1);
    156.                 float3 v = X1 - X0;
    157.                 float3 dir;
    158.                 float length = Math.NormalizeWithLength(v, out dir);
    159.                 float3 perp, perp2;
    160.                 Math.CalculatePerpendicularNormalized(dir, out perp, out perp2);
    161.                 float3 scale = length * 0.2f;
    162.  
    163.                 Handles.DrawLine(X1, X1 + (perp - dir) * scale);
    164.                 Handles.DrawLine(X1, X1 - (perp + dir) * scale);
    165.                 Handles.DrawLine(X1, X1 + (perp2 - dir) * scale);
    166.                 Handles.DrawLine(X1, X1 - (perp2 + dir) * scale);
    167. #endif
    168.             }
    169.         }
    170.  
    171.         public void DrawPlane()
    172.         {
    173.             if (!math.all(X0 == X1))
    174.             {
    175. #if UNITY_EDITOR
    176.                 Handles.color = Color;
    177.  
    178.                 Handles.DrawLine(X0, X1);
    179.                 float3 v = X1 - X0;
    180.                 float3 dir;
    181.                 float length = Math.NormalizeWithLength(v, out dir);
    182.                 float3 perp, perp2;
    183.                 Math.CalculatePerpendicularNormalized(dir, out perp, out perp2);
    184.                 float3 scale = length * 0.2f;
    185.  
    186.                 Handles.DrawLine(X1, X1 + (perp - dir) * scale);
    187.                 Handles.DrawLine(X1, X1 - (perp + dir) * scale);
    188.                 Handles.DrawLine(X1, X1 + (perp2 - dir) * scale);
    189.                 Handles.DrawLine(X1, X1 - (perp2 + dir) * scale);
    190.  
    191.                 perp *= length;
    192.                 perp2 *= length;
    193.                 Handles.DrawLine(X0 + perp + perp2, X0 + perp - perp2);
    194.                 Handles.DrawLine(X0 + perp - perp2, X0 - perp - perp2);
    195.                 Handles.DrawLine(X0 - perp - perp2, X0 - perp + perp2);
    196.                 Handles.DrawLine(X0 - perp + perp2, X0 + perp + perp2);
    197. #endif
    198.             }
    199.         }
    200.  
    201.         public void DrawCircle()
    202.         {
    203.             if (!math.all(X0 == X1))
    204.             {
    205. #if UNITY_EDITOR
    206.                 Handles.color = Color;
    207.  
    208.                 float3 v = X1 - X0;
    209.                 float3 dir;
    210.                 float length = Math.NormalizeWithLength(v, out dir);
    211.                 float3 perp, perp2;
    212.                 Math.CalculatePerpendicularNormalized(dir, out perp, out perp2);
    213.                 float3 scale = length * 0.2f;
    214.  
    215.                 const int res = 16;
    216.                 quaternion q = quaternion.AxisAngle(dir, 2.0f * (float)math.PI / res);
    217.                 float3 arm = perp * length;
    218.                 for (int i = 0; i < res; i++)
    219.                 {
    220.                     float3 nextArm = math.mul(q, arm);
    221.                     Handles.DrawLine(X0 + arm, X0 + nextArm);
    222.                     arm = nextArm;
    223.                 }
    224. #endif
    225.             }
    226.         }
    227.     }
    228.  
    229.     public struct Arc
    230.     {
    231.         public float3 Center;
    232.         public float3 Normal;
    233.         public float3 Arm;
    234.         public float Angle;
    235.         public Color Color;
    236.  
    237.         public void Draw()
    238.         {
    239. #if UNITY_EDITOR
    240.             Handles.color = Color;
    241.  
    242.             const int res = 16;
    243.             quaternion q = quaternion.AxisAngle(Normal, Angle / res);
    244.             float3 currentArm = Arm;
    245.             Handles.DrawLine(Center, Center + currentArm);
    246.             for (int i = 0; i < res; i++)
    247.             {
    248.                 float3 nextArm = math.mul(q, currentArm);
    249.                 Handles.DrawLine(Center + currentArm, Center + nextArm);
    250.                 currentArm = nextArm;
    251.             }
    252.             Handles.DrawLine(Center, Center + currentArm);
    253. #endif
    254.         }
    255.     }
    256.  
    257.     public struct Cone
    258.     {
    259.         public float3 Point;
    260.         public float3 Axis;
    261.         public float Angle;
    262.         public Color Color;
    263.  
    264.         public void Draw()
    265.         {
    266. #if UNITY_EDITOR
    267.             Handles.color = Color;
    268.  
    269.             float3 dir;
    270.             float scale = Math.NormalizeWithLength(Axis, out dir);
    271.  
    272.             float3 arm;
    273.             {
    274.                 float3 perp1, perp2;
    275.                 Math.CalculatePerpendicularNormalized(dir, out perp1, out perp2);
    276.                 arm = math.mul(quaternion.AxisAngle(perp1, Angle), dir) * scale;
    277.             }
    278.  
    279.             const int res = 16;
    280.             quaternion q = quaternion.AxisAngle(dir, 2.0f * (float)math.PI / res);
    281.             for (int i = 0; i < res; i++)
    282.             {
    283.                 float3 nextArm = math.mul(q, arm);
    284.                 Handles.DrawLine(Point, Point + arm);
    285.                 Handles.DrawLine(Point + arm, Point + nextArm);
    286.                 arm = nextArm;
    287.             }
    288. #endif
    289.         }
    290.     }
    291.  
    292.     struct Box
    293.     {
    294.         public float3 Size;
    295.         public float3 Center;
    296.         public quaternion Orientation;
    297.         public Color Color;
    298.  
    299.         public void Draw()
    300.         {
    301. #if UNITY_EDITOR
    302.             Matrix4x4 orig = Handles.matrix;
    303.  
    304.             Matrix4x4 mat = Matrix4x4.TRS(Center, Orientation, Vector3.one);
    305.             Handles.matrix = mat;
    306.             Handles.color = Color;
    307.             Handles.DrawWireCube(Vector3.zero, new Vector3(Size.x, Size.y, Size.z));
    308.  
    309.             Handles.matrix = orig;
    310. #endif
    311.         }
    312.     }
    313.  
    314.     struct Text
    315.     {
    316.         public float3 X;
    317.         public Color Color;
    318.         public int Length;
    319.  
    320.         public void Draw(ref NativeStream.Reader reader)
    321.         {
    322.             // Read string data.
    323.             char[] stringBuf = new char[Length];
    324.             for (int i = 0; i < Length; i++)
    325.             {
    326.                 stringBuf[i] = reader.Read<char>();
    327.             }
    328.  
    329.             GUIStyle style = new GUIStyle();
    330.             style.normal.textColor = Color;
    331. #if UNITY_EDITOR
    332.             Handles.Label(X, new string(stringBuf), style);
    333. #endif
    334.         }
    335.     }
    336.  
    337.     private void Draw()
    338.     {
    339.         for (int i = 0; i < m_DebugStreams.Count; i++)
    340.         {
    341.             NativeStream.Reader reader = m_DebugStreams[i].AsReader();
    342.             for (int j = 0; j != reader.ForEachCount; j++)
    343.             {
    344.                 reader.BeginForEachIndex(j);
    345.                 while (reader.RemainingItemCount != 0)
    346.                 {
    347.                     switch (reader.Read<Type>())
    348.                     {
    349.                         case Type.Point: reader.Read<Point>().Draw(); break;
    350.                         case Type.Line: reader.Read<Line>().Draw(); break;
    351.                         case Type.Arrow: reader.Read<Line>().DrawArrow(); break;
    352.                         case Type.Plane: reader.Read<Line>().DrawPlane(); break;
    353.                         case Type.Circle: reader.Read<Line>().DrawCircle(); break;
    354.                         case Type.Arc: reader.Read<Arc>().Draw(); break;
    355.                         case Type.Cone: reader.Read<Cone>().Draw(); break;
    356.                         case Type.Text: reader.Read<Text>().Draw(ref reader); break;
    357.                         case Type.Box: reader.Read<Box>().Draw(); break;
    358.                         default: return; // unknown type
    359.                     }
    360.                 }
    361.                 reader.EndForEachIndex();
    362.             }
    363.         }
    364.     }
    365.  
    366.     private class DrawComponent : MonoBehaviour
    367.     {
    368.         public DebugStream DebugDraw;
    369.  
    370.         public void OnDrawGizmos()
    371.         {
    372.             if (DebugDraw != null)
    373.             {
    374.                 // Make sure all potential debug display jobs are finished
    375.                 DebugDraw.m_EndFramePhysicsSystem.GetOutputDependency().Complete();
    376.                 DebugDraw.Draw();
    377.             }
    378.         }
    379.     }
    380.  
    381.     protected override void OnUpdate()
    382.     {
    383.         // Make sure all potential debug display jobs are finished
    384.         m_EndFramePhysicsSystem.GetOutputDependency().Complete();
    385.  
    386.         // Reset
    387.         for (int i = 0; i < m_DebugStreams.Count; i++)
    388.         {
    389.             m_DebugStreams[i].Dispose();
    390.         }
    391.         m_DebugStreams.Clear();
    392.  
    393.         // Set up component to draw
    394.         if (m_DrawComponent == null)
    395.         {
    396. #if UNITY_EDITOR
    397.             // For the editor, we create the gameobject and component with HideFlags.DontSave to not save it inside scenes.
    398.             GameObject drawObject = EditorUtility.CreateGameObjectWithHideFlags("DebugStream.DrawComponent", HideFlags.DontSave, typeof(DrawComponent));
    399.             m_DrawComponent = drawObject.GetComponent<DrawComponent>();
    400. #else
    401.             // During builds, we just create the gameobject.
    402.             GameObject drawObject = new GameObject();
    403.             m_DrawComponent = drawObject.AddComponent<DrawComponent>();
    404.             m_DrawComponent.name = "DebugStream.DrawComponent";
    405. #endif
    406.             m_DrawComponent.DebugDraw = this;
    407.         }
    408.     }
    409.  
    410.     protected override void OnDestroy()
    411.     {
    412.         // Clean up our created DrawComponent. If we're running the systems outside of playmode, we should destroy with DestroyImmediate.
    413.         if (m_DrawComponent != null)
    414.         {
    415.             if (Application.isPlaying)
    416.                 Object.Destroy(m_DrawComponent.gameObject);
    417.             else
    418.                 Object.DestroyImmediate(m_DrawComponent.gameObject);
    419.             m_DrawComponent = null;
    420.         }
    421.  
    422.         for (int i = 0; i < m_DebugStreams.Count; i++)
    423.             m_DebugStreams[i].Dispose();
    424.         m_DebugStreams.Clear();
    425.     }
    426. }
    427.  
    (Apologies for necroing my old thread, but it was on the exact same subject)
     
  6. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    Thanks for the ping and the changes. I'm pushed these to the repo so you should see them in the next point release.
     
    xseagullx, cultureulterior and Zec_ like this.