Search Unity

Static and dynamic batching with Unity PSM runtime (or lack thereof ?)

Discussion in 'PSM' started by pansapiens, Jan 16, 2015.

  1. pansapiens

    pansapiens

    Joined:
    Aug 11, 2012
    Posts:
    6
    My game renders a grid of ~1000+ quad meshes, each as an individual GameObject (all at the same transform scale, as children of a container object, all marked static).

    The quads all share a material and are normally batched by Unity so that rendering performance on most platforms is acceptable.

    In Editor, Desktop and Android builds dynamic batching alone brings the draw calls down to ~30 per frame, dynamic + static batching to ~20 (about ~2000 draw calls are saved by batching).

    However on the draw calls on PS Vita are ~ 2000 per frame. The profiler reports no dynamic or static batching is occurring when running on the PS Vita device. Any ideas ?

    I noticed that @hippocoder previously mentioned PSM on Vita doesn't support dynamic batching ( http://forum.unity3d.com/threads/reducing-draw-call.267474/ ) - I haven't seen this confirmed anywhere by a Sony rep and the official docs don't seem to mention it (eg https://psm.playstation.net/static/.../Documentation/Manual/PSMProjectSettings.html happily talks about how batching will reduce draw calls). Is batching (both static and dynamic) really not supported ? It could turn out to be a bit of a showstopper for my port, given the amount of work it will take to change the way levels are rendered.
     
    Last edited: Jan 16, 2015
  2. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Dynamic batching doesn't exist on vita, but static batching is alive and well. The reason for this is that draw calls are offloaded to another core, so they're much higher performance than draw calls normally would be - in fact for most situations, slightly quicker than if you used dynamic batching. This is not a showstopper, it's an improvement.

    I'm quite sure static batching is actually supported though. If you find it isn't working, it's probably down to the number of different materials you have. Turn off dynamic batching on the desktop version - the numbers should align then. If static batching still apparently isn't working for you, file a bug.

    I am on alpha/beta testing and Sony dev net so I'm able to get replies from the actual developers, so there you have it (this is a general Unity question, so I figured I'd answer it).

    So in short, you don't want dynamic batching on vita, it's faster without, and static batching should still work.

    Despite this, your method is a disaster for any mobile device (1000 different quad gameobjects), including stronger ones than the Vita. You should be thinking of other ways, regardless, including building your own meshes at runtime or even abusing the particle system.
     
  3. pansapiens

    pansapiens

    Joined:
    Aug 11, 2012
    Posts:
    6
    Thanks for clarifying @hippocoder. With some more testing, I find that with static batching only (dynamic batching off) the Editor shows 30 draw calls (2469 saved by batching), while on the Vita the same settings give 1831 draw calls.
    However calling StaticBatchingUtility.Combine(gameObject) explicitly on the parent does the job and now things are static batched as expected on the Vita (30 draw calls).

    This results in 3 - 5 fps improvement to about 15 fps, but as you guessed this isn't enough to get me over the line for Vita (~25 fps would be tolerable for this particular game) - even with static batching working, 65 - 75 % of the time each frame is still spent on rendering, and about half of that is frustum culling.
    The grid GameObjects are created individually during Awake (the level grid is not a pre-existing prefab) - I wonder if this is related to why the automatic static batching isn't working on Vita. Not sure if this is expected behaviour or bug-report worthy.

    I could indeed construct large parts of the level using a much smaller number of quads, it just hasn't been necessary until now - I'll have to consider my options.
    Since these numbers are worst case for a few of the larger levels, another option would be to just reduce the size of those levels :p There is a chance I can do this without compromising the design (it might even improve it :) ).

    Cheers !
     
  4. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Sorry it wasn't enough to get you over the line! Mobile is rough isn't it :D

    apart from that could you file a bug with static batching? maybe a tiny example project attached, because static batching should really be working...
     
  5. pansapiens

    pansapiens

    Joined:
    Aug 11, 2012
    Posts:
    6
    I believe I've narrowed down what's going on, and it's not a bug but a combination of Runtime and Editor differences and my misunderstanding of when and how automatic static batching works.

    In the Editor, when a group of identical child GameObjects is instantiated in Awake and marked static before the first frame (rather than existing as prefabs already in the scene), static batching is automatically applied. However, if they are instantiated in Start, automatic static batching won't be applied (unless StaticBatchingUtility.Combine is called explicitly). This appears to be normal behavior and with my test case I was able to confirm that this is consistent between the Unity 4.3.7 PSM and Unity 4.6.0f1 Editors.

    Now when running a build outside the Editor the situation is different - when the group of static GameObjects is generated in Awake (or Start), they do not static batch, even if automatic static batching is enabled in the Player settings. This is the case for PSM on Vita, but also on a regular Windows desktop build. I simply hadn't noticed the issue on other platforms because dynamic batching was working and doing the job instead - it was only when I started porting to PSM where dynamic batching isn't available that I noticed the issue with static batching in my own code.

    Just to be clear for those that stumble on this post, I made a little summary table of when automatic static batching works in this scenario:
    • Static batching is enabled in the Player settings.
    • A set of identical GameObjects are instantiated by a script before the first frame is rendered (eg quads with the same material).
    • GameObjects are marked as static using isStatic = true and are all children of a single container GameObject in the hierarchy.
    Code (csharp):
    1.            
    2.             Awake()   Start()    After calling StaticBatchCombineUtility.Combine
    3. Editor       yes       no         yes
    4. Runtime      no        no         yes
    5.  
    I guess the moral of the story is don't trust any quick and dirty profiling you might do in the Editor. Yes, we know profiling in the Editor isn't all that useful, but sometimes it's handy for a quick first look - but don't be mislead: Editor != Runtime by any stretch of the imagination !

    ----
    And for reference, here's my test script:
    Code (csharp):
    1.  
    2. /*
    3. *  A script to generate a grid of identical GameObjects for
    4. *  the purpose of testing the conditions required for automatic
    5. *  static batching.
    6. *  Attach this to an empty GameObject and assign the QuadPrefab
    7. *  field to a prefab of a Quad with a simple material.
    8. *
    9. *  Ensure "Static Batching" is enabled and "Dynamic Batching"
    10. *  is disabled in the Player settings. Examine the Draw Calls
    11. *  in the Game window Stats display, and also note the number
    12. *  saved by batching when using different settings (Awake vs.
    13. *  Start).
    14. *
    15. *  Note that the Editor and Runtime builds behave differently.
    16. *  Using Unity 4.3.7 (PSM) and 4.6.0f1, with Windows and PSM builds,
    17. *  this is when static batching works:
    18. *
    19. *              Awake()   Start()    After calling StaticBatchingUtility.Combine
    20. *  Editor      yes       no         yes
    21. *  Runtime     no        no         yes
    22. *
    23. */
    24. using UnityEngine;
    25. using System.Collections;
    26.  
    27. public class MakeManyQuads : MonoBehaviour
    28. {
    29.  
    30.     public int GridSize = 16;
    31.     public enum InitializerMethod { Awake, Start };
    32.     public InitializerMethod BuildIn;
    33.     public bool StaticBatchCombineAfterBuild = false;
    34.     public GameObject QuadPrefab;
    35.  
    36.     void Awake()
    37.     {
    38.         if (BuildIn != InitializerMethod.Awake) return;
    39.         Init();
    40.     }
    41.  
    42.     void Start()
    43.     {
    44.         if (BuildIn != InitializerMethod.Start) return;
    45.         Init();
    46.     }
    47.  
    48.     void Init()
    49.     {
    50.         BuildGrid();
    51.         PositionCamera();
    52.         if (StaticBatchCombineAfterBuild) StaticBatchingUtility.Combine(gameObject);
    53.     }
    54.  
    55.     void BuildGrid()
    56.     {
    57.         for (int y = 0; y < GridSize; y++)
    58.         {
    59.             for (int x = 0; x < GridSize; x++)
    60.             {
    61.                 var cell = Instantiate(QuadPrefab, new Vector3(x, y, 0f), Quaternion.identity) as GameObject;
    62.                 cell.transform.parent = transform;
    63.                 cell.isStatic = true;
    64.             }
    65.         }
    66.     }
    67.  
    68.     void PositionCamera()
    69.     {
    70.         float center = (GridSize / 2f) - 0.5f;
    71.         Camera.main.transform.position = new Vector3(center, center, -10f);
    72.         Camera.main.orthographic = true;
    73.         Camera.main.orthographicSize = (GridSize / 2) + 1f;
    74.     }
    75. }
    76.  
     
    hippocoder likes this.
  6. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Glad you got all that cleared up. Static batching can actually be a loss of performance on Vita or systems where your CPU time is precious. In 4.x (probably fixed in 5.x) it still has to check culling for each and every object marked static since this is how it keeps both the draw call and polygon count down.

    In our situation we simply hijacked the editor build process so when you pressed run or made a build, we made a pass through the scene and collected meshes sharing same material into grid cells of chunky batches. This reduced draw call average from 700 to 150 or so and led to excellent performance just letting unity cull it and ignoring overdraw because vita uses a PowerVR gpu, which is tile deferred and excellent at reducing overdraw for opaque shaded surfaces.

    In this scenario we wanted to avoid umbra because that itself has a big overhead in 4.3 on vita. I don't know what the situation is on 5 for that. Further gains (which were not necessary for our project) could come from manually culling these cells.

    Another consideration is that when using static batching, it really should be for infrequent high poly objects rather than lots of rocks or blades of grass or identical objects. Stuff like this needs manually batching into cells or your culling time goes insane and ram consumption is large.

    Static batching reduced it to around 70-80 however led to slower framerates as the time spent culling shot right up, so our custom batching solution at editor time was the way to go for 4.3. Since then though, there's been major strides at unity regarding culling performance and probably a couple of static batching performance increases so I would not throw the baby out with the bath water just yet and adopt my approach. Instead would wait for 5 before doing anything drastic. I'm merely adding this information just in case it's of worth to you or anyone.
     
    blackbird likes this.