Mesh memory in WebGL

Hello!
Background: my project related to mesh generation at runtime.

When I have profiled my project on WebGL, I see “mesh: 58 MB” in profiler window
screen: Screenshot by Lightshot
and increased memory in “unity: 80.3 MB”
I can show it in dynamic, and in previous step mesh memory was near 0 (+58MB from my generated meshes).

My questions:

  1. Is mesh stored in JS browser memory really? And copy on GPU?
  2. If 1 is Yes - Can I upload mesh to GPU and remove it from main memory? And How - if it’s really.
  3. Is mesh in WebGL memory consumption equals memory consumption in Editor?
  4. Can anyone know a formula for calculation of memory consumption of mesh, if it contains X triangle indices and Y vertices (x,y,z, may be u,v attributes), for example X * A + Y * B (bytes), where A and B is known const.
    I have found this thread Size of mesh in memory
    is it actual for WebGL?
  5. Generic rules for mesh size decrease? May be compression?
  6. Is WebGL limits vertices count? / other limits?
    Thanks!

Hello,

Yes, when you’re generating meshes via runtime the memory is stored on both the system memory as well as the GPU memory. I did a talk about this recently at Melbourne Unite but unfortunately Unity hasn’t published the talks yet.

Just to answer in the points you provided:

  1. Yes (as above)

  2. You can do this by invoking Mesh.UploadMeshData(true) - the argument is ‘markNoLongerReadable’ - be aware that you should assign any mesh colliders before doing this as they need to be able to read the mesh from the CPU. Secondly, this has no effect in the editor because the editor always keeps a reference for editor-related reasons so you won’t notice a difference if profiling memory usage in the editor.

  3. As far as I’m aware yes, the vertex layout should be the same, there’d be some very tiny differences based on the graphics API used but I’d be talking bytes.

  4. It depends on the vertex channels you use, but what you’ve had in mind seems about right. For example if you have vertices, positions and UVs this would be pretty accurate:

memory usage (MB) = (position + colour + uv) * vertex count / 1024 / 1024
memory usage (MB) = (12 + 12 + 8) * vertex count / 1024 / 1024

position = 3 floats, a float is 4 bytes. so 12
colour = 4 bytes - R, G, B & A
uv = 2 floats (8 bytes)

Generally video memory is not affected by the heap memory restrictions in the web so it’s not as important unless you have meshes that are not discarded from the CPU.

  1. Aside from reducing vertices where possible, try not to waste any vertex space. For example if you have 3 attributes that are spread between uv0 and uv1, you’d have 1 unused float for every vertex. Unfortunately Unity doesn’t allow us to redefine the vertex layout data types (i.e. floats instead of bytes where accuracy isn’t needed) without using the low level graphics API but again if you can discard the CPU memory it’s generally not an issue.

  2. I’m not a 100% certain with WebGL 1.0, but otherwise there is no limit i.e. you can use 32-bit index buffers if you want big meshes with more than 65,000 vertices.

Thanks for great answer!

I have tested with mesh.UploadMeshData(true)

profiler Screenshot by Lightshot show 78MB GfxDriver (“meshes: 56MB” in line also, near 17MB Gfx before mesh creation)
and 121MB used… as I understand this really not in JS unity HEAP which is 128MB allocated by me.

Also I have tested on 96MB heap in build, and it have not crashes.
(show used like 120-123MB, but I have allocated 96MB)

But it have crashes when 64MB heap :frowning:
and crashes when 80MB heap :frowning:

As I see in profiler, my peak memory like 24MB in mono + 22MB in unity, may be some peak mesh generation and uploading to GPU, but it must be 64-22-24=18MB free. No one of my meshes requires 18MB or more, it some MB each.
More, if 80 MB heap, it must be 80-22-24 = 34 MB free.

May be UploadMeshData don’t upload immediate and wait something sync signal? Or wait end of frame?
If it really waits something, my meshes must be stored in unity HEAP, and then uploaded to GPU, which increases crash risk.

My current code like this, in ONE FRAME make all of meshes

createMesh()
{
  mesh = new Mesh()
  assign pos, uv
  mesh.UploadMeshData(true)
}

for (..)
{
  createMesh()
}

Unity 5.6.5p4 (64-bit)

**
Edit: Also I have checked my code, and detected what my reusable arrays for mesh creation is copied to unity side totally.

MaxVerticesCount = 65535;
private static readonly Vector3[] ReusableVertices = new Vector3[MaxVerticesCount];
private static readonly Color32[] ReusableColors32 = new Color32[MaxVerticesCount];
private static readonly Vector2[] ReusableUv = new Vector2[MaxVerticesCount];
private static readonly Vector2[] ReusableUv2 = new Vector2[MaxVerticesCount];

createMesh()
{
  // fill ReusableVertices , etc

  mesh = new Mesh()
  mesh.vertices = ReusableVertices;
  mesh.colors32 = ReusableColors32;
  // etc

  mesh.UploadMeshData(true)
}

**
profiler
\ In Editor
when MaxVerticesCount = 65535 – then meshes 119MB Screenshot by Lightshot
when MaxVerticesCount = 32000 – then meshes 58MB Screenshot by Lightshot (2 times less)

\ In WebGL
when MaxVerticesCount = 65535 – then meshes 56 MB Screenshot by Lightshot
when MaxVerticesCount = 32000 – then meshes 29 MB Screenshot by Lightshot (2 times less)

Bad ways

  • If I will insert “yield return null” for new frame after each mesh creation for UploadMeshData it will lead to decreased performance (many frames - many seconds), and reusable arrays will not give effect for speed (but GC will be ok)
  • But if I will create new Vector3[ ] instead of ReusableVertices – it also will create garbage and increase memory consumption. And as webGL don’t have GC between frames, garbage will increase and lead to crash. Or must insert “yield return null” for new frame after each mesh creation too.

May be better ways

  • insert some “yield return null”, but not for each mesh, for Upload pipeline, and use reusable arrays as now
  • use reusable arrays with gradually size, for example: 64k, 32k, 16k - each will be selected for appropriate mesh creation (I have generate small and big meshes, and have vertices count for generation)
  • use mesh.SetVertices() with Reusable List (not tested yet for GC alloc for list items, and for memory mesh total)

Can someone provide better solution?