From bdf3de7b047062f49adfd9453e3197ecea5e0216 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Wed, 14 Jan 2026 17:03:26 -0500 Subject: [PATCH] async-ify fdm slicing layer diffs for ~3x speedup --- src/kiri/mode/fdm/work/slice.js | 33 +++++++++-- src/kiri/run/minion.js | 14 +++++ src/kiri/run/worker.js | 97 +++++++++++++++++++++------------ 3 files changed, 106 insertions(+), 38 deletions(-) diff --git a/src/kiri/mode/fdm/work/slice.js b/src/kiri/mode/fdm/work/slice.js index a3666da7..970e7152 100644 --- a/src/kiri/mode/fdm/work/slice.js +++ b/src/kiri/mode/fdm/work/slice.js @@ -672,14 +672,22 @@ export function sliceOne(settings, widget, onupdate, ondone) { async function processLayerDiffs(plo, phi) { let div = divide(plo, phi, [ 0.9, 0.05, 0.05 ]); // boolean diff layers to detect bridges and flats + let promises = []; profileStart("delta"); forSlices(div[0].lo, div[1].hi, slice => { let params = slice.params || process; let solidMinArea = params.sliceSolidMinArea; let sliceMinThick = params.sliceSolidMinThick; let sliceFillGrow = params.sliceFillGrow; - layerDiff(slice, { area: solidMinArea, grow: sliceFillGrow, thick: sliceMinThick }); + let p = layerDiff(slice, { + area: solidMinArea, + grow: sliceFillGrow, + thick: sliceMinThick, + async: true + }); + promises.push(p); }, "layer deltas"); + await Promise.all(promises); profileEnd(); // project bridges and flats up and down into part profileStart("delta-project"); @@ -1443,9 +1451,26 @@ export function layerDiff(slice, options = {}) { let newBridges = []; let newFlats = []; - POLY.subtract(topInner, downInner, newBridges, newFlats, slice.z, area, { - wasm: true - }); + if (options.async) { + return self.kiri_worker.minions + .subtract({ + a: topInner, b: downInner, + outA: newBridges, outB: newFlats, + area, wasm: true, z: slice.z, + }) + .then(() => { + layerDiffDone({ slice, bridges, flats, newBridges, newFlats, options }); + }); + } else { + POLY.subtract(topInner, downInner, newBridges, newFlats, slice.z, area, { + wasm: true + }); + layerDiffDone({ slice, bridges, flats, newBridges, newFlats, options }); + } +} + +function layerDiffDone({ slice, bridges, flats, newBridges, newFlats, options }) { + const { sla, grow, area, thick } = options; // console.log(slice.z, { newBridges, newFlats }); newBridges = newBridges.filter(p => p.areaDeep() >= area && p.thickness(true) >= thick); diff --git a/src/kiri/run/minion.js b/src/kiri/run/minion.js index ab6edb7f..0a229558 100644 --- a/src/kiri/run/minion.js +++ b/src/kiri/run/minion.js @@ -78,6 +78,20 @@ const funcs = self.minion = { } }, + subtract(data, seq) { + let { arg, opt } = data; + let { area, wasm, z } = opt; + let a = codec.decode(arg.a); + let b = codec.decode(arg.b); + let outA = [], outB = []; + POLY.subtract(a, b, outA, outB, z, area, { wasm }); + reply({ + seq, + outA: codec.encode(outA), + outB: codec.encode(outB) + }); + }, + union(data, seq) { if (!(data.polys && data.polys.length)) { reply({ seq, union: codec.encode([]) }); diff --git a/src/kiri/run/worker.js b/src/kiri/run/worker.js index 086ac6c5..34594561 100644 --- a/src/kiri/run/worker.js +++ b/src/kiri/run/worker.js @@ -101,6 +101,9 @@ function minhandler(msg) { // for concurrent operations const minwork = { + + // core functions + get concurrent() { return concurrent }, @@ -137,6 +140,66 @@ const minwork = { minions.length = 0; }, + queue(work, ondone, direct) { + minionq.push({work, ondone, direct}); + minwork.kick(); + }, + + queueAsync(work, direct) { + return new Promise(resolve => { + minwork.queue(work, resolve, direct); + }); + }, + + kick() { + if (minions.length && minionq.length) { + let qrec = minionq.shift(); + let minion = minions.shift(); + let seq = miniseq++; + qrec.work.seq = seq; + minifns[seq] = (data) => { + qrec.ondone(data); + minions.push(minion); + minwork.kick(); + }; + minion.postMessage(qrec.work, qrec.direct); + } + }, + + broadcast(cmd, data, direct) { + for (let minion of minions) { + minion.postMessage({ + cmd, ...data + }, direct); + } + }, + + // added functions (should be namespaced) + + subtract({ a, b, outA, outB, z, area, wasm }) { + return new Promise((resolve, reject) => { + if (concurrent < 2 || a.length + b.length < concurrent * 2 || POLY.points([...a,...b]) < concurrent * 50) { + POLY.subtract(a, b, outA, outB, z, area, { wasm }); + resolve(); + return; + } + minwork.queue({ + cmd: "subtract", + opt: { area, wasm, z }, + arg: { + a: codec.encode(a), + b: codec.encode(b), + outA: outA ? 1 : 0, + outB: outB ? 1 : 0, + } + }, result => { + if (outA) outA.push(...codec.decode(result.outA)); + if (outB) outB.push(...codec.decode(result.outB)); + resolve(); + }); + }); + }, + union(polys, minarea) { return new Promise((resolve, reject) => { if (concurrent < 2 || polys.length < concurrent * 2 || POLY.points(polys) < concurrent * 50) { @@ -245,40 +308,6 @@ const minwork = { }, [ floatP.buffer ]); }); }, - - queue(work, ondone, direct) { - minionq.push({work, ondone, direct}); - minwork.kick(); - }, - - queueAsync(work, direct) { - return new Promise(resolve => { - minwork.queue(work, resolve, direct); - }); - }, - - kick() { - if (minions.length && minionq.length) { - let qrec = minionq.shift(); - let minion = minions.shift(); - let seq = miniseq++; - qrec.work.seq = seq; - minifns[seq] = (data) => { - qrec.ondone(data); - minions.push(minion); - minwork.kick(); - }; - minion.postMessage(qrec.work, qrec.direct); - } - }, - - broadcast(cmd, data, direct) { - for (let minion of minions) { - minion.postMessage({ - cmd, ...data - }, direct); - } - } }; console.log(`kiri | init work | ${version || "rogue"}`);