Files
La-Fabrik/src/utils/three/optimizeGLTFScene.ts
T
Tom Boullay d217c3376b fix(handtracking): reduce GPU pressure on WebGL context loss
Several mitigations against the WebGL context lost that fires when
hand tracking starts on a loaded scene:

- Canvas: fixed DPR [1,1], antialias off, scoped id="game-canvas",
  context-lost handler releases MediaPipe and logs GPU memory counters
- optimizeGLTFScene: cap anisotropy at 2 and stop forcing mipmaps /
  needsUpdate on every pass — avoids massive texture re-uploads
- MediaPipe: force CPU delegate (HAND_TRACKING_BROWSER_DELEGATE),
  cache the landmarker instance, and expose releaseBrowserHandLandmarker
- useBrowserHandTracking / useRemoteHandTracking: idempotent cleanup
  guarded by a cleanedUp flag, try/catch around the detect loop, and
  release of the landmarker on stop
- World: mount HandTrackingGlove only when the matching hand is
  actually present in the snapshot (status connected + hands.length > 0)
- HandTrackingGlove: drop the eager useGLTF.preload that was running
  at startup whether or not hand tracking was used

Does not yet absorb the React StrictMode double-mount — that is the
follow-up commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 16:48:39 +02:00

70 lines
1.6 KiB
TypeScript

import * as THREE from "three";
const TEXTURE_KEYS = [
"map",
"alphaMap",
"aoMap",
"bumpMap",
"displacementMap",
"emissiveMap",
"envMap",
"lightMap",
"metalnessMap",
"normalMap",
"roughnessMap",
] as const;
type TextureKey = (typeof TEXTURE_KEYS)[number];
type TexturedMaterial = THREE.Material &
Partial<Record<TextureKey, THREE.Texture>>;
const optimizedTextures = new WeakSet<THREE.Texture>();
const MAX_GLTF_TEXTURE_ANISOTROPY = 2;
function optimizeTexture(texture: THREE.Texture, maxAnisotropy: number): void {
if (optimizedTextures.has(texture)) return;
optimizedTextures.add(texture);
const nextAnisotropy = Math.min(
MAX_GLTF_TEXTURE_ANISOTROPY,
Math.max(1, maxAnisotropy),
);
if (texture.anisotropy > nextAnisotropy) {
texture.anisotropy = nextAnisotropy;
texture.needsUpdate = true;
}
}
function optimizeMaterialTextures(
material: THREE.Material,
maxAnisotropy: number,
): void {
const texturedMaterial = material as TexturedMaterial;
for (const key of TEXTURE_KEYS) {
const texture = texturedMaterial[key];
if (texture instanceof THREE.Texture) {
optimizeTexture(texture, maxAnisotropy);
}
}
}
export function optimizeGLTFSceneTextures(
scene: THREE.Object3D,
maxAnisotropy: number,
): void {
scene.traverse((child) => {
if (!(child instanceof THREE.Mesh)) return;
if (Array.isArray(child.material)) {
for (const material of child.material) {
optimizeMaterialTextures(material, maxAnisotropy);
}
return;
}
optimizeMaterialTextures(child.material, maxAnisotropy);
});
}