Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Feedback Disabling built-in package doesn't remove function calls in profiler

Discussion in 'Editor & General Support' started by Pitou22, Jun 14, 2023.

  1. Pitou22

    Pitou22

    Joined:
    Sep 23, 2015
    Posts:
    66
    Hi all,

    When profiling my project, I noticed calls to:
    - EarlyUpdate.Physics2DEarlyUpdate
    - FixedUpdate.Physics2DFixedUpdate (0.003ms)
    - FixedUpdate.PhysicsClothFixedUpdate
    - FixedUpdate.DirectorFixedUpdatePostPhysics
    - PreUpdate.Physics2DUpdate
    - PreUpdate.AIUpdate (0.003ms)
    - PreUpdate.WindUpdate
    - PreUpdate.UpdateVideo
    - Update.DirectorUpdate
    - PreLateUpdate.AIUpdatePostScript
    - PreLateUpdate.DirectorUpdateAnimationBegin
    - PreLateUpdate.DirectorUpdateAnimationEnd
    - PreLateUpdate.Physics2DLateUpdate (0.002ms)
    - PostLateUpdate.AIUpdatePostScript
    - PostLateUpdate.DirectorLateUpdate
    - PostLateUpdate.PhysicsSkinnedClothBeginUpdate
    - PostLateUpate.UpdateVideoTextures
    - PostLateUpate.UpdateVideo
    - PostLateUpate.DirectorRenderImage
    - PostLateUpate.PhysicsSkinnedClothFinshUpdate
    (and maybe more)

    I know it's only 0.001ms to 0.003ms each (this is negligable) but there is 15+ useless calls and I don't use Cloth, 2D Physics, AI navigation, Director nor Wind built-in packages. I went to the built-in package list and disabled the packages I don't use (https://docs.unity3d.com/Manual/upm-ui-disable.html).

    After disabling them, the calls are still present in the editor and in builds. It does the same with a fresh new empty project and an empty scene. Tested with 2022.3.1f1 on Windows 10, Mono and IL2CPP with max stripping.

    What's the point of disabling built-in package if it doesn't remove this useless overhead?


    Related post (more focused on build size than runtime performance): https://forum.unity.com/threads/exclude-packages-from-build.780521/
    https://issuetracker.unity3d.com/is...when-they-are-disabled-in-the-package-manager (the fact it's closed "by-design" irritates me a bit...)
     
    Last edited: Oct 11, 2023
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,234
  3. Pitou22

    Pitou22

    Joined:
    Sep 23, 2015
    Posts:
    66
    Thank you!

    I didn't know much about this API.

    After your message, I found your thread about the PlayerLoop API (https://forum.unity.com/threads/prevent-custom-update-loop-running-post-playmode.1256403/) with some usefull informations and links.

    I managed to remove these useless calls using this script:
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.LowLevel;
    4. using UnityEngine.PlayerLoop;
    5.  
    6. public static class PlayerLoopCleaner
    7. {
    8.     private static readonly Type[] typesToRemove = new Type[] {
    9.         // Physics 2D
    10. #if UNITY_2022_2_OR_NEWER
    11.         typeof(EarlyUpdate.Physics2DEarlyUpdate),
    12. #endif
    13.         typeof(FixedUpdate.Physics2DFixedUpdate),
    14.         typeof(PreUpdate.Physics2DUpdate),
    15.         typeof(PreLateUpdate.Physics2DLateUpdate),
    16.  
    17.         // Director
    18.         typeof(Initialization.DirectorSampleTime),
    19.         typeof(FixedUpdate.DirectorFixedSampleTime),
    20.         typeof(FixedUpdate.DirectorFixedUpdate),
    21.         typeof(FixedUpdate.DirectorFixedUpdatePostPhysics),
    22.         typeof(Update.DirectorUpdate),
    23.         typeof(PreLateUpdate.DirectorUpdateAnimationBegin),
    24.         typeof(PreLateUpdate.DirectorUpdateAnimationEnd),
    25.         typeof(PreLateUpdate.DirectorDeferredEvaluate),
    26.         typeof(PostLateUpdate.DirectorLateUpdate),
    27.         typeof(PostLateUpdate.DirectorRenderImage),
    28.  
    29.         // AI
    30.         typeof(PreUpdate.AIUpdate),
    31.         typeof(PreLateUpdate.AIUpdatePostScript),
    32.  
    33.         // Wind
    34.         typeof(PreUpdate.WindUpdate),
    35.  
    36.         // Cloth
    37.         typeof(PostLateUpdate.PhysicsSkinnedClothBeginUpdate),
    38.         typeof(PostLateUpdate.PhysicsSkinnedClothFinishUpdate),
    39.    
    40.         // Old network system
    41.         typeof(PreLateUpdate.UpdateNetworkManager)
    42.     };
    43.  
    44.     private static readonly string[] typesToRemoveAsString = new string[] {
    45.         // Cloth internal types
    46.         "UnityEngine.PlayerLoop.FixedUpdate+PhysicsClothFixedUpdate",
    47.         "UnityEngine.PlayerLoop.PreUpdate+PhysicsClothUpdate"
    48.     };
    49.  
    50.  
    51.     [RuntimeInitializeOnLoadMethod]
    52.     private static void RemoveUnusedPackageFromPlayerLoop()
    53.     {
    54.         PlayerLoopSystem playerLoop = PlayerLoop.GetCurrentPlayerLoop();
    55.         //DisplayRecursivly(0, playerLoop);
    56.         foreach(Type type in typesToRemove)
    57.         {
    58.             TryRemoveTypeFrom(ref playerLoop, type);
    59.         }
    60.         foreach (string type in typesToRemoveAsString) {
    61.             TryRemoveTypeFrom(ref playerLoop, type);
    62.         }
    63.         //DisplayRecursivly(10, playerLoop);
    64.         PlayerLoop.SetPlayerLoop(playerLoop);
    65.     }
    66.  
    67.     private static void DisplayRecursivly(int level, PlayerLoopSystem system)
    68.     {
    69.         Debug.Log(level + " " + system.type);
    70.         if (system.subSystemList != null)
    71.         {
    72.             Debug.Log(level + " subSystemList.Length: " + system.subSystemList.Length);
    73.             foreach (PlayerLoopSystem sys in system.subSystemList)
    74.             {
    75.                 DisplayRecursivly(level + 1, sys);
    76.             }
    77.         }
    78.     }
    79.  
    80.     /// <summary>
    81.     /// From https://github.com/Baste-RainGames/PlayerLoopInterface/blob/a4c15199ecb5f88b8a5009d0c391b967d768068c/Runtime/PlayerLoopInterface.cs#L136
    82.     /// </summary>
    83.     /// <param name="currentSystem"></param>
    84.     /// <param name="type"></param>
    85.     /// <returns></returns>
    86.     private static bool TryRemoveTypeFrom(ref PlayerLoopSystem currentSystem, Type type)
    87.     {
    88.         PlayerLoopSystem[] subSystems = currentSystem.subSystemList;
    89.         if (subSystems == null)
    90.         {
    91.             return false;
    92.         }
    93.  
    94.         for (int i = 0; i < subSystems.Length; i++)
    95.         {
    96.             if (subSystems[i].type == type)
    97.             {
    98.                 PlayerLoopSystem[] newSubSystems = new PlayerLoopSystem[subSystems.Length - 1];
    99.  
    100.                 Array.Copy(subSystems, newSubSystems, i);
    101.                 Array.Copy(subSystems, i + 1, newSubSystems, i, subSystems.Length - i - 1);
    102.  
    103.                 currentSystem.subSystemList = newSubSystems;
    104.  
    105.                 return true;
    106.             }
    107.  
    108.             if (TryRemoveTypeFrom(ref subSystems[i], type))
    109.             {
    110.                 return true;
    111.             }
    112.         }
    113.  
    114.         return false;
    115.     }
    116.  
    117.     private static bool TryRemoveTypeFrom(ref PlayerLoopSystem currentSystem, string type)
    118.     {
    119.         PlayerLoopSystem[] subSystems = currentSystem.subSystemList;
    120.         if (subSystems == null) {
    121.             return false;
    122.         }
    123.  
    124.         for (int i = 0; i < subSystems.Length; i++) {
    125.             if (subSystems[i].type.ToString() == type) {
    126.                 PlayerLoopSystem[] newSubSystems = new PlayerLoopSystem[subSystems.Length - 1];
    127.  
    128.                 Array.Copy(subSystems, newSubSystems, i);
    129.                 Array.Copy(subSystems, i + 1, newSubSystems, i, subSystems.Length - i - 1);
    130.  
    131.                 currentSystem.subSystemList = newSubSystems;
    132.  
    133.                 return true;
    134.             }
    135.  
    136.             if (TryRemoveTypeFrom(ref subSystems[i], type)) {
    137.                 return true;
    138.             }
    139.         }
    140.  
    141.         return false;
    142.     }
    143. }
    144.  
    I benchmarked on an empty project with empty scene and just the script above. It sems to gain 0.050ms (on an old i5 3rd gen 4 cores 4 threads @3.10GHz). I'm not sure it's worth the time to develop this, but I least I have learned something :)

    Now I have two more questions:
    - Why the default PlayerLoop isn't nearly empty by default and each built-in package add their system in it? So if the package is not present, no useless calls.
    - Why are some cloth struct internal access only (https://github.com/Unity-Technologi...Export/PlayerLoop/PlayerLoop.bindings.cs#L141 and https://github.com/Unity-Technologi...port/PlayerLoop/PlayerLoop.bindings.cs#L158)? (i couldn't use them in my script, but I can still check with string comparaison on type to bypass this limitation)
     
    Last edited: Oct 11, 2023
    DebugLogError and _eternal like this.
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,234
    Because there's a lot more that can go wrong here than just having all the player loop systems present by default. The overhead of having them is completely negligible in 99% of cases.