Active indices and inter-frame time not reducing after disabling meshes

Hello. I’m trying to improve my performance and, besides many other optimizations I should do, I’m facing one of these tricky cases where it seems that performance drops down over time.

In this scene, once loaded (will take some time), I get > 30 FPS, 400 drawcalls and 100k Active Indices:

https://3dsmaps.com/3d/pos/@43.3855639,-8.4053317,43a,35y,169.4h,83.59t

You can get the inspector by clicking on “Settings > Inspector”.

Now, let’s move forward 500m (click+drag / WASD to move) into the line of buildings, wait for the scene tiles to load:

And now let’s move back to the original position. I disable the far away tiles, and indeed I can see the drawcalls get back to 400, but the FPS, Active Meshes and the Inter-Frame time never go back to original values, and I believe they keep accumulating over time:

I am quite suspicious of my code, as I’m doing the typical material replacements, instancing, freezing, and enabling/disabling… there’s a lot of room for errors. But I was expecting that after disabling culled tiles (entire branches in my node tree) indices would go down.

So… looking for some insight here. Is this expected (active indices not going down)? might this be some leak on Babylon side? is setEnable() a good way of disabling map chunks or maybe should I move them outside the scene to some other container?

The end result is that after loading a few map tiles rendering becomes very slow, even in areas where there are not that many objects, after having loaded other tiles.

Thank you.

You should profile your scene in the Performance tab of your browser, to know where the time is spent on the different cases you showed up.

I think one of your problem is the total number of meshes: all those meshes have to be looped on and checked for visibility (among other thing). If you have a fast way to know some meshes won’t be visible (like knowing a tile is not visible at all, so all meshes inside it won’t be visible), you should set them to disabled, that way they will be quickly put aside.

Try as much as possible to merge meshes if they are not too far from each other and if they are static (and if they share the same material): that’s the best way to get some performance back, as you would lower the number of meshes and the number of draw calls.

I am indeed disabling chunks (setEnabled(false)), and they stop showing. But interframe times stay high.

I will combine meshes further, but for some use cases I need the individual objects (picking), so at least one map chunk will have a high number of meshes. But in this case I’m trying to resolve other issue, as I think FPS should not degrade over time.

I have tried the profiler. When this happens, like in the image, the FPS is 4 and the drawcalls 700 (if I reload, the same scene will run at 50FPS so I’m definitely doing something that degrades performance).

A more detailed call hierarchy:

And the summary. I don’t quite understand this part of the chart:

I can see a getError inside e_compileRawShader taking a lot of time (may be while one of my tiles is loading and compiling a shader). I can see my usage of the heap is terribly bad (will try to improve this). I also see a drop in “listeners” and “nodes” of a few hundreds… But… I think this doesn’t explain the performance degrading. And what might be the GPU doing during those 1sec frames?

I see vertices never go down. Are meshes iterated even if they are disabled? even if they are inside a disabled TransformNode that contains them?

If they are iterated, I think I’d need to avoid this, as each chunk is (for now) a few hundreds of meshes. Is this was the case, is there some way to avoid it or should I dispose the meshes entirely? I’d ideally like to avoid it since I’d then need to recreate thin instances and reasign materials when the tile is shown again.

You should look at the “Bottom-Up” table to see the functions and time spent in them.

In your screeshot, the GPU timeline is fully filled during this time, which is quite strange… If you have a live link, it could be easier to see what’s going on.

Link: 3dsmaps (ddd-viewer)

You can get the inspector clicking on “Settings > Inspector”.

You are GPU bound:
image

It takes 13.75ms to render a frame in js but 22ms on the GPU side, so the framerate is constrained by the GPU (45fps in that case). You are using the PBR material, maybe you could use the standard material instead as the PBR material is more GPU intensive.

Sorry to insist.

Yes, I’m GPU bound, but… besides that, it gets to a point when, after loading and disabling a lot of meshes, inter-frame time goes to 500ms. And that’s what I’m trying to troubleshoot :/.

image

Here, my Inter-frame time is huge, but I’m drawing takes just 9ms.

Could you perhaps have a look after moving around enough as to have loaded a few other chunks, so total vertices rises to 2M? After that, even when drawcalls are reduced, inter-frame time never goes down (you can use the “move speed” button so you can move around faster and load more chunks for this test).

Side question: are meshes inside disabled transform nodes still iterated? if so, I have a problem.

The bottom up view:


Sorry I could not reproduce having a big inter frame time with gpu frame time / frame total being low/lower.

What I experienced is big inter-frame when GPU time was big, like:
image
which is expected.

Also, I could not get more than 1.17M vertices and ~18500 total meshes after moving for 10mn or so in the map.

All meshes are looped on to determine the list of active meshes: the total number of vertices is the sum of all vertices of all meshes that are looped on. If a mesh is disabled, the loop is continuing to the next mesh very early. The start of the loop is:

// Determine mesh candidates
const meshes = this.getActiveMeshCandidates();

// Check each mesh
const len = meshes.length;
for (let i = 0; i < len; i++) {
    const mesh = meshes.data[i];
    mesh._internalAbstractMeshDataInfo._currentLODIsUpToDate = false;
    if (mesh.isBlocked) {
        continue;
    }
    this._totalVertices.addCount(mesh.getTotalVertices(), false);
    if (!mesh.isReady() || !mesh.isEnabled() || mesh.scaling.lengthSquared() === 0) {
        continue;
    }

So, disabled meshes should not be too much of a problem, except if the list of meshes become very big and just looping over the meshes starts to eat some cycles… 18500 meshes may start to be a big number, depending on the CPU. In your case where you know which tiles are visible, you should try to override getActiveMeshCandidates and provide a function that will return a smaller list than the mesh total list (which is the default behaviour).

Also, picking is taking quite some time too. You should try to disable it while looking for the rendering problem.

1 Like