Search Unity

Meshes using way more data than expected / calculated

Discussion in 'Editor & General Support' started by joshuacwilde, Sep 22, 2018.

  1. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    So I've been recently working on a mobile game that uses procedural mesh generation. I've got it working really well, except that the memory is way over the top.

    I created a simple test case to demonstrate this issue.

    If you post the code below into a GameObject in a blank scene, it will create 16,000 meshes, each with 289 vertices, and 1,536 triangle indices. If we assume that the majority of mesh data is this, and that we are using 4 byte floats and ints, then we can calculate that the size per mesh should end up being about 1536*4 + 289*3 (for each component of the vector) * 4 * 2 (one for normals, the other for the vertex data) which equals 13,080 bytes per mesh. Multiply this by the total mesh count and we get 209,280,000 bytes, or 209.28 megabytes.

    If we run the actual code, we end up with over 700 megabytes of RAM used.

    Am I misunderstanding something, or is Unity actually creating that much more data for each mesh?

    Here is the code so you can try this yourself. Just put it in a blank object in a blank scene, and set the globalMaterial variable to some material from the editor.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class MeshTester : MonoBehaviour {
    6.  
    7.     public Material globalMaterial;
    8.  
    9.     const int chunkSize = 16;
    10.  
    11.     // Use this for initialization
    12.     void Start () {
    13.         InitializeGameObjects();
    14.     }
    15.  
    16.     void InitializeGameObjects() {
    17.         for (int x = 0; x < 40; x++)
    18.         {
    19.             for (int y = 0; y < 10; y++)
    20.             {
    21.                 for (int z = 0; z < 40; z++)
    22.                 {
    23.                     var g = new GameObject();
    24.  
    25.                     g.AddComponent<MeshRenderer>().sharedMaterial = globalMaterial;
    26.                     g.transform.position = new Vector3(x * chunkSize, y * chunkSize, z * chunkSize);
    27.  
    28.                     var mesh = new Mesh();
    29.  
    30.  
    31.  
    32.                     var verts = new Vector3[(chunkSize + 1) * (chunkSize + 1)];
    33.                     var tris = new int[chunkSize * chunkSize * 6];
    34.  
    35.                     int i = 0;
    36.  
    37.                     for (int x1 = 0; x1 < chunkSize + 1; x1++)
    38.                     {
    39.                         for (int z1 = 0; z1 < chunkSize + 1; z1++)
    40.                         {
    41.                             verts[i] = new Vector3(x1 + x * chunkSize, y * chunkSize + Mathf.PerlinNoise((x1 + x * chunkSize) * 0.1f, (z1 + z * chunkSize) * 0.1f) * 5.0f, z1 + z * chunkSize);
    42.  
    43.  
    44.  
    45.                             i++;
    46.                         }
    47.                     }
    48.  
    49.                     i = 0;
    50.  
    51.                     for (int x1 = 0; x1 < chunkSize; x1++)
    52.                     {
    53.                         for (int z1 = 0; z1 < chunkSize; z1++)
    54.                         {
    55.                             tris[(i + 0)] = Get1DIndex(x1, z1);
    56.                             tris[(i + 2)] = Get1DIndex(x1 + 1, z1);
    57.                             tris[(i + 1)] = Get1DIndex(x1, z1 + 1);
    58.  
    59.                             tris[(i + 3)] = Get1DIndex(x1 + 1, z1 + 1);
    60.                             tris[(i + 5)] = Get1DIndex(x1, z1 + 1);
    61.                             tris[(i + 4)] = Get1DIndex(x1 + 1, z1);
    62.  
    63.                             i+=6;
    64.                         }
    65.                     }
    66.  
    67.  
    68.  
    69.                     mesh.vertices = verts;
    70.                     mesh.triangles = tris;
    71.                     mesh.RecalculateNormals();
    72.  
    73.                     g.AddComponent<MeshFilter>().sharedMesh = mesh;
    74.  
    75.                 }
    76.             }
    77.         }
    78.     }
    79.  
    80.     int Get1DIndex(int x, int y) {
    81.         return y + x * (chunkSize + 1);
    82.     }
    83. }
    84.  
     
    Last edited: Sep 22, 2018
  2. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    After further investigation, it appears that it is the mesh renderer, not the mesh itself that is using up most of the RAM. This still begs the question, what the heck is going on?
     
  3. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,300
    jrumps likes this.
  4. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Thanks Karl. I tried what you said, and I'm not sure it made a big difference, if at all. The odd thing is that it shows about 800 mb of memory usage on iOS in Xcode, but in Instruments it only shows about 200 mb worth of persistent allocations, which would make a lot more sense imo.
     
  5. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    My bad! Actually UploadMeshData(true) does help, just not as much as I would've hoped.

    It shrunk the memory usage by about 200 mb. This is great!... but the memory usage is still very high.

    A few other things I have noticed:
    * It appears there is a lot of overhead with MeshRenderer. If I render the same amount of triangles, but with 1/8 of the MeshRenderers (for example), I get substantially less memory usage. About 300 mb less. It is expected that more MeshRenderers = more memory, however with this big of a difference while rendering essentially the same thing, one has to wonder what is going on
    * Instruments seems to be reporting substantially less than what Xcode reports as far as memory usage. On iOS, Xcode reports about 800 mb of memory usage after using UploadMeshData(true). Instruments reports about 200 mb in persistent allocations. Why the big difference?
     
    Last edited: Sep 22, 2018
  6. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,300
  7. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Ok, so I did another test. If I use the memory profiler you mentioned, it only accounts for about 260 mb of RAM, whereas Xcode is reporting the app is using about 1 gb of RAM. Do you have any idea why there is such a big difference? I should mention that I am using unmanaged code, but even if I profile it in Instruments, it still only shows about 370 mb worth of persistent allocations.
     
  8. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    I also have tried DrawMesh, but the performance decreased significantly.
     
  9. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Is there anyone on the Unity dev team that would be able to look at the Mesh internals and explain to me where all of the memory is going?
     
  10. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,300
    You would need to file a bug report although I suspect this is not a bug.
    Have you tried using the Profiler to see what is using the memory? A quick test showed the Mesh using 267.7MB for me. Use the detailed view and click take a sample to get further information.
     
  11. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Well it wouldn't be a bug if it actually took up that much memory. If you run it on an iOS device you will get a much higher memory usage.
     
  12. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,300
    Have you attached the profiler to the iOS device?

    https://docs.unity3d.com/Manual/ProfilerWindow.html
     
  13. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Yes, it reports a similar amount of memory usage to what you are saying. The problem is that Xcode reports a lot higher memory usage, as does the device. The iOS device and Xcode are reporting around 800 mb of memory usage, while the Unity profiler connected to the iOS device only reports about 200-300 mb of memory usage.

    Does that make sense?
     
  14. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,799
    What do you mean by the device? (Where are you seeing ram usage?).

    I am too lazy to look it up right now, but I think I've recently seen something about XCode reporting being wrong because of a Unity bug or something.
     
  15. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731

    If you look at the Debug Session tab in Xcode while you have a device connected with the running app, you will see memory usage. I know this is the actual device memory usage because I get low memory notifications on the device, which shouldn't happen with only 200-300 mb of memory usage.
     
  16. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,300
  17. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Thanks. I just tried running on an iOS 11 device. I do get a lower memory usage, but only by about 150 mb. The memory usage is still over 800 mb on iOS 11 in my game.
     
  18. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,300
    Can you file a bug report and include all your findings? The IOS team will have to look at it.