Search Unity

Android Performance

Discussion in 'Entity Component System' started by JooleanLogic, Feb 10, 2019.

  1. JooleanLogic

    JooleanLogic

    Joined:
    Mar 1, 2018
    Posts:
    447
    I've just gotten around to testing my 2d game on Android and I get 10fps.

    Luckily it's hybrid using sprites, tilemaps and physics2d so I could just disable the auto world creation to see whether the issue was non-ecs related but it's not cos disabling it gets me back up to 60fps.
    It's possible it's my own code but the scene is very simple atm with only 60 entities. About 50 systems, maybe half jobified but there's not a lot going on and nothing computationally heavy.

    Android perf has been discussed before but the last official comment I could find related to it was a year ago here.

    Is anyone building ecs to android and not having perf issues?
     
  2. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Have you tried attaching the profiler and gotten an exact overview of where performance is going?
     
  3. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    451
    I was targetting android and had about 25 FPS for "game" with more than 100 enemies following player and most of perfirperfo was used by navy mech agent. I was not using jobified version. My phone is average. I have Xiaomi redmi note 4
     
  4. JooleanLogic

    JooleanLogic

    Joined:
    Mar 1, 2018
    Posts:
    447
    Ok thanks Joachim, you forced me to try get profiler working again with success this time. Hallelujah. I'm on linux and had given up on it weeks ago.

    Right so it looks like it's exceptioning on every job and then bogging down on all the exception handling.
    In Profiler, there's a WaitForJobGroupID under one of the normal ComponentSystems and within this are some jobs that all contain multiple StackTraceUtility functions each taking up to 18ms and allocating 80kb. The whole thing alone is taking ~200ms.
    The actual ExecuteJobFunction.Invoke() calls are sub millisecond though.

    I commented out all jobs (~20) and got no errors and normal framerate.
    Uncommenting just the most simple job produced a NullReference Exception which I can't make sense of. All the jobs seem to throw this exact same error though which is the cause of the problem. Below is the job followed by the exception.
    Job System
    Code (CSharp):
    1. public class EngineInputApplicatorSystem : JobComponentSystem
    2. {
    3.     [BurstCompile]
    4.     struct Job : IJobProcessComponentData<Engine>
    5.     {
    6.         public void Execute(ref Engine engine)
    7.         {
    8.             engine.power = engine.inputPower;
    9.         }
    10.     }
    11.  
    12.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    13.     {
    14.         return new Job().Schedule(this, inputDeps);
    15.     }
    16. }
    Exception
    Code (CSharp):
    1. <i>AndroidPlayer(ADB@127.0.0.1:34999)</i> NullReferenceException: Object reference not set to an instance of an object
    2.   at Unity.Entities.JobProcessComponentDataExtensions+JobStruct_Process_D`2[T,U0].ExecuteChunk (Unity.Entities.JobProcessComponentDataExtensions+JobStruct_Process_D`2[T,U0]& jobData, System.IntPtr bufferRangePatchData, System.Int32 begin, System.Int32 end) [0x00083] in /home/joolean/Documents/Projects/Unity/TransporterECS/Library/PackageCache/com.unity.entities@0.0.12-preview.23/Unity.Entities/IJobProcessComponentData.generated.cs:408
    3.   at Unity.Entities.JobProcessComponentDataExtensions+JobStruct_Process_D`2[T,U0].Execute (Unity.Entities.JobProcessComponentDataExtensions+JobStruct_Process_D`2[T,U0]& jobData, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) [0x00031] in /home/joolean/Documents/Projects/Unity/TransporterECS/Library/PackageCache/com.unity.entities@0.0.12-preview.23/Unity.Entities/IJobProcessComponentData.generated.cs:378
    4.   at (wrapper delegate-invoke) Unity.Entities.JobProcessComponentDataExtensions+JobStruct_Process_D`2+ExecuteJobFunction[com.jooleanlogic.transporter.system.EngineInputApplicatorSystem+Job,com.jooleanlogic.transporter.component.Engine].invoke_void_JobProcessComponentDataExtensions/JobStruct_Process_D`2<T, U0>&_intptr_intptr_JobRanges&_int(Unity.Entities.JobProcessComponentDataExtensions/JobStruct_Process_D`2<com.jooleanlogic.transporter.system.EngineInputApplicatorSystem/Job, com.jooleanlogic.transporter.component.Engine>&,intptr,intptr,Unity.Jobs.LowLevel.Unsafe.JobRanges&,int)
    5. (Filename: /home/joolean/Documents/Projects/Unity/TransporterECS/Library/PackageCache/com.unity.entities@0.0.12-preview.23/Unity.Entities/IJobProcessComponentData.generated.cs Line: 408)
     
  5. JooleanLogic

    JooleanLogic

    Joined:
    Mar 1, 2018
    Posts:
    447
    Thanks for feedback Micz.
    From previous posts on this issue, I assumed it was an internal ecs problem waiting for a fix but looks like performance might be ok and is instead a code error somewhere which would be good news.

    One thing I noticed was performance dropped bigly when I switched my sprites to RenderMesh quad's which I discovered was due to no batching on RenderMesh. Makes a huge difference if you're doing 2d games.
     
  6. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235

    I am still pretty new to all of this myself, but it looks like with this below:

    Code (CSharp):
    1.         public void Execute(ref Engine engine)
    2.         {
    3.             engine.power = engine.inputPower;
    4.         }
    You are assigning it to inputPower, but where is the inputPower coming from / being assigned? I know you are trying to pull it from the current value of the Engine ComponentData, but in general, I am assuming that inputPower is getting it's value in another system?

    If you want it to actually be the value that is contained within engine.inputPower, I am interested to see if this might make a difference?

    Code (CSharp):
    1. public class EngineInputApplicatorSystem : JobComponentSystem
    2. {
    3.     [BurstCompile]
    4.     struct Job : IJobProcessComponentData<Engine>
    5.     {
    6.         public Engine engineValue;
    7.         public void Execute(ref Engine engine)
    8.         {
    9.             engineValue = engine;
    10.             engine.power = engineValue.inputPower;
    11.         }
    12.     }
    13.  
    14.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    15.     {
    16.         return new Job().Schedule(this, inputDeps);
    17.     }
    18. }
    19.  
    Again, I am fairly new to this, so I could be mistaken that it is needed, but that is how I was going about things. Either that or I was passing in a value through the OnUpdate method. If i am incorrect about this, someone please do correct me, lol.

    When it comes to trying to figure out performance stuff, definitely make sure you disable the Job Debugger and Leak detection in the Jobs menu, Leak Detection turns itself on each time you hit play and can be a bit of a performance sucker. Only mentioning it because I ran into it the other day wondering what in the world was up with my performance. I turned it off and bam, everything was great.
     
    Last edited: Feb 10, 2019
  7. JooleanLogic

    JooleanLogic

    Joined:
    Mar 1, 2018
    Posts:
    447
    Thanks for response MostHated.
    The ecs machinery behind IJobProcessComponentData takes care of passing in the Engine component to the Execute function. You don't have to pass that data in yourself via OnUpdate like you do with other job types.

    I don't think there's anything wrong with this particular job, it was just to show that something more fundamental is going wrong with jobs on Android. More probably it's my own bug somewhere else that is cascading into and breaking the job system. It works on pc though and the NullReference exceptions are coming from Unity's code so they're not much use for diagnosing.
    I'll have to strip it back gradually to find out but that's a brutally slow process on Android.