cocos基础工程
This commit is contained in:
321
client-cocos/assets/scripts/Utils/WasmLoader.ts
Normal file
321
client-cocos/assets/scripts/Utils/WasmLoader.ts
Normal file
@@ -0,0 +1,321 @@
|
||||
/**
|
||||
* 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为WASM,false为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();
|
||||
9
client-cocos/assets/scripts/Utils/WasmLoader.ts.meta
Normal file
9
client-cocos/assets/scripts/Utils/WasmLoader.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "1a752554-ae7a-4ac3-b468-31ad25757d52",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
128
client-cocos/assets/scripts/Utils/WasmLoaderExample.ts
Normal file
128
client-cocos/assets/scripts/Utils/WasmLoaderExample.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* WasmLoader 使用示例
|
||||
*
|
||||
* 这个文件展示如何使用 WasmLoader 来加载 WASM 模块
|
||||
*/
|
||||
|
||||
import { wasmLoader } from './WasmLoader';
|
||||
|
||||
// 假设我们有一个 WASM 工厂函数(需要从 JS 胶水代码中导入)
|
||||
// import wasmFactory from '../path/to/your-wasm-module.js';
|
||||
|
||||
export class WasmLoaderExample {
|
||||
|
||||
/**
|
||||
* 示例1: 加载简单的 WASM 模块
|
||||
*/
|
||||
async loadSimpleWasmExample(wasmFactory: any) {
|
||||
try {
|
||||
// 首先需要初始化加载器(通常在应用启动时执行一次)
|
||||
wasmLoader.initialize();
|
||||
|
||||
// 假设你有一个 WASM 工厂函数
|
||||
// const wasmFactory = (await import('../wasm/pinball_physics.js')).default;
|
||||
|
||||
// 加载 WASM 模块
|
||||
const instance = await wasmLoader.loadSimpleWasm(
|
||||
wasmFactory, // WASM 工厂函数
|
||||
'pinball_physics.wasm', // WASM 文件名
|
||||
'44cacb3c-e901-455d-b3e1-1c38a69718e1', // 编辑器中的 UUID(可选)
|
||||
'wasmFiles' // Bundle 名称(可选)
|
||||
);
|
||||
|
||||
// 现在可以使用 WASM 模块的函数
|
||||
console.log('WASM 模块加载成功:', instance);
|
||||
|
||||
return instance;
|
||||
} catch (error) {
|
||||
console.error('WASM 加载失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 示例2: 加载支持 ASM 回退的 WASM 模块
|
||||
*/
|
||||
async loadWasmWithFallbackExample(wasmFactory: any, asmFactory: any) {
|
||||
try {
|
||||
wasmLoader.initialize();
|
||||
|
||||
// 假设你有 WASM 和 ASM 工厂函数
|
||||
// const wasmFactory = (await import('../wasm/pinball_physics.js')).default;
|
||||
// const asmFactory = (await import('../wasm/pinball_physics.asm.js')).default;
|
||||
|
||||
const result = await wasmLoader.loadWasmWithAsmFallback(
|
||||
wasmFactory, // WASM 工厂函数
|
||||
asmFactory, // ASM 工厂函数
|
||||
'pinball_physics.wasm', // WASM 文件名
|
||||
'pinball_physics.asm.mem', // ASM 内存文件名
|
||||
'44cacb3c-e901-455d-b3e1-1c38a69718e1', // WASM UUID
|
||||
'3400003e-dc3c-43c1-8757-3e082429125a', // ASM UUID
|
||||
'wasmFiles' // Bundle 名称
|
||||
);
|
||||
|
||||
console.log('模块加载成功,使用的是:', result.isWasm ? 'WASM' : 'ASM');
|
||||
console.log('模块实例:', result.instance);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('模块加载失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 示例3: 检查平台支持情况
|
||||
*/
|
||||
checkPlatformSupport() {
|
||||
const isWasmSupported = wasmLoader.isWasmSupported();
|
||||
const strategy = wasmLoader.getRecommendedStrategy();
|
||||
|
||||
console.log('WASM 支持:', isWasmSupported);
|
||||
console.log('推荐策略:', strategy);
|
||||
|
||||
return { isWasmSupported, strategy };
|
||||
}
|
||||
|
||||
/**
|
||||
* 示例4: 高级用法 - 自定义配置
|
||||
*/
|
||||
async loadCustomWasm(wasmFactory: any, asmFactory: any) {
|
||||
try {
|
||||
wasmLoader.initialize();
|
||||
|
||||
// 使用完整配置
|
||||
const result = await wasmLoader.loadWasmModule({
|
||||
fileName: 'pinball_physics.wasm',
|
||||
bundleName: 'wasmFiles',
|
||||
editorUuid: '44cacb3c-e901-455d-b3e1-1c38a69718e1',
|
||||
supportAsm: true,
|
||||
asmFileName: 'pinball_physics.asm.mem',
|
||||
asmEditorUuid: '3400003e-dc3c-43c1-8757-3e082429125a',
|
||||
wasmFactory: wasmFactory,
|
||||
asmFactory: asmFactory
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('自定义 WASM 加载失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用说明:
|
||||
*
|
||||
* 1. 首先将 WASM 文件和对应的 JS 胶水代码放到项目中
|
||||
* 2. 将 .wasm 文件导入到编辑器的资源管理器中
|
||||
* 3. 在编辑器中获取 .wasm 文件的 UUID
|
||||
* 4. 导入 JS 胶水代码并获取工厂函数
|
||||
* 5. 调用 wasmLoader 的方法加载 WASM 模块
|
||||
*
|
||||
* 注意事项:
|
||||
* - 在编辑器环境下需要提供资源的 UUID
|
||||
* - 在运行时环境下需要提供 Bundle 名称和文件名
|
||||
* - iOS 平台不支持 WASM,需要提供 ASM 回退
|
||||
* - 确保在使用前调用 initialize() 方法
|
||||
*/
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "2ecfafcc-6d3c-443e-8e24-3207988f4742",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user