Some LOD performance related questions.

I have refactored the question in the second post.

Original discussion:

I am trying to grasp some LOD concepts, to improve performance, using LOD.

For a starter, I will use as reference point
EntityComponentSystemSamples/ECSSamples/Assets/StressTests/LODSubSceneTest/

Ops, I thought it will be bigger sorry.
Switching along LOD tree, between entities, for each LOD group of the root LOD.
6601867--750820--ezgif-3-30c2156aff7c.gif

In this case scenario, we have LOD cubes rotating.

The structure looks like this
MeshLODGroupComponent (root group), with LOD instances0 ranges ( 24, 146, inf, inf ), parent mask 0
| - MeshLODGroupComponent (group0), with LOD instances0 ranges ( 2, 21, 300, inf ), parent mask 1
… | - MeshLODComponent (s), with LOD mask 1, yellow cubes.
| - MeshLODGroupComponent (group1), with LOD instances0 ranges ( 2, 21, 300, inf ), parent mask 1
… | - MeshLODComponent (s), with LOD mask 2, red cubes ( with 2 LODs)
| - MeshLODGroupComponent (group2), with LOD instances0 ranges ( 2, 21, 300, inf ), parent mask 1
… | - MeshLODComponent (s), with LOD mask 4, blue cubes

My understanding is, that:

  • Based on group’s parent mask 1, LOD group0-2 are only active and conduct mesh LOD checks, when root group is in range 24 to 146.
  • Then that should trigger yellow cubes in between range 2 to 21.
  • Then that should trigger red cubes in between range 21 to 300.
  • Then that should trigger blue cubes in between range 300 to inf. But already outside parent mask 1 (24 to 146)! So technically, this mesh LOD should be unable to get activated?

A bit confused here however.

Another thing is, I hoped that MeshLODGroupComponent, helps on gaining some perfomrance.
But zooming close in and out, to the point that blue cubes LOD cause them to disappear, makes no performance difference. I have disabled burst in this case.

Maybe LOD instances0 can be set better? Or is too small sample for testing?

Of course there is gain on rendering part (ignore GC).

2 Questions:

  • So my first question is, can I gain on LOD group (MeshLODGroupComponent) performance, by appropriately nesting them?

  • The second question is related to translation and specifically rotation.

How do I check, which LOD level is currently active on mesh. For example I don’t want to perform rotation on meshes, which are not currently visible. Having for example 100k invisible rotating cubes, makes not viable as an option.

My use case:

Below is my current use case, which I want to further improve.

In this vid, I don’t use LOD. My performance drops significantly at 300k entities, below 30FPS, when looking at whole scene.
In later version (not in this vid), I have applied basics LOD, gaining significant performance improvement and able getting stable 30FPS at 500k entities, while looking at whole scene. However, LodRequirementsUpdateSystem takes a CPU toll, which I want to reduce, hoping by utilizing correctly MeshLODGroupComponent. I think I have gained already something. But I would like to know, how much more I can gain of LOD?

https://www.youtube.com/watch?v=Fk8EPBYnOS8

I also watched that LOD talk from 2018. But I don’t see anything mentioned, about LOD groups gain.

https://www.youtube.com/watch?v=k_ORJXmPu9M

1 Like

After giving some more play, I would assume, that LOD does not affect HybridRenrererSystem performance.
In a sense, that if LOD is active, or not, HybridRenrererSystem compute all of entities with LOD regardless.

In following case (where marker is on the profiler) I have nearly 400k entities, of which 300k are with LOD component.
LOD does the good job for rendering itself. But I want to find a way, for bring better performance, for HybridRenererSystem. Only thought so far is, base on other thread discussion, to set Disabled component tag.
I would be fine with that, as long I don’t have to calculate myself entities distance to camera, since LOD done that already.

Another thing are subscenes. But since I have dynamically created entities, and they may move, I don’t think subscene solution would work for me. Or would it?

Any thoughts?

Here’s what I did to get good performance with HR V2:

Get the Amplify Impostor asset.

Add this to all passes in the amplify shaders:

#pragma multi_compile _ DOTS_INSTANCING_ON

And set this in all passes:

        #pragma target 4.5

Create impostors and setup up your mesh as lod level 1 and your impostor as lod level 2.

Enjoy great performance.

Edit:
Are you dynamically creating the meshes or just the entites? This won’t work if your meshes are dynamically created of course.

2 Likes

No, I just have really few unique meshes. Sphere and box. Then creating prefab entities with authoring. And then instantiating entities from these few prefabs.

Problem I am facing, is not the mesh itself. Is the entities count with LOD components, used by hybrid system, which affects the performance. I can even move camera frustum away from any entities, or disable camera. Makes no difference in this case. So whether is more complex mesh, or is just 2D impostor, wont make a significant difference for hybrid system in my experience.

Impostor can help potentially with rendering however. If you see my first post and profiler graph, there is green rendering part, which is rather narrow. That is thanks to working LOD. Otherwise it would hit easily less than 30 FPS. But due to sheer count of entities with LOD component (near 400k), purple part of the graph with hybrid system, takes a significant performance toll.

Besides, in my case I need be able look at the world from top and from the side. So impostor brings some complexity.

Hence, I don’t think impostor would help in my case.
Any thoughts?

Are you able to setup an HLOD representation? So for a distant group of entities, these would be replaced by a single mesh representation. I believe at that level it then doesn’t iterate over the single entities LOD components of that group as it already knows they’re not active. Or you can use an HLOD cull where you cull a whole group of entities at a certain distance - again I think it just checks the group parent entity and doesn’t have to iterate over all children.

Yeah sorry, didn’t read your post properly. I’m getting the same CPU performance issues with many render entities.

What I’m trying to do now is use combined impostors for very far away objects and when they are gettting close i’ll instantiate the actual entities. And with static objects i’m loading / unloading subscenes.

2 Likes

That what I believe I have done.
Should I have used anything else, other than MeshLODComponent and MeshLODGroupComponent ?
Maybe I am missing something?

  • I have base entity with MeshLODGroupComponent
  • This to have set LOD values0, i.e. 0, 300, 600, inf, and values1 all inf.
  • Then in children entities, with actual meshes, using MeshLODComponent, with set group entity, and corresponding LOD level 1,2,4,8 etc.

I just assume, when LOD level 2 is triggered for example, entities with LOD level 1, 4, 8 should be committed by hybrid renderer, for any processing, until LOD is changing.
I don’t know to be honest, if there is anything else, when it comes to HLOD.

HR Lod and culling uses chunks as their partitioning mecanism, so it’s a rather naive approach and can perform badly in comparison to context specific partitioning.

That said it performs adequately for what it’s designed for. HR is not an instanced renderer. It’s very efficient for what it does. But it doesn’t really make sense to try and optimize a non instanced renderer for a use case that obviously calls for instanced rendering.

Like here an instanced indirect/gpu culled approach would probably cost you around 1ms or so total cpu time.

I don’ recognize that specific gfx wait entry but your time waiting on jobs to complete doesn’t look to be the biggest chunk of main thread time. It looks like the biggest chunk is waiting on rendering.

So if you don’t want to invest the time into instanced indirect which is a chunk of work, then the billboard/imposter route is probably your best choice.

1 Like

I think for HLOD you would define nested MeshLODGroupComponent. If the parent group isn’t active, i don’t think it even checks the group below.

1 Like

I will need to revise, if nesting Groups will help. I am not certain, why would that help in particular, but is worth to give a shot.

Problem with impostor in my case, is that each of the plant trees (see vid in first post) are generated procedurally at runtime. They can grow and shrink. Each is different, made of 10s to 100s of entities. However, I could probably approximate look of trees, when look from far distance.

By instanced indirect you mean DrawMeshInstancedIndirect?
I hoped to avoid using it. I stepped down from using DrawMeshInstanced, when upgraded to Entities 0.16.
Is just easy to work with Shader Graph.

That seems reasonable.

Or maybe indeed I could try to work with entity Disabled component, for more granular control of rendering.
It would come with additional benefits, as no need to calculate translation, for disabled entities.

1 Like