@cyberart-io/engine 0.0.7 → 0.0.8
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/README.md +3 -0
- package/dist/headless.d.ts +126 -60
- package/dist/headless.js +1 -1
- package/dist/index.d.ts +86 -67
- package/dist/index.js +1 -1
- package/docs/frame-benchmark.md +107 -0
- package/docs/headless-harness.md +5 -1
- package/package.json +1 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Frame performance benchmarking
|
|
2
|
+
|
|
3
|
+
Cart-agnostic tools for measuring update vs render cost. Use these for hash / trait investigations instead of one-off vitest probes.
|
|
4
|
+
|
|
5
|
+
Production kaleidoscope / Art Blocks playback **leave profiling unset**. When `onFrameTiming` is unset, the draw loop only does a single null check — no `performance.now()` calls.
|
|
6
|
+
|
|
7
|
+
Back to the [package README](../README.md). Related: [headless harness](headless-harness.md), [deterministic mode](deterministic-mode.md).
|
|
8
|
+
|
|
9
|
+
## Two surfaces
|
|
10
|
+
|
|
11
|
+
| Surface | Import | When to use |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Headless bench | `@cyberart-io/engine/headless` → `benchmarkCartFrames` | CI / agent hash A/B without mounting a live runtime |
|
|
14
|
+
| Live hook | `@cyberart-io/engine` → `runtime.onFrameTiming` | Overlay / collect samples on the real `rAF` or `step` path |
|
|
15
|
+
|
|
16
|
+
## Headless: `benchmarkCartFrames`
|
|
17
|
+
|
|
18
|
+
Runs a cart's `update` / `render` loop with a virtual frame clock. Does not start audio or construct `AnimationManager`.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import {
|
|
22
|
+
benchmarkCartFrames,
|
|
23
|
+
formatFrameBenchmarkResult,
|
|
24
|
+
} from '@cyberart-io/engine/headless';
|
|
25
|
+
|
|
26
|
+
const result = benchmarkCartFrames(artProject, hash, rawParams, {
|
|
27
|
+
width: 1920,
|
|
28
|
+
height: 1080,
|
|
29
|
+
frames: 30,
|
|
30
|
+
warmupFrames: 5,
|
|
31
|
+
prepareState: (state) => {
|
|
32
|
+
// e.g. skip Tone init in jsdom
|
|
33
|
+
(state as { audioContextStarted?: boolean }).audioContextStarted = true;
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
console.table(formatFrameBenchmarkResult(result));
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Result fields
|
|
41
|
+
|
|
42
|
+
| Field | Meaning |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `updateAvgMs` / `renderAvgMs` | Mean phase time over measured frames |
|
|
45
|
+
| `updateMaxMs` / `renderMaxMs` | Worst frame in the sample (catches sim hitches) |
|
|
46
|
+
| `totalAvgMs` / `estFps` | Combined average and `1000 / totalAvgMs` |
|
|
47
|
+
| `updatePct` / `renderPct` | Share of measured time |
|
|
48
|
+
|
|
49
|
+
`formatFrameBenchmarkResult` rounds for stable console / snapshot logs.
|
|
50
|
+
|
|
51
|
+
### Options
|
|
52
|
+
|
|
53
|
+
- `frames` (default 30), `warmupFrames` (default 5), `frameStepMs` (default `1000/60`)
|
|
54
|
+
- `width` / `height` (default 1920×1080)
|
|
55
|
+
- `tokenId` for `Random`
|
|
56
|
+
- `prepareState(state, featureState)` after `getDefaultState`
|
|
57
|
+
- `drawingContext` stub (default no-op `putImageData`)
|
|
58
|
+
|
|
59
|
+
One-command reproduce in this repo:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pnpm exec vitest run packages/engine/src/canvas/frameBenchmark.spec.ts
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Live: `onFrameTiming`
|
|
66
|
+
|
|
67
|
+
Optional per-frame sample on the production draw / `step` path.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { createRuntime, type FrameTimingSample } from '@cyberart-io/engine';
|
|
71
|
+
|
|
72
|
+
const samples: FrameTimingSample[] = [];
|
|
73
|
+
|
|
74
|
+
const runtime = createRuntime({
|
|
75
|
+
container,
|
|
76
|
+
// optional at construction:
|
|
77
|
+
onFrameTiming: (sample) => samples.push(sample),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// or assign later / clear to restore zero overhead:
|
|
81
|
+
runtime.onFrameTiming = (sample) => {
|
|
82
|
+
console.log(sample.frame, sample.updateMs, sample.renderMs, sample.drawMs);
|
|
83
|
+
};
|
|
84
|
+
runtime.onFrameTiming = undefined;
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Sample shape
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
type FrameTimingSample = {
|
|
91
|
+
frame: number;
|
|
92
|
+
updateMs: number;
|
|
93
|
+
renderMs: number;
|
|
94
|
+
drawMs: number;
|
|
95
|
+
totalMs: number;
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Cost when off
|
|
100
|
+
|
|
101
|
+
`createRuntime` assigns the handler **directly** to `AnimationManager`. When the host clears `runtime.onFrameTiming`, the manager property is `undefined` again. `processFrame` only calls `performance.now()` inside `if (timingHook)` branches.
|
|
102
|
+
|
|
103
|
+
Do **not** wrap with an always-on forwarder (`(s) => host?.(s)`): that would keep the hook truthy and force timing every frame.
|
|
104
|
+
|
|
105
|
+
## Cart-specific helpers
|
|
106
|
+
|
|
107
|
+
This package stays cart-agnostic. Repos that need trait decode beside the bench (e.g. kautomata `hashPerfProbe`) should wrap `benchmarkCartFrames` in the cart tree — not in the engine.
|
package/docs/headless-harness.md
CHANGED
|
@@ -13,10 +13,14 @@ Back to the [package README](../README.md).
|
|
|
13
13
|
| Surface | Import |
|
|
14
14
|
|---|---|
|
|
15
15
|
| Carts, Player, kaleidoscope, `/art` | `@cyberart-io/engine` (`createRuntime`, events, contracts, assets, presentation adapter, presentation cue, capability manifest, geometry, runtime group) |
|
|
16
|
-
| Vitest / jsdom / CI capture | `@cyberart-io/engine/headless` (`createHeadlessHarness`, `createHeadlessMultiCartHarness`, `installHeadlessCanvas`, `captureFrame`) |
|
|
16
|
+
| Vitest / jsdom / CI capture | `@cyberart-io/engine/headless` (`createHeadlessHarness`, `createHeadlessMultiCartHarness`, `installHeadlessCanvas`, `captureFrame`, `benchmarkCartFrames`) |
|
|
17
17
|
|
|
18
18
|
The main export does not re-export the harness. A browser bundle that only imports `@cyberart-io/engine` must not warn about `node:fs/promises`.
|
|
19
19
|
|
|
20
|
+
## Frame performance
|
|
21
|
+
|
|
22
|
+
For update/render timing without mounting a live runtime, use `benchmarkCartFrames` from this entry. For live `rAF` / `step` samples, use `runtime.onFrameTiming` from `@cyberart-io/engine` (unset = no `performance.now` in the draw loop). Full API: [frame benchmarking](frame-benchmark.md).
|
|
23
|
+
|
|
20
24
|
## One-command reproduce (this repo)
|
|
21
25
|
|
|
22
26
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyberart-io/engine",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "CyberArt host engine: mount carts, events, capability manifests, geometry, runtime groups, and a Node/jsdom headless entry.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|