@taole/giftstage 0.1.32 → 0.1.34
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 +636 -621
- package/dist/assets/webcodecs-mp4.worker-CDhalNDE.js +379 -0
- package/dist/core/audio-manager.d.ts +9 -2
- package/dist/core/gift-stage.d.ts +23 -0
- package/dist/core/post-animator.d.ts +1 -0
- package/dist/core/render-manager.d.ts +42 -4
- package/dist/gift-stage.cjs.js +35 -34
- package/dist/gift-stage.es.js +4053 -3302
- package/dist/gpu/webgl1-backend.d.ts +20 -2
- package/dist/gpu/webgl2-backend.d.ts +22 -2
- package/dist/gpu/webgpu-backend.d.ts +24 -2
- package/dist/renderers/alpha-video-renderer.d.ts +2 -0
- package/dist/renderers/image-renderer.d.ts +2 -0
- package/dist/renderers/svga-batch-renderer.d.ts +148 -1
- package/dist/renderers/vap-renderer.d.ts +21 -0
- package/dist/types/index.d.ts +75 -4
- package/dist/utils/gift-transform.d.ts +4 -3
- package/dist/utils/html-video-upload-gate.d.ts +5 -0
- package/dist/utils/resource-cache.d.ts +59 -0
- package/dist/video/webcodecs-mp4-core.d.ts +47 -0
- package/dist/video/webcodecs-mp4-playback.d.ts +14 -0
- package/package.json +4 -1
- package/dist/assets/webcodecs-mp4.worker-BvV8Fiyc.js +0 -369
|
@@ -19,8 +19,26 @@ export declare class WebGL1Backend implements GPUBackend {
|
|
|
19
19
|
constructor(canvas: HTMLCanvasElement, antialias?: boolean);
|
|
20
20
|
init(): Promise<void>;
|
|
21
21
|
private initPrograms;
|
|
22
|
-
private
|
|
23
|
-
|
|
22
|
+
private makeProgramEntry;
|
|
23
|
+
/**
|
|
24
|
+
* `u_transform` on `svga-batch-premul` defaults to identity; shape draws overwrite it and
|
|
25
|
+
* this re-upload restores it for the next plain batch draw. Uploaded unconditionally because
|
|
26
|
+
* renderers mutate their scratch transform arrays in place (reference never changes).
|
|
27
|
+
*/
|
|
28
|
+
private applyIdentityTransform;
|
|
29
|
+
/** Sampler uniforms are constant per shader; set them once instead of every draw. */
|
|
30
|
+
private bindProgramSamplers;
|
|
31
|
+
/**
|
|
32
|
+
* `u_transform` always comes from a renderer-owned scratch `Float32Array` that gets
|
|
33
|
+
* overwritten in place per draw, so its reference never changes — only its content does.
|
|
34
|
+
* Skipping the upload based on reference equality would silently apply a stale transform,
|
|
35
|
+
* so unlike `u_projection` (whose cached arrays are replaced, not mutated, on resize) it is
|
|
36
|
+
* uploaded unconditionally here.
|
|
37
|
+
*/
|
|
38
|
+
private applyDrawUniforms;
|
|
39
|
+
beginFrame(options?: {
|
|
40
|
+
stencil?: boolean;
|
|
41
|
+
}): void;
|
|
24
42
|
endFrame(): void;
|
|
25
43
|
private getBufferSourceByteLength;
|
|
26
44
|
private normalizeIndexSource;
|
|
@@ -34,8 +34,28 @@ export declare class WebGL2Backend implements GPUBackend {
|
|
|
34
34
|
constructor(canvas: HTMLCanvasElement, antialias?: boolean);
|
|
35
35
|
init(): Promise<void>;
|
|
36
36
|
private initPrograms;
|
|
37
|
-
private
|
|
38
|
-
|
|
37
|
+
private makeProgramEntry;
|
|
38
|
+
/**
|
|
39
|
+
* `u_transform` on `svga-batch-premul` defaults to identity; shape draws overwrite it and
|
|
40
|
+
* this re-upload restores it for the next plain batch draw. Uploaded unconditionally because
|
|
41
|
+
* renderers mutate their scratch transform arrays in place (reference never changes).
|
|
42
|
+
*/
|
|
43
|
+
private applyIdentityTransform;
|
|
44
|
+
/** Sampler uniforms are constant per shader; set them once instead of every draw. */
|
|
45
|
+
private bindProgramSamplers;
|
|
46
|
+
/** Shared by `draw()` and the instanced draw paths, which only ever supply `u_projection`. */
|
|
47
|
+
private applyProjectionUniform;
|
|
48
|
+
/**
|
|
49
|
+
* `u_transform` always comes from a renderer-owned scratch `Float32Array` that gets
|
|
50
|
+
* overwritten in place per draw, so its reference never changes — only its content does.
|
|
51
|
+
* Skipping the upload based on reference equality would silently apply a stale transform,
|
|
52
|
+
* so unlike `u_projection` (whose cached arrays are replaced, not mutated, on resize) it is
|
|
53
|
+
* uploaded unconditionally here.
|
|
54
|
+
*/
|
|
55
|
+
private applyDrawUniforms;
|
|
56
|
+
beginFrame(options?: {
|
|
57
|
+
stencil?: boolean;
|
|
58
|
+
}): void;
|
|
39
59
|
endFrame(): void;
|
|
40
60
|
private getBufferSourceByteLength;
|
|
41
61
|
createBuffer(data: GPUBufferSource, usage: BufferUsage): GPUBufferHandle;
|
|
@@ -20,15 +20,30 @@ export declare class WebGPUBackend implements GPUBackend {
|
|
|
20
20
|
private texturedBindGroupLayout;
|
|
21
21
|
private dualTexturedBindGroupLayout;
|
|
22
22
|
private instancedTextureBindGroupLayout;
|
|
23
|
+
private uniformOnlyBindGroupLayout;
|
|
23
24
|
/** `device.limits.minUniformBufferOffsetAlignment` (typically 256). */
|
|
24
25
|
private uniformSlotStride;
|
|
25
26
|
/** Ring size for per-draw uniforms (`queue.writeBuffer` must not overwrite before draw — see `draw()`). */
|
|
26
27
|
private readonly maxUniformSlots;
|
|
27
28
|
/** Resets each `beginFrame`. */
|
|
28
29
|
private uniformSlotIndex;
|
|
30
|
+
/**
|
|
31
|
+
* CPU-side mirror of `uniformBuffer`, sized identically. Per-draw uniform writes go here
|
|
32
|
+
* instead of `queue.writeBuffer` directly; `endFrame` uploads the touched byte range in one
|
|
33
|
+
* `writeBuffer` call before `submit`, cutting dozens of JS→native calls per frame down to one
|
|
34
|
+
* (writes are visible to the frame's command buffer as long as they land before `submit`).
|
|
35
|
+
* Eagerly sized from the default stride/slot count so it's always usable even before `init()`
|
|
36
|
+
* creates the real GPU buffer (matching size); `init()` reallocates it to the device's actual
|
|
37
|
+
* uniform offset alignment.
|
|
38
|
+
*/
|
|
39
|
+
private uniformShadow;
|
|
40
|
+
/** Half-open touched range `[min, max)` in `uniformShadow`; reset each `beginFrame`. */
|
|
41
|
+
private uniformShadowDirtyMin;
|
|
42
|
+
private uniformShadowDirtyMax;
|
|
29
43
|
/** Skip redundant `setPipeline` / stencil / VB-IB when drawing many quads with the same geometry. */
|
|
30
44
|
private lastDrawPipeline;
|
|
31
45
|
private lastStencilRef;
|
|
46
|
+
private frameUsesStencil;
|
|
32
47
|
private lastVertexBufferId;
|
|
33
48
|
private lastIndexBufferId;
|
|
34
49
|
/** Single 160-byte upload for textured draws (replaces multiple `writeBuffer` calls). */
|
|
@@ -98,8 +113,9 @@ export declare class WebGPUBackend implements GPUBackend {
|
|
|
98
113
|
constructor(canvas: HTMLCanvasElement, adapter: GPUAdapter, wantAntialias?: boolean);
|
|
99
114
|
init(): Promise<void>;
|
|
100
115
|
private configureCanvasContext;
|
|
101
|
-
private
|
|
116
|
+
private ensureFrameAttachments;
|
|
102
117
|
private initPipelines;
|
|
118
|
+
private ensureStencilPipelines;
|
|
103
119
|
private ensureInstancedProjectionBuffer;
|
|
104
120
|
private ensureInstancedStorageBuffer;
|
|
105
121
|
private ensureInstancedSvgaPipeline;
|
|
@@ -110,8 +126,14 @@ export declare class WebGPUBackend implements GPUBackend {
|
|
|
110
126
|
private createInstancedAlphaVideoPipeline;
|
|
111
127
|
private createTexturedPipeline;
|
|
112
128
|
private createClipMaskPipeline;
|
|
113
|
-
beginFrame(
|
|
129
|
+
beginFrame(options?: {
|
|
130
|
+
stencil?: boolean;
|
|
131
|
+
}): void;
|
|
114
132
|
endFrame(): void;
|
|
133
|
+
/** Uploads the touched range of `uniformShadow` in one call — must run before `queue.submit`. */
|
|
134
|
+
private flushUniformShadow;
|
|
135
|
+
/** Records a per-draw uniform write into the CPU shadow instead of touching the GPU buffer. */
|
|
136
|
+
private writeUniformShadow;
|
|
115
137
|
private ensureInstancedProjection;
|
|
116
138
|
private gpuBufferUsage;
|
|
117
139
|
private bufferSourceByteLength;
|
|
@@ -10,6 +10,7 @@ export declare class AlphaVideoRenderer {
|
|
|
10
10
|
private scratchTransform;
|
|
11
11
|
private readonly contentSizeScratch;
|
|
12
12
|
private readonly layoutScratch;
|
|
13
|
+
private readonly clipScratch;
|
|
13
14
|
private readonly wcDbgFirstDraw;
|
|
14
15
|
private readonly wcDbgNullWarns;
|
|
15
16
|
private readonly htmlGate;
|
|
@@ -18,6 +19,7 @@ export declare class AlphaVideoRenderer {
|
|
|
18
19
|
private readonly processedHtmlGatesScratch;
|
|
19
20
|
private readonly instancedTexturesScratch;
|
|
20
21
|
private readonly instancedUniformsScratch;
|
|
22
|
+
constructor(onHtmlVideoFrame?: (video: HTMLVideoElement) => void);
|
|
21
23
|
private readonly instancedDrawCommand;
|
|
22
24
|
private readonly geometryTexturesScratch;
|
|
23
25
|
private readonly geometryUniformsScratch;
|
|
@@ -25,8 +25,16 @@ interface SVGAClipPrewarmOptions {
|
|
|
25
25
|
}
|
|
26
26
|
export type SVGAClipMeshMap = Map<string, ClipMeshCache>;
|
|
27
27
|
export declare class SVGABatchRenderer {
|
|
28
|
+
private readonly onClipMeshReady?;
|
|
28
29
|
private static readonly INSTANCED_QUAD_VERTS;
|
|
29
30
|
private static readonly INSTANCED_QUAD_INDICES;
|
|
31
|
+
/**
|
|
32
|
+
* @param onClipMeshReady Called once a background clip mesh build (queued by the hot-path
|
|
33
|
+
* lookup in `getClipMeshForSprite`, see `queueClipMeshBuild`) finishes, so the owner can
|
|
34
|
+
* `invalidate()` a redraw — otherwise a slot whose animation has stopped dirtying may never
|
|
35
|
+
* pick up the now-available precise clip.
|
|
36
|
+
*/
|
|
37
|
+
constructor(onClipMeshReady?: (() => void) | undefined);
|
|
30
38
|
private pool;
|
|
31
39
|
private vertexBufHandles;
|
|
32
40
|
private indexBufHandles;
|
|
@@ -42,19 +50,42 @@ export declare class SVGABatchRenderer {
|
|
|
42
50
|
private clipMeshCache;
|
|
43
51
|
/** Meshes with lazily created GPU buffers (released in destroy). */
|
|
44
52
|
private clipMeshesWithBuffers;
|
|
53
|
+
/** Clip paths queued by `queueClipMeshBuild`, awaiting the next background build flush. */
|
|
54
|
+
private pendingClipMeshPaths;
|
|
55
|
+
/** True while a background build (worker or frame-budgeted main thread) is in flight. */
|
|
56
|
+
private clipMeshBuildInFlight;
|
|
45
57
|
private clipFrameDataScratch;
|
|
58
|
+
/** One-off scratch used only to capture a row for `svgaStaticBatchCache` (P1-10); never read
|
|
59
|
+
* back — each captured entry clones out of it immediately via `.slice()`. */
|
|
60
|
+
private staticBatchCacheRowScratch;
|
|
46
61
|
private instancedFrameDataScratch;
|
|
47
62
|
private clipSpriteVertsScratch;
|
|
48
63
|
private clipSpriteIndicesScratch;
|
|
49
|
-
private shapeVertsScratch;
|
|
50
64
|
private instancedQuadVB;
|
|
51
65
|
private instancedQuadIB;
|
|
52
66
|
private whiteTexture;
|
|
53
67
|
private shapeMeshCache;
|
|
68
|
+
/** Shape meshes with lazily created static GPU buffers (released in destroy). */
|
|
69
|
+
private shapeMeshesWithBuffers;
|
|
54
70
|
/** Column-major mat4 scratch for clip-mask u_transform (uploaded synchronously per draw). */
|
|
55
71
|
private clipTransformMat;
|
|
72
|
+
/** Column-major mat4 scratch for the shape draw's u_transform (consumed synchronously). */
|
|
73
|
+
private shapeTransformMat;
|
|
56
74
|
/** Scissor rect of the slot currently being drawn (canvas pixels), for clip intersection/restore. */
|
|
57
75
|
private slotScissor;
|
|
76
|
+
/**
|
|
77
|
+
* Whether `slotScissor` is currently the *active* GPU scissor rect (vs. the outer scissor
|
|
78
|
+
* having been intentionally skipped for a slot that cannot draw outside its own box — see
|
|
79
|
+
* `allFramesInsideViewBox`). Drives `restoreSlotScissor`'s fallback target.
|
|
80
|
+
*/
|
|
81
|
+
private slotScissorActive;
|
|
82
|
+
/**
|
|
83
|
+
* True while a clip stencil mask written for a shape is still live for its sprite sibling
|
|
84
|
+
* (texture + clip + shape single-write merge, P1-3). `drawClippedSprite` checks this to
|
|
85
|
+
* skip its own stencil write/clear.
|
|
86
|
+
*/
|
|
87
|
+
private sharedClipStencilActive;
|
|
88
|
+
private readonly layoutScratch;
|
|
58
89
|
/** Grow-only SpriteWork pool; cursor resets each flushGroup. */
|
|
59
90
|
private spriteWorkPool;
|
|
60
91
|
private spriteWorkCursor;
|
|
@@ -63,22 +94,109 @@ export declare class SVGABatchRenderer {
|
|
|
63
94
|
private drawGroupScratch;
|
|
64
95
|
/** Per-entity result of the "any frame has shapes" scan (no-atlas render check). */
|
|
65
96
|
private entityHasShapesCache;
|
|
97
|
+
/** 0=no clip, 1=scissor-compatible clips only, 2=stencil required, 255=not analyzed. */
|
|
98
|
+
private entityFrameClipModeCache;
|
|
99
|
+
/**
|
|
100
|
+
* Per-entity `ClipMeshCache` lookup aligned with `spriteTable.clipPaths` (index = `clipPathIndex`
|
|
101
|
+
* value), so the hot per-sprite loop can resolve a clip mesh with a direct array read instead of
|
|
102
|
+
* `getSpriteClipPath` (string build) + a string-keyed `Map` lookup. `undefined` = not yet resolved;
|
|
103
|
+
* resolution always goes through `getClipMesh`'s string cache, so array entries never go stale.
|
|
104
|
+
*/
|
|
105
|
+
private entityClipMeshArrayCache;
|
|
106
|
+
/**
|
|
107
|
+
* Per-entity result of scanning every sprite×frame layout AABB (in local viewBox space) to see
|
|
108
|
+
* whether it ever draws outside the entity's own viewBox. Entities with vector shapes are
|
|
109
|
+
* excluded (`false`) since shape geometry isn't covered by this scan. Used by `flushGroup` to
|
|
110
|
+
* skip the redundant outer scissor for contain/fill, non-rotated slots — such slots structurally
|
|
111
|
+
* cannot paint outside their own box, so consecutive same-atlas slots can share one scissor-free
|
|
112
|
+
* batch run instead of forcing a draw call per slot.
|
|
113
|
+
*/
|
|
114
|
+
private entityAllFramesInsideViewBoxCache;
|
|
66
115
|
private readonly affineScratch;
|
|
116
|
+
/**
|
|
117
|
+
* Reused DrawCommand/uniforms/textures per rendering branch (never allocated per draw).
|
|
118
|
+
* Backends consume `DrawCommand` synchronously inside `backend.draw`, so mutating these
|
|
119
|
+
* shared objects between calls is safe as long as no reference is retained past the call.
|
|
120
|
+
* Grouped by branch (plain instanced sprite / plain batch sprite / clipped instanced /
|
|
121
|
+
* clipped batch / clip-mask stencil / shape) per the uniform field sets each shader expects.
|
|
122
|
+
*/
|
|
123
|
+
private readonly plainInstancedTexturesScratch;
|
|
124
|
+
private readonly plainInstancedUniformsScratch;
|
|
125
|
+
private readonly plainInstancedDrawCommand;
|
|
126
|
+
private readonly plainBatchTexturesScratch;
|
|
127
|
+
private readonly plainBatchUniformsScratch;
|
|
128
|
+
private readonly plainBatchDrawCommand;
|
|
129
|
+
/** `drawClippedSprite`'s stencil-test pass on instanced backends (webgl2/webgpu). */
|
|
130
|
+
private readonly clipInstancedTexturesScratch;
|
|
131
|
+
private readonly clipInstancedUniformsScratch;
|
|
132
|
+
private readonly clipInstancedDrawCommand;
|
|
133
|
+
/** `drawClippedSprite`'s stencil-test pass on the WebGL1 fallback. */
|
|
134
|
+
private readonly clipBatchTexturesScratch;
|
|
135
|
+
private readonly clipBatchUniformsScratch;
|
|
136
|
+
private readonly clipBatchDrawCommand;
|
|
137
|
+
/**
|
|
138
|
+
* Clip-mask stencil write/clear, shared by `drawClippedSprite`'s pass-1 write,
|
|
139
|
+
* `drawShapeFrame`'s non-rect-clip write, and `clearClipStencil`'s clear (all mutate then
|
|
140
|
+
* synchronously `backend.draw`, never concurrently).
|
|
141
|
+
*/
|
|
142
|
+
private readonly clipMaskTexturesScratch;
|
|
143
|
+
private readonly clipMaskUniformsScratch;
|
|
144
|
+
private readonly clipMaskDrawCommand;
|
|
145
|
+
/**
|
|
146
|
+
* `drawShapeFrame`'s fill/stroke draw (always the shared white texture). `u_transform`
|
|
147
|
+
* carries the per-frame sprite x slot transform so the static shape mesh stays on the GPU;
|
|
148
|
+
* `u_opacity` carries slot opacity (per-vertex alpha is baked into the mesh).
|
|
149
|
+
*/
|
|
150
|
+
private readonly shapeTexturesScratch;
|
|
151
|
+
private readonly shapeUniformsScratch;
|
|
152
|
+
private readonly shapeDrawCommand;
|
|
67
153
|
beginFrame(backend: GPUBackend): void;
|
|
68
154
|
flush(backend: GPUBackend, slots: Map<string, GiftSlot>): void;
|
|
69
155
|
drawSlot(backend: GPUBackend, slot: GiftSlot): void;
|
|
70
156
|
drawSlots(backend: GPUBackend, slots: Iterable<GiftSlot>): void;
|
|
157
|
+
requiresStencil(slots: Iterable<GiftSlot>): boolean;
|
|
158
|
+
private getFrameClipMode;
|
|
71
159
|
prewarmClipMeshes(entity: Pick<VideoEntity, 'spriteTable'>, options?: SVGAClipPrewarmOptions): Promise<number>;
|
|
160
|
+
/** Builds every not-yet-cached path in `paths`, worker-first with a frame-budgeted fallback. */
|
|
161
|
+
private buildMissingClipMeshes;
|
|
162
|
+
/**
|
|
163
|
+
* Called from the render-loop hot path (`getClipMeshForSprite`) when a clip path has no cached
|
|
164
|
+
* mesh yet — instead of triangulating synchronously inline, queue it and build it in the
|
|
165
|
+
* background (batched with any other paths queued in the same tick) so the current frame keeps
|
|
166
|
+
* moving. Safe to call repeatedly: paths already cached or already queued are no-ops.
|
|
167
|
+
*/
|
|
168
|
+
private queueClipMeshBuild;
|
|
169
|
+
private scheduleClipMeshBuildFlush;
|
|
170
|
+
private flushPendingClipMeshBuilds;
|
|
72
171
|
hydrateClipMeshes(meshes: SVGAClipMeshMap | null | undefined): void;
|
|
73
172
|
snapshotClipMeshes(entity: Pick<VideoEntity, 'spriteTable'>): SVGAClipMeshMap;
|
|
74
173
|
private flushGroup;
|
|
75
174
|
private acquireSpriteWork;
|
|
175
|
+
/**
|
|
176
|
+
* Like `acquireSpriteWork`, but for a P1-10 static batch cache replay entry: only `handle` and
|
|
177
|
+
* `cachedRow` matter, since `packFrameData` short-circuits on `cachedRow` and every other draw
|
|
178
|
+
* call only reads `work.handle`.
|
|
179
|
+
*/
|
|
180
|
+
private acquireCachedSpriteWork;
|
|
76
181
|
/** Drop object references so removed slots/entities are not retained by the pool. */
|
|
77
182
|
private releaseSpriteWorks;
|
|
183
|
+
/** Snapshot check for a P1-10 static batch cache: any mismatch means rebuild, never a stale hit. */
|
|
184
|
+
private staticBatchCacheMatches;
|
|
185
|
+
/** Clones the packed row for `work` into a fresh array so it survives past this frame's scratch reuse. */
|
|
186
|
+
private captureStaticBatchCacheEntry;
|
|
187
|
+
/** Replays a P1-10 static batch cache hit: same override/batch branching as the live loop, just
|
|
188
|
+
* sourced from pre-packed rows instead of recomputing layout/transform/texture lookups. */
|
|
189
|
+
private drawFromStaticBatchCache;
|
|
78
190
|
private getProjection;
|
|
79
191
|
private drawBatch;
|
|
80
192
|
private drawBatchInstancedWebGL;
|
|
81
193
|
private drawClippedSprite;
|
|
194
|
+
/**
|
|
195
|
+
* WebGL1 clipped-sprite draw: pack + upload the quad, then draw with stencil 'test'.
|
|
196
|
+
* Caller is responsible for the surrounding stencil write/clear. Uses separate buffers to
|
|
197
|
+
* avoid WebGPU writeBuffer aliasing with the batch draw.
|
|
198
|
+
*/
|
|
199
|
+
private drawBatchSpriteWithStencilTest;
|
|
82
200
|
/** Rect clip in canvas space intersected with the slot scissor. Returns false when fully clipped. */
|
|
83
201
|
private applyClipRectScissor;
|
|
84
202
|
private restoreSlotScissor;
|
|
@@ -92,8 +210,23 @@ export declare class SVGABatchRenderer {
|
|
|
92
210
|
/** Resolve atlas/override texture into a pooled SpriteWork entry. Returns false when missing. */
|
|
93
211
|
private resolveSpriteTextureInto;
|
|
94
212
|
private drawShapeFrame;
|
|
213
|
+
/**
|
|
214
|
+
* Clear the shared mask after a shape-first clip merge once the sprite sibling has drawn.
|
|
215
|
+
* Called by `drawClippedSprite`'s shared-mask branch.
|
|
216
|
+
*/
|
|
217
|
+
private clearSharedClipStencil;
|
|
218
|
+
private ensureShapeMeshBuffers;
|
|
219
|
+
private packShapeTransform;
|
|
95
220
|
private hasAtlasTexture;
|
|
96
221
|
private hasRenderableSVGA;
|
|
222
|
+
/**
|
|
223
|
+
* True when every sprite frame's transformed layout rect stays within the entity's own
|
|
224
|
+
* viewBox (with a small epsilon for float error). Computed once per entity and cached — the
|
|
225
|
+
* scan touches every sprite×frame combination, which is only affordable as a one-time cost.
|
|
226
|
+
* Entities with vector shapes always return `false`: shape geometry has its own coordinate
|
|
227
|
+
* system that this scan does not cover, so we conservatively keep per-slot scissoring for them.
|
|
228
|
+
*/
|
|
229
|
+
private allFramesInsideViewBox;
|
|
97
230
|
private getWhiteTexture;
|
|
98
231
|
private getShapeMesh;
|
|
99
232
|
private appendShape;
|
|
@@ -103,6 +236,20 @@ export declare class SVGABatchRenderer {
|
|
|
103
236
|
private clamp01;
|
|
104
237
|
private cleanupPoints;
|
|
105
238
|
private getClipMesh;
|
|
239
|
+
/**
|
|
240
|
+
* Hot-path clip mesh lookup: reads `clipPathIndex` directly (no string build/compare) and
|
|
241
|
+
* memoizes the resolved mesh per (entity, clipPathIndex) so repeat frames skip the string-keyed
|
|
242
|
+
* `clipMeshCache` lookup too.
|
|
243
|
+
*
|
|
244
|
+
* Returns `undefined` (rather than triangulating inline) when the path hasn't been built yet —
|
|
245
|
+
* prewarm normally finishes before an entity is ever drawn, but with `svgaPrewarmClipMeshes:
|
|
246
|
+
* false`, an aborted/partially failed prewarm, or a slot rendered before prewarm completes, this
|
|
247
|
+
* lookup would otherwise pay a synchronous SVG-parse + triangulate cost inside the render loop.
|
|
248
|
+
* The path is queued for a background build instead (see `queueClipMeshBuild`); callers already
|
|
249
|
+
* treat a falsy return as "no clip this frame" (draw unclipped / assume stencil conservatively),
|
|
250
|
+
* and a redraw is requested once the build lands so later frames clip precisely.
|
|
251
|
+
*/
|
|
252
|
+
private getClipMeshForSprite;
|
|
106
253
|
private ensureVertexBuffer;
|
|
107
254
|
private ensureIndexBuffer;
|
|
108
255
|
private ensureClipSpriteVertexBuffer;
|
|
@@ -17,6 +17,27 @@ export declare class VAPRenderer {
|
|
|
17
17
|
private scratchTransform;
|
|
18
18
|
private readonly processedHtmlGatesScratch;
|
|
19
19
|
private readonly vapVertexScratch;
|
|
20
|
+
private readonly layoutScratch;
|
|
21
|
+
private readonly clipScratch;
|
|
22
|
+
/** `drawInstancedVapBatches` grow-only scratch: avoids a fresh array/Map per frame. */
|
|
23
|
+
private readonly instancedListScratch;
|
|
24
|
+
private readonly instancedByTexScratch;
|
|
25
|
+
private readonly instancedTexturesScratch;
|
|
26
|
+
private readonly instancedUniformsScratch;
|
|
27
|
+
private readonly instancedDrawCommand;
|
|
28
|
+
/** `drawGeometry` (plain VAP quad) reused command/uniforms/textures. */
|
|
29
|
+
private readonly geometryTexturesScratch;
|
|
30
|
+
private readonly geometryUniformsScratch;
|
|
31
|
+
private readonly geometryDrawCommand;
|
|
32
|
+
/** `drawMixedGeometry` (composited rgba overlay) reused command/uniforms/textures. */
|
|
33
|
+
private readonly mixedGeometryTexturesScratch;
|
|
34
|
+
private readonly mixedGeometryUniformsScratch;
|
|
35
|
+
private readonly mixedGeometryDrawCommand;
|
|
36
|
+
/** `drawMixedDirectGeometry` per-frame-object overlay reused command/uniforms/textures. */
|
|
37
|
+
private readonly overlayTexturesScratch;
|
|
38
|
+
private readonly overlayUniformsScratch;
|
|
39
|
+
private readonly overlayDrawCommand;
|
|
40
|
+
constructor(onHtmlVideoFrame?: (video: HTMLVideoElement) => void);
|
|
20
41
|
private projCacheW;
|
|
21
42
|
private projCacheH;
|
|
22
43
|
private projCache;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -62,7 +62,9 @@ export interface GPUBackend {
|
|
|
62
62
|
/** Maximum supported 2D texture dimension for this backend/device. */
|
|
63
63
|
readonly maxTextureSize?: number;
|
|
64
64
|
init(): Promise<void>;
|
|
65
|
-
beginFrame(
|
|
65
|
+
beginFrame(options?: {
|
|
66
|
+
stencil?: boolean;
|
|
67
|
+
}): void;
|
|
66
68
|
endFrame(): void;
|
|
67
69
|
createBuffer(data: GPUBufferSource, usage: BufferUsage): GPUBufferHandle;
|
|
68
70
|
updateBuffer(handle: GPUBufferHandle, data: GPUBufferSource, offset?: number): void;
|
|
@@ -269,6 +271,47 @@ export interface ResolvedSVGASlotTexture {
|
|
|
269
271
|
logicalWidth: number;
|
|
270
272
|
logicalHeight: number;
|
|
271
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* SVGABatchRenderer-internal: one packed instance row for a `stopped` slot's static batch
|
|
276
|
+
* cache (P1-10). `row` mirrors the exact `FRAME_DATA_FLOATS` layout `packFrameData` produces.
|
|
277
|
+
* `isOverride` mirrors whether the sprite used a `svgaSlots` texture override (drawn immediately,
|
|
278
|
+
* breaking any running atlas batch) vs. a normal atlas sprite (batched with same-texture sprites).
|
|
279
|
+
*/
|
|
280
|
+
export interface SVGAStaticBatchCacheEntry {
|
|
281
|
+
row: Float32Array;
|
|
282
|
+
handle: GPUTextureHandle;
|
|
283
|
+
isOverride: boolean;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* SVGABatchRenderer-internal: cached packed-instance-row list for a `stopped` SVGA slot, plus
|
|
287
|
+
* the exact snapshot of every input it was built from. Any mismatch against the slot's current
|
|
288
|
+
* values invalidates the cache (see `SVGABatchRenderer`'s match check) — favors over-invalidating
|
|
289
|
+
* to guarantee correctness over maximizing hit rate.
|
|
290
|
+
*/
|
|
291
|
+
export interface SVGAStaticBatchCache {
|
|
292
|
+
entries: SVGAStaticBatchCacheEntry[];
|
|
293
|
+
/** `VideoEntity` reference the cache was built against (covers spriteTable/atlasRects identity). */
|
|
294
|
+
entity: unknown;
|
|
295
|
+
/** `entity.atlasTextureHandles` (if any) or `entity.atlasTextureHandle`, whichever is in use. */
|
|
296
|
+
atlasHandlesDep: unknown;
|
|
297
|
+
/**
|
|
298
|
+
* `slot.svgaSlotTextures` map reference. `svgaSlots` overrides are immutable post-load, but
|
|
299
|
+
* tracked defensively so a future change to that invariant can't silently go stale here.
|
|
300
|
+
*/
|
|
301
|
+
svgaSlotTextures: unknown;
|
|
302
|
+
currentFrame: number;
|
|
303
|
+
opacity: number;
|
|
304
|
+
x: number;
|
|
305
|
+
y: number;
|
|
306
|
+
width: number;
|
|
307
|
+
height: number;
|
|
308
|
+
scale: number;
|
|
309
|
+
rotation: number;
|
|
310
|
+
sx: number;
|
|
311
|
+
sy: number;
|
|
312
|
+
offsetX: number;
|
|
313
|
+
offsetY: number;
|
|
314
|
+
}
|
|
272
315
|
export interface ResolvedVAPResource {
|
|
273
316
|
source: CanvasImageSource;
|
|
274
317
|
width: number;
|
|
@@ -318,9 +361,9 @@ export interface GiftStageOptions {
|
|
|
318
361
|
} | false;
|
|
319
362
|
/**
|
|
320
363
|
* Edge smoothing: WebGL2 uses the context multisampled framebuffer; WebGPU uses 4× MSAA + resolve.
|
|
321
|
-
* Default: **true** for WebGL2
|
|
322
|
-
*
|
|
323
|
-
*
|
|
364
|
+
* Default: **true** for WebGL2 and WebGPU.
|
|
365
|
+
* Many concurrent transparent videos: consider setting `antialias: false` and lowering {@link resolution}
|
|
366
|
+
* when fill-rate or framebuffer bandwidth becomes the bottleneck.
|
|
324
367
|
*/
|
|
325
368
|
antialias?: boolean;
|
|
326
369
|
/**
|
|
@@ -611,6 +654,32 @@ export interface GiftSlot {
|
|
|
611
654
|
map: unknown;
|
|
612
655
|
size: number;
|
|
613
656
|
};
|
|
657
|
+
/**
|
|
658
|
+
* RenderManager internal: cached coincident-visual-duplicate key + the exact field snapshot it
|
|
659
|
+
* was built from. Rebuilt only when one of those fields actually changes, avoiding a template
|
|
660
|
+
* string array + `join('|')` allocation on every dirty frame for every visible slot.
|
|
661
|
+
*/
|
|
662
|
+
coincidentVisualKeyCache?: {
|
|
663
|
+
key: string;
|
|
664
|
+
resourceKey: string;
|
|
665
|
+
frameKey: string;
|
|
666
|
+
svgaSlotKey: string;
|
|
667
|
+
objectFit: string;
|
|
668
|
+
x: number;
|
|
669
|
+
y: number;
|
|
670
|
+
width: number;
|
|
671
|
+
height: number;
|
|
672
|
+
scale: number;
|
|
673
|
+
opacity: number;
|
|
674
|
+
rotation: number | undefined;
|
|
675
|
+
originX: string | number;
|
|
676
|
+
originY: string | number;
|
|
677
|
+
};
|
|
678
|
+
/**
|
|
679
|
+
* SVGABatchRenderer internal: cached packed instance rows for a `stopped` slot with no
|
|
680
|
+
* shape/clip sprites this frame (P1-10). Rebuilt whenever any tracked input changes.
|
|
681
|
+
*/
|
|
682
|
+
svgaStaticBatchCache?: SVGAStaticBatchCache | null;
|
|
614
683
|
onFrame?: (frame: number, gift: GiftHandle) => void;
|
|
615
684
|
onComplete?: (gift: GiftHandle) => void;
|
|
616
685
|
}
|
|
@@ -652,6 +721,8 @@ export interface VAPFrameObj {
|
|
|
652
721
|
mt?: number;
|
|
653
722
|
frame: [number, number, number, number];
|
|
654
723
|
mFrame: [number, number, number, number];
|
|
724
|
+
/** `String(srcId)` precomputed once by `buildVAPFrameMap` to avoid per-draw re-stringification. */
|
|
725
|
+
srcKey?: string;
|
|
655
726
|
}
|
|
656
727
|
export interface FlyAnimation {
|
|
657
728
|
giftId: string;
|
|
@@ -20,10 +20,11 @@ export declare function resolveGiftTransformOrigin(slot: GiftSlot, out?: GiftTra
|
|
|
20
20
|
export declare function fillGiftTransformMatrix(slot: GiftSlot, x: number, y: number, width: number, height: number, out: Float32Array): Float32Array;
|
|
21
21
|
/** Apply the gift's parent rotation to an existing local-to-stage affine transform. */
|
|
22
22
|
export declare function rotateGiftAffine(slot: GiftSlot, transform: Affine2D, out?: Affine2D): Affine2D;
|
|
23
|
-
|
|
24
|
-
export declare function getRotatedRectAABB(slot: GiftSlot, x: number, y: number, width: number, height: number): {
|
|
23
|
+
export interface RectAABB {
|
|
25
24
|
x: number;
|
|
26
25
|
y: number;
|
|
27
26
|
w: number;
|
|
28
27
|
h: number;
|
|
29
|
-
}
|
|
28
|
+
}
|
|
29
|
+
/** AABB of a rotated stage-space rectangle, suitable for the backend scissor. */
|
|
30
|
+
export declare function getRotatedRectAABB(slot: GiftSlot, x: number, y: number, width: number, height: number, out?: RectAABB): RectAABB;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* when many `<video>` elements run together (especially with WebGPU texture copies).
|
|
5
5
|
*/
|
|
6
6
|
export declare class HtmlVideoUploadGate {
|
|
7
|
+
private readonly onFrameAvailable?;
|
|
7
8
|
private readonly frameInfo;
|
|
8
9
|
private readonly pending;
|
|
9
10
|
private readonly lastHandle;
|
|
@@ -18,12 +19,16 @@ export declare class HtmlVideoUploadGate {
|
|
|
18
19
|
private readonly stagnationLogged;
|
|
19
20
|
/** When `requestVideoFrameCallback` is missing, fall back to `currentTime` changes (coarser than rVFC). */
|
|
20
21
|
private readonly lastTimeFallback;
|
|
22
|
+
/** Low-frequency wake-up used to detect browsers that stop delivering rVFC callbacks. */
|
|
23
|
+
private readonly healthTimers;
|
|
24
|
+
constructor(onFrameAvailable?: (video: HTMLVideoElement) => void);
|
|
21
25
|
/**
|
|
22
26
|
* Returns true when `updateTexture` should run for this slot: first time, or a new decoded frame was signaled.
|
|
23
27
|
*/
|
|
24
28
|
preUpload(slotId: string, video: HTMLVideoElement): boolean;
|
|
25
29
|
getFrameInfo(slotId: string): VideoFrameCallbackMetadata | undefined;
|
|
26
30
|
private ensureRvf;
|
|
31
|
+
private ensureHealthTimer;
|
|
27
32
|
private consumeTimeAdvance;
|
|
28
33
|
private markProgress;
|
|
29
34
|
private tryRecoverStalledPlayback;
|
|
@@ -1,6 +1,65 @@
|
|
|
1
1
|
import type { SVGAWorkerMoviePayload } from '../parsers/svga-parser-worker-client';
|
|
2
2
|
type PersistKind = 'arrayBuffer' | 'text';
|
|
3
3
|
type MemoryKind = PersistKind | 'json' | 'svga-payload' | 'svga-asset';
|
|
4
|
+
export declare class AsyncLimiter {
|
|
5
|
+
private maxConcurrent;
|
|
6
|
+
private active;
|
|
7
|
+
private queue;
|
|
8
|
+
constructor(maxConcurrent: number);
|
|
9
|
+
setMaxConcurrent(maxConcurrent: number): void;
|
|
10
|
+
run<T>(task: () => Promise<T>): Promise<T>;
|
|
11
|
+
private drain;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Structural estimate; avoids JSON.stringify of the whole sprites tree (main-thread spike).
|
|
15
|
+
*
|
|
16
|
+
* When the worker-binary path is used, `imageBlob` is the *entire* deserialized SGPB buffer
|
|
17
|
+
* (see `deserializeSVGAWorkerPayload`), not just the image bytes — `spriteTable.frameData`/
|
|
18
|
+
* `clipPathIndex`/`shapesIndex` are typed-array *views* sharing that same underlying
|
|
19
|
+
* `ArrayBuffer` (see `readSpriteTable` in `svga-sprite-table-binary.ts`). Adding their
|
|
20
|
+
* `byteLength` again on top of `imageBlob.byteLength` would double-count them, systematically
|
|
21
|
+
* inflating the estimate and causing the memory LRU to evict/re-parse too eagerly. Only add
|
|
22
|
+
* them when they do *not* share `imageBlob`'s buffer (the legacy `images` record path, where
|
|
23
|
+
* the sprite table is a separate allocation).
|
|
24
|
+
*/
|
|
25
|
+
export declare function estimateSVGAWorkerPayloadBytes(payload: SVGAWorkerMoviePayload): number;
|
|
26
|
+
export declare class ResourceCacheManager {
|
|
27
|
+
private readonly limiter;
|
|
28
|
+
private readonly pending;
|
|
29
|
+
private readonly memory;
|
|
30
|
+
private memoryBytes;
|
|
31
|
+
private maxMemoryBytes;
|
|
32
|
+
private maxPersistentBytes;
|
|
33
|
+
private dbPromise;
|
|
34
|
+
private persistentTrimPromise;
|
|
35
|
+
configure(opts?: {
|
|
36
|
+
maxConcurrent?: number;
|
|
37
|
+
maxMemoryBytes?: number;
|
|
38
|
+
maxPersistentBytes?: number;
|
|
39
|
+
}): void;
|
|
40
|
+
runLimited<T>(task: () => Promise<T>): Promise<T>;
|
|
41
|
+
fetchArrayBuffer(url: string, signal?: AbortSignal): Promise<ArrayBuffer>;
|
|
42
|
+
fetchText(url: string, signal?: AbortSignal): Promise<string>;
|
|
43
|
+
fetchJSON<T>(url: string, signal?: AbortSignal): Promise<T>;
|
|
44
|
+
getSVGAWorkerPayload(cacheKey: string): SVGAWorkerMoviePayload | null;
|
|
45
|
+
setSVGAWorkerPayload(cacheKey: string, payload: SVGAWorkerMoviePayload): void;
|
|
46
|
+
getMemoryValue<T>(key: string, kind: MemoryKind): T | null;
|
|
47
|
+
setMemoryValue<T>(key: string, kind: MemoryKind, value: T, size: number): void;
|
|
48
|
+
getPersistedArrayBuffer(key: string): Promise<ArrayBuffer | null>;
|
|
49
|
+
setPersistedArrayBuffer(key: string, value: ArrayBuffer): Promise<void>;
|
|
50
|
+
deletePersistedValue(key: string): Promise<void>;
|
|
51
|
+
private getOrCreatePending;
|
|
52
|
+
private getMemory;
|
|
53
|
+
private setMemory;
|
|
54
|
+
private trimMemory;
|
|
55
|
+
private openDb;
|
|
56
|
+
private getPersistent;
|
|
57
|
+
private setPersistent;
|
|
58
|
+
private touchPersistent;
|
|
59
|
+
private schedulePersistentTrim;
|
|
60
|
+
private trimPersistent;
|
|
61
|
+
private deletePersistent;
|
|
62
|
+
}
|
|
4
63
|
export declare function configureResourceCache(opts?: {
|
|
5
64
|
maxConcurrent?: number;
|
|
6
65
|
maxMemoryBytes?: number;
|
|
@@ -32,6 +32,53 @@ export declare function decodeH264ToFrames(codec: string, description: ArrayBuff
|
|
|
32
32
|
frames: VideoFrame[];
|
|
33
33
|
timesSec: Float64Array;
|
|
34
34
|
}>;
|
|
35
|
+
export interface IncrementalH264DecoderCallbacks {
|
|
36
|
+
onFrame: (index: number, frame: VideoFrame) => void;
|
|
37
|
+
onEos?: () => void;
|
|
38
|
+
onError?: (error: unknown) => void;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Playhead-driven incremental H.264 decode: feeds chunks in decode order only up to
|
|
42
|
+
* `needIdx + decodeAheadFrames` (via {@link ensure}), so memory stays bounded by how far ahead
|
|
43
|
+
* of playback the caller asks for instead of decoding (and retaining) the entire clip upfront.
|
|
44
|
+
* Originally the Worker's demux/decode pump (see `webcodecs-mp4.worker.ts`); lifted here so the
|
|
45
|
+
* main-thread WebCodecs fallback (no Worker available, or `useWorker: false`) can reuse the exact
|
|
46
|
+
* same backpressure/rewind semantics instead of decoding the whole file into memory at once —
|
|
47
|
+
* see P1-10 optimization notes / `WebCodecsMp4Playback.createOnMainThread`.
|
|
48
|
+
*/
|
|
49
|
+
export declare class IncrementalH264Decoder {
|
|
50
|
+
private readonly decodeOrder;
|
|
51
|
+
private readonly presTimes;
|
|
52
|
+
private readonly timescale;
|
|
53
|
+
private readonly codec;
|
|
54
|
+
private readonly description;
|
|
55
|
+
private readonly duration;
|
|
56
|
+
private readonly callbacks;
|
|
57
|
+
private decoder;
|
|
58
|
+
private nextChunk;
|
|
59
|
+
private chunksFed;
|
|
60
|
+
private targetNeedIdx;
|
|
61
|
+
private lastPostedMax;
|
|
62
|
+
private minPosted;
|
|
63
|
+
private playbackNeedFloor;
|
|
64
|
+
private freeSlots;
|
|
65
|
+
private eosClosed;
|
|
66
|
+
private pumpScheduled;
|
|
67
|
+
private pumping;
|
|
68
|
+
private destroyed;
|
|
69
|
+
constructor(decodeOrder: Sample[], presTimes: Float64Array, timescale: number, codec: string, description: ArrayBuffer | undefined, duration: number, callbacks: IncrementalH264DecoderCallbacks, initialNeedIdx: number);
|
|
70
|
+
/** Raise the decode target and/or update main-thread ring headroom; resumes the pump if idle. */
|
|
71
|
+
ensure(needIdx: number, freeSlots: number): void;
|
|
72
|
+
/** Restart decode from the nearest keyframe at/before t=0 (backward seek / loop wrap). */
|
|
73
|
+
rewind(): void;
|
|
74
|
+
destroy(): void;
|
|
75
|
+
private schedulePump;
|
|
76
|
+
private doRewind;
|
|
77
|
+
private openDecoder;
|
|
78
|
+
private feedOneChunk;
|
|
79
|
+
private finalizeEos;
|
|
80
|
+
private runPump;
|
|
81
|
+
}
|
|
35
82
|
export declare function frameIndexForTime(timesSec: Float64Array, clipDurationSec: number, t: number): number;
|
|
36
83
|
export declare function computeClipDurationSeconds(metaDurationSec: number, timesSec: Float64Array, frames: VideoFrame[]): number;
|
|
37
84
|
/** Clip duration from demux metadata + presentation timeline only (no decoded frames). */
|