Thanks for the input. I got it working with Vite and as module worker after some trial and error.
For anyone else trying to get WebGPU running in offscreen canvas and bundling under Vite.
My vite.config.js looks like this:
import { defineConfig } from 'vite';
import { comlink } from 'vite-plugin-comlink';
import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";
import { viteCommonjs } from '@originjs/vite-plugin-commonjs'
export default defineConfig({
plugins: [
viteCommonjs(),
wasm(),
topLevelAwait(),
{
name: "configure-response-headers",
configureServer: (server) => {
server.middlewares.use((_req, res, next) => {
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
next();
});
},
},
comlink(),
],
worker: {
plugins: [
viteCommonjs(),
wasm(),
topLevelAwait(),
comlink(),
{
name: "configure-response-headers",
configureServer: (server) => {
server.middlewares.use((_req, res, next) => {
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
next();
});
},
},
],
},
server: {
port: 3000,
},
build: {
target: 'esnext',
},
});
viteCommonjs plugin is needed to deal with the glslang and twgls wasm modules and their js factory wrappers.
All other plugins are required as well. You’ll see I am using comlink + vite-plugin-comlink for auto wrapping and exposing of worker methods with type inferring & easy async handling between main and worker threads.
Worker code for initializing babylon WebGPUEngine within a module worker (no importScript required)
import * as BABYLON from "@babylonjs/core";
import { EngineMessage } from "../Game";
import glslangWasm from '../Libs/glslang.wasm?url';
import glslang from '../Libs/glslang';
import twgslWasm from '../Libs/twgsl.wasm?url';
import twgsl from '../Libs/twgsl';
let _engine: BABYLON.WebGPUEngine | null = null;
let _canvas: HTMLCanvasElement | null = null;
let _callbackState: ((message: EngineMessage) => void) | null = null;
let _glslangModule: any = null;
let _twgslModule: any = null;
export const InitEngine = async (canvas: HTMLCanvasElement) => {
try {
if(!_callbackState) throw new Error("State callback not registered");
const webgpuSupported = await BABYLON.WebGPUEngine.IsSupportedAsync;
if(!webgpuSupported) throw new Error("WebGPU not supported");
_glslangModule = await glslang(glslangWasm);
_twgslModule = await twgsl(twgslWasm);
_canvas = canvas;
_engine = new BABYLON.WebGPUEngine(canvas, {
twgslOptions: { twgsl: _twgslModule },
glslangOptions: { glslang: _glslangModule }
});
await _engine.initAsync();
_callbackState(EngineMessage.Init);
} catch (err) {
console.error("Engine Init Error", err);
_callbackState(EngineMessage.Error);
}
}
You’ll see the wasm modules are imported with ‘?url’ postfix. Afterwards wasm modules are loaded simply like so await glslang(glslangWasm)…
Ignore _callbackState method, just a state update method for reporting to main thread.