When I change a texture on a material at runtime, I see a noticeable flicker / intermediate white looking surface. Im guessing this is because the material is being updated while the render loop is running. I know there are several material observables that are being fired to detect the various states of the material but has anyone come across this and if so is there a way to check if the material is ready to be rendered?
ps: I have also tried to stop the render loop, update texture, and re-start the render loop with no success.
pps: I realize a PG example would be helpful and I intend to try to repro via PG if there isnt anything obvious.
It may be because the texture is not loaded yet: you should wait for the texture to be loaded before setting it on the material (texture.onLoadObservable
).
2 Likes
I am using assetContainers and assetsManager to load textures and await assetsManager.loadAsync() first before using any of the loaded texture(s). Also to tripple check i make sure the texture has a uniqueID and also if isReady() is true. All of that checks out. Is it possible that for some reason the renderer is showing the first mip level on the first render tick after the asset is loaded? The texture does have mips and wonder if its possible to force the highest mip to load first? Im not seeing where to set the mip level in the docs.
If isReady()
is true for all textures, then I think a PG will be needed to understand what’s going on.
1 Like
Could also be the time it takes to the shader to recompile now there is a texture if there were none before. A PG would help.
Yes, I thought also about that, but we normally should keep the existing effect and switch only when the new one is ready, so we shouldn’t have flickering (except if for some reason parallel compilation is disabled - like in WebGL1).
1 Like
pg example: https://playground.babylonjs.com/#ZJYNY#1510
press any key to swap textures.
Cannot repro on PG Looks like onEffectCreatedObservable is called every time the scene is rendered. I was hoping to use that to determine if there were some pending material calculations happening but it does not seem that is the case. It fires after scene.render() is called. Which leaves me a bit confused. I will try to dig around to see if there are other callbacks that can be used.
Back to more investigation.
Update: I modified my PG example (see above)
Ok I think i figured out what was causing the problem. Assigning a texture creates the need for a material compilation step. The way to avoid this is for surfaces that dont initially have a texture (aka albedoTexture=null). to create a placeholder texture (eg a 1x1 black texture). Then when you assign a new texture there is no need to recompile the shader.
So my question is, how do I dynamically create a 1x1 black texture? prefer this over having to load a pre-authored texture.
Alternatively, id like to know if there is a way to see if a material is dirty (needs compilation). In which cause I can also just force that material to compile before rendering the scene.
I don’t see any flickering in your PG?
As said, if your mesh has already a material (even without a texture), adding a texture to it later on will recreate a new effect but it shouldn’t flicker because the old effect won’t be replaced until the new one is compiled.
1 Like
Ok, I have a hunch as to whats going on. We have several async calls that is trying to change the state of one material. This is possibly causing BJS to get confuse or put it in a strange state as some of those are texture updates that fire off material compilation events. The proper / clean way it seems is to have whatever function does the updating of the material to only return/resolve once the material has compiled (assuming it needs to be compiled). Alternatively, we could force the material to be compiled regardless but i think that is heavy on perf. Is there a way to check to see if a material needs to be compiled or is dirty? (If we know ahead of time that a material does infact need compilation, we can await that material to be compiled before returning, else just return right away) . If there is no such thing then we can perhaps use a timeout to see if the material get compiled or not before returning.
I found this… would I need to check this flag? PBRCustomMaterial | Babylon.js Documentation
A material by itself does not have a dirty state, it’s a material+mesh (sub-mesh in reality) which matters.
You can know if a material must be recompiled for a sub-mesh if defines.isDirty
is set. defines
is coming from subMesh.materialDefines
and corresponds to the defines for the current render pass