Hi,
I want to know if there is a way to reduce the number of batches using RenderMesh without enabling GPU instancing in the shader?
I published a 2d RTS game and used Graphics.DrawMeshInstanced() but about 20% of players android devices doesn’t support instancing.
I think the only option for me is RenderMesh, but the high number of batches is the barrier.
Also some shaders has no option for GPU instancing.
You can enable dynamic batching. Unity will then automatically combine meshes up to 300 vertices in size into one mesh reducing drawcalls.
You can also manually combine meshes through your code. This will be paid in cpu cost though. However if you do this manually, you can multithread it.
As I tested enabling dynamic batching doesn’t work with ECS RenderMesh.
Are you talking about combining meshes using RenderMesh?
Every frame, you can manually combine some meshes into larger ones, then use render mesh on the new meshes you create. This will reduce batches and you can use it with render mesh. Doing this manually is faster than dynamic batching anyway, because you can combine different meshes on different threads.
If you are using render mesh then automatic dynamic batching won’t work, but you can do it manually.
Also, if the device does not support instancing, likely the hardware is not that powerful anyway. So on those devices, you can reduce the quality of your meshes to reduce the CPU load of manually combining meshes.
Thanks, I should to test it.
Is there any limit to combine the meshes this way?
I have thousands of 2d units in the scene, and they are animated using 30 materials each with a 1024 sprite sheet texture. Can I combine the meshes with different materials or same material with different UV?
I am not an expert, but if they have different materials it is very difficult to combine them into same mesh. But if two meshes have multiple materials, but they are the same, I think you can combine them. Basically you would create a new mesh, and fill with vertices of mesh 1, then fill with vertices of mesh 2. Then pass this new mesh to render mesh.
You can achieve this two ways. Remove render mesh component from each entity, and have separate entities for combined meshed, with render mesh component containing combined mesh.
Or, do not use ECS render mesh and use Graphics.DrawMesh instead. Manually calling the draw yourself.
I believe they can have different UV, vertices, but must share materials. There is no limit to how many you can combine, but be aware that combining too many into one mesh may lead to poor CPU performance. So you must test this to see how it can perform.
I have done this with combining dozens of 3D meshes into one on mobile devices and worked well.
Another solution is to give meshes LOD. Meshes far away you can use all the same mesh and material and will make batching easy. Depends on your game though.
Thanks again.
I tested Graphics.DrawMesh without combining and it had some problems.
My game is a 2d RTS in one view, so I can’t use LOD.
So I have to test combining the meshes. But I’m concerned about layering objects correctly using this method.
The game has an isometric view. So the lower unit or object should be in front.
-
Sort all your meshes into ones which are overlapping and ones which aren’t. You should use spatial partitioning like a grid,hashmap,or tree. Any meshes not overlapping you can simply combine and batch.
-
For all meshes which do overlap, you will need to test. I believe vertices are drawn in the order they appear in the array, I am not 100% sure. If you test and find this is true, when combining meshes which overlap, add the bottom meshes first, up through to the top. I am not too familiar with 2D, but this may be the solution.
Edit: By bottom meshes I mean the ones in the back.
Ok. Thanks a lot, I will give it a try and post the result.
Awesome! Do let me know, as I have other ideas which may help.
Well I tested a simple mesh combining with 2 materials.
I managed the layering with setting z position of the meshes.
But I have one problem. I don’t know how to set different UVs for the combined meshes!
Right now all the combined meshes are showing same tile of the texture.
Each texture of soldiers contains 128 frames which I use to animate them.
You can set the UV the same way as vertices: Mesh.uv. Are you having trouble with this, or are you doing this but they are still showing the same tile? Forgive me if you already know this, I just want to make sure.
Thank you very much.
Yes I knew about Mesh.uv but I didn’t know I could use it with mesh combining. Also I was not curious enough to test it.
I managed to combine meshes with different UVs.
But right now it has serious performance issue with 1000 units because I’m testing with Monobehaviour Update.
I should to take it to the ECS and do lots of optimizations to see if it works or not.
Good to hear you got it working! Multi-threading and using burst should fix any performance issues. Try to keep combined meshes below 300 vertices because around that point the cpu cost of combining outweighs the performance from batching. For example, if you are combing all 1000 meshes into 1 it is much better to create more batches (number of batches you choose depends on how many vertices your meshes have), with smaller combined meshes.
About keeping the combined meshes below 300 vertices, as I’m combining quads so the limit would be 75 meshes. Right?
But as I tested the process of splitting the similar meshes into 75 groups is much heavier than combining all of them into one mesh.
Test result with 2000 units and 12 materials:
If I combine all of the same units into one mesh => 12 batches, 12 setPassCalls, 70 fps, each mesh up to 600 vertices
If I limit the combining to 75 meshes => ~22 batches, ~22 setPassCalls, 60 fps, each mesh limited to 300 vetices
Interesting. If it works for you than keep doing it. I was just quoting the Unity documentation. It could be that on your mobile drawcalls and set/pass calls are more significant than the cpu overhead of combining meshes.
Also because you are using 12 materials. I suspect if you only had 1 or 2 materials smaller meshes would be more performance. But since each material is a drawcall and set/pass, its more performant for you to combine into large meshes.
Thanks to you @MintTree117 I succeeded to make it.
Although the bottleneck is the mesh combining which I have to do it on the main thread. And also it has too much allocation each frame (about 120kb).
If I could do that inside a job, it would be great. Any suggestion?