@aardworx/wombat.rendering 0.19.19 → 0.19.21

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.
@@ -1 +1 @@
1
- {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/window/loop.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAQ,aAAa,EAAe,MAAM,2BAA2B,CAAC;AAElF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAsBpD,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;OAQG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;AAE5E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,QAAQ,CACtB,UAAU,EAAE,gBAAgB,EAC5B,KAAK,EAAE,aAAa,EACpB,IAAI,GAAE,eAAoB,GACzB;IAAE,IAAI,IAAI,IAAI,CAAA;CAAE,CA4FlB"}
1
+ {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/window/loop.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAQ,aAAa,EAAe,MAAM,2BAA2B,CAAC;AAElF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAoDpD,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;OAQG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;AAE5E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,QAAQ,CACtB,UAAU,EAAE,gBAAgB,EAC5B,KAAK,EAAE,aAAa,EACpB,IAAI,GAAE,eAAoB,GACzB;IAAE,IAAI,IAAI,IAAI,CAAA;CAAE,CA2FlB"}
@@ -33,18 +33,52 @@ import { AVal, transact } from "@aardworx/wombat.adaptive";
33
33
  // AFTER each frame's pacer resolves (i.e. GPU work done), so a
34
34
  // harness can serialize edit → frame-complete → next edit and read
35
35
  // honest end-to-end frame costs.
36
- const UNCAPPED = globalThis
37
- .__wombatUncappedRenderLoop === true;
36
+ // Read at runFrame() start (NOT module load) — consumers set the flag
37
+ // from app code that runs after this module is imported.
38
+ function isUncapped() {
39
+ return globalThis
40
+ .__wombatUncappedRenderLoop === true;
41
+ }
38
42
  function bumpFrameCount() {
39
43
  const g = globalThis;
40
44
  g.__wombatFrameCount = (g.__wombatFrameCount ?? 0) + 1;
41
45
  }
46
+ // setTimeout(0) is useless for an uncapped loop: Chrome clamps nested
47
+ // timers to a 4 ms minimum after depth 5. MessageChannel.postMessage
48
+ // is the unthrottled macrotask primitive.
49
+ const immediateCbs = new Map();
50
+ let immediateNext = 1;
51
+ const immediateChannel = typeof MessageChannel !== "undefined" ? new MessageChannel() : null;
52
+ if (immediateChannel !== null) {
53
+ immediateChannel.port1.onmessage = (e) => {
54
+ const id = e.data;
55
+ const cb = immediateCbs.get(id);
56
+ immediateCbs.delete(id);
57
+ cb?.();
58
+ };
59
+ }
60
+ function setImmediate2(cb) {
61
+ if (immediateChannel === null)
62
+ return setTimeout(cb, 0);
63
+ const id = immediateNext++;
64
+ immediateCbs.set(id, cb);
65
+ immediateChannel.port2.postMessage(id);
66
+ return id;
67
+ }
68
+ function clearImmediate2(id) {
69
+ if (immediateChannel === null) {
70
+ clearTimeout(id);
71
+ return;
72
+ }
73
+ immediateCbs.delete(id);
74
+ }
42
75
  export function runFrame(attachment, frame, opts = {}) {
43
76
  let stopped = false;
44
77
  let frameNo = 0;
45
78
  let lastTime = performance.now();
46
79
  let rafId = 0;
47
80
  let pending = false;
81
+ const UNCAPPED = isUncapped();
48
82
  // Wrap the frame callback in an AVal so the adaptive system tracks
49
83
  // its dependency set. Each `force()` re-reads the inputs and runs
50
84
  // the frame; subsequent input marks fire the marking callback.
@@ -78,12 +112,10 @@ export function runFrame(attachment, frame, opts = {}) {
78
112
  sub.dispose();
79
113
  }
80
114
  };
81
- const schedule = (cb) => UNCAPPED
82
- ? setTimeout(cb, 0)
83
- : requestAnimationFrame(cb);
115
+ const schedule = (cb) => UNCAPPED ? setImmediate2(cb) : requestAnimationFrame(cb);
84
116
  const cancel = (h) => {
85
117
  if (UNCAPPED)
86
- clearTimeout(h);
118
+ clearImmediate2(h);
87
119
  else
88
120
  cancelAnimationFrame(h);
89
121
  };
@@ -1 +1 @@
1
- {"version":3,"file":"loop.js","sourceRoot":"","sources":["../../src/window/loop.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loop.js","sourceRoot":"","sources":["../../src/window/loop.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aardworx/wombat.rendering",
3
- "version": "0.19.19",
3
+ "version": "0.19.21",
4
4
  "description": "WebGPU rendering layer for the Wombat TypeScript stack — RenderObject/RenderTask/runtime + window glue, port of Aardvark.Rendering's lower layers on top of WebGPU and wombat.shader.",
5
5
  "license": "MIT",
6
6
  "author": "krauthaufen",
@@ -37,15 +37,45 @@ import type { CanvasAttachment } from "./canvas.js";
37
37
  // AFTER each frame's pacer resolves (i.e. GPU work done), so a
38
38
  // harness can serialize edit → frame-complete → next edit and read
39
39
  // honest end-to-end frame costs.
40
- const UNCAPPED: boolean =
41
- (globalThis as Record<string, unknown> & typeof globalThis)
40
+ // Read at runFrame() start (NOT module load) — consumers set the flag
41
+ // from app code that runs after this module is imported.
42
+ function isUncapped(): boolean {
43
+ return (globalThis as Record<string, unknown> & typeof globalThis)
42
44
  .__wombatUncappedRenderLoop === true;
45
+ }
43
46
 
44
47
  function bumpFrameCount(): void {
45
48
  const g = globalThis as Record<string, unknown> & typeof globalThis;
46
49
  g.__wombatFrameCount = ((g.__wombatFrameCount as number | undefined) ?? 0) + 1;
47
50
  }
48
51
 
52
+ // setTimeout(0) is useless for an uncapped loop: Chrome clamps nested
53
+ // timers to a 4 ms minimum after depth 5. MessageChannel.postMessage
54
+ // is the unthrottled macrotask primitive.
55
+ const immediateCbs = new Map<number, () => void>();
56
+ let immediateNext = 1;
57
+ const immediateChannel: MessageChannel | null =
58
+ typeof MessageChannel !== "undefined" ? new MessageChannel() : null;
59
+ if (immediateChannel !== null) {
60
+ immediateChannel.port1.onmessage = (e: MessageEvent): void => {
61
+ const id = e.data as number;
62
+ const cb = immediateCbs.get(id);
63
+ immediateCbs.delete(id);
64
+ cb?.();
65
+ };
66
+ }
67
+ function setImmediate2(cb: () => void): number {
68
+ if (immediateChannel === null) return setTimeout(cb, 0) as unknown as number;
69
+ const id = immediateNext++;
70
+ immediateCbs.set(id, cb);
71
+ immediateChannel.port2.postMessage(id);
72
+ return id;
73
+ }
74
+ function clearImmediate2(id: number): void {
75
+ if (immediateChannel === null) { clearTimeout(id); return; }
76
+ immediateCbs.delete(id);
77
+ }
78
+
49
79
  export interface RunFrameOptions {
50
80
  /** Stop after N frames. Useful for tests + finite animations. */
51
81
  readonly maxFrames?: number;
@@ -93,6 +123,7 @@ export function runFrame(
93
123
  let lastTime = performance.now();
94
124
  let rafId = 0;
95
125
  let pending = false;
126
+ const UNCAPPED = isUncapped();
96
127
 
97
128
  // Wrap the frame callback in an AVal so the adaptive system tracks
98
129
  // its dependency set. Each `force()` re-reads the inputs and runs
@@ -127,11 +158,9 @@ export function runFrame(
127
158
  };
128
159
 
129
160
  const schedule = (cb: () => void): number =>
130
- UNCAPPED
131
- ? (setTimeout(cb, 0) as unknown as number)
132
- : requestAnimationFrame(cb);
161
+ UNCAPPED ? setImmediate2(cb) : requestAnimationFrame(cb);
133
162
  const cancel = (h: number): void => {
134
- if (UNCAPPED) clearTimeout(h);
163
+ if (UNCAPPED) clearImmediate2(h);
135
164
  else cancelAnimationFrame(h);
136
165
  };
137
166