@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.
- package/app/src/renderer/bootstrap.ts +0 -6
- package/app/src/renderer/feature-log.ts +1 -9
- package/app/src/renderer/mcp-harness.ts +8 -11
- package/app/src/renderer/standard-automation-tools.ts +8 -145
- package/core/src/index.ts +2 -2
- package/core/src/module/host.ts +3 -6
- package/core/src/module/index.ts +1 -1
- package/core/src/module/module.ts +4 -47
- package/core/src/physics/interface.ts +5 -0
- package/core/src/platform/render-surface.ts +13 -0
- package/core/src/platform/rpc.ts +4 -1
- package/core/src/render/canvas-resize-watcher.ts +1 -10
- package/core/src/render/game-renderer.ts +164 -79
- package/core/src/render/index.ts +0 -2
- package/core/src/render/input-manager.ts +7 -7
- package/core/src/render/renderer-module-host.ts +2 -4
- package/core/src/sab/grid-buffer.ts +55 -2
- package/core/src/sab/record-buffer.ts +48 -5
- package/core/src/sab/sim-channel.ts +181 -17
- package/core/src/sab/slot-buffer.ts +55 -1
- package/core/src/sab/types.ts +11 -0
- package/core/src/telemetry/gpu-resource-tracker.ts +24 -0
- package/core/src/ui-font-scale.ts +5 -38
- package/core/src/util/index.ts +1 -0
- package/core/src/util/session-timer.ts +23 -0
- package/core/src/worker/base-worker-host.ts +2 -1
- package/core/src/worker/buffer-sync.ts +20 -13
- package/core/src/worker/rpc.ts +119 -5
- package/core/src/worker/sim-worker-base.ts +5 -2
- package/core/src/worker/task-pool.ts +4 -0
- package/dist/.dd-manifest.json +893 -908
- package/dist/app/src/renderer/bootstrap.js +0 -4
- package/dist/app/src/renderer/feature-log.js +1 -10
- package/dist/app/src/renderer/mcp-harness.js +8 -11
- package/dist/app/src/renderer/standard-automation-tools.js +6 -138
- package/dist/core/src/index.js +2 -2
- package/dist/core/src/module/host.js +3 -6
- package/dist/core/src/platform/rpc.js +2 -1
- package/dist/core/src/render/canvas-resize-watcher.js +1 -10
- package/dist/core/src/render/game-renderer.js +166 -74
- package/dist/core/src/render/index.js +0 -1
- package/dist/core/src/render/input-manager.js +7 -7
- package/dist/core/src/render/renderer-module-host.js +2 -4
- package/dist/core/src/sab/grid-buffer.js +53 -1
- package/dist/core/src/sab/record-buffer.js +49 -5
- package/dist/core/src/sab/sim-channel.js +173 -17
- package/dist/core/src/sab/slot-buffer.js +54 -1
- package/dist/core/src/telemetry/gpu-resource-tracker.js +26 -0
- package/dist/core/src/ui-font-scale.js +5 -38
- package/dist/core/src/util/index.js +1 -0
- package/dist/core/src/util/session-timer.js +22 -0
- package/dist/core/src/worker/base-worker-host.js +2 -1
- package/dist/core/src/worker/buffer-sync.js +16 -9
- package/dist/core/src/worker/rpc.js +105 -8
- package/dist/core/src/worker/sim-worker-base.js +5 -2
- package/dist/core/src/worker/task-pool.js +5 -0
- package/dist/libraries/blitz-ui/src/native-module.js +8 -1
- package/dist/libraries/devtools/src/backend.js +35 -9
- package/dist/libraries/devtools/src/collectors.js +73 -22
- package/dist/libraries/devtools/src/native-providers.js +0 -2
- package/dist/libraries/entities/src/model-renderer.js +87 -27
- package/dist/libraries/entities/src/shaders/model.wgsl +13 -4
- package/dist/libraries/networking/src/index.js +1 -0
- package/dist/libraries/networking/src/ws-server.js +387 -0
- package/dist/libraries/physics-rapier/src/api.js +15 -0
- package/dist/libraries/physics-rapier/src/backend.js +15 -0
- package/dist/libraries/physics-rapier/src/rapier-backend.js +5 -0
- package/dist/libraries/postfx/src/post-process-stack.js +1 -3
- package/dist/libraries/sand/src/materials.js +2 -32
- package/dist/libraries/sand/src/sand-world.js +0 -3
- package/dist/modules/devtools/src/api.js +8 -23
- package/dist/modules/devtools/src/blitz/fonts.js +75 -0
- package/dist/modules/devtools/src/blitz/host.js +124 -30
- package/dist/modules/devtools/src/blitz/panels/console.js +19 -7
- package/dist/modules/devtools/src/blitz/panels/elements.js +217 -28
- package/dist/modules/devtools/src/blitz/panels/gpu.js +5 -2
- package/dist/modules/devtools/src/blitz/panels/performance.js +26 -12
- package/dist/modules/devtools/src/blitz/panels/snapshot.js +31 -8
- package/dist/modules/devtools/src/blitz/panels/types.js +2 -1
- package/dist/modules/devtools/src/blitz/theme.js +26 -4
- package/dist/modules/devtools/src/data-bridge.js +0 -11
- package/dist/modules/devtools/src/index.js +0 -6
- package/dist/modules/devtools/src/init.js +8 -42
- package/dist/modules/devtools/src/scene-inspector.js +1 -4
- package/dist/modules/devtools/src/worker-sync.js +3 -11
- package/dist/modules/html-ui/src/doc-backend.js +52 -3
- package/dist/modules/html-ui/src/host.js +6 -3
- package/dist/test/src/vision-test.js +1 -3
- package/libraries/blitz-ui/native/Cargo.toml +6 -0
- package/libraries/blitz-ui/native-osr/Cargo.toml +3 -1
- package/libraries/blitz-ui/src/native-module.ts +8 -1
- package/libraries/devtools/src/backend.ts +38 -12
- package/libraries/devtools/src/collectors.ts +75 -22
- package/libraries/devtools/src/native-providers.ts +0 -2
- package/libraries/entities/src/model-renderer.ts +88 -27
- package/libraries/entities/src/shaders/model.wgsl +13 -4
- package/libraries/networking/src/index.ts +4 -0
- package/libraries/networking/src/ws-server.ts +397 -0
- package/libraries/physics-rapier/src/api.ts +15 -0
- package/libraries/physics-rapier/src/backend.ts +15 -0
- package/libraries/physics-rapier/src/rapier-backend.ts +6 -0
- package/libraries/postfx/src/post-process-stack.ts +1 -2
- package/libraries/sand/src/materials.ts +2 -32
- package/libraries/sand/src/sand-world.ts +0 -4
- package/modules/devtools/README.md +1 -3
- package/modules/devtools/src/api.ts +10 -34
- package/modules/devtools/src/blitz/fonts.ts +78 -0
- package/modules/devtools/src/blitz/host.ts +107 -31
- package/modules/devtools/src/blitz/panels/console.ts +20 -8
- package/modules/devtools/src/blitz/panels/elements.ts +212 -31
- package/modules/devtools/src/blitz/panels/gpu.ts +4 -2
- package/modules/devtools/src/blitz/panels/performance.ts +24 -12
- package/modules/devtools/src/blitz/panels/snapshot.ts +30 -8
- package/modules/devtools/src/blitz/panels/types.ts +2 -1
- package/modules/devtools/src/blitz/theme.ts +26 -4
- package/modules/devtools/src/data-bridge.ts +0 -16
- package/modules/devtools/src/index.ts +1 -11
- package/modules/devtools/src/init.ts +8 -51
- package/modules/devtools/src/scene-inspector.ts +2 -6
- package/modules/devtools/src/types.ts +5 -90
- package/modules/devtools/src/worker-sync.ts +3 -13
- package/modules/html-ui/src/doc-backend.ts +67 -4
- package/modules/html-ui/src/host.ts +11 -3
- package/modules/html-ui/src/protocol.ts +1 -1
- package/package.json +4 -5
- package/test/src/vision-test.ts +2 -4
- package/core/src/render/sab-canvas-overlay.ts +0 -122
- package/dist/core/src/render/sab-canvas-overlay.js +0 -83
- package/dist/modules/devtools/src/debug-overlay.js +0 -181
- package/dist/modules/devtools/src/doctor-panel.js +0 -163
- package/dist/modules/devtools/src/label-overlay.js +0 -129
- package/dist/modules/devtools/src/material-stats-panel.js +0 -171
- package/dist/modules/devtools/src/sim-stats-panel.js +0 -250
- package/modules/devtools/src/debug-overlay.ts +0 -238
- package/modules/devtools/src/doctor-panel.ts +0 -182
- package/modules/devtools/src/label-overlay.ts +0 -154
- package/modules/devtools/src/material-stats-panel.ts +0 -188
- package/modules/devtools/src/sim-stats-panel.ts +0 -272
package/core/src/worker/rpc.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// ============================================================================
|
|
6
6
|
|
|
7
7
|
import { createLogger } from "../util/logger";
|
|
8
|
+
import { untrackedTimeout } from "../util/session-timer";
|
|
8
9
|
|
|
9
10
|
const log = createLogger();
|
|
10
11
|
|
|
@@ -91,6 +92,66 @@ interface EventMessage {
|
|
|
91
92
|
data: any;
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
interface EventBatchMessage {
|
|
96
|
+
__eventBatch: true;
|
|
97
|
+
events: { kind: string; data: any }[];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// --- Event channel stats ---
|
|
101
|
+
// Cheap counters the DOWNDRAFT_EVENT_STATS logger (and QA tooling) read to
|
|
102
|
+
// verify batching is actually engaging: emitted events vs postMessage posts
|
|
103
|
+
// on the worker side, messages vs delivered events on the host side.
|
|
104
|
+
const eventChannelStats = {
|
|
105
|
+
/** Worker side: emit() calls. */
|
|
106
|
+
emitted: 0,
|
|
107
|
+
/** Worker side: postMessage calls carrying events (batch or single). */
|
|
108
|
+
posts: 0,
|
|
109
|
+
/** Host side: messages received carrying events. */
|
|
110
|
+
messages: 0,
|
|
111
|
+
/** Host side: events delivered to listeners. */
|
|
112
|
+
events: 0,
|
|
113
|
+
/** Host side: largest observed __eventBatch. */
|
|
114
|
+
maxBatch: 0,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export function getEventChannelStats(): Readonly<typeof eventChannelStats> {
|
|
118
|
+
return eventChannelStats;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function envFlag(name: string): boolean {
|
|
122
|
+
try {
|
|
123
|
+
return (
|
|
124
|
+
(globalThis as { process?: { env?: Record<string, string> } }).process?.env?.[name] === "1"
|
|
125
|
+
);
|
|
126
|
+
} catch {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let statsLoggerStarted = false;
|
|
132
|
+
function startEventStatsLogger(): void {
|
|
133
|
+
if (statsLoggerStarted || !envFlag("DOWNDRAFT_EVENT_STATS")) return;
|
|
134
|
+
statsLoggerStarted = true;
|
|
135
|
+
let prev = { emitted: 0, posts: 0, messages: 0, events: 0 };
|
|
136
|
+
const t = setInterval(() => {
|
|
137
|
+
const s = eventChannelStats;
|
|
138
|
+
const d = {
|
|
139
|
+
emitted: s.emitted - prev.emitted,
|
|
140
|
+
posts: s.posts - prev.posts,
|
|
141
|
+
messages: s.messages - prev.messages,
|
|
142
|
+
events: s.events - prev.events,
|
|
143
|
+
};
|
|
144
|
+
prev = { emitted: s.emitted, posts: s.posts, messages: s.messages, events: s.events };
|
|
145
|
+
if (d.emitted || d.posts || d.messages || d.events) {
|
|
146
|
+
log.info(
|
|
147
|
+
"rpc-events",
|
|
148
|
+
`5s Δ emitted=${d.emitted} posts=${d.posts} msgs=${d.messages} events=${d.events} maxBatch=${s.maxBatch}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}, 5000);
|
|
152
|
+
(t as { unref?: () => void }).unref?.();
|
|
153
|
+
}
|
|
154
|
+
|
|
94
155
|
function isRpcRequest(msg: any): msg is RpcRequest {
|
|
95
156
|
return msg && msg.__rpc === true && typeof msg.method === "string";
|
|
96
157
|
}
|
|
@@ -103,6 +164,10 @@ function isEventMessage(msg: any): msg is EventMessage {
|
|
|
103
164
|
return msg && msg.__event === true;
|
|
104
165
|
}
|
|
105
166
|
|
|
167
|
+
function isEventBatchMessage(msg: any): msg is EventBatchMessage {
|
|
168
|
+
return msg && msg.__eventBatch === true;
|
|
169
|
+
}
|
|
170
|
+
|
|
106
171
|
// --- Worker side: expose an API object ---
|
|
107
172
|
|
|
108
173
|
export type WorkerApi = Record<string, (...args: any[]) => any>;
|
|
@@ -138,13 +203,55 @@ export interface WorkerEventEmitter {
|
|
|
138
203
|
|
|
139
204
|
export function exposeEvents(host?: WorkerHost): WorkerEventEmitter {
|
|
140
205
|
const h = host ?? getWorkerHost();
|
|
206
|
+
startEventStatsLogger();
|
|
141
207
|
return {
|
|
142
208
|
emit: (kind: string, data: any) => {
|
|
209
|
+
eventChannelStats.emitted++;
|
|
210
|
+
eventChannelStats.posts++;
|
|
143
211
|
h.postToHost({ __event: true, kind, data } as EventMessage);
|
|
144
212
|
},
|
|
145
213
|
};
|
|
146
214
|
}
|
|
147
215
|
|
|
216
|
+
export interface BatchedWorkerEventEmitter extends WorkerEventEmitter {
|
|
217
|
+
/** Post any queued events immediately. */
|
|
218
|
+
flush: () => void;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Event emitter that coalesces emit() calls into a single `__eventBatch`
|
|
223
|
+
* postMessage per macrotask. A sim emitting per-tick or per-entity events
|
|
224
|
+
* otherwise produces one structured clone + one main-thread task per event,
|
|
225
|
+
* and the task burst competes with rAF. Events emitted during a tick batch
|
|
226
|
+
* (the worker never yields to timers mid-tick) all land in one message;
|
|
227
|
+
* events emitted between ticks flush on the next macrotask, so latency for
|
|
228
|
+
* isolated events is unchanged.
|
|
229
|
+
*/
|
|
230
|
+
export function exposeBatchedEvents(host?: WorkerHost): BatchedWorkerEventEmitter {
|
|
231
|
+
const h = host ?? getWorkerHost();
|
|
232
|
+
startEventStatsLogger();
|
|
233
|
+
let queue: { kind: string; data: any }[] = [];
|
|
234
|
+
let flushScheduled = false;
|
|
235
|
+
const flush = () => {
|
|
236
|
+
flushScheduled = false;
|
|
237
|
+
if (queue.length === 0) return;
|
|
238
|
+
eventChannelStats.posts++;
|
|
239
|
+
h.postToHost({ __eventBatch: true, events: queue } as EventBatchMessage);
|
|
240
|
+
queue = [];
|
|
241
|
+
};
|
|
242
|
+
return {
|
|
243
|
+
emit: (kind: string, data: any) => {
|
|
244
|
+
eventChannelStats.emitted++;
|
|
245
|
+
queue.push({ kind, data });
|
|
246
|
+
if (!flushScheduled) {
|
|
247
|
+
flushScheduled = true;
|
|
248
|
+
setTimeout(flush, 0);
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
flush,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
148
255
|
// --- Main side: wrap a worker as a typed async proxy ---
|
|
149
256
|
|
|
150
257
|
type AnyWorker = {
|
|
@@ -171,6 +278,7 @@ export interface WrapOptions {
|
|
|
171
278
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
172
279
|
|
|
173
280
|
export function wrap<T extends WorkerApi>(worker: AnyWorker, options?: WrapOptions): WorkerProxy<T> {
|
|
281
|
+
startEventStatsLogger();
|
|
174
282
|
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
175
283
|
// A worker nobody terminates must not pin the runtime after the host's
|
|
176
284
|
// window closes — in-flight RPCs still hold the loop via their timeout.
|
|
@@ -196,10 +304,16 @@ export function wrap<T extends WorkerApi>(worker: AnyWorker, options?: WrapOptio
|
|
|
196
304
|
return;
|
|
197
305
|
}
|
|
198
306
|
|
|
199
|
-
if (isEventMessage(msg)) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
307
|
+
if (isEventMessage(msg) || isEventBatchMessage(msg)) {
|
|
308
|
+
const events = isEventBatchMessage(msg) ? msg.events : [msg];
|
|
309
|
+
eventChannelStats.messages++;
|
|
310
|
+
eventChannelStats.events += events.length;
|
|
311
|
+
if (events.length > eventChannelStats.maxBatch) eventChannelStats.maxBatch = events.length;
|
|
312
|
+
for (const ev of events) {
|
|
313
|
+
for (const cb of eventListeners.values()) {
|
|
314
|
+
try { cb(ev.kind, ev.data); } catch (err) {
|
|
315
|
+
log.error("worker-proxy", `Event listener error: ${err}`);
|
|
316
|
+
}
|
|
203
317
|
}
|
|
204
318
|
}
|
|
205
319
|
}
|
|
@@ -248,7 +362,7 @@ export function wrap<T extends WorkerApi>(worker: AnyWorker, options?: WrapOptio
|
|
|
248
362
|
const id = ++reqId;
|
|
249
363
|
const entry: { resolve: (v: unknown) => void; reject: (e: Error) => void; timer?: ReturnType<typeof setTimeout> } = { resolve, reject };
|
|
250
364
|
if (timeoutMs > 0) {
|
|
251
|
-
entry.timer =
|
|
365
|
+
entry.timer = untrackedTimeout(() => {
|
|
252
366
|
if (pending.delete(id)) {
|
|
253
367
|
reject(new Error(`RPC '${method}' timed out after ${timeoutMs}ms`));
|
|
254
368
|
}
|
|
@@ -52,7 +52,7 @@ import type {
|
|
|
52
52
|
GCController
|
|
53
53
|
} from "../telemetry/gc-controller";
|
|
54
54
|
import { createLogger } from "../util/logger";
|
|
55
|
-
import { expose,
|
|
55
|
+
import { expose, exposeBatchedEvents, type WorkerApi } from "./rpc";
|
|
56
56
|
|
|
57
57
|
const log = createLogger();
|
|
58
58
|
|
|
@@ -465,7 +465,7 @@ export function createSimWorker(opts: CreateSimWorkerOptions): SimWorkerControl
|
|
|
465
465
|
// Serializes withLoopStopped critical sections.
|
|
466
466
|
let stopChain: Promise<unknown> = Promise.resolve();
|
|
467
467
|
|
|
468
|
-
const events =
|
|
468
|
+
const events = exposeBatchedEvents();
|
|
469
469
|
|
|
470
470
|
// Deterministic RNG — reseedable via control.setSeed(); the stable `rng`
|
|
471
471
|
// closure is what ctx.rng and the Math.random shim draw from.
|
|
@@ -750,6 +750,9 @@ export function createSimWorker(opts: CreateSimWorkerOptions): SimWorkerControl
|
|
|
750
750
|
syncWorker = null; // release sync worker reference
|
|
751
751
|
saveStore = null;
|
|
752
752
|
await opts.onShutdown?.();
|
|
753
|
+
// Drain any events queued during onShutdown so they aren't lost to
|
|
754
|
+
// a flush timer the terminating worker never runs.
|
|
755
|
+
events.flush();
|
|
753
756
|
},
|
|
754
757
|
|
|
755
758
|
async setSpeed(speed: number): Promise<void> {
|
|
@@ -121,6 +121,10 @@ export class TaskPool {
|
|
|
121
121
|
for (let i = 0; i < this.workerCount; i++) {
|
|
122
122
|
const worker = this.createWorker();
|
|
123
123
|
worker.onerror = (e: ErrorEvent) => {
|
|
124
|
+
// Session teardown tags tracked workers __ddExpectTerminate and
|
|
125
|
+
// fires synthetic error events to settle waiters — not faults
|
|
126
|
+
// (see session-tracker.ts terminateTrackedWorker).
|
|
127
|
+
if ((worker as any).__ddExpectTerminate || this.destroyed) return;
|
|
124
128
|
if (this.onWorkerError) this.onWorkerError(i, e);
|
|
125
129
|
else log.error("task-pool", `Worker ${i} error: ${e.message}`);
|
|
126
130
|
};
|