import test from "node:test"; import assert from "node:assert/strict"; import { Worker } from "node:worker_threads"; import { Mailbox } from "./mailbox.js"; import { StrokeProcessor } from "./stroke-worker.js"; import { Sculpt, defaults, StrokePath } from "./engine.js"; import { sphere } from "./mesh.js"; const row = () => ({ buffer: new SharedArrayBuffer(128), descriptor: { offset: 64 }, }); test("mailbox overwrites one slot, keeps a newer publication pending, wraps and rejects torn reads", (t) => { const m = new Mailbox(row()), p = { x: 12, y: -8, pressure: 0.5 }; const first = m.write(7, p); assert.deepEqual(m.read(7), { ...p, seed: undefined, version: first }); const second = m.write(7, { x: 20, y: 3, pressure: 0.75 }); m.acknowledge(first); assert.equal(m.read(7).version, second); m.acknowledge(second); assert.equal(m.read(7), null); Atomics.store(m.words, 0, 0x7ffffffe); const wrapped = m.write(7, p); assert.equal(wrapped, -2147483648); assert.equal(m.read(7).x, 12); const load = Atomics.load; let changed = false; t.mock.method(Atomics, "load", (array, index) => { const v = load(array, index); if (array === m.words && index === 5 && !changed) { changed = true; m.write(7, { x: 99, y: 100, pressure: 0.2 }); } return v; }); assert.equal(m.read(7), null); Atomics.store(m.words, 0, 11); assert.equal(m.read(7), null); }); test("actual concurrent SAB writer and reader never observe mixed coordinates", async (t) => { const shared = row(), m = new Mailbox(shared), control = new Int32Array(shared.buffer, 0, 1); const code = `import {parentPort,workerData} from 'node:worker_threads'; const {Mailbox}=await import(workerData.module); const m=new Mailbox(workerData.row),control=new Int32Array(workerData.row.buffer,0,1); let reads=0,last=0; parentPort.postMessage('ready'); while(!Atomics.load(control,0)||last!==100000){ const p=m.read(7);if(!p)continue; if(p.y!==-p.x||p.pressure!==p.x*2||p.seed!==p.x*3)throw new Error('torn tuple'); last=p.x;reads++;m.acknowledge(p.version); }parentPort.postMessage({reads,last});`; const worker = new Worker( new URL("data:text/javascript," + encodeURIComponent(code)), { workerData: { module: new URL("./mailbox.js", import.meta.url).href, row: shared, }, }, ); t.after(() => worker.terminate()); await new Promise((resolve, reject) => { worker.once("message", resolve); worker.once("error", reject); }); const result = new Promise((resolve, reject) => { worker.once("message", resolve); worker.once("error", reject); }); for (let i = 1; i <= 100000; i++) { m.write(7, { x: i, y: -i, pressure: i * 2 }, i * 3); if (i % 1000 === 0) await new Promise((resolve) => setImmediate(resolve)); } Atomics.store(control, 0, 1); const out = await result; assert.ok(out.reads > 0); assert.equal(out.last, 100000); }); function setup(settings = {}, sample = { x: 400, y: 300, pressure: 0.7 }) { const mailbox = new Mailbox(row()), messages = [], mesh = sphere(4); let now = 0; const processor = new StrokeProcessor( mesh, mailbox, (m) => messages.push(m), () => now, ); const version = mailbox.write(1, sample); processor.begin({ id: 1, version, sample, positions: mesh.positions.slice(), settings: { ...defaults, symmetry: false, ...settings }, camera: { yaw: 0, pitch: 0, zoom: 1.5, target: [0, 0, 0] }, rect: { width: 800, height: 600 }, hit: { position: [0, 0, 1], normal: [0, 0, 1] }, }); const ack = () => { for (const m of messages) if (m.type === "geometry") processor.acknowledge(m.packet); }; return { processor, mailbox, messages, ack, advance: (ms) => { now += ms; }, }; } test("mask packets are single-flight and canceled geometry is repaired before masking", () => { const { processor, mailbox, messages } = setup(); // Cancel intentionally leaves provisional worker geometry. const before = processor.engine.mesh.positions.slice(); processor.frame(); assert.notDeepEqual(processor.engine.mesh.positions, before); processor.cancel(1); const sample = { x: 420, y: 290, pressure: 0.8 }; const version = mailbox.write(2, sample); processor.begin({ id: 2, version, sample, positions: before, settings: { ...defaults, brush: "mask", symmetry: false }, camera: { yaw: 0, pitch: 0, zoom: 1.5, target: [0, 0, 0] }, rect: { width: 800, height: 600 }, hit: { position: [0, 0, 1], normal: [0, 0, 1] }, }); // Independently rebuild at the fixture's actual level. const fresh = new Sculpt(sphere(processor.engine.mesh.level)); assert.deepEqual(processor.engine.mesh.positions, fresh.mesh.positions); assert.deepEqual(processor.engine.mesh.normals, fresh.mesh.normals); const start = messages.length; processor.frame(); const packet = messages.at(-1); assert.equal(packet.type, "mask"); const values = packet.masks.slice(); const end = mailbox.write(2, { x: 450, y: 310, pressure: 0.9 }); processor.end(2, end); processor.frame(); assert.equal(messages.length, start + 1); assert.deepEqual(packet.masks, values); processor.acknowledge(packet.packet); processor.frame(); const last = messages.at(-1); assert.equal(last.type, "mask"); processor.acknowledge(last.packet); processor.frame(); assert.equal(messages.at(-1).type, "complete"); assert.deepEqual(processor.engine.mesh.positions, before); }); test("latest frame target equals feeding only that target, intermediate samples create no hidden queue", () => { const a = setup(), b = setup(); a.processor.frame(); b.processor.frame(); a.ack(); b.ack(); for (let i = 0; i < 10000; i++) a.mailbox.write(1, { x: 410 + i / 100, y: 300 + Math.sin(i) * 30, pressure: 0.4, }); const end = { x: 470, y: 310, pressure: 0.8 }; const version = a.mailbox.write(1, end); b.mailbox.write(1, end); a.processor.frame(); b.processor.frame(); assert.deepEqual( a.processor.engine.mesh.positions, b.processor.engine.mesh.positions, ); assert.equal(a.mailbox.processed, version); assert.equal(a.processor.job.target, null); }); test("Grab seeds pressure-radius from first effective pressure and ignores a zero-pressure tail", () => { const a = setup( { brush: "grab", pressureRadius: true }, { x: 400, y: 300, pressure: 0 }, ); a.processor.frame(); const version = a.mailbox.write(1, { x: 460, y: 310, pressure: 0.8 }, 0.25); a.processor.frame(); const expected = new Sculpt(sphere(4)); expected.begin({ brush: "grab", pressureRadius: true, symmetry: false }); expected.dab({ position: [0, 0, 1], normal: [0, 0, 1] }, 0.25, [0, 0, 0]); expected.dab( { position: [0, 0, 1], normal: [0, 0, 1] }, Math.fround(0.8), [0.3, -0.05, 0], ); assert.deepEqual(a.processor.engine.mesh.positions, expected.mesh.positions); assert.equal(a.mailbox.processed, version); }); test("one immutable output in flight, new dirty work survives acknowledgement, end waits for applied output", () => { const a = setup(); a.processor.frame(); const first = a.messages.find((m) => m.type === "geometry"), bytes = first.vertices.slice(); const version = a.mailbox.write(1, { x: 470, y: 310, pressure: 0.9 }); a.processor.end(1, version); a.processor.frame(); assert.deepEqual(first.vertices, bytes); assert.equal(a.messages.filter((m) => m.type === "geometry").length, 1); assert.ok(a.processor.engine.dirty.size); assert.ok(!a.messages.some((m) => m.type === "complete")); a.processor.acknowledge(first.packet); a.processor.frame(); const second = a.messages.filter((m) => m.type === "geometry").at(-1); assert.notEqual(second.packet, first.packet); a.processor.acknowledge(first.packet); assert.equal(a.processor.outstanding, second.packet); a.processor.acknowledge(second.packet); a.processor.frame(); assert.equal(a.messages.at(-1).type, "complete"); }); test("worker bounds real work; unfinished segment age is not reset by release or newer samples", () => { const a = setup(); a.processor.frame(); a.ack(); const dab = a.processor.engine.dab.bind(a.processor.engine); let calls = 0; a.processor.engine.dab = (...args) => { calls++; a.advance(5); dab(...args); }; a.mailbox.write(1, { x: 590, y: 300, pressure: 0.8 }); a.processor.frame(); assert.equal(calls, 1); assert.ok(a.processor.job.target); a.advance(250); const latest = a.mailbox.write(1, { x: 610, y: 305, pressure: 0.9 }); a.processor.end(1, latest); a.processor.frame(); assert.equal(a.processor.job, null); assert.equal(a.messages.at(-1).type, "canceled"); }); test("resuming a segment preserves independently computed positions and pressure", () => { const emitted = [], path = new StrokePath(5, (p) => emitted.push(p)); path.push({ x: 0, y: 0, pressure: 0.2 }); while (!path.push({ x: 24, y: 18, pressure: 0.8 }, 1)) {} assert.equal(emitted.length, 7); emitted.forEach((p, i) => { assert.ok(Math.abs(p.x - 4 * i) < 1e-10); assert.ok(Math.abs(p.y - 3 * i) < 1e-10); assert.ok(Math.abs(p.pressure - (0.2 + 0.1 * i)) < 1e-10); }); });