How do I activate the webGPU in a complex scene?

Hello, after unsuccessful searches on the forum for a solution, I’m asking the Babylon community for help. I’m not used to asking for help, but I’m really stuck…

I want to enable WebGPU in my project. Here’s some background:

  • All assets have been modeled by myself on Blender. They are optimized for lightness, going from a high-poly scene of over 500MB on Blender to less than 20MB (including all assets, including LODs) on BabylonJS. All assets exported as GLTF
  • Assets display and load correctly in my scene.
  • I can move around my scene using a first-person view.
  • The scene lags enormously, probably because 20MB is still very heavy to manage in WebGL.
  • I want to activate the WebGPU, I’m using Chrome 114 (official build), I’m also testing on Chrome Canary and Edge (my scene displays and behaves “normally” in WebGL, at 6 FPS). My PC is equipped with an Nvidia RTX 3060, drivers up to date.
  • Here’s what I try to do to activate webGPU, but without success, the scene doesn’t load. The code is over 100 lines long, so I’ll remove anything that doesn’t concern the engine and get straight to the point:
import * as BABYLON from "babylonjs";
import "babylonjs-loaders";
import "babylonjs-serializers";
import * as Lighting from "./Lighting";
import Display from "./Display";
import CameraController from "./Controller";
import '@babylonjs/core/Engines/WebGPU/Extensions/'

let engine;
let scene;
let cameraController;

async function initScene(canvas) {
    if (navigator.gpu) {
        engine = new BABYLON.WebGPUEngine(canvas);
        await engine.initAsync();
    } else {
        let engineOptions = { useWebGL2: true };
        engine = new BABYLON.Engine(canvas, true, engineOptions);
    }

    scene = new BABYLON.Scene(engine);
    Lighting.applyLighting(scene, BABYLON.Tools.ToRadians(115));

    cameraController = new CameraController(scene, canvas);
    cameraController.createCamera();

// various settings for the rendering pipeline

    qualityParameters();

    new Display(scene); 

    engine.runRenderLoop(() => {
        scene.render();
    });

    window.addEventListener("resize", onWindowResize);
    window.addEventListener('resize', function () {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        if (engine) {
            engine.resize();
        }
    });
}

window.onload = async function () {
    const canvas = document.getElementById("renderCanvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    await initScene(canvas);
};

window.onbeforeunload = function () {
    if (cameraController) {
        cameraController.dispose();
    }

    engine.dispose();
    window.removeEventListener("resize", onWindowResize);
};

function onWindowResize() {
    if (engine) {
        engine.resize();
        const camera = scene.getCameraByName("Camera");
        if (camera) {
            camera.aspectRatio = engine.getAspectRatio(camera);
            camera.fov = Math.min(window.innerWidth / window.innerHeight, 1);
            camera.fovMode = BABYLON.Camera.FOVMODE_HORIZONTAL_FIXED;
        }
    }
}

export { initScene };

I had made a first attempt to install the webGPU, there again I had only WebGL but at least my scene was displayed and I could move inside, despite the significant lag due to the mediocre framerate :

import * as BABYLON from "babylonjs";
import "babylonjs-loaders";
import "babylonjs-serializers";
import * as Lighting from "./Lighting";
import Display from "./Display";
import CameraController from "./Controller";

let engine;
let scene;
let cameraController;

function initScene(canvas) {
    let engineOptions = { useWebGL2: true };

    if (navigator.gpu) {
        engineOptions = { useWebGPU: true };
    }

    engine = new BABYLON.Engine(canvas, true, engineOptions);
    scene = new BABYLON.Scene(engine);

    Lighting.applyLighting(scene, BABYLON.Tools.ToRadians(115));

    cameraController = new CameraController(scene, canvas);
    cameraController.createCamera();

// various settings for the rendering pipeline

  qualityParameters();

  new Display(scene); // Utilisez Display.js pour upload et afficher les assets

  engine.runRenderLoop(() => {
      scene.render();
  });

    window.addEventListener("resize", onWindowResize);
    window.addEventListener('resize', function () {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
      if (engine) {
        engine.resize();
      }
    });
}

window.onload = function () {
    const canvas = document.getElementById("renderCanvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    initScene(canvas);
};

window.onbeforeunload = function () {
    if (cameraController) {
        cameraController.dispose();
    }

    engine.dispose();
    window.removeEventListener("resize", onWindowResize);
};

function onWindowResize() {
    if (engine) {
        engine.resize();
        const camera = scene.getCameraByName("Camera");
        if (camera) {
            camera.aspectRatio = engine.getAspectRatio(camera);
            camera.fov = Math.min(window.innerWidth / window.innerHeight, 1);
            camera.fovMode = BABYLON.Camera.FOVMODE_HORIZONTAL_FIXED;
        }
    }
}

export { initScene };

Here are 2 screenshots of my BabylonJS scene and a render preview I made on blender, this is the result I’d like to approach. I’m just a 3D artist, not a developer.

Can anyone help me? Thank you very much



What happens if you try to use only the WebGPU engine:

engine = new BABYLON.WebGPUEngine(canvas);
await engine.initAsync();

Your code should use the WebGPU engine if navigator.gpu is true and Chrome is up to date. It’s strange.

On the other hand in your second block of code, you do not initialize the WebGPU engine but WebGL2

For WebGL new BABYLON.Engine()
For WebGPU new BABYLON.WebGPUEngine()

This code will not initialize WebGPU but WebGL

if (navigator.gpu) {
    engineOptions = { useWebGPU: true };
}
new BABYLON.Engine(canvas, true, engineOptions);

Thanks for your answer. Indeed, when I go back to the 1st code, which should activate WebGPU, the scene starts loading, a few assets, then abruptly stops loading. When I wait a full minute, the scene loads almost completely but freezes and displays a blank screen when I want to move within the scene. I then go to the console to see what’s going on, and there I have dozens and dozens of errors of this type (I’m only copying a few):

logger.ts:103 BJS - [16:40:37]: WebGPU uncaptured error (1): [object GPUValidationError] - [Texture “Texture2D_opaqueSceneTexture_1024x1024x1_wmips_rgba16float_samples1”] usage (TextureUsage::(TextureBinding|RenderAttachment)) includes writable usage and another usage in the same synchronization scope.

  • While validating render pass usage.

[Invalid CommandBuffer] is invalid.
at ValidateObject (…..\third_party\dawn\src\dawn\native\Device.cpp:689)
at ValidateSubmit (…..\third_party\dawn\src\dawn\native\Queue.cpp:444)

logger.ts:103 BJS - [16:44:18]: WebGPU uncaptured error (2): [object GPUValidationError] - [Invalid CommandBuffer] is invalid.
at ValidateObject (…..\third_party\dawn\src\dawn\native\Device.cpp:689)
at ValidateSubmit (…..\third_party\dawn\src\dawn\native\Queue.cpp:444)

Do you know if there is a simple project in the Playground that shows how to import and use webGPU in a project? I’ve seen plenty of demos that use webGPU, but without explicitly finding code that imports webGPU and then uses it in the scene.

this is not a very good means to measure the performance of a 3D scene. It can probably give a general indication that the file is smaller/lighter in a few regards yes , but your 20mb measurement doesnt say what makes up the file really.

In your screenshot of the inspector i see all the info that matters. 347 objects, about 5 million polygons , 644 draw calls , 198 materials and 266 textures.

This is not going to run well ever. I doubt webGPU will help to the extent you think it will. Is this project meant to be public facing, consumed by the average user?

For the moment, it’s a prototype that I’m using myself locally on my PC. I’d like to activate the webGPU to see if there’s any difference in performance on such a scene.

Indeed, it’s extremely heavy, because in reality the Blender scene is only supposed to have less than 30 materials, and I don’t need more in BabylonJS. 266 textures is also excessive, in fact I was counting on 40/50 (with NRM textures, diffuse, etc., all included), no more. I therefore included console logs in my code to find errors, especially when assigning materials:

if (!(mesh instanceof InstancedMesh && sourceMesh)) {
              console.log("Unaffected mesh material name:", mesh.material.name);
            }
          }
        });

And in total, I have more than 500 errors in the console for “Unaffected mesh material name:”, and for the following, although the scene is displayed normally. I also had an initial problem importing GLTFs, some of which weren’t displayed, so I had to use a try catch to find them, which worked fine

loadAssets(assetNames) {
    assetNames.forEach((assetName) => {
      SceneLoader.ImportMesh("", "./assets/", `${assetName}.glb`, this.scene, (meshes) => {
        let model = meshes[0];
        console.log(`${assetName} mesh loaded:`, model);
        let allMeshes = model.getChildren(); // Access child meshes
        allMeshes.forEach((m) => {
          try {
            m.receiveShadows = true;
          } catch (error) {
            console.warn("Error setting receiveShadows for object:", m, error);
          }
          m.checkCollisions = true;
        });

I just flipped normals on blender to see these assets.

In the end, I have the impression that the scene is much heavier than it should be.

When using WebGPU, you must ensure that the scene is created after the engine is loaded.
I have already had several errors because the motor was not yet charged.

for exemple :

await engine.initAsync().then(() => {

    scene = new BABYLON.Scene(engine);

    Lighting.applyLighting(scene, BABYLON.Tools.ToRadians(115));

    cameraController = new CameraController(scene, canvas);
    cameraController.createCamera();

    // various settings for the rendering pipeline

    qualityParameters();

    new Display(scene); 

    engine.runRenderLoop(() => {
        scene.render();
    });

});

Thanks, I also noted what you said about the weight of my scene. What I’m going to do tonight is re-export my scene, I’ll see if the Blender to Babylon export addon works with my version, as previously I exported everything asset by asset. Then I’ll do the test again once all the materials have been installed or replaced by PBRs created on BabylonJS, and finally I’ll compare the results with the debug layer. Then I could try your solution, which will be much more convincing on a more accurate scene.

Please check inside browser’s console “navigator.gpu”. Ensure that it returns object (not undefined) on your app page(!)
I had the same problem that webGPU engine is not created by babylon. Strange problem with webGPU is supported check - #18 by TooCalm
And if it returns undefined ensure that you use https protocol or localhost URL

FYI you can create engine with method BABYLON.EngineFactory.CreateAsync(canvas, {antialias : true});
It will create webGPU if possible webGL otherwise

This error:

logger.ts:103 BJS - [16:40:37]: WebGPU uncaptured error (1): [object GPUValidationError] - 
[Texture “Texture2D_opaqueSceneTexture_1024x1024x1_wmips_rgba16float_samples1”] 
usage (TextureUsage::(TextureBinding|RenderAttachment)) includes writable usage 
and another usage in the same synchronization scope.

means that you are indeed using WebGPU. However, this error should not occur: are you able to share a link to your project so that we can have a deeper look?

Unfortunately, you won’t see any significant difference just by switching to WebGPU. What you could try is enabling the snapshot rendering mode:

1 Like

Thanks for your feedback, it’s helped me understand that in addition to my concern about activating the webGPU, I had a major problem with the weight of my scene, which I’ve started to work on. It’s taking a bit longer than expected to lighten it, so I’m going ahead with it (a matter of a few days) and I’ll be back to let you know as soon as I’ve managed to activate the webGPU in the end, taking your advice into account. To be continued

News from the project. Hurrah! It works, I’ve simplified the code, taking the project point by point:

  • In the scene, I’ve imported the assets asynchronously with
await SceneLoader.ImportMeshAsync("", "./assets/", `${asset}.glb`, scene);
  • I then exported the scene to the index.js component
import { createScene } from './Scene.js';

window.onload = async function() {
    const canvas = document.getElementById('renderCanvas');
    const engine = new BABYLON.WebGPUEngine(canvas);
    
    await engine.initAsync();
    const scene = await createScene(canvas, engine);

And it works, the webGPU is activated. I took the opportunity to review my assets. For example, I didn’t know that joining all the meshes into one before exporting the asset would reduce the number of draw calls. Well, joining all the meshes removes the instances and therefore makes the model heavier, but at least it reduces draw calls. I’m continuing to optimize the scene and will keep you posted on performance!

1 Like

Hi everyone,

I’ve finally managed to get the BabylonJS project on Github to facilitate sharing and collaboration. You can find it here: GitHub - Geoff4vrs/share-babylonjs-scene

I’m very grateful for all the help you’ve provided, and I’m glad to share my work in this example scene! Now that everything is in place, I’m seeking your help to improve the lighting, shadows, and performance of the scene. All codes and assets are in the repository.

To test the project, once you’ve cloned the repository, use npm install to install the dependencies, then npm start to launch the development server. You can find the component files in the src directory. To navigate in the scene, use the arrow keys.

I’m looking forward to your advice and suggestions. How can I improve the visual aspect and performance of the scene?

Thanks a lot!

You should provide a live link so that anyone can look at your project and give feedback / advice.

You can use github.io for this. This is what I did for the WebGPU Ocean demo, where the live link points directly to a dist package built in the repo.

Please also note that my schedule is quite busy and I can’t devote too much time to general code or project review. I’ll be happy to answer any questions you might have, though!

As for performance optimization, there’s a lot of discussion on the subject in this forum, and these dedicated documentation pages:

Thanks for the links and the information for Github.io, I didn’t know, it is helpful. I think that all I need is already here in the forum. Have a nice day!