Search Unity

StaticBatchingUtility.Combine Broken in Unity 5

Discussion in 'Editor & General Support' started by tripknotix, Mar 6, 2015.

  1. tripknotix

    tripknotix

    Joined:
    Apr 21, 2011
    Posts:
    744
    It seems calling StaticBatchingUtility.Combine() inside of Start() or Awake() methods will not allow the models to be static batched.

    I read in a forum post that static batching is now only called once when the scene loads so that the information is only sent to the gpu once, however my game uses a Custom Map Builder on a mobile device. I relied on calling static batching at run time after the players level information has finished downloading and all objects have been instantiated.

    I vertex color these objects in an Awake based on users predefined choices, then i call static batching utility to combine in a Start method when the map is ready to play inside of.

    is StaticBatchingUtility.Combine() no longer supported in Unity 5? or is this something that needs additional steps on my end to fix? Perhaps its just a bug?

    Also i did test with just setting the objects to static in the inspector, and that works just fine. its only when i try to do it in code that theres a problem.
     
    Last edited: Mar 6, 2015
  2. tripknotix

    tripknotix

    Joined:
    Apr 21, 2011
    Posts:
    744
    Anyone else tried this and had success batching from code in unity 5?
     
  3. Dream_Iverson

    Dream_Iverson

    Joined:
    Sep 22, 2012
    Posts:
    10
    i got the same issue
     
  4. tripknotix

    tripknotix

    Joined:
    Apr 21, 2011
    Posts:
    744
    So there seems to be a change in the order of operations if you instantiate or setactive to an object from an awake, it will force the new objects awake AND start, before the original script even calls its Start.

    Thats probably why they announced last week, that they are changing the order of operations in the future to something more sane.

    in order to get around this issue, i use an Event, and i have a coroutine, basically after ive instantiated all objects, i then call the coroutine which uses an ienumerator and i do "Wait for (5) seconds" at which point, then call the event, and thats when my script for batching is listening to the event. you can do this with just a method call but i have other things attached thats why i used an event
     
  5. droolz

    droolz

    Joined:
    Apr 29, 2009
    Posts:
    34
    StaticBatchingUtility.Combine isn't doing anything for me either, at any point (I've even set it to trigger onKeyDown space).. Manually placed instances batch just fine. tripknotix, are you saying you're able to combine fine, just not in start / awake?
     
  6. tripknotix

    tripknotix

    Joined:
    Apr 21, 2011
    Posts:
    744
    Pretty much, i call an event instead of calling it in the start/awake, some scenes dont have these custom objects, so i have designed it to accept a bool that will batch immediately in the start, since everything is done in the awake of the other objects.
     
  7. droolz

    droolz

    Joined:
    Apr 29, 2009
    Posts:
    34
    Hmm, so ran as simple a test as I could think of:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class TempMove : MonoBehaviour {
    6.  
    7.     private Transform parent;
    8.  
    9.     // Use this for initialization
    10.     void Start () {
    11.         Debug.Log("start");
    12.         parent = new GameObject().transform;
    13.         parent.position = Vector3.zero;
    14.         List<GameObject> objectPrefabs = new List<GameObject>();
    15.         Transform tiles_parent = GameObject.Find("_terrainTiles").transform;
    16.         foreach (Transform child in tiles_parent)
    17.         {
    18.             objectPrefabs.Add(child.gameObject);
    19.         }
    20.  
    21.         for (int i = 0; i < 50; i++)
    22.         {
    23.             Transform newTile = Instantiate(objectPrefabs[Random.Range(0, objectPrefabs.Count)].transform);
    24.             Vector3 temp = Vector3.zero;
    25.             temp.x = Random.Range(-10, 10);
    26.             temp.z = Random.Range(-10, 10);
    27.             newTile.position = temp;
    28.             newTile.parent = parent;
    29.             newTile.gameObject.SetActive(true);
    30.         }
    31.     }
    32.  
    33.     // Update is called once per frame
    34.     void Update() {
    35.         if (Input.GetKeyDown(KeyCode.Space))
    36.         {
    37.             Debug.Log("batching");
    38.             GameObject[] gos = new GameObject[parent.childCount];
    39.             for (int i = 0; i < gos.Length; i++)
    40.             {
    41.                 gos[i] = parent.GetChild(i).gameObject;
    42.             }
    43.             StaticBatchingUtility.Combine(gos, parent.gameObject);
    44.         }
    45.     }
    46. }
    47.  
    This doesn't work. Hitting space I can see the spike in the profiler and the call to the combine function, but nothing actually happens - nothing gets combined, and the meshes remain un-batched.

    • Arranging un-instanced meshes in the scene results in them being batched, so there's no issues with materials etc.
    • The source prefabs are all parented under an empty game object, and set to inactive
    • If I set the source prefabs to active, the source objects are all batched AND the newly instantiated game objects get batched immediately (i.e. not user triggered with the space bar) but they any transform information (makes sense I guess, since they're instantiating an something already batched, and therefore immovable object?)
    Any one have any pointers as to what I'm doing wrong here?

    Cheers,

    Jules
     
    Last edited: Jan 14, 2016
  8. tripknotix

    tripknotix

    Joined:
    Apr 21, 2011
    Posts:
    744
    on line 41, add a debug.log for each object, with their gameobject.name to make sure its actually trying to batchin children
     
  9. droolz

    droolz

    Joined:
    Apr 29, 2009
    Posts:
    34
    Thanks for getting back, yep - reproting the cloned object:
    Not entirely sure what else to try...
     
  10. droolz

    droolz

    Joined:
    Apr 29, 2009
    Posts:
    34
    If anyone fancies a repro, I've attached a package..
     

    Attached Files:

  11. Jordi-Bonastre

    Jordi-Bonastre

    Unity Technologies

    Joined:
    Jul 6, 2015
    Posts:
    102
    I'm trying to import your package in 5.3.1p2 but the compiler returns errors for Constants and SS_Terrain. Did you forget to include those classes into the package?
     
  12. droolz

    droolz

    Joined:
    Apr 29, 2009
    Posts:
    34
    Hi Jordi,

    Please delete the TileControl.cs file, it should run then, Apologies.
     
  13. Jordi-Bonastre

    Jordi-Bonastre

    Unity Technologies

    Joined:
    Jul 6, 2015
    Posts:
    102
  14. Jordi-Bonastre

    Jordi-Bonastre

    Unity Technologies

    Joined:
    Jul 6, 2015
    Posts:
    102
    I send you attached your project with Combine() working correctly on Start. After running your project, I didn't see the text "Combined" on the Mesh Filter.

    An important thing to consider: currently, Combine() is limited to only affect meshes not already batched. This means it is not possible to add more meshes to a previously created batch. Instead, a new batch will be created.

    In practice, this implies that, to get the best results, you need to call Combine() only once per desired batch, after all meshes were added to the scene. Or if not possible, at least try to call it as few times as possible, as every call will likely result in one batch added.

    And remember, there is a requirement for imported meshes to be usable on Combine(): "Read/Write Enabled" flag must be set on mesh import settings.
     

    Attached Files:

  15. Sebioff

    Sebioff

    Joined:
    Dec 22, 2013
    Posts:
    218
    Oh, wow. Has it always been like this or is this some temporary limitation?
    In any case this should really be added to the documentation. Also the part about "Read/Write Enabled" flag. Neither the "Draw Call Batching" docu nor the StaticBatchingUtility.Combine() docu contain that info, and at least for me this is not how I thought it works.

    Edit: judging by the "Batches" statistic and the assigned batch name, I might have misunderstood that sentence. Calling Combine() on MeshA, then calling it later on MeshA + MeshB seems to result in 1 batch.
     
    Last edited: Jan 16, 2016
    ayushvora likes this.
  16. droolz

    droolz

    Joined:
    Apr 29, 2009
    Posts:
    34
    Hi Jordi, thanks very much for looking into this. Looking though your code, I see that you batching the parent of the children that I wanted to batch on start, which works, but isn't what I wanted to achieve: I need to be able to batch groups of randomly chosen objects as and when they are instantiated. Interestingly it got me thinking about the fact that you submit the parent to the combine function, not an array of individual items. Turns out this works. As a code example, from what I posted earlier, changing this:

    Code (CSharp):
    1.     // Update is called once per frame
    2.     void Update() {
    3.         if (Input.GetKeyDown(KeyCode.Space))
    4.         {
    5.             Debug.Log("batching");
    6.             GameObject[] gos = new GameObject[parent.childCount];
    7.             for (int i = 0; i < gos.Length; i++)
    8.             {
    9.                 gos[i] = parent.GetChild(i).gameObject;
    10.             }
    11.             StaticBatchingUtility.Combine(gos, parent.gameObject);
    12.         }
    13.     }
    to just this:

    Code (CSharp):
    1.         if (Input.GetKeyDown(KeyCode.Space))
    2.         {
    3.             Debug.Log("batching");
    4.             StaticBatchingUtility.Combine(parent.gameObject);
    5.         }
    Works. Which strikes me as very odd, since according to the docs it should be ok to pass an array of GO to the function. Is this a bug?

    Full code:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class TempMove : MonoBehaviour
    6. {
    7.  
    8.     private Transform parent;
    9.  
    10.     // Use this for initialization
    11.     void Start()
    12.     {
    13.         Debug.Log("start");
    14.         parent = new GameObject().transform;
    15.         parent.position = Vector3.zero;
    16.         List<GameObject> objectPrefabs = new List<GameObject>();
    17.         Transform tiles_parent = GameObject.Find("_terrainTiles").transform;
    18.         foreach (Transform child in tiles_parent)
    19.         {
    20.             objectPrefabs.Add(child.gameObject);
    21.         }
    22.  
    23.         for (int i = 0; i < 50; i++)
    24.         {
    25.             Transform newTile = Instantiate(objectPrefabs[Random.Range(0, objectPrefabs.Count)].transform);
    26.             Vector3 temp = Vector3.zero;
    27.             temp.x = Random.Range(-10, 10);
    28.             temp.z = Random.Range(-10, 10);
    29.             newTile.position = temp;
    30.             newTile.parent = parent;
    31.             newTile.gameObject.SetActive(true);
    32.         }
    33.     }
    34.  
    35.     // Update is called once per frame
    36.     void Update()
    37.     {
    38.         if (Input.GetKeyDown(KeyCode.Space))
    39.         {
    40.             Debug.Log("batching");
    41.             StaticBatchingUtility.Combine(parent.gameObject);
    42.         }
    43.     }
    44. }
    Cheers, Jules.
     
  17. Jordi-Bonastre

    Jordi-Bonastre

    Unity Technologies

    Joined:
    Jul 6, 2015
    Posts:
    102
    Hi @droolz , if you want to use the call with the GameObjects array, you should have in mind that the GameObjects included in this array, need to be the GameObjects with the MeshFilter.

    Change in your code this line and it should work fine:
    Code (CSharp):
    1. gos[i]=parent.GetChild(i).gameObject;
    to
    Code (CSharp):
    1. gos[i]=parent.GetChild(i).gameObject.GetChild(0);
     
  18. droolz

    droolz

    Joined:
    Apr 29, 2009
    Posts:
    34
    a massive DOH! from me, thanks Jordi, that would almost certainly be the root of the problem I've had all along.
     
    Jordi-Bonastre likes this.
  19. rickypu

    rickypu

    Joined:
    Aug 7, 2013
    Posts:
    9
    does StaticBatchingUtility.Combine() not support in mobile?
    I use this code in assetbundle
    Code (csharp):
    1.  
    2. public class StaticBatching : MonoBehaviour {
    3.  
    4.   public GameObject[] gos;
    5.  
    6.   void Start()
    7.   {
    8.   if (gos == null || gos.Length == 0)
    9.   {
    10.   StaticBatchingUtility.Combine(this.gameObject);
    11.   }
    12.   else
    13.   {
    14.   StaticBatchingUtility.Combine(gos,this.gameObject);
    15.   }
    16.   }
    17. }
    18.  
    it workd in editor but in both iphone or android the staticbatching fail.
     
  20. Antony-Blackett

    Antony-Blackett

    Joined:
    Feb 15, 2011
    Posts:
    1,778
    I'm still having trouble with static batching.

    I call Combine on Start to combine a bunch of the same mesh into a single mesh. Nothing complicated. In the editor at runtime the mesh filters say they are combined but they are still a single draw call each which strikes me as odd...
     
  21. Antony-Blackett

    Antony-Blackett

    Joined:
    Feb 15, 2011
    Posts:
    1,778
    Follow up:

    I've noticed that static batching works for some things in my game but not others. I can't seem to pinpoint why. Is there a vert limit to static batching or any other kind of requirement or limitation to the meshes i'm not aware of? All the import settings for the meshes that work and the meshes that do not are the same...
     
  22. Antony-Blackett

    Antony-Blackett

    Joined:
    Feb 15, 2011
    Posts:
    1,778
    Follow up 2:

    You can see in the frame debugger that there are 4 static batches. I would have thought all the objects would all be static batched at once but they seem to be static batching in pairs...

    Screen Shot 2016-05-30 at 12.16.13 PM.png
     
  23. fffMalzbier

    fffMalzbier

    Joined:
    Jun 14, 2011
    Posts:
    3,276
    I hope this clears it up for you.
    http://docs.unity3d.com/Manual/DrawCallBatching.html

    Ps: Love your game Mini Golf MatchUp
     
  24. Antony-Blackett

    Antony-Blackett

    Joined:
    Feb 15, 2011
    Posts:
    1,778
    Oh ok, thanks a bunch.

    Cheers for playing Mini Golf. :)
     
  25. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,820
    How is using the StaticBatchingUtility any different than just marking the gameobjects as Batching Static in the editor?
    Or is this only useful after instantiating objects at runtime which are static?
     
  26. fffMalzbier

    fffMalzbier

    Joined:
    Jun 14, 2011
    Posts:
    3,276
    It is for anything that can not be set to static inside the editor for one reason or another.
    Procedural levels or runtime generated content and models loaded from assetbundles can this way be batched.
    For any objects that are set to static inside the editor, this is not needed.
     
    ArachnidAnimal likes this.
  27. tripknotix

    tripknotix

    Joined:
    Apr 21, 2011
    Posts:
    744
    I used it for editing vertex colors of my houses, so that its the same prefab, with different color roofs, carpet and walls
     
  28. Antony-Blackett

    Antony-Blackett

    Joined:
    Feb 15, 2011
    Posts:
    1,778
    I don't know i this is still the case but making things static at edit time use to combine the meshes at build time creating copies of the mesh data in each scene and as a result blowout your build size which was a problem for mobile. I haven't checked if this is still the case.
     
  29. tripknotix

    tripknotix

    Joined:
    Apr 21, 2011
    Posts:
    744
    yes unity still creates copies, that does still happen, so that would definitely help to batch at run time for that reason
     
  30. ayushvora

    ayushvora

    Joined:
    Jul 1, 2016
    Posts:
    12
    +1
    This must be documented IMO too.
     
  31. Cliwo

    Cliwo

    Joined:
    Oct 12, 2016
    Posts:
    12
    Last edited: Aug 1, 2017
  32. chrismarch

    chrismarch

    Joined:
    Jul 24, 2013
    Posts:
    472
  33. yotcho

    yotcho

    Joined:
    Feb 11, 2013
    Posts:
    3
    In 2018.1.1f1 it is.
    Just fixed the issues by clicking the toggle.
    Thanks Jordi.