Files
shooter-demo/client-cocos/assets/scripts/Utils/WasmLoader.ts

321 lines
10 KiB
TypeScript
Raw Normal View History

2025-11-28 18:10:10 +08:00
/**
* WASM加载器
* Cocos Creator官方文档的WASM/ASM加载最佳实践
* https://docs.cocos.com/creator/3.8/manual/zh/advanced-topics/wasm-asm-load.html
*/
import { Asset, assetManager, sys } from 'cc';
import { EDITOR } from 'cc/env';
// ASM模块内存配置常量
const PAGESIZE = 65536; // 64KiB
const PAGECOUNT = 32 * 16; // 可根据需要调整
const MEMORYSIZE = PAGESIZE * PAGECOUNT; // 32 MiB
export interface WasmLoadOptions {
bundleName?: string;
fileName: string;
editorUuid?: string; // 编辑器内的资源UUID
supportAsm?: boolean; // 是否支持ASM回退
asmFileName?: string; // ASM文件名
asmEditorUuid?: string; // ASM文件的编辑器UUID
wasmFactory?: any; // WASM工厂函数
asmFactory?: any; // ASM工厂函数
}
export interface WasmLoadResult {
instance: any;
isWasm: boolean; // true为WASMfalse为ASM
}
export class WasmLoader {
private static instance: WasmLoader;
private initialized = false;
private constructor() { }
public static getInstance(): WasmLoader {
if (!WasmLoader.instance) {
WasmLoader.instance = new WasmLoader();
}
return WasmLoader.instance;
}
/**
* WASM加载器
* WASM/ASM文件的下载器和解析器
*/
public initialize(): void {
if (this.initialized) {
return;
}
// 判断当前平台是否支持加载WASM文件
// Cocos引擎目前暂不支持在iOS平台加载WASM文件
if (sys.hasFeature(sys.Feature.WASM) || (sys.isNative && sys.os !== sys.OS.IOS)) {
if (sys.isNative) {
// 原生平台
//@ts-ignore
assetManager.downloader.register('.wasm', assetManager.downloader._downloaders[".bin"]);
//@ts-ignore
assetManager.parser.register('.wasm', assetManager.parser._parsers[".bin"]);
} else if (sys.isBrowser || sys.platform === sys.Platform.WECHAT_GAME) {
// 浏览器或微信小游戏平台
//@ts-ignore
assetManager.downloader.register('.wasm', assetManager.downloader._downloadArrayBuffer);
//@ts-ignore
assetManager.downloader.register('.mem', assetManager.downloader._downloadArrayBuffer);
}
} else {
// 不支持WASM的平台注册ASM相关文件
if (sys.isNative) {
//@ts-ignore
assetManager.downloader.register('.mem', assetManager.downloader._downloaders[".bin"]);
//@ts-ignore
assetManager.parser.register('.mem', assetManager.parser._parsers[".bin"]);
}
}
this.initialized = true;
console.log('[WasmLoader] 初始化完成');
}
/**
* WASM模块
* @param options
* @returns Promise<WasmLoadResult>
*/
public async loadWasmModule(options: WasmLoadOptions): Promise<WasmLoadResult> {
if (!this.initialized) {
this.initialize();
}
// 判断是否支持WASM
if (sys.hasFeature(sys.Feature.WASM) || (sys.isNative && sys.os !== sys.OS.IOS)) {
// 加载WASM
return this.loadWasm(options);
} else if (options.supportAsm && options.asmFileName && options.asmFactory) {
// 回退到ASM
return this.loadAsm(options);
} else {
throw new Error('当前平台不支持WASM且未配置ASM回退选项');
}
}
/**
* WASM
*/
private async loadWasm(options: WasmLoadOptions): Promise<WasmLoadResult> {
if (!options.wasmFactory) {
throw new Error('WASM工厂函数未提供');
}
try {
const wasmFactory = options.wasmFactory;
// 加载WASM二进制文件
const wasmFile = await this.loadWasmFile(
options.bundleName || '',
options.fileName,
options.editorUuid
);
// 初始化WASM
const instance = await this.initWasm(wasmFactory, wasmFile);
return {
instance,
isWasm: true
};
} catch (error) {
console.error('[WasmLoader] WASM加载失败:', error);
throw error;
}
}
/**
* ASM
*/
private async loadAsm(options: WasmLoadOptions): Promise<WasmLoadResult> {
if (!options.asmFactory) {
throw new Error('ASM工厂函数未提供');
}
try {
const asmFactory = options.asmFactory;
// 加载ASM内存文件
const asmFile = await this.loadWasmFile(
options.bundleName || '',
options.asmFileName || options.fileName,
options.asmEditorUuid
);
// 初始化ASM
const instance = await this.initAsm(asmFactory, asmFile);
return {
instance,
isWasm: false
};
} catch (error) {
console.error('[WasmLoader] ASM加载失败:', error);
throw error;
}
}
/**
* WASM/ASM二进制文件
*/
private loadWasmFile(bundleName: string, fileName: string, editorUuid?: string): Promise<Asset> {
return new Promise<Asset>((resolve, reject) => {
if (EDITOR) {
// 编辑器内通过UUID加载资源
if (editorUuid) {
assetManager.loadAny(editorUuid, (err, file: Asset) => {
if (!err) {
resolve(file);
} else {
reject(err);
}
});
} else {
reject(new Error('编辑器环境下需要提供editorUuid'));
}
} else {
// 运行时通过Bundle加载
if (bundleName && fileName) {
assetManager.loadBundle(bundleName, (err, bundle) => {
if (!err) {
bundle.load(fileName, Asset, (err2: any, file: Asset) => {
if (!err2) {
resolve(file);
} else {
reject(err2);
}
});
} else {
reject(err);
}
});
} else {
reject(new Error('运行时环境下需要提供bundleName和fileName'));
}
}
});
}
/**
* WASM模块
*/
private initWasm(wasmFactory: any, wasmFile: Asset): Promise<any> {
const self = this;
return new Promise<any>((resolve, reject) => {
wasmFactory({
instantiateWasm(importObject: WebAssembly.Imports, receiveInstance: any) {
self.instantiateWasm(wasmFile, importObject).then((result) => {
receiveInstance(result.instance, result.module);
}).catch((err) => reject(err));
}
}).then((instance: any) => {
resolve(instance);
}).catch((err: any) => reject(err));
});
}
/**
* WASM
*/
private instantiateWasm(wasmFile: Asset, importObject: WebAssembly.Imports): Promise<any> {
if (sys.isBrowser || sys.isNative) {
//@ts-ignore
return WebAssembly.instantiate(wasmFile._file, importObject);
} else if (sys.platform === sys.Platform.WECHAT_GAME) {
//@ts-ignore
return CCWebAssembly.instantiate(wasmFile.nativeUrl, importObject);
} else {
return Promise.reject(new Error('不支持的平台'));
}
}
/**
* ASM模块
*/
private initAsm(asmFactory: any, asmFile: Asset): Promise<any> {
const asmMemory: any = {};
asmMemory.buffer = new ArrayBuffer(MEMORYSIZE);
const module = {
asmMemory,
memoryInitializerRequest: {
//@ts-ignore
response: asmFile._file,
status: 200,
} as Partial<XMLHttpRequest>,
};
return asmFactory(module);
}
/**
* 便WASM模块WASMASM回退
*/
public async loadSimpleWasm(wasmFactory: any, fileName: string, editorUuid?: string, bundleName?: string): Promise<any> {
const result = await this.loadWasmModule({
fileName,
editorUuid,
bundleName,
supportAsm: false,
wasmFactory
});
return result.instance;
}
/**
* 便ASM回退的WASM模块
*/
public async loadWasmWithAsmFallback(
wasmFactory: any,
asmFactory: any,
fileName: string,
asmFileName: string,
editorUuid?: string,
asmEditorUuid?: string,
bundleName?: string
): Promise<WasmLoadResult> {
return this.loadWasmModule({
fileName,
editorUuid,
bundleName,
supportAsm: true,
asmFileName,
asmEditorUuid,
wasmFactory,
asmFactory
});
}
/**
* WASM
*/
public isWasmSupported(): boolean {
return sys.hasFeature(sys.Feature.WASM) || (sys.isNative && sys.os !== sys.OS.IOS);
}
/**
*
*/
public getRecommendedStrategy(): 'wasm' | 'asm' | 'unsupported' {
if (this.isWasmSupported()) {
return 'wasm';
} else if (sys.isNative || sys.isBrowser) {
return 'asm';
} else {
return 'unsupported';
}
}
}
// 导出单例实例
export const wasmLoader = WasmLoader.getInstance();