[adapters] 优化多线程特性文件结构,增加部分多线程音频系统代码,支持 Worker 子包特性(默认不开启),修复 Devtools 下强制不启用 Worker 问题

This commit is contained in:
SmallMain
2024-10-24 17:27:28 +08:00
parent 514e203483
commit fc9792c562
15 changed files with 187 additions and 56 deletions

View File

@@ -0,0 +1,24 @@
var assetManagerWorkerAdapter = {
// 返回当前 cc.assetManager.bundles 的 [name, base]
getAllBundles(args, cmdId, callback) {
var bundles = [];
cc.assetManager.bundles.forEach((v, k) => {
bundles.push([v.name, v.base]);
});
callback(cmdId, [bundles]);
},
// 删除缓存文件记录
removeCachedFiles(args, cmdId, callback) {
const deletedFiles = args[0];
for (let i = 0, l = deletedFiles.length; i < l; i++) {
cc.assetManager.cacheManager.cachedFiles.remove(deletedFiles[i]);
}
},
// 添加缓存文件记录
addCachedFiles(args, cmdId, callback) {
const [id, cacheBundleRoot, localPath, time] = args[0];
cc.assetManager.cacheManager.cachedFiles.add(id, { bundle: cacheBundleRoot, url: localPath, lastTime: time });
},
};
module.exports = assetManagerWorkerAdapter;

View File

@@ -0,0 +1,77 @@
let _id = 0;
class WorkerAudio {
id = ++_id;
get src() {
}
set src(str) {
}
get loop() {
}
set loop(v) {
}
_loop = false;
get volume() {
}
set volume(v) {
}
_volume = 1;
// 只读,从 Worker 单向同步值
duration = 0;
currentTime = 0;
paused = true;
constructor() {
}
get src() {
}
set src(clip) {
}
play() {
}
pause() {
}
seek() {
}
stop() {
}
destroy() {
}
}
var audioWorkerAdapter = {
on(id, callback) {
},
off(id, callback) {
},
};
globalThis.WorkerAudio = WorkerAudio;
module.exports = audioWorkerAdapter;

View File

@@ -0,0 +1,9 @@
if (CC_WORKER_ASSET_PIPELINE) {
const assetManagerWorkerAdapter = require("./asset-manager.js");
ipcMain.registerHandler("assetManager", assetManagerWorkerAdapter);
}
if (CC_WORKER_AUDIO_SYSTEM && cc._Audio) {
const audioWorkerAdapter = require("./audio.js");
ipcMain.registerHandler("audioAdapter", audioWorkerAdapter);
}

View File

@@ -0,0 +1,26 @@
require("./macro");
require("./ipc-main.js");
require("./handlers.js");
module.exports = {
init(callback) {
if (CC_USE_WORKER) {
var t = Date.now();
ipcMain.init(() => {
console.log("worker init cost:", Date.now() - t);
console.log("worker settings:", {
CC_USE_WORKER,
CC_WORKER_DEBUG,
CC_WORKER_ASSET_PIPELINE,
CC_WORKER_AUDIO_SYSTEM,
CC_WORKER_SCHEDULER,
CC_WORKER_FS_SYNC,
CC_WORKER_SUB_PACKAGE,
});
callback && callback();
});
} else {
callback && callback();
}
},
};

View File

@@ -0,0 +1,234 @@
const ipcMain = {
worker: null,
cmdId: 0,
callbacks: {},
_cmd: 0,
handlers: {},
_inited: false,
_initCallback: null,
init(callback) {
this._initCallback = callback;
const loadSrc = (cb) => {
if (CC_WORKER_SUB_PACKAGE) {
wx.preDownloadSubpackage({
packageType: "workers",
success() {
cb();
},
fail(res) {
console.error("load worker fail:", res);
loadSrc(cb);
}
});
} else {
cb();
}
};
loadSrc(() => {
// NOTE { useExperimentalWorker: true } 会让有状态的 Worker 处理很复杂,暂时不使用
this.worker = wx.createWorker("workers/index.js");
this.worker.onMessage(
CC_WORKER_SCHEDULER
? msgs => {
for (let index = 0; index < msgs.length; index++) {
const msg = msgs[index];
this._handleWorkerMessage(msg);
}
}
: this._handleWorkerMessage.bind(this)
);
if (CC_WORKER_SCHEDULER) {
sendScheduler.init(this);
}
this._init();
});
},
_handleWorkerMessage(msg) {
// 格式:[id, cmd, [args] | null]
// 如果 cmd 是正数,则是返回到主线程的 Callback
// 反之,是 Worker 的调用
// [-, 0, handlers] 为初始化调用
const id = msg[0];
const cmd = msg[1];
const args = msg[2];
if (cmd > 0) {
if (CC_WORKER_DEBUG) {
console.log("main thread recv callback:", msg);
}
const callback = this.callbacks[id];
if (callback) {
callback(args);
delete this.callbacks[id];
}
} else if (cmd < 0) {
const handler = this.handlers[cmd];
if (handler) {
const { func, name, key, callback } = handler;
if (CC_WORKER_DEBUG) {
console.log(`main thread recv call (${name}.${key}):`, msg);
}
func(args, id, callback);
} else {
console.error("main thread recv unknown call:", msg);
}
} else {
if (CC_WORKER_DEBUG) {
console.log("main thread recv init:", msg);
}
this._initFromWorker(args);
}
},
_init() {
const _handlers = [];
for (const cmd in this.handlers) {
const { name, key } = this.handlers[cmd];
_handlers.push({ name, cmd: Number(cmd), key });
}
this.callToWorker(0, [
_handlers,
CC_WORKER_FS_SYNC,
CC_WORKER_ASSET_PIPELINE,
CC_WORKER_AUDIO_SYSTEM,
]);
},
_initFromWorker(wrappers) {
for (const wrapper of wrappers) {
const { name, key, cmd } = wrapper;
if (!worker[name]) {
worker[name] = {};
}
worker[name][key] = (args, callback) => {
this.callToWorker(cmd, args, callback);
};
}
this._inited = true;
if (this._initCallback) this._initCallback();
},
callbackToWorker(id, cmd, args) {
const msg = [id, cmd, args];
if (CC_WORKER_DEBUG) {
console.log("main thread send callback:", msg);
}
if (CC_WORKER_SCHEDULER) {
sendScheduler.send(msg);
} else {
this.worker.postMessage(msg);
}
},
callToWorker(cmd, args, callback) {
const id = ++this.cmdId;
if (callback) {
this.callbacks[id] = callback;
}
const msg = [id, cmd, args];
if (CC_WORKER_DEBUG) {
console.log("main thread send call:", msg);
}
if (CC_WORKER_SCHEDULER) {
sendScheduler.send(msg);
} else {
this.worker.postMessage(msg);
}
},
registerHandler(name, obj) {
const descs = Object.getOwnPropertyDescriptors(obj);
for (const key in descs) {
const desc = descs[key];
if (typeof desc.value === "function") {
const cmd = ++this._cmd;
this.handlers[cmd] = {
name,
key,
func: obj[key].bind(obj),
callback: (id, args) => this.callbackToWorker(id, cmd, args),
};
} else {
// getter/setter
const cmd1 = ++this._cmd;
this.handlers[cmd1] = {
name,
key: "get_" + key,
func: (args, id, callback) => {
this.callbackToWorker(id, cmd1, [obj[key]]);
},
callback: null,
};
const cmd2 = ++this._cmd;
this.handlers[cmd2] = {
name,
key: "set_" + key,
func: (args, id, callback) => {
obj[key] = args[0];
}
};
const cmd3 = ++this._cmd;
this.handlers[cmd3] = {
name,
key: "write_" + key,
func: (args, id, callback) => {
obj[key] = args[0];
this.callbackToWorker(id, cmd3, null);
}
};
}
}
},
};
const sendScheduler = {
queue: [],
ipc: null,
init(ipc) {
this.ipc = ipc;
setInterval(() => {
if (this.queue.length > 0) {
this.ipc.worker.postMessage(this.queue);
this.queue = [];
}
}, 0);
},
send(msg) {
this.queue.push(msg);
},
};
const worker = {};
globalThis.ipcMain = ipcMain;
globalThis.worker = worker;

View File

@@ -0,0 +1,36 @@
const isSubContext = wx.getOpenDataContext === undefined;
const sysinfo = wx.getSystemInfoSync();
const platform = sysinfo.platform.toLowerCase();
const isAndroid = platform === "android";
const isDevtools = platform === "devtools";
const sdkVersion = sysinfo.SDKVersion.split('.').map(Number);
// >= 2.20.2
const hasWorker = sdkVersion[0] > 2 || (sdkVersion[0] === 2 && (sdkVersion[1] > 20 || (sdkVersion[1] === 20 && sdkVersion[2] >= 2)));
// >= 2.27.3
const useSubpackage = sdkVersion[0] > 2 || (sdkVersion[0] === 2 && (sdkVersion[1] > 27 || (sdkVersion[1] === 27 && sdkVersion[2] >= 3)));
// 是否启用 Worker 驱动资源管线
globalThis.CC_WORKER_ASSET_PIPELINE = false;
// 是否启用 Worker 驱动音频系统
globalThis.CC_WORKER_AUDIO_SYSTEM = false;
// NOTE 截止 2024.10.22,微信未修复 iOS、Windows、Mac 上仅文件系统 API 可以正常使用的问题
globalThis.CC_WORKER_ASSET_PIPELINE = (isAndroid || isDevtools) && globalThis.CC_WORKER_ASSET_PIPELINE;
// 是否启用 Worker
globalThis.CC_USE_WORKER = (CC_WORKER_ASSET_PIPELINE) && hasWorker && !isSubContext;
// 是否启用 Worker 调试模式
globalThis.CC_WORKER_DEBUG = false;
// 是否启用 Worker 调度模式,这也许能减少通信次数带来的性能消耗(必须一致)
globalThis.CC_WORKER_SCHEDULER = true;
// 是否启用 Worker 使用同步版本的文件系统 API
// NOTE: IOS 不支持 async 文件系统 APIAndroid 不支持部分 sync 文件系统 API其余系统暂不确定
globalThis.CC_WORKER_FS_SYNC = !isAndroid && !isDevtools;
// 是否启用 Worker 子包
// NOTE 截止 2024.10.22,部分安卓机型声明使用子包 Worker 会报 java.string 错误
globalThis.CC_WORKER_SUB_PACKAGE = false;