Tested on Version “@babylonjs/core”: “^8.37.1”
Video
https://github.com/user-attachments/assets/8f34cd11-2ec6-49f3-bcaf-e6c69930deed
Desktop:
-
OS: Mac M4 - 15.7.1
-
Browser: chrome
Additional context
Hi, I noticed that model flickers during animation as shown in video on WebGPU in version 8.37.1 (works well on 7.42.0) when I switch to WebGL flickering is gone. Below minimal code for reproduction:
// MinimalAnimDemo.js
import { Engine } from '@babylonjs/core/Engines/engine';
import { WebGPUEngine } from '@babylonjs/core/Engines/webgpuEngine';
import { Scene } from '@babylonjs/core/scene';
import { ArcRotateCamera } from '@babylonjs/core/Cameras/arcRotateCamera';
import { Vector3 } from '@babylonjs/core/Maths/math.vector';
import { HemisphericLight } from '@babylonjs/core/Lights/hemisphericLight';
import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
import { SceneLoader } from '@babylonjs/core/Loading/sceneLoader';
import '@babylonjs/loaders/glTF';
export class MinimalAnimDemo {
static async create(canvas, { modelUrl, dpr = 1 } = {}) {
if (!canvas) throw new Error('Canvas is required');
if (!modelUrl) throw new Error('modelUrl is required');
const engine = await MinimalAnimDemo._createEngine(canvas, dpr);
const scene = new Scene(engine);
const camera = new ArcRotateCamera(
'cam',
Math.PI / 4,
Math.PI / 3,
2.5,
new Vector3(0, 0.8, 0),
scene
);
camera.attachControl(canvas, true);
new HemisphericLight('hemi', new Vector3(0, 1, 0), scene);
const root = new TransformNode('root', scene);
const result = await SceneLoader.ImportMeshAsync('', '', modelUrl, scene);
result.meshes.forEach((m) => {
if (m === scene.meshes[0]) return;
m.parent = root;
});
if (result.meshes.length > 0) {
const bb = result.meshes[0].getHierarchyBoundingVectors(true);
const center = bb.min.add(bb.max).scale(0.5);
camera.target = center;
}
console.log(
'[MinimalAnimDemo] loaded animationGroups:',
result.animationGroups.length
);
const app = new MinimalAnimDemo(engine, scene, camera, root, {
animationGroups: result.animationGroups || [],
});
// 4) Render loop
engine.runRenderLoop(() => {
scene.render();
});
// resize
const onResize = () => engine.resize();
window.addEventListener('resize', onResize);
app._onResize = onResize;
console.log(scene);
scene.animationGroups.forEach((g) => {
g.stop();
g.reset();
g.play(true); // loop = true
});
return app;
}
static async _createEngine(canvas, dpr) {
const hasNavigatorGPU = typeof navigator !== 'undefined' && !!navigator.gpu;
if (hasNavigatorGPU) {
const engine = new WebGPUEngine(canvas, {
adaptToDeviceRatio: false,
});
await engine.initAsync();
engine.setHardwareScalingLevel(1 / dpr);
console.log('[MinimalAnimDemo] WebGPU engine');
return engine;
}
const engine = new Engine(canvas, true);
engine.setHardwareScalingLevel(1 / dpr);
console.log('[MinimalAnimDemo] WebGL engine');
return engine;
}
constructor(engine, scene, camera, root, { animationGroups }) {
this.engine = engine;
this.scene = scene;
this.camera = camera;
this.root = root;
this.animationGroups = animationGroups || [];
this._rotationObserver = null;
}
dispose() {
if (this._onResize) {
window.removeEventListener('resize', this._onResize);
this._onResize = null;
}
this.scene.dispose();
this.engine.dispose();
}
}