@downdraft/engine 0.2.0 → 0.3.1

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.
Files changed (138) hide show
  1. package/app/src/renderer/bootstrap.ts +0 -6
  2. package/app/src/renderer/feature-log.ts +1 -9
  3. package/app/src/renderer/mcp-harness.ts +8 -11
  4. package/app/src/renderer/standard-automation-tools.ts +8 -145
  5. package/core/src/index.ts +2 -2
  6. package/core/src/module/host.ts +3 -6
  7. package/core/src/module/index.ts +1 -1
  8. package/core/src/module/module.ts +4 -47
  9. package/core/src/physics/interface.ts +5 -0
  10. package/core/src/platform/render-surface.ts +13 -0
  11. package/core/src/platform/rpc.ts +4 -1
  12. package/core/src/render/canvas-resize-watcher.ts +1 -10
  13. package/core/src/render/game-renderer.ts +164 -79
  14. package/core/src/render/index.ts +0 -2
  15. package/core/src/render/input-manager.ts +7 -7
  16. package/core/src/render/renderer-module-host.ts +2 -4
  17. package/core/src/sab/grid-buffer.ts +55 -2
  18. package/core/src/sab/record-buffer.ts +48 -5
  19. package/core/src/sab/sim-channel.ts +181 -17
  20. package/core/src/sab/slot-buffer.ts +55 -1
  21. package/core/src/sab/types.ts +11 -0
  22. package/core/src/telemetry/gpu-resource-tracker.ts +24 -0
  23. package/core/src/ui-font-scale.ts +5 -38
  24. package/core/src/util/index.ts +1 -0
  25. package/core/src/util/session-timer.ts +23 -0
  26. package/core/src/worker/base-worker-host.ts +2 -1
  27. package/core/src/worker/buffer-sync.ts +20 -13
  28. package/core/src/worker/rpc.ts +119 -5
  29. package/core/src/worker/sim-worker-base.ts +5 -2
  30. package/core/src/worker/task-pool.ts +4 -0
  31. package/dist/.dd-manifest.json +893 -908
  32. package/dist/app/src/renderer/bootstrap.js +0 -4
  33. package/dist/app/src/renderer/feature-log.js +1 -10
  34. package/dist/app/src/renderer/mcp-harness.js +8 -11
  35. package/dist/app/src/renderer/standard-automation-tools.js +6 -138
  36. package/dist/core/src/index.js +2 -2
  37. package/dist/core/src/module/host.js +3 -6
  38. package/dist/core/src/platform/rpc.js +2 -1
  39. package/dist/core/src/render/canvas-resize-watcher.js +1 -10
  40. package/dist/core/src/render/game-renderer.js +166 -74
  41. package/dist/core/src/render/index.js +0 -1
  42. package/dist/core/src/render/input-manager.js +7 -7
  43. package/dist/core/src/render/renderer-module-host.js +2 -4
  44. package/dist/core/src/sab/grid-buffer.js +53 -1
  45. package/dist/core/src/sab/record-buffer.js +49 -5
  46. package/dist/core/src/sab/sim-channel.js +173 -17
  47. package/dist/core/src/sab/slot-buffer.js +54 -1
  48. package/dist/core/src/telemetry/gpu-resource-tracker.js +26 -0
  49. package/dist/core/src/ui-font-scale.js +5 -38
  50. package/dist/core/src/util/index.js +1 -0
  51. package/dist/core/src/util/session-timer.js +22 -0
  52. package/dist/core/src/worker/base-worker-host.js +2 -1
  53. package/dist/core/src/worker/buffer-sync.js +16 -9
  54. package/dist/core/src/worker/rpc.js +105 -8
  55. package/dist/core/src/worker/sim-worker-base.js +5 -2
  56. package/dist/core/src/worker/task-pool.js +5 -0
  57. package/dist/libraries/blitz-ui/src/native-module.js +8 -1
  58. package/dist/libraries/devtools/src/backend.js +35 -9
  59. package/dist/libraries/devtools/src/collectors.js +73 -22
  60. package/dist/libraries/devtools/src/native-providers.js +0 -2
  61. package/dist/libraries/entities/src/model-renderer.js +87 -27
  62. package/dist/libraries/entities/src/shaders/model.wgsl +13 -4
  63. package/dist/libraries/networking/src/index.js +1 -0
  64. package/dist/libraries/networking/src/ws-server.js +387 -0
  65. package/dist/libraries/physics-rapier/src/api.js +15 -0
  66. package/dist/libraries/physics-rapier/src/backend.js +15 -0
  67. package/dist/libraries/physics-rapier/src/rapier-backend.js +5 -0
  68. package/dist/libraries/postfx/src/post-process-stack.js +1 -3
  69. package/dist/libraries/sand/src/materials.js +2 -32
  70. package/dist/libraries/sand/src/sand-world.js +0 -3
  71. package/dist/modules/devtools/src/api.js +8 -23
  72. package/dist/modules/devtools/src/blitz/fonts.js +75 -0
  73. package/dist/modules/devtools/src/blitz/host.js +124 -30
  74. package/dist/modules/devtools/src/blitz/panels/console.js +19 -7
  75. package/dist/modules/devtools/src/blitz/panels/elements.js +217 -28
  76. package/dist/modules/devtools/src/blitz/panels/gpu.js +5 -2
  77. package/dist/modules/devtools/src/blitz/panels/performance.js +26 -12
  78. package/dist/modules/devtools/src/blitz/panels/snapshot.js +31 -8
  79. package/dist/modules/devtools/src/blitz/panels/types.js +2 -1
  80. package/dist/modules/devtools/src/blitz/theme.js +26 -4
  81. package/dist/modules/devtools/src/data-bridge.js +0 -11
  82. package/dist/modules/devtools/src/index.js +0 -6
  83. package/dist/modules/devtools/src/init.js +8 -42
  84. package/dist/modules/devtools/src/scene-inspector.js +1 -4
  85. package/dist/modules/devtools/src/worker-sync.js +3 -11
  86. package/dist/modules/html-ui/src/doc-backend.js +52 -3
  87. package/dist/modules/html-ui/src/host.js +6 -3
  88. package/dist/test/src/vision-test.js +1 -3
  89. package/libraries/blitz-ui/native/Cargo.toml +6 -0
  90. package/libraries/blitz-ui/native-osr/Cargo.toml +3 -1
  91. package/libraries/blitz-ui/src/native-module.ts +8 -1
  92. package/libraries/devtools/src/backend.ts +38 -12
  93. package/libraries/devtools/src/collectors.ts +75 -22
  94. package/libraries/devtools/src/native-providers.ts +0 -2
  95. package/libraries/entities/src/model-renderer.ts +88 -27
  96. package/libraries/entities/src/shaders/model.wgsl +13 -4
  97. package/libraries/networking/src/index.ts +4 -0
  98. package/libraries/networking/src/ws-server.ts +397 -0
  99. package/libraries/physics-rapier/src/api.ts +15 -0
  100. package/libraries/physics-rapier/src/backend.ts +15 -0
  101. package/libraries/physics-rapier/src/rapier-backend.ts +6 -0
  102. package/libraries/postfx/src/post-process-stack.ts +1 -2
  103. package/libraries/sand/src/materials.ts +2 -32
  104. package/libraries/sand/src/sand-world.ts +0 -4
  105. package/modules/devtools/README.md +1 -3
  106. package/modules/devtools/src/api.ts +10 -34
  107. package/modules/devtools/src/blitz/fonts.ts +78 -0
  108. package/modules/devtools/src/blitz/host.ts +107 -31
  109. package/modules/devtools/src/blitz/panels/console.ts +20 -8
  110. package/modules/devtools/src/blitz/panels/elements.ts +212 -31
  111. package/modules/devtools/src/blitz/panels/gpu.ts +4 -2
  112. package/modules/devtools/src/blitz/panels/performance.ts +24 -12
  113. package/modules/devtools/src/blitz/panels/snapshot.ts +30 -8
  114. package/modules/devtools/src/blitz/panels/types.ts +2 -1
  115. package/modules/devtools/src/blitz/theme.ts +26 -4
  116. package/modules/devtools/src/data-bridge.ts +0 -16
  117. package/modules/devtools/src/index.ts +1 -11
  118. package/modules/devtools/src/init.ts +8 -51
  119. package/modules/devtools/src/scene-inspector.ts +2 -6
  120. package/modules/devtools/src/types.ts +5 -90
  121. package/modules/devtools/src/worker-sync.ts +3 -13
  122. package/modules/html-ui/src/doc-backend.ts +67 -4
  123. package/modules/html-ui/src/host.ts +11 -3
  124. package/modules/html-ui/src/protocol.ts +1 -1
  125. package/package.json +4 -5
  126. package/test/src/vision-test.ts +2 -4
  127. package/core/src/render/sab-canvas-overlay.ts +0 -122
  128. package/dist/core/src/render/sab-canvas-overlay.js +0 -83
  129. package/dist/modules/devtools/src/debug-overlay.js +0 -181
  130. package/dist/modules/devtools/src/doctor-panel.js +0 -163
  131. package/dist/modules/devtools/src/label-overlay.js +0 -129
  132. package/dist/modules/devtools/src/material-stats-panel.js +0 -171
  133. package/dist/modules/devtools/src/sim-stats-panel.js +0 -250
  134. package/modules/devtools/src/debug-overlay.ts +0 -238
  135. package/modules/devtools/src/doctor-panel.ts +0 -182
  136. package/modules/devtools/src/label-overlay.ts +0 -154
  137. package/modules/devtools/src/material-stats-panel.ts +0 -188
  138. package/modules/devtools/src/sim-stats-panel.ts +0 -272
@@ -132,15 +132,11 @@ export async function bootstrapGame(opts) {
132
132
  return;
133
133
  const { initDevTools } = devtoolsMod;
134
134
  const simStatsProvider = opts.devtools.createSimStatsProvider?.(renderer);
135
- const panels = typeof opts.devtools.panels === "function"
136
- ? opts.devtools.panels(renderer)
137
- : opts.devtools.panels ?? [];
138
135
  const workerHosts = typeof opts.devtools.workerHosts === "function"
139
136
  ? opts.devtools.workerHosts(renderer)
140
137
  : opts.devtools.workerHosts;
141
138
  const devtoolsBridge = await importOrDead(initDevTools(renderer, {
142
139
  simStatsProvider,
143
- panels,
144
140
  sceneInspector: opts.devtools.sceneInspector,
145
141
  bridgeClass: opts.devtools.bridgeClass,
146
142
  workerHosts,
@@ -82,22 +82,13 @@ export function collectRendererFeatureLog(opts) {
82
82
  data.mem = `${nav.deviceMemory}G`;
83
83
  }
84
84
  catch { /* navigator unavailable */ }
85
- // --- SharedArrayBuffer + crossOriginIsolated (browser isolation only —
86
- // there is no origin model on the native host) ---
85
+ // --- SharedArrayBuffer ---
87
86
  try {
88
87
  data.sab = typeof SharedArrayBuffer !== "undefined" ? 1 : 0;
89
88
  }
90
89
  catch {
91
90
  data.sab = 0;
92
91
  }
93
- if (getHostCapabilities().hasDom) {
94
- try {
95
- data.coi = self.crossOriginIsolated ? 1 : 0;
96
- }
97
- catch {
98
- data.coi = 0;
99
- }
100
- }
101
92
  // --- Worker load status (best-effort; na = not checked) ---
102
93
  data.wk = "na";
103
94
  // --- Active plugins ---
@@ -90,17 +90,14 @@ export function createMcpHarness(opts) {
90
90
  // These utilities are used by multiple games' MCP automation harnesses.
91
91
  // Games import them instead of duplicating the implementation.
92
92
  /** Convert a Blob to a base64 string (without the data: prefix). */
93
- export function blobToBase64(blob) {
94
- return new Promise((resolve, reject) => {
95
- const reader = new FileReader();
96
- reader.onloadend = () => {
97
- const result = reader.result;
98
- const base64 = result.split(",")[1];
99
- resolve(base64 ?? "");
100
- };
101
- reader.onerror = reject;
102
- reader.readAsDataURL(blob);
103
- });
93
+ export async function blobToBase64(blob) {
94
+ const bytes = new Uint8Array(await blob.arrayBuffer());
95
+ let bin = "";
96
+ const CHUNK = 0x8000;
97
+ for (let i = 0; i < bytes.length; i += CHUNK) {
98
+ bin += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
99
+ }
100
+ return btoa(bin);
104
101
  }
105
102
  /** MCP result helper: wrap data as a JSON content array. */
106
103
  export function jsonResult(data) {
@@ -25,7 +25,7 @@
25
25
  // }),
26
26
  // });
27
27
  // ============================================================================
28
- import { getHostCapabilities, KEY } from "@downdraft/engine";
28
+ import { KEY } from "@downdraft/engine";
29
29
  import { createLogger } from "@downdraft/engine/util/logger";
30
30
  import { downdraft } from "./index.js";
31
31
  import { blobToBase64, createMcpHarness, errorResult, jsonResult } from "./mcp-harness.js";
@@ -290,21 +290,13 @@ export function createStandardAutomationTools(ctx) {
290
290
  const rect = surface.getBoundingClientRect();
291
291
  const clientX = rect.left + x;
292
292
  const clientY = rect.top + y;
293
- const hasDom = getHostCapabilities().hasDom;
294
- // DOM hosts hit-test the topmost element (mimics a real user click
295
- // through UI overlays). On native there is no DOM tree — the surface
296
- // itself is the input target.
297
- const target = hasDom
298
- ? (document.elementFromPoint(clientX, clientY) ?? surface)
299
- : surface;
300
- // MiniEventTarget (native event polyfill) has no capture/bubble —
301
- // mirror real propagation by also dispatching on window. In a real
302
- // DOM this would double-fire, so only do it under the polyfill.
303
- const isNativeDom = !hasDom;
293
+ // There is no DOM tree — the surface itself is the input target.
294
+ const target = surface;
295
+ // MiniEventTarget (native event target) has no capture/bubble —
296
+ // mirror DOM propagation by also dispatching on window.
304
297
  const fire = (ev) => {
305
298
  target.dispatchEvent(ev);
306
- if (isNativeDom)
307
- window.dispatchEvent?.(ev);
299
+ window.dispatchEvent?.(ev);
308
300
  };
309
301
  if (type === "wheel") {
310
302
  const deltaX = params.deltaX ?? 0;
@@ -487,130 +479,6 @@ export function createStandardAutomationTools(ctx) {
487
479
  handler: () => jsonResult(ctx.getUiState()),
488
480
  });
489
481
  }
490
- // ── get_element_bounds ──
491
- if (want("get_element_bounds")) {
492
- tools.push({
493
- def: {
494
- name: "get_element_bounds",
495
- description: "Get the bounding box of a DOM element matching a CSS selector. Returns {x, y, width, height, top, left, bottom, right} or found:false.",
496
- inputSchema: {
497
- type: "object",
498
- properties: { selector: { type: "string", description: "CSS selector" } },
499
- required: ["selector"],
500
- },
501
- },
502
- handler: (params) => {
503
- const selector = params.selector;
504
- try {
505
- const el = document.querySelector(selector);
506
- if (!el)
507
- return jsonResult({ found: false, selector });
508
- const rect = el.getBoundingClientRect();
509
- return jsonResult({
510
- found: true, selector,
511
- x: rect.x, y: rect.y, width: rect.width, height: rect.height,
512
- top: rect.top, left: rect.left, bottom: rect.bottom, right: rect.right,
513
- });
514
- }
515
- catch (e) {
516
- return errorResult(`Failed to query selector "${selector}": ${e.message}`);
517
- }
518
- },
519
- });
520
- }
521
- // ── inspect_dom ──
522
- if (want("inspect_dom")) {
523
- tools.push({
524
- def: {
525
- name: "inspect_dom",
526
- description: "Inspect the main thread's DOM for debugging CSS/layout issues. 'stylesheets' lists all <style>/<link> tags; 'element' gets details for a selector.",
527
- inputSchema: {
528
- type: "object",
529
- properties: {
530
- action: { type: "string", enum: ["stylesheets", "element"] },
531
- selector: { type: "string", description: "CSS selector (for action='element')" },
532
- },
533
- required: ["action"],
534
- },
535
- },
536
- handler: (params) => {
537
- if (!getHostCapabilities().hasDom) {
538
- return errorResult("inspect_dom requires a real DOM — the native host's document is a canvas-compat facade, not a DOM tree");
539
- }
540
- const action = params.action;
541
- if (action === "stylesheets") {
542
- const styles = Array.from(document.querySelectorAll("style, link[rel='stylesheet']")).map((el) => {
543
- const tag = el.tagName.toLowerCase();
544
- if (tag === "style") {
545
- const text = el.textContent ?? "";
546
- return { tag, length: text.length, preview: text.slice(0, 200) };
547
- }
548
- return { tag, href: el.href };
549
- });
550
- return jsonResult({ stylesheets: styles, count: styles.length });
551
- }
552
- if (action === "element" && params.selector) {
553
- const el = document.querySelector(params.selector);
554
- if (!el)
555
- return jsonResult({ found: false, selector: params.selector });
556
- const cs = getComputedStyle(el);
557
- return jsonResult({
558
- found: true,
559
- selector: params.selector,
560
- tagName: el.tagName,
561
- className: el.className,
562
- id: el.id,
563
- style: {
564
- width: cs.width, height: cs.height,
565
- maxWidth: cs.maxWidth, maxHeight: cs.maxHeight,
566
- display: cs.display, position: cs.position,
567
- top: cs.top, left: cs.left, overflow: cs.overflow,
568
- },
569
- bounds: el.getBoundingClientRect().toJSON(),
570
- childCount: el.children.length,
571
- });
572
- }
573
- return errorResult("Invalid action or missing selector");
574
- },
575
- });
576
- }
577
- // ── get_element_style ──
578
- if (want("get_element_style")) {
579
- tools.push({
580
- def: {
581
- name: "get_element_style",
582
- description: "Get computed style of a DOM element matching a CSS selector. Pass properties[] to read specific CSS properties.",
583
- inputSchema: {
584
- type: "object",
585
- properties: {
586
- selector: { type: "string", description: "CSS selector" },
587
- properties: { type: "array", items: { type: "string" }, description: "CSS properties to read" },
588
- },
589
- required: ["selector"],
590
- },
591
- },
592
- handler: (params) => {
593
- if (!getHostCapabilities().hasDom) {
594
- return errorResult("get_element_style requires a real DOM — no computed styles on the native host");
595
- }
596
- const selector = params.selector;
597
- const props = params.properties ?? [];
598
- try {
599
- const el = document.querySelector(selector);
600
- if (!el)
601
- return jsonResult({ found: false, selector });
602
- const cs = getComputedStyle(el);
603
- const result = { found: true, selector };
604
- props.forEach((p) => { result[p] = cs.getPropertyValue(p); ; });
605
- result._className = el.className;
606
- return jsonResult(result);
607
- }
608
- catch (e) {
609
- return errorResult(`Failed to query style for "${selector}": ${e.message}`);
610
- }
611
- },
612
- });
613
- }
614
482
  // ── inspectable_list / inspectable_get / inspectable_set ──
615
483
  const inspectables = ctx.inspectables;
616
484
  if (want("inspectable_list") && inspectables) {
@@ -45,7 +45,7 @@ export { createMultiInputChannel, DEFAULT_KEY_BITFIELD_COUNT, DEFAULT_MAX_PLAYER
45
45
  // Game Input SAB Channel
46
46
  export { INP, INP_FLAG, INP_HDR, INPUT_MAGIC, INPUT_VERSION, InputBufferReader, InputBufferWriter, InputChannel, KEY, MAX_INPUT_PLAYERS } from "./sab/game-input.js";
47
47
  // Sim SAB Channel
48
- export { allocateInputBuffer, allocateSimBuffer, ENT, MAX_ENTITIES, MAX_PLAYERS, PLR, PLR_FLAG, SIM_ENTITY_SLOT_SIZE, SIM_HDR, SIM_MAGIC, SIM_PLAYER_SLOT_SIZE, SIM_VERSION, SimBufferReader, SimBufferWriter, SimChannel } from "./sab/sim-channel.js";
48
+ export { allocateInputBuffer, allocateSimBuffer, ENT, MAX_ENTITIES, MAX_PLAYERS, PLR, PLR_FLAG, PREV, SIM_ENTITY_SLOT_SIZE, SIM_HDR, SIM_MAGIC, SIM_PLAYER_SLOT_SIZE, SIM_VERSION, SimBufferReader, SimBufferWriter, SimChannel } from "./sab/sim-channel.js";
49
49
  // SAB Framework
50
50
  export { defineChannel, defineManifest } from "./sab/define.js";
51
51
  export { isDebug, resetWarnings, ValidationError as SABValidationError, setDebug, warnOnce } from "./sab/errors.js";
@@ -57,7 +57,7 @@ export { BufferSyncHost, BufferSyncWorker, isBufferSyncMessage } from "./worker/
57
57
  export { CrashRecoveryManager, DEFAULT_RECOVERY_CONFIG } from "./worker/crash-recovery.js";
58
58
  export { EntitySimWorkerHost } from "./worker/entity-sim-worker-host.js";
59
59
  export { exposeProfilingApi, InstrumentedWorkerHost } from "./worker/instrumented-worker-host.js";
60
- export { expose, exposeEvents, getWorkerHost, wrap } from "./worker/rpc.js";
60
+ export { expose, exposeBatchedEvents, exposeEvents, getWorkerHost, wrap } from "./worker/rpc.js";
61
61
  export { createSimWorker } from "./worker/sim-worker-base.js";
62
62
  export { RawInputRegionWriter, SimWorkerHost } from "./worker/sim-worker-host.js";
63
63
  export { createTaskWorker, PortChannel, TaskPool } from "./worker/task-pool.js";
@@ -23,15 +23,15 @@ export class ModuleHost {
23
23
  }
24
24
  /**
25
25
  * Inject the DevTools API. Called by the game bootstrap after
26
- * `initDevTools()` so plugins can self-register debug panels via
27
- * `ctx.devtools.registerPanel(...)` during their `register()` lifecycle.
26
+ * `initDevTools()` so plugins can self-register data feeds via
27
+ * `ctx.devtools.registerDataFeed(...)` during their `register()` lifecycle.
28
28
  */
29
29
  setDevToolsAPI(api) {
30
30
  this._devtools = api;
31
31
  }
32
32
  get devtools() {
33
33
  // Return the injected API, or a no-op stub if not set (so plugins that
34
- // call ctx.devtools.registerPanel() don't crash if devtools isn't wired).
34
+ // call ctx.devtools.register*() don't crash if devtools isn't wired).
35
35
  return this._devtools ?? NoopDevToolsAPI;
36
36
  }
37
37
  getRegistry() {
@@ -439,10 +439,7 @@ export class ModuleHost {
439
439
  }
440
440
  }
441
441
  // No-op DevTools API stub — used when setDevToolsAPI() hasn't been called.
442
- // Plugins that call ctx.devtools.registerPanel() will silently no-op.
443
442
  const NoopDevToolsAPI = {
444
- registerPanel: () => { },
445
- registerOverlayToggle: () => { },
446
443
  registerDataFeed: () => { },
447
444
  registerCommand: () => { },
448
445
  registerSABStat: () => { },
@@ -1,3 +1,4 @@
1
+ import { untrackedTimeout } from "../util/session-timer.js";
1
2
  export class RPC {
2
3
  handlers = new Map();
3
4
  pending = new Map();
@@ -20,7 +21,7 @@ export class RPC {
20
21
  }
21
22
  const id = this.messageIdCounter = (this.messageIdCounter + 1) % 0xFFFFFFFF;
22
23
  return new Promise((resolve, reject) => {
23
- const timer = setTimeout(() => {
24
+ const timer = untrackedTimeout(() => {
24
25
  if (this.pending.has(id)) {
25
26
  this.pending.delete(id);
26
27
  reject(new Error(`[RPC] Call to "${channel}" timed out after ${timeoutMs}ms`));
@@ -1,20 +1,13 @@
1
1
  export class CanvasResizeWatcher {
2
2
  canvas;
3
3
  handler;
4
- observer = null;
5
4
  dpr = 1;
6
5
  onSurfaceResize = () => this.resize();
7
6
  constructor(canvas, handler) {
8
7
  this.canvas = canvas;
9
8
  this.handler = handler;
10
9
  this.dpr = (typeof window !== "undefined" ? window.devicePixelRatio : 0) || 1;
11
- // DOM hosts: element box changes fire ResizeObserver. Native surfaces
12
- // aren't Elements — they push a "resize" event instead. Both paths feed
13
- // the same resize() so either host drives layout updates.
14
- if (typeof ResizeObserver !== "undefined") {
15
- this.observer = new ResizeObserver(() => this.resize());
16
- this.observer.observe(canvas);
17
- }
10
+ // Native surfaces push a "resize" event when the backing store changes.
18
11
  canvas.addEventListener("resize", this.onSurfaceResize);
19
12
  this.resize();
20
13
  }
@@ -29,8 +22,6 @@ export class CanvasResizeWatcher {
29
22
  return this.dpr;
30
23
  }
31
24
  destroy() {
32
- this.observer?.disconnect();
33
- this.observer = null;
34
25
  this.canvas.removeEventListener("resize", this.onSurfaceResize);
35
26
  }
36
27
  }