Skip to content

ImageLoader: Optimize caching.#31258

Merged
Mugen87 merged 4 commits into
mrdoob:devfrom
Mugen87:dev3
Jun 13, 2025
Merged

ImageLoader: Optimize caching.#31258
Mugen87 merged 4 commits into
mrdoob:devfrom
Mugen87:dev3

Conversation

@Mugen87
Copy link
Copy Markdown
Collaborator

@Mugen87 Mugen87 commented Jun 12, 2025

Fixed #31256.

Description

The idea of this PR is to cache the image object in ImageLoader a bit earlier. In this way, concurrent pending requests get the same cached image.

If the request fails, the cache entry is removed so subsequent load() calls (like retries) perform a fresh request.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 12, 2025

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 337.54
78.73
337.54
78.73
+0 B
+0 B
WebGPU 553.97
153.41
553.97
153.41
+0 B
+0 B
WebGPU Nodes 553.32
153.26
553.32
153.26
+0 B
+0 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 468.74
113.38
468.74
113.38
+0 B
+0 B
WebGPU 629.59
170.31
629.59
170.31
+0 B
+0 B
WebGPU Nodes 584.44
159.66
584.44
159.66
+0 B
+0 B

@mrxz
Copy link
Copy Markdown
Contributor

mrxz commented Jun 12, 2025

The onLoad callback was guaranteed to be called when the image completed loading. This early caching strategy introduces the possibility that image.complete === false for concurrent loads. This in turn can cause issues when the caller expects the image to be fully loaded.

For example, the following snippet logs a warning:

THREE.Cache.enabled = true;

const loader = new THREE.TextureLoader();
loader.load( 'image.png' );
loader.load( 'image.png', (texture) => renderer.initTexture(texture) );
// THREE.WebGLRenderer: Texture marked for update but image is incomplete

It might still be possible to cache early, but there'll have to be an additional code path for when subsequent requests encounter cached.complete === false. Though looking at the code this quickly becomes complex.

@Mugen87
Copy link
Copy Markdown
Collaborator Author

Mugen87 commented Jun 13, 2025

Good catch! I've adopted a similar approach like FileLoader to prevent this issue. Do you see any other issues with the new code?

Comment thread src/loaders/ImageLoader.js Outdated
@mrxz
Copy link
Copy Markdown
Contributor

mrxz commented Jun 13, 2025

Do you see any other issues with the new code?

The _loading.delete( this ); calls should happen right after retrieving the list of callbacks. If the onError callback would immediately retry through the loader it would encounter the (failed) cached image and append its onLoad/onError handlers to the list that is currently being processed.
Edit: Spoke too soon, that won't happen as the image is already removed from the Cache.

For consistency the calls to scope.manager.itemError/End should probably happen after processing all callbacks. Looking at FileLoader that is how it's done there as well.

Other than that, looks good to me.

@Mugen87 Mugen87 added this to the r178 milestone Jun 13, 2025
@Mugen87 Mugen87 merged commit 4dd9c05 into mrdoob:dev Jun 13, 2025
26 of 28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

How about change the ImageLoader cache save timing

2 participants