@vectojs/core 1.20.0 → 1.22.0
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/dist/{chunk-YLH7F4ZV.js → chunk-AGP4VLF4.js} +32 -0
- package/dist/{chunk-TJCXB2F6.mjs → chunk-FRMLD4PP.mjs} +32 -0
- package/dist/{chunk-L4SWVP2H.js → chunk-LHONR3GO.js} +336 -38
- package/dist/{chunk-QS3CUV7H.mjs → chunk-UPULSLKA.mjs} +298 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +451 -218
- package/dist/index.mjs +253 -20
- package/dist/renderer/CanvasRenderer.d.ts +20 -1
- package/dist/renderer/GlyphRasterAtlas.d.ts +186 -0
- package/dist/renderer/IRenderer.d.ts +94 -0
- package/dist/renderer/SVGRenderer.d.ts +2 -0
- package/dist/renderer/WebGLPointRenderer.d.ts +33 -0
- package/dist/renderer/index.d.ts +1 -0
- package/dist/renderer.js +4 -2
- package/dist/renderer.mjs +3 -1
- package/dist/text.js +2 -2
- package/dist/text.mjs +1 -1
- package/dist/tree/Entity.d.ts +128 -0
- package/dist/tree/Scene.d.ts +132 -1
- package/package.json +2 -2
|
@@ -12,7 +12,70 @@
|
|
|
12
12
|
* r.fill('#38bdf8');
|
|
13
13
|
* }
|
|
14
14
|
*/
|
|
15
|
+
/**
|
|
16
|
+
* Per-backend draw counters, for a DevTools GPU readout.
|
|
17
|
+
*
|
|
18
|
+
* Opt-in via {@link IRenderer.setDrawCounters}: when off, counting compiles to a
|
|
19
|
+
* single boolean test per op, matching how `Scene` gates its phase timing and
|
|
20
|
+
* dirty tracking. Totals are cumulative until cleared rather than per-frame,
|
|
21
|
+
* because a per-frame log answers the question less directly at far more memory —
|
|
22
|
+
* the same reasoning `Scene.renderPhases` documents.
|
|
23
|
+
*/
|
|
24
|
+
export interface DrawCounters {
|
|
25
|
+
/** `fill()` calls, each committing one path. */
|
|
26
|
+
fills: number;
|
|
27
|
+
/** `stroke()` calls. */
|
|
28
|
+
strokes: number;
|
|
29
|
+
/** `fillText()` calls. */
|
|
30
|
+
texts: number;
|
|
31
|
+
/** `drawImage`/`drawImageRect` blits. */
|
|
32
|
+
images: number;
|
|
33
|
+
/** `fillCircle()` calls — batched, so this exceeds the fills they produce. */
|
|
34
|
+
circles: number;
|
|
35
|
+
/** Batch commits from {@link IRenderer.flush}. */
|
|
36
|
+
flushes: number;
|
|
37
|
+
/** `save()` calls. */
|
|
38
|
+
saves: number;
|
|
39
|
+
/** `restore()` calls. */
|
|
40
|
+
restores: number;
|
|
41
|
+
/** `clip()` calls. */
|
|
42
|
+
clips: number;
|
|
43
|
+
/**
|
|
44
|
+
* Times a font or fill style actually changed, as opposed to being re-set to
|
|
45
|
+
* the value it already had.
|
|
46
|
+
*
|
|
47
|
+
* The renderer already elides redundant sets; this counts the ones that got
|
|
48
|
+
* through, which is the number that costs anything.
|
|
49
|
+
*/
|
|
50
|
+
stateSwitches: number;
|
|
51
|
+
/**
|
|
52
|
+
* Sum of drawn primitive areas divided by canvas area.
|
|
53
|
+
*
|
|
54
|
+
* A PROXY for overdraw, not a measurement: Canvas2D exposes no pixel-coverage
|
|
55
|
+
* readback, so this counts area submitted, ignoring clipping and off-screen
|
|
56
|
+
* rejection. It overstates, sometimes badly. Useful as a trend between two
|
|
57
|
+
* states of the same scene; meaningless as an absolute.
|
|
58
|
+
*/
|
|
59
|
+
overdrawRatio: number;
|
|
60
|
+
}
|
|
15
61
|
export interface IRenderer {
|
|
62
|
+
/**
|
|
63
|
+
* Stable backend identifier.
|
|
64
|
+
*
|
|
65
|
+
* A discriminator rather than `constructor.name`, which minifies to something
|
|
66
|
+
* unusable in a production bundle — and a debug tool that cannot name the
|
|
67
|
+
* backend in the build where it matters is not much of a debug tool.
|
|
68
|
+
*/
|
|
69
|
+
readonly kind?: 'canvas2d' | 'svg' | 'three' | string;
|
|
70
|
+
/**
|
|
71
|
+
* Enable or disable draw counting. Optional; a backend that cannot count omits
|
|
72
|
+
* it and a reader treats the absence as "not available".
|
|
73
|
+
*/
|
|
74
|
+
setDrawCounters?(enabled: boolean): void;
|
|
75
|
+
/** Current counter totals, or null when counting is off. */
|
|
76
|
+
getDrawCounters?(): DrawCounters | null;
|
|
77
|
+
/** Zero the totals without disabling counting. */
|
|
78
|
+
clearDrawCounters?(): void;
|
|
16
79
|
/** Clear the entire drawing surface to transparent / background color. */
|
|
17
80
|
clear(): void;
|
|
18
81
|
/** Push the current transform + state onto the renderer's stack. */
|
|
@@ -115,6 +178,37 @@ export interface IRenderer {
|
|
|
115
178
|
* @param dh - Destination height.
|
|
116
179
|
*/
|
|
117
180
|
drawImage(source: CanvasImageSource, dx: number, dy: number, dw: number, dh: number): void;
|
|
181
|
+
/**
|
|
182
|
+
* Draw a sub-rectangle of an image source — the 9-argument `drawImage`.
|
|
183
|
+
*
|
|
184
|
+
* **Optional.** Callers must feature-detect and keep a fallback path:
|
|
185
|
+
*
|
|
186
|
+
* ```ts
|
|
187
|
+
* if (r.drawImageRect) r.drawImageRect(atlas, sx, sy, sw, sh, dx, dy, dw, dh);
|
|
188
|
+
* else r.fillText(glyph, x, baselineY, font, color);
|
|
189
|
+
* ```
|
|
190
|
+
*
|
|
191
|
+
* This exists for texture atlases (see `GlyphRasterAtlas`), where selecting one slot
|
|
192
|
+
* out of a shared canvas is what makes the blit cheap: a per-source-canvas
|
|
193
|
+
* cache re-binds a different texture almost every call and measured *slower*
|
|
194
|
+
* than the `fillText` it replaced on Chrome at scale, while atlas blits stay
|
|
195
|
+
* flat and run ~2x faster on both engines.
|
|
196
|
+
*
|
|
197
|
+
* `SVGRenderer` deliberately omits it: an SVG image embeds its source as a data
|
|
198
|
+
* URL, so a per-cell sub-rect would inline the entire atlas once per cell —
|
|
199
|
+
* and vector text is the correct output for a vector export regardless.
|
|
200
|
+
*
|
|
201
|
+
* @param source - The image source.
|
|
202
|
+
* @param sx - Source X, in source-image pixels.
|
|
203
|
+
* @param sy - Source Y, in source-image pixels.
|
|
204
|
+
* @param sw - Source width, in source-image pixels.
|
|
205
|
+
* @param sh - Source height, in source-image pixels.
|
|
206
|
+
* @param dx - Destination X.
|
|
207
|
+
* @param dy - Destination Y.
|
|
208
|
+
* @param dw - Destination width.
|
|
209
|
+
* @param dh - Destination height.
|
|
210
|
+
*/
|
|
211
|
+
drawImageRect?(source: CanvasImageSource, sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): void;
|
|
118
212
|
/**
|
|
119
213
|
* Fill the current path with the given color or gradient.
|
|
120
214
|
*
|
|
@@ -13,6 +13,8 @@ export interface SVGLinearGradient {
|
|
|
13
13
|
createMatrix: number[];
|
|
14
14
|
}
|
|
15
15
|
export declare class SVGRenderer implements IRenderer {
|
|
16
|
+
/** Backend discriminator; see {@link IRenderer.kind}. */
|
|
17
|
+
readonly kind = "svg";
|
|
16
18
|
private width;
|
|
17
19
|
private height;
|
|
18
20
|
private buffer;
|
|
@@ -4,6 +4,29 @@
|
|
|
4
4
|
* render `getBatchCircle()` / `getBatchRect()` entities — the point-cloud /
|
|
5
5
|
* particle case where Canvas2D tops out at ~7 fps for 100k primitives.
|
|
6
6
|
*/
|
|
7
|
+
/** Per-frame and cumulative WebGL draw accounting. */
|
|
8
|
+
export interface WebGLDrawStats {
|
|
9
|
+
/** Draw calls issued for the last completed frame. */
|
|
10
|
+
drawCalls: number;
|
|
11
|
+
/** Cumulative draw calls since creation. */
|
|
12
|
+
totalDrawCalls: number;
|
|
13
|
+
/** Cumulative mid-frame MSDF atlas switches, each costing an extra draw. */
|
|
14
|
+
atlasSwitches: number;
|
|
15
|
+
/** Programs compiled at creation — a fixed capability, not a per-frame cost. */
|
|
16
|
+
programs: number;
|
|
17
|
+
/** Textures currently allocated (colour atlas and/or MSDF atlas). */
|
|
18
|
+
textures: number;
|
|
19
|
+
/**
|
|
20
|
+
* Circles routed to the quad path rather than `gl.POINTS`, cumulatively.
|
|
21
|
+
*
|
|
22
|
+
* A circle takes the quad path when it could clip off-viewport or exceeds the
|
|
23
|
+
* driver's maximum aliased point size. A high share means the POINTS fast path
|
|
24
|
+
* is not being used and each circle costs four vertices instead of one.
|
|
25
|
+
*/
|
|
26
|
+
circleQuadFallbacks: number;
|
|
27
|
+
/** Circles drawn through the `gl.POINTS` fast path, cumulatively. */
|
|
28
|
+
circlePoints: number;
|
|
29
|
+
}
|
|
7
30
|
export interface PointRenderer {
|
|
8
31
|
/** Resize the backing buffer + GL viewport to a logical `w × h` (DPR applied). */
|
|
9
32
|
resize(width: number, height: number): void;
|
|
@@ -21,6 +44,16 @@ export interface PointRenderer {
|
|
|
21
44
|
maxDPR?: number;
|
|
22
45
|
/** Begin a frame: reset the accumulated primitive buffers. */
|
|
23
46
|
begin(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Draw-call counters for the most recent frame, plus cumulative totals.
|
|
49
|
+
*
|
|
50
|
+
* Batching here is by primitive type — one draw per active type — so draw calls
|
|
51
|
+
* and batches are the same number, and both are bounded at five plus one per
|
|
52
|
+
* mid-frame MSDF atlas switch. That switch is the only variable term and the
|
|
53
|
+
* only thing worth watching: it forces a commit of glyphs batched against the
|
|
54
|
+
* previous font.
|
|
55
|
+
*/
|
|
56
|
+
stats?(): WebGLDrawStats;
|
|
24
57
|
/** Add one circle in world (CSS-pixel) coordinates; `alpha` multiplies the color's. */
|
|
25
58
|
addCircle(x: number, y: number, radius: number, color: string, alpha?: number): void;
|
|
26
59
|
/**
|
package/dist/renderer/index.d.ts
CHANGED
package/dist/renderer.js
CHANGED
|
@@ -6,12 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
var _chunkL4SWVP2Hjs = require('./chunk-L4SWVP2H.js');
|
|
10
9
|
|
|
10
|
+
var _chunkLHONR3GOjs = require('./chunk-LHONR3GO.js');
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
exports.CanvasRenderer = _chunkLHONR3GOjs.CanvasRenderer; exports.GlyphRasterAtlas = _chunkLHONR3GOjs.GlyphRasterAtlas; exports.SVGRenderer = _chunkLHONR3GOjs.SVGRenderer; exports.TextRasterCache = _chunkLHONR3GOjs.TextRasterCache; exports.WebGPUParticleSystemManager = _chunkLHONR3GOjs.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkLHONR3GOjs.createWebGLPointRenderer; exports.parseColorToRGBA = _chunkLHONR3GOjs.parseColorToRGBA;
|
package/dist/renderer.mjs
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CanvasRenderer,
|
|
3
|
+
GlyphRasterAtlas,
|
|
3
4
|
SVGRenderer,
|
|
4
5
|
TextRasterCache,
|
|
5
6
|
WebGPUParticleSystemManager,
|
|
6
7
|
createWebGLPointRenderer,
|
|
7
8
|
parseColorToRGBA
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UPULSLKA.mjs";
|
|
9
10
|
export {
|
|
10
11
|
CanvasRenderer,
|
|
12
|
+
GlyphRasterAtlas,
|
|
11
13
|
SVGRenderer,
|
|
12
14
|
TextRasterCache,
|
|
13
15
|
WebGPUParticleSystemManager,
|
package/dist/text.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _chunkAGP4VLF4js = require('./chunk-AGP4VLF4.js');
|
|
6
6
|
|
|
7
7
|
// src/text/index.ts
|
|
8
8
|
var _text = require('@vectojs/text'); _createStarExport(_text);
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
exports.MSDFTextEntity =
|
|
12
|
+
exports.MSDFTextEntity = _chunkAGP4VLF4js.MSDFTextEntity; exports.SVGEntity = _chunkAGP4VLF4js.SVGEntity;
|
package/dist/text.mjs
CHANGED
package/dist/tree/Entity.d.ts
CHANGED
|
@@ -159,6 +159,107 @@ export interface TextInputStyle {
|
|
|
159
159
|
* to create and label the shadow DOM node (e.g. a real `<button>` or `<a href>`)
|
|
160
160
|
* so the canvas stays accessible and clickable by automation/agents.
|
|
161
161
|
*/
|
|
162
|
+
/**
|
|
163
|
+
* One value in a {@link DevtoolsDescriptor} group.
|
|
164
|
+
*
|
|
165
|
+
* JSON-safe by construction: DevTools serializes descriptors to render a panel,
|
|
166
|
+
* to write a snapshot, and to cross a `postMessage` bridge, so a value that
|
|
167
|
+
* cannot survive `structuredClone` is a bug rather than a limitation.
|
|
168
|
+
*/
|
|
169
|
+
export interface DevtoolsField {
|
|
170
|
+
/** Field name as shown in the inspector, e.g. `'scrollTop'`. */
|
|
171
|
+
label: string;
|
|
172
|
+
/** Current value. Keep to primitives, or short arrays/records of primitives. */
|
|
173
|
+
value: string | number | boolean | null | ReadonlyArray<string | number> | Record<string, string | number | boolean>;
|
|
174
|
+
/**
|
|
175
|
+
* Optional one-line explanation, shown as a tooltip.
|
|
176
|
+
*
|
|
177
|
+
* Worth spending: a reader looking at `visibleRange: [12, 34]` cannot tell
|
|
178
|
+
* whether the bounds are inclusive without being told.
|
|
179
|
+
*/
|
|
180
|
+
hint?: string;
|
|
181
|
+
/**
|
|
182
|
+
* Mark a value that reflects derived or externally-owned state, so the panel
|
|
183
|
+
* can show it as read-only rather than inviting an edit that will be silently
|
|
184
|
+
* reverted. A `Stack`-laid-out child's `x` is the canonical example.
|
|
185
|
+
*/
|
|
186
|
+
readOnly?: boolean;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* A component's self-description for DevTools.
|
|
190
|
+
*
|
|
191
|
+
* Without this, the inspector can only show generic `Entity` properties —
|
|
192
|
+
* position, size, opacity — so everything that makes a component a component is
|
|
193
|
+
* invisible: `Input.value`, `Slider.min`/`max`, `ScrollView.scrollTop`,
|
|
194
|
+
* `VirtualList.visibleRange`, a `Markdown` block's token counts. The alternative
|
|
195
|
+
* is DevTools carrying a table of component types, which inverts the dependency
|
|
196
|
+
* (a debug tool would gate every new component) and breaks under minified builds
|
|
197
|
+
* where `constructor.name` is unreliable.
|
|
198
|
+
*
|
|
199
|
+
* Implement {@link Entity.getDevtoolsDescriptor} to opt in. Cost is paid only
|
|
200
|
+
* when a panel actually inspects the entity, so a descriptor may compute values
|
|
201
|
+
* it would not compute per frame.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```ts
|
|
205
|
+
* public override getDevtoolsDescriptor(): DevtoolsDescriptor {
|
|
206
|
+
* return {
|
|
207
|
+
* kind: 'ScrollView',
|
|
208
|
+
* groups: [{
|
|
209
|
+
* label: 'Scroll',
|
|
210
|
+
* fields: [
|
|
211
|
+
* { label: 'scrollTop', value: this.scrollTop },
|
|
212
|
+
* { label: 'contentHeight', value: this.contentHeight, readOnly: true },
|
|
213
|
+
* ],
|
|
214
|
+
* }],
|
|
215
|
+
* };
|
|
216
|
+
* }
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
export interface DevtoolsDescriptor {
|
|
220
|
+
/**
|
|
221
|
+
* Component kind for display, e.g. `'VirtualList'`.
|
|
222
|
+
*
|
|
223
|
+
* Provided explicitly rather than read from `constructor.name`, which minifies
|
|
224
|
+
* to something meaningless in a production bundle.
|
|
225
|
+
*/
|
|
226
|
+
kind: string;
|
|
227
|
+
/** Grouped fields, rendered as sections in the order given. */
|
|
228
|
+
groups: ReadonlyArray<{
|
|
229
|
+
label: string;
|
|
230
|
+
fields: ReadonlyArray<DevtoolsField>;
|
|
231
|
+
}>;
|
|
232
|
+
/**
|
|
233
|
+
* Free-form notes: a caveat, a known-slow path, a link to a doc section.
|
|
234
|
+
* Rendered under the groups.
|
|
235
|
+
*/
|
|
236
|
+
notes?: ReadonlyArray<string>;
|
|
237
|
+
/**
|
|
238
|
+
* Stable identity for snapshot diffing, independent of tree position.
|
|
239
|
+
*
|
|
240
|
+
* Snapshot paths are structural indices (`root > Card[0] > Text[2]`), so
|
|
241
|
+
* inserting at the head of a list renames every sibling, so the diff describes
|
|
242
|
+
* different nodes than the ones that changed. A key that survives reordering — a
|
|
243
|
+
* row id, a message id — keeps each entry attributed to the node it belongs to.
|
|
244
|
+
* Most relevant to `VirtualList` and `Table`, where recycling moves entities
|
|
245
|
+
* constantly.
|
|
246
|
+
|
|
247
|
+
*/
|
|
248
|
+
devtoolsKey?: string;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Entity properties a parent computes for its children.
|
|
252
|
+
*
|
|
253
|
+
* Editing one of these on a child is silently reverted by the next layout pass,
|
|
254
|
+
* which looks like the editor being broken rather than the value being owned
|
|
255
|
+
* elsewhere. A container declares what it controls so a tool can say so up front
|
|
256
|
+
* instead of letting a user discover it by watching their change disappear.
|
|
257
|
+
*
|
|
258
|
+
* Declared by the parent rather than detected by the tool: only the container
|
|
259
|
+
* knows whether it writes `x` unconditionally, and a table of container types
|
|
260
|
+
* inside DevTools would gate every new layout component on a debug-tool change.
|
|
261
|
+
*/
|
|
262
|
+
export type LayoutControlledProperty = 'x' | 'y' | 'width' | 'height' | 'scaleX' | 'scaleY' | 'rotation' | 'opacity';
|
|
162
263
|
export interface A11yAttributes {
|
|
163
264
|
/** Shadow element tag to create. Defaults to `'div'`. */
|
|
164
265
|
tag?: 'div' | 'a' | 'button' | 'img' | 'input' | 'textarea';
|
|
@@ -726,6 +827,33 @@ export declare abstract class Entity {
|
|
|
726
827
|
*
|
|
727
828
|
* @returns The {@link A11yAttributes} for this entity's shadow node.
|
|
728
829
|
*/
|
|
830
|
+
/**
|
|
831
|
+
* Describe this entity's own debug surface for DevTools.
|
|
832
|
+
*
|
|
833
|
+
* Returns `null` by default, meaning "nothing beyond the generic `Entity`
|
|
834
|
+
* fields the inspector already shows". Override in a component to expose the
|
|
835
|
+
* state that makes it inspectable — see {@link DevtoolsDescriptor}.
|
|
836
|
+
*
|
|
837
|
+
* Called only while a panel is inspecting this entity, never per frame, so it
|
|
838
|
+
* may compute values that would be too expensive to track continuously.
|
|
839
|
+
*
|
|
840
|
+
* @returns A descriptor, or `null` to opt out.
|
|
841
|
+
*/
|
|
842
|
+
getDevtoolsDescriptor(): DevtoolsDescriptor | null;
|
|
843
|
+
/**
|
|
844
|
+
* Which of a child's properties this entity computes during layout.
|
|
845
|
+
*
|
|
846
|
+
* Returns an empty array by default, meaning "this entity does not position its
|
|
847
|
+
* children". A container that lays out children — `Stack`, `Table`, `Tabs` —
|
|
848
|
+
* overrides it so tooling can mark those values as parent-owned: editing `x` on
|
|
849
|
+
* a `Stack` child is reverted by the next layout, and knowing that in advance is
|
|
850
|
+
* the difference between a confusing tool and a correct one.
|
|
851
|
+
*
|
|
852
|
+
* @param child - The child being asked about. Containers whose control depends
|
|
853
|
+
* on the child (a `Table` cell versus its header) can answer per child.
|
|
854
|
+
* @returns Property names this entity overwrites on that child.
|
|
855
|
+
*/
|
|
856
|
+
getLayoutControlledProperties(child: Entity): ReadonlyArray<LayoutControlledProperty>;
|
|
729
857
|
getA11yAttributes(): A11yAttributes;
|
|
730
858
|
/**
|
|
731
859
|
* Local-space axis-aligned bounding box of what this entity's {@link render}
|
package/dist/tree/Scene.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface IWebGPUParticleSystemManager {
|
|
|
12
12
|
}
|
|
13
13
|
import { Entity } from './Entity';
|
|
14
14
|
import { IRenderer } from '../renderer/IRenderer';
|
|
15
|
+
import type { WebGLDrawStats } from '../renderer/WebGLPointRenderer';
|
|
15
16
|
import { type WasmModuleSource, type WasmTransformBackend } from '../wasm/backend';
|
|
16
17
|
import { type CoreWasmRuntime } from '../wasm/runtime';
|
|
17
18
|
import { type HitModuleSource, type HitTestBackend } from '../wasm/hit-backend';
|
|
@@ -25,7 +26,41 @@ import { type ParticleModuleSource, type ParticleBackend } from '../wasm/particl
|
|
|
25
26
|
* `a11ySync` and `a11yOrder` run after `render` in the frame loop, so they are
|
|
26
27
|
* siblings of it, not children.
|
|
27
28
|
*/
|
|
28
|
-
export type RenderPhase = 'render' | 'transform' | 'drawWalk' | 'flush' | 'a11ySync'
|
|
29
|
+
export type RenderPhase = 'render' | 'transform' | 'drawWalk' | 'flush' | 'a11ySync'
|
|
30
|
+
/**
|
|
31
|
+
* Time inside {@link Scene.syncContentGridProjection} materializing DOM
|
|
32
|
+
* carriers, nested inside `a11ySync`.
|
|
33
|
+
*
|
|
34
|
+
* Split out because `a11ySync` for a streaming code block measured 1661-1875 ms
|
|
35
|
+
* against a 210-671 ms render, and attributing that to grid materialization was
|
|
36
|
+
* an assumption. Nothing should be optimised here on the strength of the parent
|
|
37
|
+
* phase alone.
|
|
38
|
+
*/
|
|
39
|
+
| 'gridMaterialize'
|
|
40
|
+
/**
|
|
41
|
+
* Whole of {@link Scene.syncContentProjection}, nested inside `a11ySync`.
|
|
42
|
+
*
|
|
43
|
+
* Measured at 99.8-99.9% of `a11ySync` for a streaming code block, so per-node
|
|
44
|
+
* a11y attribute and geometry work is not where that phase's cost lives.
|
|
45
|
+
*/
|
|
46
|
+
| 'contentProjection'
|
|
47
|
+
/** Per-node a11y attribute/geometry work, excluding content projection and descendants. */
|
|
48
|
+
| 'a11yNodes'
|
|
49
|
+
/** Whole of `syncContentGridProjection`, of which `gridMaterialize` is one part. */
|
|
50
|
+
| 'gridSync'
|
|
51
|
+
/**
|
|
52
|
+
* Synchronous part of `scheduleContentGridCalibration` — building the probe DOM.
|
|
53
|
+
*
|
|
54
|
+
* The measurement itself is deferred to a rAF, but the probe is constructed
|
|
55
|
+
* here. Measured at 77-80% of `gridSync` on Chrome (3.7-4.5 ms per frame, i.e.
|
|
56
|
+
* the entire 240Hz budget) against about 1 ms on Firefox, making it the largest
|
|
57
|
+
* remaining cost of projecting a streaming code block once carrier reuse landed.
|
|
58
|
+
*/
|
|
59
|
+
| 'gridCalibrateSchedule'
|
|
60
|
+
/** The `querySelectorAll` + per-cell scan inside calibration scheduling. */
|
|
61
|
+
| 'calibScan'
|
|
62
|
+
/** Probe DOM construction and insertion inside calibration scheduling. */
|
|
63
|
+
| 'calibProbeBuild' | 'a11yOrder'
|
|
29
64
|
/** Sum of every entity's own render(), nested inside drawWalk. */
|
|
30
65
|
| 'entityPaint';
|
|
31
66
|
export interface RenderPhaseEntry {
|
|
@@ -358,6 +393,29 @@ export declare class Scene {
|
|
|
358
393
|
private contentGridCalibrationFrames;
|
|
359
394
|
/** Detached, untransformed font probes used by the cold calibration pass. */
|
|
360
395
|
private contentGridCalibrationProbes;
|
|
396
|
+
/**
|
|
397
|
+
* Monotonic stamp identifying the conditions grid cells were calibrated under.
|
|
398
|
+
*
|
|
399
|
+
* Calibration measures the difference between the advance the canvas grid assigns
|
|
400
|
+
* a cluster and the width the browser lays it out at, then writes a per-cell
|
|
401
|
+
* `scaleX`. That result stays valid until the font or the page scale changes, and
|
|
402
|
+
* it lives on the cell element — so a cell carrying this stamp needs no further
|
|
403
|
+
* work.
|
|
404
|
+
*
|
|
405
|
+
* The scan that feeds calibration was O(cells) on every revision bump: for a
|
|
406
|
+
* streaming code block it re-derived a measurement key for every cell in the
|
|
407
|
+
* block each frame in order to produce only ~20 distinct keys, costing about
|
|
408
|
+
* 2.5 ms/frame after the `style.font` fix and still over half of `a11ySync`. Since
|
|
409
|
+
* carrier reuse (#244) leaves untouched lines — and therefore their calibrated
|
|
410
|
+
* transforms — in place, cells stamped with the current generation can simply be
|
|
411
|
+
* skipped, making the scan O(new cells) instead.
|
|
412
|
+
*
|
|
413
|
+
* A plain incrementing integer rather than the descriptive calibration key,
|
|
414
|
+
* because it goes into an attribute selector and must not need escaping.
|
|
415
|
+
*/
|
|
416
|
+
private contentGridCalibrationGeneration;
|
|
417
|
+
/** The `(fontEpoch, pageScale)` pair the current generation corresponds to. */
|
|
418
|
+
private contentGridCalibrationStamp;
|
|
361
419
|
/** Invalidates grid font calibration after browser font availability changes. */
|
|
362
420
|
private contentFontEpoch;
|
|
363
421
|
/** Cached Canvas-to-client scale for the current font/viewport epoch. */
|
|
@@ -751,6 +809,23 @@ export declare class Scene {
|
|
|
751
809
|
particleBackend: 'auto' | 'webgpu' | 'cpu';
|
|
752
810
|
private _webgpuDisabled;
|
|
753
811
|
get webgpuDisabled(): boolean;
|
|
812
|
+
/**
|
|
813
|
+
* Draw accounting for the WebGL point layer, or null when that layer is not in
|
|
814
|
+
* use.
|
|
815
|
+
*
|
|
816
|
+
* Null and all-zero mean different things: null is "this backend is not
|
|
817
|
+
* running", zero is "it ran and drew nothing". A readout that conflates them
|
|
818
|
+
* sends someone looking for a performance problem in a backend that was never
|
|
819
|
+
* active.
|
|
820
|
+
*/
|
|
821
|
+
get webglDrawStats(): WebGLDrawStats | null;
|
|
822
|
+
/**
|
|
823
|
+
* Whether a WebGPU device is currently live for particle compute.
|
|
824
|
+
*
|
|
825
|
+
* The WebGPU path only activates when a `ComputeParticleEntity` is present, so
|
|
826
|
+
* most scenes never touch it.
|
|
827
|
+
*/
|
|
828
|
+
get webgpuActive(): boolean;
|
|
754
829
|
set webgpuDisabled(value: boolean);
|
|
755
830
|
private recoveryTimerId;
|
|
756
831
|
private manager;
|
|
@@ -800,6 +875,15 @@ export declare class Scene {
|
|
|
800
875
|
*/
|
|
801
876
|
private setupGLContextRecovery;
|
|
802
877
|
private endContentSelectionDrag;
|
|
878
|
+
/**
|
|
879
|
+
* Index of the carrier line currently holding a selection inside `el`, or
|
|
880
|
+
* `null`.
|
|
881
|
+
*
|
|
882
|
+
* Lets a partial re-materialization decide whether the user's selection is even
|
|
883
|
+
* affected. Checks the tracked anchor first (it survives a drag) and falls back
|
|
884
|
+
* to the live DOM selection.
|
|
885
|
+
*/
|
|
886
|
+
private contentGridSelectionLine;
|
|
803
887
|
private releaseContentSelectionForRebuild;
|
|
804
888
|
/**
|
|
805
889
|
* Rebuild a content-projection element's DOM (`rebuild`) while preserving a
|
|
@@ -836,6 +920,17 @@ export declare class Scene {
|
|
|
836
920
|
* @example scene.add(new CircleEntity());
|
|
837
921
|
*/
|
|
838
922
|
add(entity: Entity): this;
|
|
923
|
+
/**
|
|
924
|
+
* Reset per-grid calibration and bookkeeping before a (re)materialization.
|
|
925
|
+
*
|
|
926
|
+
* @param entityId - Owning entity, keyed into the calibration maps.
|
|
927
|
+
* @param el - The projection element.
|
|
928
|
+
* @param releaseSelection - Whether to drop a selection this element owns.
|
|
929
|
+
* Pass `false` when carrier lines are being reused: the selection's DOM nodes
|
|
930
|
+
* survive the pass, so tearing it down would wipe a user's selection on every
|
|
931
|
+
* streamed chunk — the exact bug `preserveContentSelectionAcrossRebuild`
|
|
932
|
+
* exists to prevent on the non-grid path.
|
|
933
|
+
*/
|
|
839
934
|
private clearContentGridState;
|
|
840
935
|
/**
|
|
841
936
|
* Drop any projected elements under `node` without touching the entity tree.
|
|
@@ -933,6 +1028,26 @@ export declare class Scene {
|
|
|
933
1028
|
get rootEntity(): Entity;
|
|
934
1029
|
/** The overlay layer root (see {@link showOverlay}), read-only for tooling. */
|
|
935
1030
|
get overlayRootEntity(): Entity;
|
|
1031
|
+
/**
|
|
1032
|
+
* Advance and render exactly one frame, synchronously.
|
|
1033
|
+
*
|
|
1034
|
+
* This renders UNCONDITIONALLY: it consults neither {@link renderMode} nor
|
|
1035
|
+
* {@link dirty}, and it does not apply the `always`-mode idle auto-throttle.
|
|
1036
|
+
* That is deliberate — a deterministic driver (video export, a test, a
|
|
1037
|
+
* fixed-step benchmark) asks for a frame because it wants that frame, not a
|
|
1038
|
+
* scheduler opinion about whether it is needed.
|
|
1039
|
+
*
|
|
1040
|
+
* The consequence is a measurement footgun worth stating explicitly: a
|
|
1041
|
+
* benchmark that drives frames through `step()` CANNOT observe frame skipping,
|
|
1042
|
+
* so `always` and `onDemand` produce byte-identical draw counts through this
|
|
1043
|
+
* path. An investigation into whether `onDemand` skips redundant repaints once
|
|
1044
|
+
* concluded "it does not" on exactly that basis; on the real rAF loop the same
|
|
1045
|
+
* workload rendered ~1.0 frames per content change. To measure anything about
|
|
1046
|
+
* scheduling, use {@link start} and let `requestAnimationFrame` drive.
|
|
1047
|
+
*
|
|
1048
|
+
* @param dt Seconds to advance. Not clamped by `MAX_FRAME_DT` — the caller
|
|
1049
|
+
* chooses the step, since determinism is the point.
|
|
1050
|
+
*/
|
|
936
1051
|
step(dt: number): void;
|
|
937
1052
|
/**
|
|
938
1053
|
* Mark the scene as needing a redraw on the next frame.
|
|
@@ -941,6 +1056,22 @@ export declare class Scene {
|
|
|
941
1056
|
* entity state outside of {@link Entity.animate} so the change is rendered.
|
|
942
1057
|
*/
|
|
943
1058
|
markDirty(source?: DirtySource): void;
|
|
1059
|
+
/**
|
|
1060
|
+
* Increments whenever the tree's shape changes: add, remove or reparent.
|
|
1061
|
+
*
|
|
1062
|
+
* Already maintained for the resident WASM transform store (see
|
|
1063
|
+
* {@link markStructureChanged}, called from `Entity.add`/`remove`), and exposed
|
|
1064
|
+
* here because a cache of the tree's shape — a DevTools tree model, a serialized
|
|
1065
|
+
* snapshot — is valid exactly as long as this value is unchanged. Comparing it is
|
|
1066
|
+
* O(1) against re-walking the tree, which is what it replaces: DevTools rebuilt
|
|
1067
|
+
* both trees on a fixed 500 ms interval, a constant cost proportional to entity
|
|
1068
|
+
* count, purely because it had no way to ask whether the shape had changed.
|
|
1069
|
+
*
|
|
1070
|
+
* Property changes do NOT bump it. Moving or restyling an entity leaves the
|
|
1071
|
+
* shape intact, so a consumer that also cares about values must read those
|
|
1072
|
+
* directly rather than rebuilding a tree.
|
|
1073
|
+
*/
|
|
1074
|
+
get structureVersion(): number;
|
|
944
1075
|
/**
|
|
945
1076
|
* Record who marked the scene dirty and why.
|
|
946
1077
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vectojs/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@vectojs/animation": "^0.1.1",
|
|
68
|
-
"@vectojs/layout": "^0.
|
|
68
|
+
"@vectojs/layout": "^0.4.0",
|
|
69
69
|
"@vectojs/math": "^0.1.1",
|
|
70
70
|
"@vectojs/text": "^0.2.0"
|
|
71
71
|
},
|