When is `mesh.resetDrawCache` necessary?

Hi,

I have a big complicated scene, and I noticed that when I add a new PointLight none of my existing meshes react to it until I call mesh.resetDrawCache on that mesh. I’m trying to figure out specifically what my scene is doing that causes this - e.g. a material being frozen, a mesh having its world matrix frozen, etc. - but I’ve been unable to find it. That is, unfreezing materials and meshes doesn’t make the meshes react to new lights, only mesh.resetDrawCache.

Can anyone explain what this API is doing, or take a guess at why new lights aren’t working in my scene until I call this API?

resetDrawCache deletes all the data from the cache (mainly the effect) for all the rendering passes of the mesh (actually the sub-meshes of the mesh).

If you have to call it manually, it means that some changes you make are not detected as changes and the material effect is not recreated automatically. This is probably related to the freezing of materials: if you don’t freeze, does it work as expected?

It is expected that a number of things will not work when a material is frozen, in order to prioritize speed over usability. If freezing is the problem, you can try unfreezing and refreezing but only in the next frame (setTimeout(() => mat.freeze(), 0)).

Alternatively, instead of calling resetDrawCache, you can also try mat.markDirty(true) which was made specifically to handle changes when materials are frozen, without having to unfreeze/refreeze them.

Thank you Evgeni, markDirty() solves most of the problem - I thought I had removed all the code that was freezing materials but I guess I missed something.

However one exception - my scene has some ThinInstance meshes, and I cannot seem to get them to be affected by newly-added lights. Even if I change the material for the base mesh, the thin instances in my scene get rendered with the new material, but they don’t get lit by lights that were added after the thin instances were set up.

Unfortunately I wasn’t able to reproduce this in the playground, so I’m not sure of the cause. Do you know what might be happening here?

We have an issue opened about frozen materials:

But I’m not sure it would fix your problem. Does it work if the material is not frozen? If it works, you could use Spector to find what differs when the material is frozen.

No, strangely this issue doesn’t seem to be related to materials at all. My ThinInstance meshes are not receiving light from any light added after the ThinInstances were set up - even if the base mesh changes materials, has no material at all, even after I call resetDrawCache or markDirty on the base mesh.

Is it correct to call markDirty() etc on the base mesh from which the thinInstances are derived? Or is there some other equivalent thing to do for them?

If it does not work when the material is not frozen, then markDirty won’t probably be the solution. Are you setting a scene performance mode? If yes, can you reset to the Backward mode and see if that works?

I guess you meant resetDrawCache, as markDirty is a method from the Material class. Thin instances only exist as part of a mesh, so resetDrawCache must be called on the owner mesh.

[quote=“Evgeni_Popov, post:6, topic:40232”]
Are you setting a scene performance mode? If yes, can you reset to the Backward mode and see if that works?[/quote]

I was using performance mode Intermediate, but changing to Backwards doesn’t affect the issue. I was trying both resetDrawCache on the thinInstance owner mesh, and the material flags on that mesh’s material, but no effect so far.

I’m trying to reproduce this in the playground, but I haven’t been able to so far. Unfortunately my real scene is complicated - octrees, etc. So it’s hard to know where to start in trying to replicate it minimally…

You should try to use Spector to be sure that the lights are not passed to the shader… Maybe they are but the intensity is 0?

The lights are working for all the regular (non-thinInstance) meshes in the scene - and also they work correctly for any thinInstances that exist before the lights are added to the scene. So I think the light’s settings seem to be okay, it’s just not affecting thinInstances that existed before the light is added to the scene.

I have not used Spector before but I can give it a try. What should I look for?

You need to look for the draw call that corresponds to the thin instances (the screenshots on the left should help you find this draw call), then click on it and copy everything that appears on the right side of the screen (don’t worry about the images, just select everything and save it in a text file).

Do the same thing when the rendering is correct (so create the lights before creating the thin instances) and do the same manipulations.

Now compare the two text files (I use WinMerge for this). There will be some expected differences (some WebGL identifiers, maybe some other things), but you should be able to see what really differs between the two cases and hopefully that will help you find the problem.

I tried this, but the only difference between the draw calls in the correct and incorrect cases is the uniforms - there are three uniforms vLightData1, vLightDiffuse1, vLightSpecular1 that are present if I add the point light before setting up the thin instances, and they are missing if the point light was added later. But does knowing that give any clues why Babylon isn’t sending the uniforms?

Incidentally those three uniforms are present in earlier and later draw calls for the non-working case. They are only omitted in the draw call that draws the thin instances.

This confirms that it is not the shader code that would not take into account the light calculation, but that it is the light that is not taken into account when creating the effect: make sure that, for some reason, the mesh with thin instances would be excluded from this light.

I don’t understand why creating the light before the mesh would change the behavior… In this PG, creating the light later works :

So there must be something else, but I’m afraid it would be hard to find without a repro.

Sorry about that, it took me a little white to make a rep page. Please try this link:

lighting voxel demo

The row of small white posts are all thin instances, and when the scene initializes one PointLight is already created that lights some of them. Once you focus the page, if you hit the '2' key a second PointLight is added to the scene, and you can see that it illuminates the ground and the player mesh but not the thin instances.

I’m kind of at a loss here, can you tell what might be wrong from checking spector? Also the scene object is in window, if you want to poke around scene.lights or whatever.

Thanks for your help!

It seems you are doing some very unorthodox things, the scene is not the normal Scene object of Babylon.js!

Even doing scene.meshes[0].getScene() I don’t get the right scene, only an object that seems to be the result of await SceneLoader.AppendAsync(...)… Or maybe it’s actually a bug, you assign the scene to the result of SceneLoader.AppendAsync()?

No, I’m not doing anything unusual with the scene that I know of. scene.meshes[0] happens to be a mesh that’s usually disabled - maybe that’s the issue? But I don’t know what you’re seeing; this returns true for me in the JS console:

window.scene.meshes[0].getScene() === window.scene // true

Anyway since the issue is probably to do with the thin instances, do you see anything fishy about this?

// this is the base mesh of the thin instances:
window.scene._selectionOctree.dynamicContent.find(m => m.name==='pole')

My bad, the scene object is ok, I was probably drunk when I tested it!

The root cause of the problem is that the “pole” mesh is not in the scene.meshes array.

When a new light is added, this code is executed by the scene (this==scene):

for (const mesh of this.meshes) {
    if (mesh.lightSources.indexOf(newLight) === -1) {
        mesh.lightSources.push(newLight);
        mesh._resyncLightSources();
    }
}

So, you should either add the “pole” mesh to scene.meshes or push the light to mesh.lightSources and call mesh._resyncLightSources() yourself.

1 Like

Ahhh, thanks so much for finding the issue! You said elsewhere in a recent thread that the most performant way to not render a mesh is to remove it from scene.meshes, so that’s usually what I’ve been doing, but I guess there are some edge cases.

Thank you!