entangled-worlds-orbiters-shared 2.9.0 → 2.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "entangled-worlds-orbiters-shared",
3
- "version": "2.9.0",
3
+ "version": "2.9.1",
4
4
  "description": "Shared library for entangled worlds orbiters - contains core world rendering, moons system, and UI components",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -56,25 +56,47 @@ function detectExtension(faceKeys) {
56
56
  return 'ktx2'; // default assumption
57
57
  }
58
58
 
59
+ // How many faces may be in flight at once. Not six: the KTX2 worker pool three.js gives us holds
60
+ // four transcoder workers, each carrying its own copy of the basis wasm, and that same pool also
61
+ // serves body textures and GLBs. Asking for all six together spawned every worker and queued the
62
+ // other loaders behind the whole sky; measured on cold fetches it was no faster than three.
63
+ const FACES_IN_FLIGHT = 3;
64
+
59
65
  // Load KTX2 faces and create skybox mesh
60
66
  async function loadFacesKTX2(faceKeys, renderer) {
61
67
  const loader = getKTX2LoaderForRenderer(renderer);
68
+ const anisotropy = Math.min(8, renderer.capabilities.getMaxAnisotropy());
69
+
70
+ const loadFace = async face => {
71
+ const tex = await loader.loadAsync(faceKeys[face]);
72
+ tex.flipY = false;
73
+ tex.wrapS = THREE.ClampToEdgeWrapping;
74
+ tex.wrapT = THREE.ClampToEdgeWrapping;
75
+ tex.anisotropy = anisotropy;
76
+ return { face, tex };
77
+ };
78
+
79
+ // Faces overlap instead of chaining. Awaiting each one in turn made every face wait on the
80
+ // previous face's download *and* its transcode, so the sky arrived six round trips deep.
81
+ const settled = [];
82
+ for (let i = 0; i < FACE_ORDER.length; i += FACES_IN_FLIGHT) {
83
+ const wave = FACE_ORDER.slice(i, i + FACES_IN_FLIGHT).map(face => loadFace(face));
84
+ settled.push(...await Promise.allSettled(wave));
85
+ }
86
+
87
+ // allSettled, not all: a bare Promise.all rejects on the first failure and leaks the faces that
88
+ // land after it. Settle every face first, then dispose everything that arrived before rethrowing.
62
89
  const loaded = [];
63
-
64
- for (const face of FACE_ORDER) {
65
- const url = faceKeys[face];
66
- try {
67
- const tex = await loader.loadAsync(url);
68
- tex.flipY = false;
69
- tex.wrapS = THREE.ClampToEdgeWrapping;
70
- tex.wrapT = THREE.ClampToEdgeWrapping;
71
- tex.anisotropy = Math.min(8, renderer.capabilities.getMaxAnisotropy());
72
- loaded.push({ face, tex });
73
- } catch (e) {
74
- // Dispose already loaded textures on failure
75
- loaded.forEach(f => f.tex.dispose?.());
76
- throw e;
77
- }
90
+ let failure = null;
91
+ for (const result of settled) {
92
+ if (result.status === 'fulfilled') loaded.push(result.value);
93
+ else if (!failure) failure = result.reason;
94
+ }
95
+
96
+ if (failure) {
97
+ // Dispose already loaded textures on failure
98
+ loaded.forEach(f => f.tex.dispose?.());
99
+ throw failure;
78
100
  }
79
101
 
80
102
  const size = 500;