@volter/blender-engine 0.1.58 → 0.1.60

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.
@@ -116,6 +116,8 @@ export interface BlenderCallMetrics {
116
116
  * otherwise the worst call this session has ever seen is invisible for
117
117
  * exactly as long as it keeps running. */
118
118
  readonly maxCallMs: number | null;
119
+ /** Operation and load boundary owning the longest request; no payload data. */
120
+ readonly maxCallLabel?: string | null;
119
121
  /** Calls whose duration passed 5s, counting outstanding ones already past it. */
120
122
  readonly callsOver5s: number;
121
123
  /** The same at 30s — a call this side of it is slow, past it is a wedge. */
@@ -163,6 +165,9 @@ export class BlenderRuntime {
163
165
  };
164
166
  /** `performance.now()` at the `postMessage` of every outstanding call. */
165
167
  readonly #callStarts = new Map<number, number>();
168
+ readonly #callLabels = new Map<number, string>();
169
+ #loadBoundary = "engine-start";
170
+ #maxCompletedLabel: string | null = null;
166
171
  readonly #callWork = new Map<number, () => void>();
167
172
  #lastCallMs: number | null = null;
168
173
  #maxCompletedMs = 0;
@@ -265,6 +270,7 @@ export class BlenderRuntime {
265
270
  async #load(request: Extract<Request, { op: 'start' }>): Promise<RuntimeStart> {
266
271
  let result = await this.#request(request) as PullResult<RuntimeStart>;
267
272
  while (result?.load === 'continue') {
273
+ this.#loadBoundary = result.phase;
268
274
  result = await this.#request({ op: 'load-next', token: result.token }) as PullResult<RuntimeStart>;
269
275
  }
270
276
  return result?.load === 'done' ? result.value : result as unknown as RuntimeStart;
@@ -518,12 +524,13 @@ export class BlenderRuntime {
518
524
  metrics(now: number = performance.now()): BlenderCallMetrics {
519
525
  let oldest: number | null = null;
520
526
  let max = this.#maxCompletedMs;
527
+ let maxLabel = this.#maxCompletedLabel;
521
528
  let over5 = this.#completedOver5s;
522
529
  let over30 = this.#completedOver30s;
523
- for (const started of this.#callStarts.values()) {
530
+ for (const [id, started] of this.#callStarts) {
524
531
  const elapsed = now - started;
525
532
  if (oldest === null || elapsed > oldest) oldest = elapsed;
526
- if (elapsed > max) max = elapsed;
533
+ if (elapsed > max) { max = elapsed; maxLabel = this.#callLabels.get(id) ?? null; }
527
534
  if (elapsed >= 5_000) over5 += 1;
528
535
  if (elapsed >= 30_000) over30 += 1;
529
536
  }
@@ -533,6 +540,7 @@ export class BlenderRuntime {
533
540
  // Zero here would mean "no call has ever taken any time", which is a
534
541
  // different claim from "no call has happened yet".
535
542
  maxCallMs: this.#lastCallMs === null && oldest === null ? null : Math.round(max),
543
+ maxCallLabel: maxLabel,
536
544
  callsOver5s: over5,
537
545
  callsOver30s: over30,
538
546
  lastCallWindow: this.#lastCallWindow,
@@ -547,9 +555,11 @@ export class BlenderRuntime {
547
555
  const started = this.#callStarts.get(id);
548
556
  if (started === undefined) return;
549
557
  this.#callStarts.delete(id);
558
+ const label = this.#callLabels.get(id) ?? null;
559
+ this.#callLabels.delete(id);
550
560
  const elapsed = performance.now() - started;
551
561
  this.#lastCallMs = elapsed;
552
- if (elapsed > this.#maxCompletedMs) this.#maxCompletedMs = elapsed;
562
+ if (elapsed > this.#maxCompletedMs) { this.#maxCompletedMs = elapsed; this.#maxCompletedLabel = label; }
553
563
  if (elapsed >= 5_000) this.#completedOver5s += 1;
554
564
  if (elapsed >= 30_000) this.#completedOver30s += 1;
555
565
  if (this.#lastCallWindow !== null && this.#lastCallWindow.start === started)
@@ -564,6 +574,7 @@ export class BlenderRuntime {
564
574
  this.#pending.set(id, { resolve, reject });
565
575
  const started = performance.now();
566
576
  this.#callStarts.set(id, started);
577
+ this.#callLabels.set(id, request.op === "load-next" ? `load-next after ${this.#loadBoundary}` : request.op);
567
578
  this.#lastCallWindow = { start: started, end: null };
568
579
  this.#callWork.set(id, this.#work(`waiting for worker ${request.op}`));
569
580
  try {
package/browser/worker.ts CHANGED
@@ -71,7 +71,8 @@ const pendingPresents = new Map<
71
71
 
72
72
  async function streamToTab(value: unknown): Promise<PresentAnswer> {
73
73
  return await sendFrameValue(value, async chunk => {
74
- await loadCheckpoint?.("frame-transfer");
74
+ const kind = value as { op?: string; mesh?: unknown; image?: unknown };
75
+ await loadCheckpoint?.(`frame-transfer/${kind.op ?? "unknown"}/${kind.mesh !== undefined ? "mesh" : kind.image !== undefined ? "image" : "manifest"}/${chunk.kind}`);
75
76
  const id = ++presentId;
76
77
  return new Promise<PresentAnswer>((resolve, reject) => {
77
78
  pendingPresents.set(id, { resolve, reject });
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@volter/blender-engine",
3
3
  "author": "Volter AI, Inc.",
4
4
  "license": "GPL-3.0-or-later",
5
- "version": "0.1.58",
5
+ "version": "0.1.60",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
package/wasm/BUNDLE.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "blender": "5.2.0 LTS",
3
- "source": "54e4af619a285c6d76c56ef960dc99d45aca5084",
3
+ "source": "9fdd05db03bf0e4aa1028607be3e113be7f98d01",
4
4
  "upstream": "fbe6228777e7d9afefcd61a413844e790ae75db7",
5
5
  "sourceRepository": "https://github.com/volter-ai/blender",
6
- "sourceRecipe": "https://github.com/volter-ai/blender/blob/54e4af619a285c6d76c56ef960dc99d45aca5084/build_files/emscripten/RELEASE.md",
6
+ "sourceRecipe": "https://github.com/volter-ai/blender/blob/9fdd05db03bf0e4aa1028607be3e113be7f98d01/build_files/emscripten/RELEASE.md",
7
7
  "factoryEngine": "BLENDER_EEVEE",
8
8
  "files": {
9
9
  "blender_browser.js": "raw",
@@ -16,8 +16,8 @@
16
16
  "sha256": "0c63c29e7d4b1ba1fce4e94057778b0757767b25ea4f6f01d329e99351568f30"
17
17
  },
18
18
  "blender_browser.wasm": {
19
- "bytes": 86332914,
20
- "sha256": "15c4b7072ebc655418849da79a6fefed8bff26774e7446ade8d3a54bd6ad7a61"
19
+ "bytes": 86337036,
20
+ "sha256": "9d8cb5bd0dbb34fa7f04858fb6bc831195d0b06e8b82bb26889f3c6d807a9588"
21
21
  },
22
22
  "blender_browser.data": {
23
23
  "bytes": 52698568,
@@ -30,8 +30,8 @@
30
30
  "sha256": "0c63c29e7d4b1ba1fce4e94057778b0757767b25ea4f6f01d329e99351568f30"
31
31
  },
32
32
  "blender_browser.wasm.br": {
33
- "bytes": 18601239,
34
- "sha256": "8cd29a87fcce046c292c52cc62523fe0ebbd1db8f05b19503d56311ae12c84c5"
33
+ "bytes": 18604996,
34
+ "sha256": "782c1f8519851e8b901d5309d239429ecd07c4ed374adaf117214f9ac22f84c9"
35
35
  },
36
36
  "blender_browser.data.br": {
37
37
  "bytes": 8056563,
@@ -45,6 +45,6 @@
45
45
  "note": "Stoneguard c7/c14/c15 engine. Emscripten 6.0.5, Release -O3 objects and -O2 link. Portable native arm64 matrix reciprocal plus fused generic OpenSubdiv stencil and patch accumulation; bodice four-stage comparison 28/28 arrays exact. Full-scene comparison and memory scope recorded in provenance/stoneguard-memory.json. Historical battery absent; no full-battery or hosted acceptance claim.",
46
46
  "callTrampoline": "CPython 3.13.13 is compiled with PY_CALL_TRAMPOLINE for Blender CPython slot arity casts. This applies to CPython dispatch, not arbitrary indirect calls.",
47
47
  "engineIds": "The existing session registers Python render engines under CYCLES, BLENDER_EEVEE and BLENDER_WORKBENCH; the headless build permits built-in IDs to be shadowed.",
48
- "exportDoor": "The source-owned bpy_web_export.cc exports Principled physical inputs, normal images, eight named UV layers and evaluated corner normals, and takes graph_materials/graph_images/graph_generated (a graph-drawn shader mix reduced to the one Principled BSDF it reaches, graph images at the door revisions with every UDIM tile, orco for deformed meshes whose graph reads Generated coordinates). A frame can defer its changed meshes and pictures (defer) for the caller to pull one at a time (export_mesh, export_image), each call freeing the previous one's columns and a picture it decoded; an undo step keeps the revisions when Blender kept the depsgraph (session_undo); Blender's CPU subdivision descriptor is released once a mesh is read; the memory door reports malloc in-use and free. Datablock revisions are keyed by session_uid. writefile.cc writes one copy of the document per save in the browser build. CPU evaluators adopt stencil tables without cloning; browser subdivision descriptors are released immediately after CPU tessellation/deformation. Native evaluated corner triangles accompany corner normals. Consumed local conversion stencils are released before later UV channels are constructed.",
48
+ "exportDoor": "The source-owned bpy_web_export.cc exports Principled physical inputs, normal images, eight named UV layers and evaluated corner normals, and takes graph_materials/graph_images/graph_generated (a graph-drawn shader mix reduced to the one Principled BSDF it reaches, graph images at the door revisions with every UDIM tile, orco for deformed meshes whose graph reads Generated coordinates). A frame can defer its changed meshes and pictures (defer) for the caller to pull one at a time (export_mesh, export_image), each call freeing the previous one's columns and a picture it decoded; an undo step keeps the revisions when Blender kept the depsgraph (session_undo); Blender's CPU subdivision descriptor is released once a mesh is read; the memory door reports malloc in-use and free. Datablock revisions are keyed by session_uid. writefile.cc writes one copy of the document per save in the browser build. CPU evaluators adopt stencil tables without cloning; browser subdivision descriptors are released immediately after CPU tessellation/deformation. Native evaluated corner triangles accompany corner normals. Consumed local conversion stencils are released before later UV channels are constructed. Caller-thread file-read, evaluated-object and subdivision checkpoints provide backpressure for resumable startup. Shared geometry revisions advance once per frame. Existing synchronous export remains available.",
49
49
  "preloadedPayload": "releasePreloadedFileData releases the JS preload buffer after session ready. The measured working payload exclusions are retained."
50
50
  }
Binary file