@volter/blender-engine 0.1.38 → 0.1.40

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.
@@ -133,6 +133,10 @@ export interface BlenderCallMetrics {
133
133
  readonly wasmMemoryMB: number | null;
134
134
  }
135
135
 
136
+ /** The engine's linear-memory ceiling, its link's `-sMAXIMUM_MEMORY` (4 GiB, wasm32's limit;
137
+ * `blender_web_headless.cmake`). JavaScript cannot read a memory's maximum back. */
138
+ const ENGINE_MEMORY_CEILING_MB = 4096;
139
+
136
140
  export class BlenderRuntime {
137
141
  readonly #worker: Worker;
138
142
  readonly #options: BlenderRuntimeOptions;
@@ -187,8 +191,15 @@ export class BlenderRuntime {
187
191
  // Error ("Uncaught [object Object]"), while the reason (an allocation that failed, the file
188
192
  // it was reading) is the last thing Blender printed.
189
193
  const last = this.#lastLines.length > 0 ? `\nBlender's last output:\n${this.#lastLines.join('\n')}` : '';
194
+ // OUT OF MEMORY IS SAID AS SUCH, from the heap the worker reported as the thread died.
195
+ const heapMB = this.#wasmBytes === null ? null : Math.round(this.#wasmBytes / 1048576);
196
+ const exhausted =
197
+ heapMB !== null && heapMB >= ENGINE_MEMORY_CEILING_MB - 16
198
+ ? `Blender ran out of memory: its heap reached ${heapMB} MB of the in-tab engine's ` +
199
+ `${ENGINE_MEMORY_CEILING_MB} MB, and this scene needs more. `
200
+ : '';
190
201
  const error = new Error(
191
- `Blender worker failed: ${said || 'the worker script did not load'}${where}${last}`,
202
+ `${exhausted}Blender worker failed: ${said || 'the worker script did not load'}${where}${last}`,
192
203
  );
193
204
  // The page console is the editor's ledger (`installEditorConsoleReporting`
194
205
  // captures it, source-blind), and this package may import nothing of the
@@ -2352,10 +2352,14 @@ class NativeHistory:
2352
2352
  finally:
2353
2353
  self.moving = False
2354
2354
  self.cursor += -1 if direction == "undo" else 1
2355
- # Native undo replaces datablocks. Both revision caches must forget their
2356
- # old pointers before the restored scene is exported to the presenter.
2357
- _blender_web.session_reset()
2358
- SESSION.forget()
2355
+ # AN UNDO STEP IS AN ORDINARY UPDATE when Blender kept its datablocks and depsgraph:
2356
+ # memfile undo re-reads only the changed IDs and tags exactly those, so the next frame
2357
+ # re-ships what changed and the presenter keeps the rest (`session_undo`). A step that
2358
+ # replaced the depsgraph starts both revision caches over, as does an engine without
2359
+ # the door.
2360
+ if not hasattr(_blender_web, "session_undo") or _blender_web.session_undo():
2361
+ _blender_web.session_reset()
2362
+ SESSION.forget()
2359
2363
  SESSION.present()
2360
2364
  return {"moved": True}
2361
2365
 
package/browser/worker.ts CHANGED
@@ -704,6 +704,11 @@ function reportMemory(): void {
704
704
  if (bytes !== null) post({ op: 'memory', bytes });
705
705
  }
706
706
 
707
+ // A BLENDER THREAD THAT DIES IS WEIGHED FIRST: its error reaches this scope before the page's
708
+ // `onerror`, so the heap it died at is posted ahead of the failure, and the page can say whether
709
+ // the engine ran out of memory (`runtime.ts`) instead of "Uncaught [object Object]".
710
+ self.addEventListener('error', reportMemory);
711
+
707
712
  self.onmessage = (event: MessageEvent<WorkerRequest>) => {
708
713
  const request = event.data;
709
714
  if (request.op === 'present-result') {
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.38",
5
+ "version": "0.1.40",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },