Files
shooter-demo/client-cocos/assets/scripts/Utils/WasmLoader.ts
2025-11-28 18:10:10 +08:00

321 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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模块仅WASM不支持ASM回退
*/
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();