Files
yawn/core/worker.js
T
Ampandheaust 92638e6c9f core: writer-owned relocation
The worker no longer copies rows when an array cannot grow in place. It
reserves a zeroed region, returns the staged descriptor with a commit
flag, and queues every other request until the main thread (the only
writer) has copied the old bytes and sent commit-rows. Writes that raced
the old worker-side copy were lost; lights in the glTF viewer regularly
ended up at the origin because of it.

Amp-Thread-ID: https://ampcode.com/threads/T-01a0b2dd-db32-712d-99ce-12ce4a86d979
Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
2026-09-18 06:40:44 +00:00

97 lines
3.3 KiB
JavaScript

import initWasm, { Core } from "./pkg/yawn_core.js";
let core;
let profileTimer;
// While a relocation is staged, every request except its commit waits here.
let awaitingCommit = false;
const queue = [];
const fail = code => { throw new Error(code); };
async function handle(message) {
try {
let result;
if (message?.type === "init") {
if (core || !(message.canvas instanceof OffscreenCanvas)) fail("INIT");
const wasm = await initWasm();
core = new Core(message.arenaBytes);
await core.initialize(message.canvas);
const buffer = wasm.memory.buffer;
if (!(buffer instanceof SharedArrayBuffer)) fail("WASM_MEMORY_NOT_SHARED");
result = { buffer, rows: JSON.parse(core.rows()), features: JSON.parse(core.features()) };
} else {
if (!core) fail("UNINITIALIZED");
switch (message?.type) {
case "create-rows":
result = JSON.parse(core.create_rows(message.name, message.rows, message.stride, message.format));
awaitingCommit = result.commit;
break;
case "create-rows-batch":
result = JSON.parse(core.create_rows_batch(JSON.stringify(message.rows)));
awaitingCommit = result.commit.length > 0;
break;
case "commit-rows":
awaitingCommit = false;
core.commit_rows(JSON.stringify(message.names));
break;
case "delete-rows":
core.delete_rows(message.name);
break;
case "allocate-object":
result = JSON.parse(core.allocate_object(message.name, message.count ?? 1));
awaitingCommit = result.commit;
break;
case "delete-object":
core.delete_object(message.name, message.id);
break;
case "sync-rows":
core.sync_rows(message.name, message.first ?? 0, message.count);
break;
case "compile-graph":
result = core.compile_graph(message.serialized);
break;
case "switch-loadout":
core.switch_loadout(message.id);
break;
case "upload-texture":
core.upload_texture(message.name, message.mipLevel, message.image);
break;
case "delete-texture":
core.delete_texture(message.name);
break;
case "play":
core.play();
break;
case "pause":
core.pause();
break;
case "set-fps":
core.set_fps(message.fps);
break;
case "set-profiler":
result = core.set_profiler(Boolean(message.enabled));
clearInterval(profileTimer);
profileTimer = result && message.enabled ? setInterval(() => {
const stats = core.take_profile();
if (stats) postMessage({ type: "profile", stats: JSON.parse(stats) });
}, 250) : undefined;
break;
default:
fail("MESSAGE");
}
}
postMessage({ request: message.request, result });
} catch (error) {
postMessage({ request: message?.request, error: error?.message ?? "CORE_ERROR" });
}
}
addEventListener("message", async ({ data: message }) => {
if (awaitingCommit && message?.type !== "commit-rows") {
queue.push(message);
return;
}
await handle(message);
while (!awaitingCommit && queue.length) await handle(queue.shift());
});