Texture sometimes suddenly unloads

Hi all!

I am developing a 3d-game for iOS and is almost done. I use threejs + capacitor.

I have a long outstanding intermittent bug: :face_with_raised_eyebrow:

The animated player character (a glb-model) sometimes looses the texture and becomes entirely black.

See attached image.

This happens when I return to the game after a long time of having it in the background on the phone. It seems that things have been swapped out (or something) … things that are not properly restored when resuming.

The game still works and everything else is perfectly restored … only the main character is black.

Has anyone experienced something like this?
Anyone with a clue on what’s going on?
Is this on threejs or capacitor or webkit?

Would be really thankful for your ideas / thoughts here!
I need to get this game out the door. :sweat_smile:

All the Best,
Tobias

That’s pretty odd, and does kind of have the sound of a WebGL bug … is this only happening in iOS, or can you see the same in (for example) Chrome on a non-iOS device?

Also, what’s the resolution of the texture?

2 Likes

you need to add environment in the scene.environment using a hdri or a simple jpg cubemap image

its not a bug, its perfectly working you just have to add an hdri or a cubemap image texture into the scene.environment, or add the roomenvironment, i prefer the adding no lights instead I would use hdri on to the scene.environment to illuminate the scene

@Umbawa_Sarosong I do not follow. I have lighting. I have enivironment backgrounds. I do see the texture of the character perfectly all the time … except about 1% of the times when resuming the app on iOS. Those unfortunate times it goes totally black for no reason. Would setting the scene.environment solve that highly intermittent problem? :thinking:

@donmccurdy I have only seen it on iOS on the phone. Not on the simulator, nor in chrome-browser while developing. I do not develop for Android for this first release … so I cannot tell if it occurs in other rendering contexts. :neutral_face:

I will get back about the dimensions on the texturemap (some texture atlas if I remember correctly).

@donmccurdy the texture atlas is 2048x2048.

Hm, that shouldn’t be any issue… Unfortunately I’m guessing this could be an iOS/WebGL implementation issue if there’s nothing special about this particular texture. :confused:

I have decorated it with detectors now and will investigate it a bit further. I’ll report back here if I find something of value.

Without own analysis options, I can only speculate wildly. It sounds like in rare cases the code doesn’t wait until the texture is loaded. Maybe this will help you a little. I load the textures extra because I modify some model textures.

async function loadModelAndTextures() {

	const gltfLoader = new GLTFLoader();
	const textureLoader = new THREE.TextureLoader();

	try {

		// load model
		const gltf = await gltfLoader.loadAsync( 'path/model.gltf' );
		const model = gltf.scene;

		// load textures manually and with await async
		const texture1 = await textureLoader.loadAsync( 'path/texture1.jpg' );
		const texture2 = await textureLoader.loadAsync( 'path/texture2.jpg' );
        // more textures ...

        // for debug maybe check the textures with readRenderTargetPixels

		// assign textures to the meshes
		model.traverse( ( child ) => {

			if ( child.isMesh ) {

				// assign texture1 to mesh1
				child.material.map = texture1;

				// assign texture2 to any other mesh
				if ( child.name === 'meshName' ) {

					child.material.map = texture2;

				}

                //other meshes...

				child.material.needsUpdate = true;

			}

		} );

		return model;

	} catch ( error ) {

		console.error( 'Error, can\'t load model or textures:', error );

	}

}


//in your code
const model = await loadModelAndTextures();
scene.add(model);

1 Like

Yes,

This last comment from @Attila_Schroeder got me into the right thought process … I think.

I backed up a bit and thought: what if this in some way attributed to how I load the model files … and not at all a low level bug?

After some analysis + heavy logging I found a suspect lurking onLoaded-listener hanging around from a previous load … of that same model file. I could even make that listener count increase if doing a certain sequence of (quite unlikely) steps. Not good. :melting_face:

Ever since I cleared up that extra listener properly I have not observed the model with black texture-bug. My guess is that there was some kind of race-condition among stale listeners (but a bit unclear exactly what/how). I will test a bit more before I am confident. :thinking:

Thanks a lot for the feedback guys. :raised_hands:

2 Likes

You need for own loader to prevent unloaded things. First put all textures, models, sounds to THREE.LoadingManager. Add requestAnimationframe which will checking is amount of pended files = loaded files. if loaded all, then start function which apply textures to models.
Also after all things you can render once all scene to load it in gpu and user will not have freezes.
loader.js (3.5 KB)

let tex=[];
let texture_loader=new THREE.TextureLoader(loadingManager);
tex["shadow"]=texture_loader.load("./images/shadow.png");
1 Like

Spoke to soon. It is still there. It was not that extra listener that was the problem. :neutral_face:

Will dig deeper. :face_with_raised_eyebrow:

In the past year, Chrome has added multiple resource controls related to users’ power preference and memory allocation. Also with extensions/plugins, there have been broad reductions. With tabs there is now generally a 30-minute countdown to a hard-pause. The same is true with Safari, where tabs’ group memory footprint has been overhauled.

More specifically, there are limits to the global resources available to canvas contexts. Based on system performance, your inactive session seems to have been deprioritized. Especially with resource-heavy loops a memory budget may easily be exceeded.

  • use visibilitychange to (1) pause the render loop, or (2) defer/reduce resources or (3) listen for specific quirks such as texture mismatch.
  • add game logic to screensaver/autosave after 10 minutes of inactivity, regardless of visibility. Or reduce the renderer size but preserve gameplay responsiveness.

It would be important to know if the schedule error occurs (1) in the background, or (2) on return precisely. Perhaps an event queue piles up and blows the stack… i.e. could you soft-pause the character’s idle animation?

Behest Processing,
Tapsn8kr “Bubbleshield” Hershadowg