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
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
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 ])
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
2 Likes
Ah, that explains it perfectly fine !
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 :-))
2 Likes
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
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