Hey there, I’m having some issues getting generated meshes to render efficiently using hybrid renderer.
I have a decent amount of meshes which are mostly unique. After attaching RenderMesh, Translation and LocalToWorld components, the profiler tells me that the MeshRendererV2 is really busy calling addBatches for the dynamic batcher, giving bad performance.
The meshes don’t move so I’m adding FrozenRenderSceneTag. Without SectionIndex set I guess it’ll add all the meshes into the same batch, giving bad performance the more meshes are loaded.
When I populate SectionIndex with a unique integer per for instance 32 RenderMeshes, I get pretty good render performance.
Still, in this case, when I add more meshes to the world, AddBatches() of the StaticBatcher takes a lot of time, also generating lots of garbage. I’m seeing people suggesting to load this kind of stuff in a separate world and then MoveEntitiesFrom() them to the main world and I’ve tried that, but still the moment you move those entities into the main world the Renderer kicks in and slows stuff down again.
So my main question is: how would you load lots of meshes at runtime without slowing the application down so much?
Thnx Radu392 for your answer! Indeed I saw the link you posted but it’s about culling, not about render batching.
Currently UpdateFrozenRenderBatches is taking at least 32 ms per frame, it doesn’t matter much what I do it stays the same, or slower. When I don’t add the FrozenRenderSceneTag to the RenderMesh entities, the problem is simply moved to UpdateDynamicRenderBatches.
So how would I load additional (static) meshes, and keep the frame at an acceptable rate?
Will the SubScene route solve this for me? And if so, how would I pass some initialization data to those subscenes in order to generate the correct mesh (the position where the subscene is loaded for instance).
Should I be able to warm-up the batches in a secondary world and the MoveEntitiesFrom() them to the main world?
I only have one material, loaded in a system through Resources.Load and assigned this variable to the materials field of RenderMesh. So it should be shared. Thanks for checking, snacktime!
Looking at the RenderMeshSystemV2 code I determined that the static and dynamic batching implementation are almost the same and should result in similar performance. The thing static batcher does is calling EntityManager.GetSharedComponentOrderVersion which does a lot of Equals() checks and EntityManager.GetAllUniqueSharedComponentData which also does the Equals() checks and uses lists which generate garbage.
The amount of garbage generated grows when I add more RenderMeshes. I’m not sure yet if other SharedComponentData influences this. When it does it may have to do with an inefficient Equals() check on my side.
If these two methods would be optimized it seems that adding more static stuff should be equal performance as one dynamic frame render.
I did some testing with SubScenes and the issue persists. I’m loading a SubScene after 100 frames and I have a huge lagspike; InitializationSystemGroup (50ms, 250kb gc), PresentationSystemGroup (15ms, 100kb gc) only to load one cube I have turned LeaksDetection, JobsDebugger, DeepProfile off.
I’m totally clueless now how to load more meshes while the game is running without lagspikes. If only I could warm-up the render batches async… If I attach the RenderMeshSystemV2 to another world, I’m unable to call otherWorld.Update() async, right?
To answer my own question, my conclusion is that RenderMeshSystemV2 is not optimized for procedural generated terrain. While I still think it can be optimized as described above, I currently have amazing performance using Graphics.DrawProcedural().
It has it’s own challenges of course but in two days I’m pretty far with the implementation already.
Thanks again @Radu392 and @snacktime for taking the time to help me out.