BaseTexture.WhenAllReady returns too early

When using BaseTexture.WhenAllReady to wait for the ready state of a texture which has just been added, the callback is called before the texture is actually ready:

Also the video with activated network throttling shows rather clear that the WhenAllReady callbacks for each added texture returns immediately even though all textures are still loading in the background for quite some time:

Question

Am I missing something here or is this a bug?

Another observation

When using scene.textures with WhenAllReady instead of the just added texture, it behaves different but still rather weird IMHO. Some callbacks actually wait for something to happen, but even the last one still doesn’t wait for all textures to be fully downloaded :man_shrugging:

More findings

I just realized that there’s a Texture.onLoadObservable which is actually being used by BaseTexture.WhenAllReady internally when available in the given textures.

However, this function does neither exist on the texture being passed to the callback of scene.onNewTextureAddedObservable, nor does it exist on the texture within the scene, even though both textures appear to be of type Texture :man_shrugging:

This is rather surely the reason, why BaseTexture.WhenAllReady returns early but raises the question, why onLoadObservable does not exist on the textures at hand?

Also, this does not explain, why BaseTexture.WhenAllReady(scene.textures) behaves differently then BaseTexture.WhenAllReady([ textureParamFromAddedObservable ]) :thinking:

The reason it doesn’t exist is that the texture is added to the scene before being fully initialized. We discussed this with materials. The texture is added in the constructor of the base texture before the constructor is done.
The solution back then was calling the callback after a single tick (i.e. using a setTimeout() with 0 timeout). but it was never implemented because of side effects that will break back-compat.
But I understand what you are saying. Thanks for the investigation. I’ll see what we can do about it

1 Like

Ah, that explains it perfectly fine :pray:!

Using the “setTimeout trick” on my end seems to be working just fine for me:

// ...

function onTextureAdded(texture) {
    // ❌ `texture.onLoadObservable` does not exist
    window.setTimeout(() => {
        // âś… `texture.onLoadObservable` does exist
        if (texture.onLoadObservable) {
            texture.onLoadObservable.addOnce(() => onTextureReady(texture));
        }
    }, 0);
}

// ...

scene.onNewTextureAddedObservable.add(onTextureAdded);

Would you advice me not to use this for some reason?

1 Like

Just to be clear: Is it guaranteed, that the texture is always fully initialized in the next execution context (tick), or could there be situations where I’d have to wait “another round” (or some other duration etc.)?

Yep, it is. No reason not to do that, other than the asynchronous nature of this workflow (which just requires you to know that it is async :-))

1 Like

This solves it for me then but I’d still appreciate if you could have a look at the fundamental problem behind this at some point, as my accepted code is a working but still rather hacky workaround for a slightly flawed API in this regard.

Thanks again for the (as always) very fast & competent help :muscle:

We are aware of this issue and will take care of it internally in a future version of the scene. We won’t solve it any other way - the observable will be notified only when the texture was fully initialized (i.e. - before the next frame is rendered).

EDIT - at the moment this is an issue that will not be solved, because it is changing the way we notify and is a breaking change. If anyone has an issue, use setTimeout or add a callback to the onBeforeRenderObservable that will be executed once. Then the objects will be fully constructed.

2 Likes