playcanvas
Version:
PlayCanvas WebGL game engine
166 lines (164 loc) • 6.27 kB
JavaScript
// wrapper function that caches the func result on first invocation and
// then subsequently returns the cached value
var cachedResult = (func)=>{
var uninitToken = {};
var result = uninitToken;
return ()=>{
if (result === uninitToken) {
result = func();
}
return result;
};
};
class Impl {
// load a script
static loadScript(url, callback) {
var s = document.createElement("script");
s.setAttribute('src', url);
s.onload = ()=>{
callback(null);
};
s.onerror = ()=>{
callback("Failed to load script='" + url + "'");
};
document.body.appendChild(s);
}
// load a wasm module
static loadWasm(moduleName, config, callback) {
var loadUrl = Impl.wasmSupported() && config.glueUrl && config.wasmUrl ? config.glueUrl : config.fallbackUrl;
if (loadUrl) {
Impl.loadScript(loadUrl, (err)=>{
if (err) {
callback(err, null);
} else {
var module = window[moduleName];
// clear the module from the global window since we used to store global instance here
window[moduleName] = undefined;
// instantiate the module
module({
locateFile: ()=>config.wasmUrl,
onAbort: ()=>{
callback('wasm module aborted.');
}
}).then((instance)=>{
callback(null, instance);
});
}
});
} else {
callback('No supported wasm modules found.', null);
}
}
// get state object for the named module
static getModule(name) {
if (!Impl.modules.hasOwnProperty(name)) {
Impl.modules[name] = {
config: null,
initializing: false,
instance: null,
callbacks: []
};
}
return Impl.modules[name];
}
static initialize(moduleName, module) {
if (module.initializing) {
return;
}
var config = module.config;
if (config.glueUrl || config.wasmUrl || config.fallbackUrl) {
module.initializing = true;
Impl.loadWasm(moduleName, config, (err, instance)=>{
if (err) {
if (config.errorHandler) {
config.errorHandler(err);
} else {
console.error("failed to initialize module=" + moduleName + " error=" + err);
}
} else {
module.instance = instance;
module.callbacks.forEach((callback)=>{
callback(instance);
});
}
});
}
}
}
Impl.modules = {};
// returns true if the running host supports wasm modules (all browsers except IE)
Impl.wasmSupported = cachedResult(()=>{
try {
if (typeof WebAssembly === 'object' && typeof WebAssembly.instantiate === 'function') {
var module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00));
if (module instanceof WebAssembly.Module) {
return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
}
}
} catch (e) {}
return false;
});
/**
* Callback used by {@link Module#setConfig}.
*
* @callback ModuleErrorCallback
* @param {string} error - If the instance fails to load this will contain a description of the error.
*/ /**
* Callback used by {@link Module#getInstance}.
*
* @callback ModuleInstanceCallback
* @param {any} moduleInstance - The module instance.
*/ /**
* A pure static utility class which supports immediate and lazy loading of wasm modules.
*/ class WasmModule {
/**
* Set a wasm module's configuration.
*
* @param {string} moduleName - Name of the module.
* @param {object} [config] - The configuration object.
* @param {string} [config.glueUrl] - URL of glue script.
* @param {string} [config.wasmUrl] - URL of the wasm script.
* @param {string} [config.fallbackUrl] - URL of the fallback script to use when wasm modules
* aren't supported.
* @param {number} [config.numWorkers] - For modules running on worker threads, the number of
* threads to use. Default value is based on module implementation.
* @param {ModuleErrorCallback} [config.errorHandler] - Function to be called if the module fails
* to download.
*/ static setConfig(moduleName, config) {
var module = Impl.getModule(moduleName);
module.config = config;
if (module.callbacks.length > 0) {
// start module initialize immediately since there are pending getInstance requests
Impl.initialize(moduleName, module);
}
}
/**
* Get a wasm module's configuration.
*
* @param {string} moduleName - Name of the module.
* @returns {object | undefined} The previously set configuration.
*/ static getConfig(moduleName) {
var _Impl_modules_moduleName, _Impl_modules;
return (_Impl_modules = Impl.modules) == null ? undefined : (_Impl_modules_moduleName = _Impl_modules[moduleName]) == null ? undefined : _Impl_modules_moduleName.config;
}
/**
* Get a wasm module instance. The instance will be created if necessary and returned
* in the second parameter to callback.
*
* @param {string} moduleName - Name of the module.
* @param {ModuleInstanceCallback} callback - The function called when the instance is
* available.
*/ static getInstance(moduleName, callback) {
var module = Impl.getModule(moduleName);
if (module.instance) {
callback(module.instance);
} else {
module.callbacks.push(callback);
if (module.config) {
// config has been provided, kick off module initialize
Impl.initialize(moduleName, module);
}
}
}
}
export { WasmModule };