Skip to content

Commit

Permalink
optimize: ensure wasm module ready before importing (cocos#15631)
Browse files Browse the repository at this point in the history
* optimize: ensure wasm module ready before importing

* Update @types/pal/minigame.d.ts

* Update pal/wasm/wasm-minigame.ts

* use reject instead of warnning

* optimize: add promiseToLoadWasmModule

* add shouldUseWasmModule method to simplify code

* fix: subpackage loading on taobao minigame platform

* Update pal/wasm/wasm-minigame.ts
  • Loading branch information
PPpro authored Jul 6, 2023
1 parent 08d9a34 commit 649e81a
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 49 deletions.
19 changes: 19 additions & 0 deletions @types/pal/minigame.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ declare module 'pal/minigame' {

// file system
getFileSystemManager(): FileSystemManager;
loadSubpackage? (option: LoadSubpackageOption): LoadSubpackageTask;

// input
onTouchStart: IEventManager<TouchEvent>;
Expand Down Expand Up @@ -249,3 +250,21 @@ declare class InnerAudioContext {
seek(position:number): any;
stop(): any;
}

interface LoadSubpackageOption {
name: string;
fail?: (...args: unknown[]) => void;
success?: (...args: unknown[]) => void;
complete?: (...args: unknown[]) => void;
}
interface LoadSubpackageTask {
onProgressUpdate(
listener: LoadSubpackageTaskOnProgressUpdateCallback
): void;
}
type LoadSubpackageTaskOnProgressUpdateCallback = (result: LoadSubpackageTaskOnProgressUpdateListenerResult) => void;
interface LoadSubpackageTaskOnProgressUpdateListenerResult {
progress: number;
totalBytesExpectedToWrite: number;
totalBytesWritten: number;
}
7 changes: 7 additions & 0 deletions @types/pal/wasm.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,11 @@ declare module 'pal/wasm' {
* @param binaryUrl the url of wasm or js mem, this should be a url relative from build output chunk.
*/
export function fetchBuffer (binaryUrl: string): Promise<ArrayBuffer>;

/**
* Sometimes we need to put wasm modules in subpackage to reduce code size.
* In this case we need to ensure that the wasm modules is ready before we import them.
* Please remember to invoke this method before we import wasm modules.
*/
export function ensureWasmModuleReady (): Promise<void>;
}
6 changes: 6 additions & 0 deletions cc.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,12 @@
"type": "boolean",
"value": false,
"internal": true
},
"WASM_SUBPACKAGE": {
"comment": "An internal constant to indicate whether we use wasm assets as minigame subpackage.\nThis is useful when we need to reduce code size.",
"type": "boolean",
"value": false,
"internal": true
}
},

Expand Down
70 changes: 40 additions & 30 deletions cocos/physics/bullet/instantiated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@
THE SOFTWARE.
*/

import { instantiateWasm } from 'pal/wasm';
import { ensureWasmModuleReady, instantiateWasm } from 'pal/wasm';
import { CULL_ASM_JS_MODULE, FORCE_BANNING_BULLET_WASM, WASM_SUPPORT_MODE } from 'internal:constants';
import bulletWasmUrl from 'external:emscripten/bullet/bullet.wasm';
import asmFactory from 'external:emscripten/bullet/bullet.asm.js';
import { game } from '../../game';
import { debug, error, getError, sys } from '../../core';
import { pageSize, pageCount, importFunc } from './bullet-env';
Expand Down Expand Up @@ -77,19 +75,21 @@ function initWasm (wasmUrl: string, importObject: WebAssembly.Imports) {
});
}

function initAsm (resolve, reject) {
if (CULL_ASM_JS_MODULE) {
reject(getError(4601));
return;
}
debug('[Physics][Bullet]: Using asmjs Bullet libs.');
const env: any = importFunc;
const wasmMemory: any = {};
wasmMemory.buffer = new ArrayBuffer(pageSize * pageCount);
env.memory = wasmMemory;
const btInstance = asmFactory(env, wasmMemory);
Object.assign(bt, btInstance);
resolve();
function initAsmJS (asmFactory): Promise<void> {
return new Promise<void>((resolve, reject) => {
if (CULL_ASM_JS_MODULE) {
reject(getError(4601));
return;
}
debug('[Physics][Bullet]: Using asmjs Bullet libs.');
const env: any = importFunc;
const wasmMemory: any = {};
wasmMemory.buffer = new ArrayBuffer(pageSize * pageCount);
env.memory = wasmMemory;
const btInstance = asmFactory(env, wasmMemory);
Object.assign(bt, btInstance);
resolve();
});
}

function getImportObject (): WebAssembly.Imports {
Expand Down Expand Up @@ -117,23 +117,33 @@ if (!FORCE_BANNING_BULLET_WASM) {
}
}

function shouldUseWasmModule () {
if (FORCE_BANNING_BULLET_WASM) {
return false;
} else if (WASM_SUPPORT_MODE === WebAssemblySupportMode.MAYBE_SUPPORT) {
return sys.hasFeature(sys.Feature.WASM);
} else if (WASM_SUPPORT_MODE === WebAssemblySupportMode.SUPPORT) {
return true;
} else {
return false;
}
}

export function waitForAmmoInstantiation () {
return new Promise<void>((resolve, reject) => {
const errorReport = (msg: any) => { error(msg); };
if (FORCE_BANNING_BULLET_WASM) {
initAsm(resolve, reject);
} else if (WASM_SUPPORT_MODE === WebAssemblySupportMode.MAYBE_SUPPORT) {
if (sys.hasFeature(sys.Feature.WASM)) {
initWasm(bulletWasmUrl, importObject).then(resolve).catch(errorReport);
} else {
initAsm(resolve, reject);
}
} else if (WASM_SUPPORT_MODE === WebAssemblySupportMode.SUPPORT) {
initWasm(bulletWasmUrl, importObject).then(resolve).catch(errorReport);
const errorReport = (msg: any) => { error(msg); };
return ensureWasmModuleReady().then(() => Promise.all([
import('external:emscripten/bullet/bullet.wasm'),
import('external:emscripten/bullet/bullet.asm.js'),
]).then(([
{ default: bulletWasmUrl },
{ default: asmFactory },
]) => {
if (shouldUseWasmModule()) {
return initWasm(bulletWasmUrl, importObject);
} else {
initAsm(resolve, reject);
return initAsmJS(asmFactory);
}
});
})).catch(errorReport);
}

game.onPostInfrastructureInitDelegate.add(waitForAmmoInstantiation);
46 changes: 30 additions & 16 deletions cocos/spine/lib/instantiated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@
THE SOFTWARE.
*/

import { instantiateWasm, fetchBuffer } from 'pal/wasm';
import { instantiateWasm, fetchBuffer, ensureWasmModuleReady } from 'pal/wasm';
import { JSB, WASM_SUPPORT_MODE, CULL_ASM_JS_MODULE } from 'internal:constants';
import asmFactory from 'external:emscripten/spine/spine.asm.js';
import asmJsMemUrl from 'external:emscripten/spine/spine.js.mem';
import wasmFactory from 'external:emscripten/spine/spine.wasm.js';
import spineWasmUrl from 'external:emscripten/spine/spine.wasm';
import { game } from '../../game';
import { getError, error, sys } from '../../core';
import { WebAssemblySupportMode } from '../../misc/webassembly-support';
Expand All @@ -44,8 +40,9 @@ const MEMORYSIZE = PAGESIZE * PAGECOUNT; // 32 MiB

let wasmInstance: SpineWasm.instance = null!;
const registerList: any[] = [];

///////////////////////////////////////////////////////////////////////////////////////////////////
function initWasm (wasmUrl): Promise<void> {
function initWasm (wasmFactory, wasmUrl): Promise<void> {
return new Promise<void>((resolve, reject) => {
const errorMessage = (err: any): string => `[Spine]: Spine wasm load failed: ${err}`;
wasmFactory({
Expand All @@ -65,7 +62,7 @@ function initWasm (wasmUrl): Promise<void> {
});
}

function initAsm (): Promise<void> {
function initAsmJS (asmFactory, asmJsMemUrl): Promise<void> {
return new Promise<void>((resolve, reject) => {
if (CULL_ASM_JS_MODULE) {
reject(getError(4601));
Expand All @@ -92,20 +89,37 @@ function initAsm (): Promise<void> {
});
}

export function waitForSpineWasmInstantiation (): Promise<void> {
const errorReport = (msg: any) => { error(msg); };
function shouldUseWasmModule (): boolean {
if (WASM_SUPPORT_MODE === WebAssemblySupportMode.MAYBE_SUPPORT) {
if (sys.hasFeature(sys.Feature.WASM)) {
return initWasm(spineWasmUrl).catch(errorReport);
} else {
return initAsm().catch(errorReport);
}
return sys.hasFeature(sys.Feature.WASM);
} else if (WASM_SUPPORT_MODE === WebAssemblySupportMode.SUPPORT) {
return initWasm(spineWasmUrl).catch(errorReport);
return true;
} else {
return initAsm().catch(errorReport);
return false;
}
}

export function waitForSpineWasmInstantiation (): Promise<void> {
const errorReport = (msg: any) => { error(msg); };
return ensureWasmModuleReady().then(() => Promise.all([
import('external:emscripten/spine/spine.asm.js'),
import('external:emscripten/spine/spine.js.mem'),
import('external:emscripten/spine/spine.wasm.js'),
import('external:emscripten/spine/spine.wasm'),
]).then(([
{ default: asmFactory },
{ default: asmJsMemUrl },
{ default: wasmFactory },
{ default: spineWasmUrl },
]) => {
if (shouldUseWasmModule()) {
return initWasm(wasmFactory, spineWasmUrl);
} else {
return initAsmJS(asmFactory, asmJsMemUrl);
}
})).catch(errorReport);
}

if (!JSB) {
game.onPostInfrastructureInitDelegate.add(waitForSpineWasmInstantiation);
registerList.push(overrideSpineDefine);
Expand Down
2 changes: 2 additions & 0 deletions pal/minigame/taobao_minigame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,6 @@ function adapterGL (gl) {
}
}

minigame.loadSubpackage = my.loadSubPackage.bind(my);

export { minigame };
56 changes: 53 additions & 3 deletions pal/wasm/wasm-minigame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@
THE SOFTWARE.
*/

import { XIAOMI } from 'internal:constants';
import { HUAWEI, TAOBAO_MINIGAME, WASM_SUBPACKAGE, XIAOMI } from 'internal:constants';
import { minigame } from 'pal/minigame';
import { basename } from '../../cocos/core/utils/path';

export function instantiateWasm (wasmUrl: string, importObject: WebAssembly.Imports): Promise<any> {
wasmUrl = `cocos-js/${wasmUrl}`;
return WebAssembly.instantiate(wasmUrl, importObject);
return getPlatformBinaryUrl(wasmUrl).then((url) => WebAssembly.instantiate(url, importObject));
}

export function fetchBuffer (binaryUrl: string): Promise<ArrayBuffer> {
Expand All @@ -44,6 +45,49 @@ export function fetchBuffer (binaryUrl: string): Promise<ArrayBuffer> {
});
}

function loadSubpackage (name: string): Promise<void> {
return new Promise((resolve, reject) => {
if (minigame.loadSubpackage) {
minigame.loadSubpackage({
name,
success () {
resolve();
},
fail (err) {
reject(err);
},
});
} else {
reject(new Error(`Subpackage is not supported on this platform`));
}
});
}

let promiseToLoadWasmModule: Promise<void> | undefined;

export function ensureWasmModuleReady (): Promise<void> {
if (promiseToLoadWasmModule) {
return promiseToLoadWasmModule;
}
return promiseToLoadWasmModule = new Promise<void>((resolve, reject) => {
if (WASM_SUBPACKAGE) {
if (HUAWEI) {
// NOTE: huawei quick game doesn't support concurrent loading subpackage.
loadSubpackage('__ccWasmAssetSubpkg__').then(() => loadSubpackage('__ccWasmChunkSubpkg__')).then(() => {
resolve();
}).catch(reject);
} else {
Promise.all(['__ccWasmAssetSubpkg__', '__ccWasmChunkSubpkg__'].map((pkgName) => loadSubpackage(pkgName)))
.then(() => {
resolve();
}).catch(reject);
}
} else {
resolve();
}
});
}

/**
* The binary url can be different on different platforms.
* @param binaryUrl the basic build output binary url
Expand All @@ -53,6 +97,12 @@ function getPlatformBinaryUrl (binaryUrl: string): Promise<string> {
return new Promise((resolve) => {
if (XIAOMI) {
resolve(`src/cocos-js/${binaryUrl}`);
} if (TAOBAO_MINIGAME && WASM_SUBPACKAGE) {
if (minigame.isDevTool) {
resolve(`cocos-js/${binaryUrl}`);
} else {
resolve(`__ccWasmAssetSubpkg__/${basename(binaryUrl)}`);
}
} else {
resolve(`cocos-js/${binaryUrl}`);
}
Expand Down
4 changes: 4 additions & 0 deletions pal/wasm/wasm-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,7 @@ export function fetchBuffer (binaryUrl: string): Promise<ArrayBuffer> {
}
});
}

export function ensureWasmModuleReady (): Promise<void> {
return Promise.resolve();
}
4 changes: 4 additions & 0 deletions pal/wasm/wasm-web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,7 @@ export function fetchBuffer (binaryUrl: string): Promise<ArrayBuffer> {
}
});
}

export function ensureWasmModuleReady (): Promise<void> {
return Promise.resolve();
}

0 comments on commit 649e81a

Please sign in to comment.