okgeometry-api 1.26.1 → 1.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Arc.js +1 -1
- package/dist/Arc.js.map +1 -1
- package/dist/Brep.js +1 -1
- package/dist/Brep.js.map +1 -1
- package/dist/Circle.js +1 -1
- package/dist/Circle.js.map +1 -1
- package/dist/Line.js +1 -1
- package/dist/Line.js.map +1 -1
- package/dist/Mesh.d.ts +41 -0
- package/dist/Mesh.d.ts.map +1 -1
- package/dist/Mesh.js +235 -21
- package/dist/Mesh.js.map +1 -1
- package/dist/MeshSurface.js +1 -1
- package/dist/MeshSurface.js.map +1 -1
- package/dist/NurbsCurve.js +1 -1
- package/dist/NurbsCurve.js.map +1 -1
- package/dist/NurbsSurface.js +1 -1
- package/dist/NurbsSurface.js.map +1 -1
- package/dist/PolyCurve.js +1 -1
- package/dist/PolyCurve.js.map +1 -1
- package/dist/Polyline.js +1 -1
- package/dist/Polyline.js.map +1 -1
- package/dist/Ray.js +1 -1
- package/dist/Ray.js.map +1 -1
- package/dist/SculptSession.js +1 -1
- package/dist/SculptSession.js.map +1 -1
- package/dist/engine-threaded.d.ts +2 -0
- package/dist/engine-threaded.d.ts.map +1 -0
- package/dist/engine-threaded.js +33 -0
- package/dist/engine-threaded.js.map +1 -0
- package/dist/engine.d.ts +21 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +42 -0
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mesh-boolean.pool.d.ts +23 -1
- package/dist/mesh-boolean.pool.d.ts.map +1 -1
- package/dist/mesh-boolean.pool.js +97 -12
- package/dist/mesh-boolean.pool.js.map +1 -1
- package/dist/mesh-boolean.protocol.d.ts +21 -1
- package/dist/mesh-boolean.protocol.d.ts.map +1 -1
- package/dist/mesh-boolean.protocol.js.map +1 -1
- package/dist/mesh-boolean.worker.d.ts.map +1 -1
- package/dist/mesh-boolean.worker.js +94 -1
- package/dist/mesh-boolean.worker.js.map +1 -1
- package/dist/wasm-active.d.ts +20 -0
- package/dist/wasm-active.d.ts.map +1 -0
- package/dist/wasm-active.js +26 -0
- package/dist/wasm-active.js.map +1 -0
- package/dist/wasm-base64-threaded.d.ts +2 -0
- package/dist/wasm-base64-threaded.d.ts.map +1 -0
- package/dist/wasm-base64-threaded.js +3 -0
- package/dist/wasm-base64-threaded.js.map +1 -0
- package/dist/wasm-base64.d.ts +1 -1
- package/dist/wasm-base64.d.ts.map +1 -1
- package/dist/wasm-base64.js +1 -1
- package/dist/wasm-base64.js.map +1 -1
- package/dist/wasm-bindings-threaded.d.ts +2664 -0
- package/dist/wasm-bindings-threaded.d.ts.map +1 -0
- package/dist/wasm-bindings-threaded.js +5008 -0
- package/dist/wasm-bindings-threaded.js.map +1 -0
- package/dist/wasm-bindings.d.ts +130 -0
- package/dist/wasm-bindings.d.ts.map +1 -1
- package/dist/wasm-bindings.js +208 -0
- package/dist/wasm-bindings.js.map +1 -1
- package/dist/workerHelpers-threaded.d.ts +2 -0
- package/dist/workerHelpers-threaded.d.ts.map +1 -0
- package/dist/workerHelpers-threaded.js +100 -0
- package/dist/workerHelpers-threaded.js.map +1 -0
- package/package.json +8 -3
- package/src/Arc.ts +117 -117
- package/src/Brep.ts +1 -1
- package/src/Circle.ts +166 -166
- package/src/Line.ts +171 -171
- package/src/Mesh.ts +300 -45
- package/src/MeshSurface.ts +1 -1
- package/src/NurbsCurve.ts +655 -655
- package/src/NurbsSurface.ts +721 -721
- package/src/PolyCurve.ts +203 -203
- package/src/Polyline.ts +316 -316
- package/src/Ray.ts +90 -90
- package/src/SculptSession.ts +377 -377
- package/src/engine-threaded.ts +39 -0
- package/src/engine.ts +46 -0
- package/src/index.ts +1 -1
- package/src/mesh-boolean.pool.ts +208 -86
- package/src/mesh-boolean.protocol.ts +44 -1
- package/src/mesh-boolean.worker.ts +170 -56
- package/src/wasm-active.ts +31 -0
- package/src/wasm-base64-threaded.ts +2 -0
- package/src/wasm-base64.ts +1 -1
- package/src/wasm-bindings-threaded.d.ts +1989 -0
- package/src/wasm-bindings-threaded.js +5235 -0
- package/src/wasm-bindings.d.ts +104 -0
- package/src/wasm-bindings.js +225 -0
- package/src/workerHelpers-threaded.js +107 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threaded engine loader (KERNEL-PERF-ARC lever 3).
|
|
3
|
+
*
|
|
4
|
+
* Loaded LAZILY (dynamic import from engine.ts) so the threaded wasm's
|
|
5
|
+
* inlined bytes are only fetched on pages that can actually use them
|
|
6
|
+
* (cross-origin isolated, SharedArrayBuffer available). Instantiates the
|
|
7
|
+
* rayon-enabled artifact, starts the worker thread pool, and swaps the
|
|
8
|
+
* active bindings module. Never call directly — use `initThreads()` from
|
|
9
|
+
* engine.ts, which enforces the boot-time ordering rules.
|
|
10
|
+
*/
|
|
11
|
+
import { WASM_THREADED_B64 } from "./wasm-base64-threaded.js";
|
|
12
|
+
import { setActiveWasmModule } from "./wasm-active.js";
|
|
13
|
+
import threadedInit, { initThreadPool } from "./wasm-bindings-threaded.js";
|
|
14
|
+
import * as threadedModule from "./wasm-bindings-threaded.js";
|
|
15
|
+
|
|
16
|
+
export async function initThreadedEngine(threads?: number): Promise<boolean> {
|
|
17
|
+
if (!WASM_THREADED_B64) return false; // artifact absent at build time
|
|
18
|
+
|
|
19
|
+
const raw = atob(WASM_THREADED_B64);
|
|
20
|
+
const bytes = new Uint8Array(raw.length);
|
|
21
|
+
for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);
|
|
22
|
+
|
|
23
|
+
await threadedInit({ module_or_path: bytes });
|
|
24
|
+
|
|
25
|
+
// Default pool: compute-bound kernel loops saturate well before core
|
|
26
|
+
// count, and each worker holds a wasm instance — cap at 8.
|
|
27
|
+
const requested =
|
|
28
|
+
threads ??
|
|
29
|
+
Math.min(
|
|
30
|
+
8,
|
|
31
|
+
typeof navigator !== "undefined" && Number.isFinite(navigator.hardwareConcurrency)
|
|
32
|
+
? Math.max(1, Math.floor(navigator.hardwareConcurrency))
|
|
33
|
+
: 4,
|
|
34
|
+
);
|
|
35
|
+
await initThreadPool(requested);
|
|
36
|
+
|
|
37
|
+
setActiveWasmModule(threadedModule as unknown as Parameters<typeof setActiveWasmModule>[0]);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
package/src/engine.ts
CHANGED
|
@@ -31,3 +31,49 @@ export function ensureInit(): void {
|
|
|
31
31
|
export function isInitialized(): boolean {
|
|
32
32
|
return initialized;
|
|
33
33
|
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Initialize the THREADED engine (KERNEL-PERF-ARC lever 3): rayon worker
|
|
37
|
+
* pool over shared WebAssembly memory. Requires a cross-origin isolated
|
|
38
|
+
* page (COOP: same-origin + COEP; check `crossOriginIsolated`). Returns
|
|
39
|
+
* false — without touching engine state — when the environment cannot run
|
|
40
|
+
* threads or the threaded artifact is unavailable, so callers can fall
|
|
41
|
+
* back to `init()`.
|
|
42
|
+
*
|
|
43
|
+
* MUST be the first init call: stored meshes and sculpt sessions are
|
|
44
|
+
* per-wasm-instance state, so the module cannot be swapped after use.
|
|
45
|
+
*/
|
|
46
|
+
export async function initThreads(options: { threads?: number } = {}): Promise<boolean> {
|
|
47
|
+
if (initialized) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
"OkGeometry already initialized — initThreads() must run INSTEAD of init(), before any geometry use.",
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const g = globalThis as typeof globalThis & {
|
|
53
|
+
crossOriginIsolated?: boolean;
|
|
54
|
+
SharedArrayBuffer?: unknown;
|
|
55
|
+
};
|
|
56
|
+
if (!g.crossOriginIsolated || typeof g.SharedArrayBuffer === "undefined") {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const mod = await import("./engine-threaded.js");
|
|
61
|
+
const ok = await mod.initThreadedEngine(options.threads);
|
|
62
|
+
if (!ok) return false;
|
|
63
|
+
initialized = true;
|
|
64
|
+
return true;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.warn("okgeometry threaded init failed; falling back to single-thread.", error);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Convenience boot: try the threaded engine, fall back to the standard
|
|
73
|
+
* single-thread engine. Returns which one is active.
|
|
74
|
+
*/
|
|
75
|
+
export async function initAuto(options: { threads?: number } = {}): Promise<"threaded" | "standard"> {
|
|
76
|
+
if (await initThreads(options)) return "threaded";
|
|
77
|
+
await init();
|
|
78
|
+
return "standard";
|
|
79
|
+
}
|
package/src/index.ts
CHANGED
package/src/mesh-boolean.pool.ts
CHANGED
|
@@ -1,36 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MeshBooleanExecutionError,
|
|
3
|
-
type MeshBooleanAsyncOptions,
|
|
4
|
-
type MeshBooleanErrorPayload,
|
|
5
|
-
type MeshBooleanOperation,
|
|
6
|
-
type MeshBooleanOptions,
|
|
7
|
-
type MeshBooleanProgressEvent,
|
|
8
|
-
type MeshBooleanWorkerRequest,
|
|
9
|
-
type MeshBooleanWorkerResponse,
|
|
10
|
-
|
|
1
|
+
import {
|
|
2
|
+
MeshBooleanExecutionError,
|
|
3
|
+
type MeshBooleanAsyncOptions,
|
|
4
|
+
type MeshBooleanErrorPayload,
|
|
5
|
+
type MeshBooleanOperation,
|
|
6
|
+
type MeshBooleanOptions,
|
|
7
|
+
type MeshBooleanProgressEvent,
|
|
8
|
+
type MeshBooleanWorkerRequest,
|
|
9
|
+
type MeshBooleanWorkerResponse,
|
|
10
|
+
type MeshHeavyMeta,
|
|
11
|
+
type MeshHeavyOperation,
|
|
12
|
+
type MeshHeavyParams,
|
|
13
|
+
type MeshHeavyWorkerRequest,
|
|
14
|
+
} from "./mesh-boolean.protocol.js";
|
|
11
15
|
|
|
12
16
|
interface PoolAcquireOptions {
|
|
13
17
|
size?: number;
|
|
14
18
|
workerFactory?: () => Worker;
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
interface BooleanJob {
|
|
18
|
-
id: number;
|
|
19
|
-
operation: MeshBooleanOperation;
|
|
20
|
-
options: MeshBooleanOptions;
|
|
21
|
-
onProgress?: (event: MeshBooleanProgressEvent) => void;
|
|
22
|
-
signal?: AbortSignal;
|
|
23
|
-
timeoutMs?: number;
|
|
24
|
-
bufferA: ArrayBuffer;
|
|
21
|
+
interface BooleanJob {
|
|
22
|
+
id: number;
|
|
23
|
+
operation: MeshBooleanOperation | MeshHeavyOperation;
|
|
24
|
+
options: MeshBooleanOptions;
|
|
25
|
+
onProgress?: (event: MeshBooleanProgressEvent) => void;
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
bufferA: ArrayBuffer;
|
|
25
29
|
bufferB: ArrayBuffer;
|
|
26
30
|
trustedInputA: boolean;
|
|
27
31
|
trustedInputB: boolean;
|
|
32
|
+
/** Heavy multi-buffer job payload (KERNEL-PERF-ARC lever 4); when set,
|
|
33
|
+
* bufferA/bufferB are unused empty buffers. */
|
|
34
|
+
heavy?: {
|
|
35
|
+
buffers: ArrayBuffer[];
|
|
36
|
+
trusted?: boolean[];
|
|
37
|
+
params?: MeshHeavyParams;
|
|
38
|
+
};
|
|
39
|
+
/** Receives structured extras from heavy job responses before resolve. */
|
|
40
|
+
onMeta?: (meta: MeshHeavyMeta | undefined) => void;
|
|
28
41
|
cancelView?: Int32Array;
|
|
29
|
-
timeoutToken?: number;
|
|
30
|
-
abortHandler?: () => void;
|
|
31
|
-
resolve: (value: Float64Array) => void;
|
|
32
|
-
reject: (reason: Error) => void;
|
|
33
|
-
}
|
|
42
|
+
timeoutToken?: number;
|
|
43
|
+
abortHandler?: () => void;
|
|
44
|
+
resolve: (value: Float64Array) => void;
|
|
45
|
+
reject: (reason: Error) => void;
|
|
46
|
+
}
|
|
34
47
|
|
|
35
48
|
interface WorkerSlot {
|
|
36
49
|
id: number;
|
|
@@ -127,22 +140,22 @@ export class MeshBooleanWorkerPool {
|
|
|
127
140
|
const job: BooleanJob = {
|
|
128
141
|
id: jobId,
|
|
129
142
|
operation,
|
|
130
|
-
options: {
|
|
131
|
-
allowUnsafe: options?.allowUnsafe ?? true,
|
|
132
|
-
limits: options?.limits,
|
|
133
|
-
debugForceFaceID: options?.debugForceFaceID,
|
|
134
|
-
},
|
|
135
|
-
onProgress: options?.onProgress,
|
|
136
|
-
signal: options?.signal,
|
|
137
|
-
timeoutMs: options?.timeoutMs,
|
|
138
|
-
bufferA: new Float64Array(bufferA).buffer,
|
|
143
|
+
options: {
|
|
144
|
+
allowUnsafe: options?.allowUnsafe ?? true,
|
|
145
|
+
limits: options?.limits,
|
|
146
|
+
debugForceFaceID: options?.debugForceFaceID,
|
|
147
|
+
},
|
|
148
|
+
onProgress: options?.onProgress,
|
|
149
|
+
signal: options?.signal,
|
|
150
|
+
timeoutMs: options?.timeoutMs,
|
|
151
|
+
bufferA: new Float64Array(bufferA).buffer,
|
|
139
152
|
bufferB: new Float64Array(bufferB).buffer,
|
|
140
|
-
trustedInputA,
|
|
141
|
-
trustedInputB,
|
|
142
|
-
cancelView: createCancellationView(),
|
|
143
|
-
resolve,
|
|
144
|
-
reject,
|
|
145
|
-
};
|
|
153
|
+
trustedInputA,
|
|
154
|
+
trustedInputB,
|
|
155
|
+
cancelView: createCancellationView(),
|
|
156
|
+
resolve,
|
|
157
|
+
reject,
|
|
158
|
+
};
|
|
146
159
|
|
|
147
160
|
if (job.signal?.aborted) {
|
|
148
161
|
this.rejectJob(job, rejectPayload("worker_aborted", `Boolean ${operation} aborted.`));
|
|
@@ -162,6 +175,78 @@ export class MeshBooleanWorkerPool {
|
|
|
162
175
|
});
|
|
163
176
|
}
|
|
164
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Run a heavy multi-buffer kernel job (KERNEL-PERF-ARC lever 4) on the
|
|
180
|
+
* pool. Buffers are COPIED before transfer, so callers keep their meshes.
|
|
181
|
+
*/
|
|
182
|
+
runHeavy(
|
|
183
|
+
operation: MeshHeavyOperation,
|
|
184
|
+
buffers: Float64Array[],
|
|
185
|
+
heavyOptions?: MeshBooleanAsyncOptions & {
|
|
186
|
+
trusted?: boolean[];
|
|
187
|
+
params?: MeshHeavyParams;
|
|
188
|
+
},
|
|
189
|
+
): Promise<{ buffer: Float64Array; meta?: MeshHeavyMeta }> {
|
|
190
|
+
if (this.disposed) {
|
|
191
|
+
return Promise.reject(
|
|
192
|
+
new MeshBooleanExecutionError(
|
|
193
|
+
rejectPayload("worker_unavailable", "Boolean worker pool has been disposed."),
|
|
194
|
+
),
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.ensureWorkers();
|
|
199
|
+
const jobId = this.nextJobId++;
|
|
200
|
+
const empty = new ArrayBuffer(0);
|
|
201
|
+
|
|
202
|
+
return new Promise((resolve, reject) => {
|
|
203
|
+
let meta: MeshHeavyMeta | undefined;
|
|
204
|
+
const job: BooleanJob = {
|
|
205
|
+
id: jobId,
|
|
206
|
+
operation,
|
|
207
|
+
options: {
|
|
208
|
+
allowUnsafe: heavyOptions?.allowUnsafe ?? true,
|
|
209
|
+
limits: heavyOptions?.limits,
|
|
210
|
+
debugForceFaceID: heavyOptions?.debugForceFaceID,
|
|
211
|
+
},
|
|
212
|
+
onProgress: heavyOptions?.onProgress,
|
|
213
|
+
signal: heavyOptions?.signal,
|
|
214
|
+
timeoutMs: heavyOptions?.timeoutMs,
|
|
215
|
+
bufferA: empty,
|
|
216
|
+
bufferB: empty,
|
|
217
|
+
trustedInputA: false,
|
|
218
|
+
trustedInputB: false,
|
|
219
|
+
heavy: {
|
|
220
|
+
buffers: buffers.map((b) => new Float64Array(b).buffer),
|
|
221
|
+
trusted: heavyOptions?.trusted,
|
|
222
|
+
params: heavyOptions?.params,
|
|
223
|
+
},
|
|
224
|
+
onMeta: (m) => {
|
|
225
|
+
meta = m;
|
|
226
|
+
},
|
|
227
|
+
cancelView: createCancellationView(),
|
|
228
|
+
resolve: (buffer) => resolve({ buffer, meta }),
|
|
229
|
+
reject,
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
if (job.signal?.aborted) {
|
|
233
|
+
this.rejectJob(job, rejectPayload("worker_aborted", `${operation} aborted.`));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (job.signal) {
|
|
238
|
+
job.abortHandler = () => {
|
|
239
|
+
this.cancelJob(job.id, "worker_aborted", `${job.operation} aborted.`);
|
|
240
|
+
};
|
|
241
|
+
job.signal.addEventListener("abort", job.abortHandler, { once: true });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
this.queue.push(job);
|
|
245
|
+
this.emitProgress(job, "queued");
|
|
246
|
+
this.dispatch();
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
165
250
|
dispose(): void {
|
|
166
251
|
if (this.disposed) return;
|
|
167
252
|
this.disposed = true;
|
|
@@ -239,9 +324,9 @@ export class MeshBooleanWorkerPool {
|
|
|
239
324
|
continue;
|
|
240
325
|
}
|
|
241
326
|
|
|
242
|
-
slot.currentJobId = job.id;
|
|
243
|
-
this.running.set(job.id, job);
|
|
244
|
-
this.emitProgress(job, "running", slot.id);
|
|
327
|
+
slot.currentJobId = job.id;
|
|
328
|
+
this.running.set(job.id, job);
|
|
329
|
+
this.emitProgress(job, "running", slot.id);
|
|
245
330
|
|
|
246
331
|
if (job.timeoutMs !== undefined && Number.isFinite(job.timeoutMs) && job.timeoutMs > 0) {
|
|
247
332
|
job.timeoutToken = setTimeout(() => {
|
|
@@ -249,20 +334,37 @@ export class MeshBooleanWorkerPool {
|
|
|
249
334
|
}, job.timeoutMs) as unknown as number;
|
|
250
335
|
}
|
|
251
336
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
337
|
+
let request: MeshBooleanWorkerRequest | MeshHeavyWorkerRequest;
|
|
338
|
+
let transfers: ArrayBuffer[];
|
|
339
|
+
if (job.heavy) {
|
|
340
|
+
request = {
|
|
341
|
+
kind: "mesh-heavy-request",
|
|
342
|
+
jobId: job.id,
|
|
343
|
+
operation: job.operation as MeshHeavyOperation,
|
|
344
|
+
buffers: job.heavy.buffers,
|
|
345
|
+
trusted: job.heavy.trusted,
|
|
346
|
+
params: job.heavy.params,
|
|
347
|
+
cancelBuffer: job.cancelView?.buffer as SharedArrayBuffer | undefined,
|
|
348
|
+
options: job.options,
|
|
349
|
+
};
|
|
350
|
+
transfers = job.heavy.buffers;
|
|
351
|
+
} else {
|
|
352
|
+
request = {
|
|
353
|
+
kind: "mesh-boolean-request",
|
|
354
|
+
jobId: job.id,
|
|
355
|
+
operation: job.operation as MeshBooleanOperation,
|
|
356
|
+
bufferA: job.bufferA,
|
|
357
|
+
bufferB: job.bufferB,
|
|
358
|
+
trustedInputA: job.trustedInputA,
|
|
359
|
+
trustedInputB: job.trustedInputB,
|
|
360
|
+
cancelBuffer: job.cancelView?.buffer as SharedArrayBuffer | undefined,
|
|
361
|
+
options: job.options,
|
|
362
|
+
};
|
|
363
|
+
transfers = [request.bufferA, request.bufferB];
|
|
364
|
+
}
|
|
263
365
|
|
|
264
366
|
try {
|
|
265
|
-
slot.worker.postMessage(request,
|
|
367
|
+
slot.worker.postMessage(request, transfers);
|
|
266
368
|
} catch (error) {
|
|
267
369
|
const details = error instanceof Error ? error.message : String(error);
|
|
268
370
|
this.finishRunningJob(
|
|
@@ -300,18 +402,19 @@ export class MeshBooleanWorkerPool {
|
|
|
300
402
|
return;
|
|
301
403
|
}
|
|
302
404
|
|
|
303
|
-
if (data.ok) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
405
|
+
if (data.ok) {
|
|
406
|
+
job.onMeta?.(data.meta);
|
|
407
|
+
this.finishRunningJob(slot, job, undefined, new Float64Array(data.buffer));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
this.finishRunningJob(
|
|
412
|
+
slot,
|
|
413
|
+
job,
|
|
414
|
+
new MeshBooleanExecutionError(data.error),
|
|
415
|
+
undefined,
|
|
416
|
+
);
|
|
417
|
+
}
|
|
315
418
|
|
|
316
419
|
private failWorker(slot: WorkerSlot, payload: MeshBooleanErrorPayload): void {
|
|
317
420
|
const jobId = slot.currentJobId;
|
|
@@ -371,18 +474,18 @@ export class MeshBooleanWorkerPool {
|
|
|
371
474
|
this.dispatch();
|
|
372
475
|
}
|
|
373
476
|
|
|
374
|
-
private finishRunningJob(
|
|
375
|
-
slot: WorkerSlot,
|
|
376
|
-
job: BooleanJob,
|
|
377
|
-
error: Error | undefined,
|
|
378
|
-
result: Float64Array | undefined,
|
|
379
|
-
): void {
|
|
380
|
-
this.running.delete(job.id);
|
|
381
|
-
slot.currentJobId = null;
|
|
382
|
-
this.cleanupJob(job);
|
|
383
|
-
|
|
384
|
-
if (error) {
|
|
385
|
-
job.reject(error);
|
|
477
|
+
private finishRunningJob(
|
|
478
|
+
slot: WorkerSlot,
|
|
479
|
+
job: BooleanJob,
|
|
480
|
+
error: Error | undefined,
|
|
481
|
+
result: Float64Array | undefined,
|
|
482
|
+
): void {
|
|
483
|
+
this.running.delete(job.id);
|
|
484
|
+
slot.currentJobId = null;
|
|
485
|
+
this.cleanupJob(job);
|
|
486
|
+
|
|
487
|
+
if (error) {
|
|
488
|
+
job.reject(error);
|
|
386
489
|
} else if (result) {
|
|
387
490
|
job.resolve(result);
|
|
388
491
|
} else {
|
|
@@ -410,15 +513,15 @@ export class MeshBooleanWorkerPool {
|
|
|
410
513
|
}
|
|
411
514
|
|
|
412
515
|
private emitProgress(job: BooleanJob, stage: MeshBooleanProgressEvent["stage"], workerId?: number): void {
|
|
413
|
-
if (!job.onProgress) return;
|
|
414
|
-
job.onProgress({
|
|
415
|
-
stage,
|
|
416
|
-
jobId: job.id,
|
|
417
|
-
workerId,
|
|
418
|
-
queueSize: this.queue.length,
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
}
|
|
516
|
+
if (!job.onProgress) return;
|
|
517
|
+
job.onProgress({
|
|
518
|
+
stage,
|
|
519
|
+
jobId: job.id,
|
|
520
|
+
workerId,
|
|
521
|
+
queueSize: this.queue.length,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
}
|
|
422
525
|
|
|
423
526
|
let defaultPoolSizeOverride: number | undefined;
|
|
424
527
|
const defaultPools = new Map<number, MeshBooleanWorkerPool>();
|
|
@@ -489,3 +592,22 @@ export function runMeshBooleanInWorkerPool(
|
|
|
489
592
|
});
|
|
490
593
|
return pool.run(operation, bufferA, bufferB, trustedInputA, trustedInputB, options);
|
|
491
594
|
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Run a heavy multi-buffer kernel job on the shared worker pool
|
|
598
|
+
* (KERNEL-PERF-ARC lever 4). See MeshHeavyWorkerRequest for buffer shapes.
|
|
599
|
+
*/
|
|
600
|
+
export function runMeshHeavyInWorkerPool(
|
|
601
|
+
operation: MeshHeavyOperation,
|
|
602
|
+
buffers: Float64Array[],
|
|
603
|
+
options?: MeshBooleanAsyncOptions & {
|
|
604
|
+
trusted?: boolean[];
|
|
605
|
+
params?: MeshHeavyParams;
|
|
606
|
+
},
|
|
607
|
+
): Promise<{ buffer: Float64Array; meta?: MeshHeavyMeta }> {
|
|
608
|
+
const pool = getMeshBooleanWorkerPool({
|
|
609
|
+
workerFactory: options?.workerFactory,
|
|
610
|
+
size: options?.poolSize,
|
|
611
|
+
});
|
|
612
|
+
return pool.runHeavy(operation, buffers, options);
|
|
613
|
+
}
|
|
@@ -90,13 +90,18 @@ export interface MeshBooleanWorkerCancel {
|
|
|
90
90
|
jobId: number;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
export type MeshBooleanWorkerInbound =
|
|
93
|
+
export type MeshBooleanWorkerInbound =
|
|
94
|
+
| MeshBooleanWorkerRequest
|
|
95
|
+
| MeshHeavyWorkerRequest
|
|
96
|
+
| MeshBooleanWorkerCancel;
|
|
94
97
|
|
|
95
98
|
export interface MeshBooleanWorkerSuccessResponse {
|
|
96
99
|
kind: "mesh-boolean-result";
|
|
97
100
|
jobId: number;
|
|
98
101
|
ok: true;
|
|
99
102
|
buffer: ArrayBuffer;
|
|
103
|
+
/** Structured extras for heavy jobs (e.g. makeWatertight method). */
|
|
104
|
+
meta?: Record<string, string | number | boolean>;
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
export interface MeshBooleanWorkerErrorResponse {
|
|
@@ -119,3 +124,41 @@ export class MeshBooleanExecutionError extends Error {
|
|
|
119
124
|
this.details = payload.details;
|
|
120
125
|
}
|
|
121
126
|
}
|
|
127
|
+
|
|
128
|
+
// ---- Heavy kernel jobs (KERNEL-PERF-ARC lever 4) ---------------------------
|
|
129
|
+
//
|
|
130
|
+
// Multi-second kernel operations beyond pairwise booleans run on the same
|
|
131
|
+
// worker pool: batch booleans, watertight repair, voxel remesh, solid edge
|
|
132
|
+
// chamfer. Buffers are op-specific:
|
|
133
|
+
// unionAll: [mesh0, mesh1, ...]
|
|
134
|
+
// differenceAll: [host, cutter0, cutter1, ...]
|
|
135
|
+
// makeWatertight: [mesh]
|
|
136
|
+
// voxelRemesh: [mesh]
|
|
137
|
+
// chamferEdges: [mesh, edgeSegments] (flat [ax,ay,az,bx,by,bz] per edge)
|
|
138
|
+
|
|
139
|
+
export type MeshHeavyOperation =
|
|
140
|
+
| "unionAll"
|
|
141
|
+
| "differenceAll"
|
|
142
|
+
| "makeWatertight"
|
|
143
|
+
| "voxelRemesh"
|
|
144
|
+
| "chamferEdges";
|
|
145
|
+
|
|
146
|
+
export interface MeshHeavyParams {
|
|
147
|
+
resolution?: number;
|
|
148
|
+
iso?: number;
|
|
149
|
+
distance?: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface MeshHeavyWorkerRequest {
|
|
153
|
+
kind: "mesh-heavy-request";
|
|
154
|
+
jobId: number;
|
|
155
|
+
operation: MeshHeavyOperation;
|
|
156
|
+
buffers: ArrayBuffer[];
|
|
157
|
+
trusted?: boolean[];
|
|
158
|
+
params?: MeshHeavyParams;
|
|
159
|
+
cancelBuffer?: SharedArrayBuffer;
|
|
160
|
+
options: MeshBooleanOptions;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Structured extras for heavy results (e.g. makeWatertight method). */
|
|
164
|
+
export type MeshHeavyMeta = Record<string, string | number | boolean>;
|