@taole/giftstage 0.2.2 → 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.
@@ -144,6 +144,7 @@ export declare class WebGL2Backend implements GPUBackend {
144
144
  private recordSubmittedDraw;
145
145
  private rejectSVGAFastPathDraw;
146
146
  private initPrograms;
147
+ private ensureParticleSpriteProgram;
147
148
  private makeProgramEntry;
148
149
  /**
149
150
  * `u_transform` on `svga-batch-premul` defaults to identity; shape draws overwrite it and
@@ -238,6 +239,7 @@ export declare class WebGL2Backend implements GPUBackend {
238
239
  /** Shape/clip meshes fetch their child transform from the same resident frame table. */
239
240
  private drawSVGAFrameTableMeshGL;
240
241
  private drawInstancedParticlesGL;
242
+ private drawInstancedParticleSpritesGL;
241
243
  private drawInstancedSvgaGL;
242
244
  private ensureInstancedClipMaskBuffer;
243
245
  private drawInstancedClipMaskGL;
@@ -214,6 +214,7 @@ export declare class WebGPUBackend implements GPUBackend {
214
214
  private ensureSVGAFrameTableMeshPipeline;
215
215
  private ensureMotionSvgaPipeline;
216
216
  private ensureParticlePipeline;
217
+ private ensureParticleSpritePipeline;
217
218
  private createInstancedSvgaPipeline;
218
219
  private createInstancedClipMaskPipeline;
219
220
  private createVapInstancedPipeline;
package/dist/index.d.ts CHANGED
@@ -33,4 +33,4 @@ export { initWasm, isWasmReady } from './wasm/wasm-bridge.js';
33
33
  export { WebCodecsMp4Playback } from './video/webcodecs-mp4-playback.js';
34
34
  export type { WebCodecsMp4CreateOptions } from './video/webcodecs-mp4-playback.js';
35
35
  export type { WebCodecsPlaybackHandle, GPUBackend, BackendOperationFailureReason, BackendOperationResult, GPUBufferHandle, GPUTextureHandle, TextureOpts, BufferUsage, DrawCommand, ShaderType, BackendType, ParentMotionMode, SVGADrawBatchMode, SVGAFrameEvaluation, SVGAFrameEvaluationStrategy, SVGAFrameTableBinding, SVGAFrameTableBindingBase, SVGAFrameTableCreateOptions, SVGAFrameTableState, SVGAFrameTimelineFallbackReason, SVGAFrameTimelinePath, SVGAFrameTimelineRenderInfo, SVGAFrameTimelineStrategy, SVGAFrameTimelineTransport, SVGARenderFallbackReason, SVGARenderInfo, SVGARenderPath, SVGARenderPathState, SVGARenderPathChangeInfo, GiftStageRuntimeDiagnosticType, GiftStageRuntimeDiagnosticEvent, GiftStageRuntimeDiagnostics, SVGAHybridCommandTable, SVGAHybridOperation, SVGAHybridOperationType, SVGATimelineDrawRun, WebGL2FrameTableBinding, WebGPUFrameTableBinding, GiftStageOptions, AddGiftOptions, GiftHandle, GiftSlot, GiftType, GiftObjectFit, GiftCoordinate, GiftState, GiftAnimationStep, GiftAnimationChain, SVGAMovieEntity, SVGAParams, SVGASpriteTable, SVGAAudio, SVGATransform, SVGAShape, VideoEntity, UVRect, FrameJobData, FlyAnimation, VAPConfig, VAPSrcItem, VAPFrameItem, VAPFrameObj, } from './types/index.js';
36
- export type { GiftAssetLease, GiftAssetMetadata, GiftStageCapabilities, SVGAFrameTimelineCapabilities, GiftStagePlugin, GiftStagePluginHost, MountedGiftStagePlugin, MotionBindingOptions, MotionTrackDescriptor, MotionTrackLease, ParticleBatchDescriptor, ParticleBatchHandle, PlaybackFrame, PlaybackScope, PlaybackScopeStartOptions, PlaybackScopeState, ReadonlyTableDescriptor, ReadonlyTableLease, } from './plugin/types.js';
36
+ export type { GiftAssetLease, GiftAssetMetadata, GiftStageCapabilities, SVGAFrameTimelineCapabilities, GiftStagePlugin, GiftStagePluginHost, MountedGiftStagePlugin, MotionBindingOptions, MotionTrackDescriptor, MotionTrackLease, BallisticV1ParticleBatchDescriptor, ParticleAtlasDescriptor, ParticleAtlasHandle, ParticleBatchDescriptor, ParticleBatchEnvelope, ParticleBatchGroupHandle, ParticleBatchHandle, ParticleEnvelopeSegment, SpriteV2ParticleBatchDescriptor, PlaybackFrame, PlaybackScope, PlaybackScopeStartOptions, PlaybackScopeState, ReadonlyTableDescriptor, ReadonlyTableLease, } from './plugin/types.js';
@@ -1,5 +1,5 @@
1
1
  import type { AddGiftOptions, GiftHandle } from '../types/index.js';
2
- import type { MotionBindingOptions, MotionTrackDescriptor, MotionTrackLease, ParticleBatchDescriptor, ParticleBatchHandle, PlaybackFrame, PlaybackFrameController, PlaybackScope, PlaybackScopeHost, PlaybackScopeStartOptions, PlaybackScopeState } from './types.js';
2
+ import type { MotionBindingOptions, MotionTrackDescriptor, MotionTrackLease, ParticleBatchDescriptor, ParticleBatchGroupHandle, ParticleBatchHandle, PlaybackFrame, PlaybackFrameController, PlaybackScope, PlaybackScopeHost, PlaybackScopeStartOptions, PlaybackScopeState } from './types.js';
3
3
  export declare class PlaybackScopeImpl implements PlaybackScope, PlaybackFrameController {
4
4
  readonly id: string;
5
5
  private readonly host;
@@ -8,6 +8,7 @@ export declare class PlaybackScopeImpl implements PlaybackScope, PlaybackFrameCo
8
8
  private readonly tracks;
9
9
  private readonly bindings;
10
10
  private readonly particleBatches;
11
+ private readonly particleBatchGroups;
11
12
  private readonly frameListeners;
12
13
  private readonly finishListeners;
13
14
  private unregisterController;
@@ -23,6 +24,7 @@ export declare class PlaybackScopeImpl implements PlaybackScope, PlaybackFrameCo
23
24
  addGift(options: AddGiftOptions): Promise<GiftHandle>;
24
25
  createMotionTrack(descriptor: MotionTrackDescriptor): MotionTrackLease;
25
26
  createParticleBatch(descriptor: ParticleBatchDescriptor): ParticleBatchHandle;
27
+ createParticleBatchGroup(descriptors: readonly ParticleBatchDescriptor[]): ParticleBatchGroupHandle;
26
28
  bindMotion(gift: GiftHandle, track: MotionTrackLease, trackIndex: number, options?: MotionBindingOptions): () => void;
27
29
  onFrame(listener: (frame: PlaybackFrame) => void): () => void;
28
30
  onFinish(listener: () => void): () => void;
@@ -51,6 +51,16 @@ export interface GiftStageCapabilities {
51
51
  particles: {
52
52
  backend: 'webgpu' | 'webgl2' | 'unavailable';
53
53
  canvas2D: false;
54
+ /** Optional feature negotiation. Absent on pre-sprite-v2 hosts. */
55
+ formats?: Array<'ballistic-v1' | 'sprite-v2'>;
56
+ spriteAtlas?: {
57
+ source: 'rgba8-premultiplied';
58
+ maxSize: number;
59
+ };
60
+ compositeLayers?: Array<'behind-media' | 'above-media'>;
61
+ batchEnvelope?: boolean;
62
+ atomicBatchGroup?: boolean;
63
+ maxInstancesPerBatch?: number;
54
64
  };
55
65
  /** Public SVGA frame-table path support exposed to playback plugins. */
56
66
  svgaFrameTimeline?: SVGAFrameTimelineCapabilities;
@@ -107,16 +117,65 @@ export interface PlaybackFrame {
107
117
  * Particle instance layout (16 floats): birth, lifetime, x, y, vx, vy, ax, ay,
108
118
  * scale, scaleSpeed, rotation, spin, r, g, b, a. Times are seconds; positions are CSS pixels.
109
119
  */
110
- export interface ParticleBatchDescriptor {
120
+ export interface BallisticV1ParticleBatchDescriptor {
121
+ /** Omitted for backward compatibility. */
122
+ format?: 'ballistic-v1';
111
123
  count: number;
112
124
  instances: Float32Array;
113
125
  particleSize?: number;
114
126
  opacity?: number;
115
- /** Sorts particle batches only. Particle batches composite after all media gift slots. */
127
+ /** Sorts particle batches inside their composite phase only. */
116
128
  zIndex?: number;
117
129
  blend?: 'normal' | 'additive';
130
+ /** v1 remains above media unless explicitly requested otherwise. */
131
+ compositeLayer?: 'behind-media' | 'above-media';
118
132
  label?: string;
119
133
  }
134
+ export interface ParticleAtlasDescriptor {
135
+ width: number;
136
+ height: number;
137
+ pixels: Uint8Array;
138
+ colorSpace: 'srgb';
139
+ alphaMode: 'premultiplied';
140
+ filter?: 'linear';
141
+ label?: string;
142
+ }
143
+ export interface ParticleAtlasHandle {
144
+ readonly width: number;
145
+ readonly height: number;
146
+ /** The caller lease was released. Existing groups may retain the GPU resource. */
147
+ readonly destroyed: boolean;
148
+ /** The owning backend generation was lost. Lost handles cannot be reused. */
149
+ readonly lost: boolean;
150
+ destroy(): void;
151
+ }
152
+ export interface ParticleEnvelopeSegment {
153
+ /** Absolute playback milliseconds. Segments use the half-open interval [startMs, endMs). */
154
+ startMs: number;
155
+ endMs: number;
156
+ from: number;
157
+ to: number;
158
+ interpolation?: 'linear' | 'hold';
159
+ }
160
+ export interface ParticleBatchEnvelope {
161
+ opacity?: readonly ParticleEnvelopeSegment[];
162
+ size?: readonly ParticleEnvelopeSegment[];
163
+ }
164
+ export interface SpriteV2ParticleBatchDescriptor {
165
+ format: 'sprite-v2';
166
+ atlas: ParticleAtlasHandle;
167
+ count: number;
168
+ /** 24 floats per instance; the first 16 preserve ballistic-v1 semantics. */
169
+ instances: Float32Array;
170
+ opacity?: number;
171
+ fadeMode?: 'none' | 'lifetime';
172
+ envelope?: ParticleBatchEnvelope;
173
+ compositeLayer?: 'behind-media' | 'above-media';
174
+ zIndex?: number;
175
+ blend?: 'normal' | 'additive';
176
+ label?: string;
177
+ }
178
+ export type ParticleBatchDescriptor = BallisticV1ParticleBatchDescriptor | SpriteV2ParticleBatchDescriptor;
120
179
  export interface ParticleBatchHandle {
121
180
  readonly count: number;
122
181
  readonly destroyed: boolean;
@@ -124,6 +183,14 @@ export interface ParticleBatchHandle {
124
183
  setOpacity(opacity: number): void;
125
184
  destroy(): void;
126
185
  }
186
+ export interface ParticleBatchGroupHandle {
187
+ readonly count: number;
188
+ readonly destroyed: boolean;
189
+ readonly faulted: boolean;
190
+ setTime(timeMs: number): void;
191
+ setOpacity(opacity: number): void;
192
+ destroy(): void;
193
+ }
127
194
  export interface PlaybackScope {
128
195
  readonly id: string;
129
196
  readonly signal: AbortSignal;
@@ -132,6 +199,8 @@ export interface PlaybackScope {
132
199
  addGift(options: AddGiftOptions): Promise<GiftHandle>;
133
200
  createMotionTrack(descriptor: MotionTrackDescriptor): MotionTrackLease;
134
201
  createParticleBatch(descriptor: ParticleBatchDescriptor): ParticleBatchHandle;
202
+ /** Optional in API v1; feature-detect through `capabilities.particles.atomicBatchGroup`. */
203
+ createParticleBatchGroup?(descriptors: readonly ParticleBatchDescriptor[]): ParticleBatchGroupHandle;
135
204
  bindMotion(gift: GiftHandle, track: MotionTrackLease, trackIndex: number, options?: MotionBindingOptions): () => void;
136
205
  onFrame(listener: (frame: PlaybackFrame) => void): () => void;
137
206
  onFinish(listener: () => void): () => void;
@@ -150,6 +219,8 @@ export interface GiftStagePluginHost {
150
219
  height: number;
151
220
  };
152
221
  preload(type: 'svga' | 'image', source: string | ArrayBuffer, signal?: AbortSignal): Promise<GiftAssetLease>;
222
+ /** Optional in API v1; uploads one immutable premultiplied RGBA8 atlas. */
223
+ createParticleAtlas?(descriptor: ParticleAtlasDescriptor): Promise<ParticleAtlasHandle>;
153
224
  createPlaybackScope(id?: string): PlaybackScope;
154
225
  }
155
226
  export interface GiftStagePlugin<TApi = unknown> {
@@ -176,6 +247,7 @@ export interface PlaybackScopeHost {
176
247
  invalidate(resetClock?: boolean): void;
177
248
  registerFrameController(controller: PlaybackFrameController): () => void;
178
249
  createParticleBatch(descriptor: ParticleBatchDescriptor): ParticleBatchHandle;
250
+ createParticleBatchGroup?(descriptors: readonly ParticleBatchDescriptor[]): ParticleBatchGroupHandle;
179
251
  }
180
252
  export interface PlaybackFrameController {
181
253
  readonly active: boolean;
@@ -1,19 +1,62 @@
1
1
  import type { GPUBackend } from '../types/index.js';
2
- import type { ParticleBatchDescriptor, ParticleBatchHandle } from '../plugin/types.js';
2
+ import type { ParticleAtlasDescriptor, ParticleAtlasHandle, ParticleBatchDescriptor, ParticleBatchGroupHandle, ParticleBatchHandle } from '../plugin/types.js';
3
+ type ParticleCompositeLayer = 'behind-media' | 'above-media';
4
+ export declare class ParticleBatchGroupDrawError extends Error {
5
+ readonly group: ParticleBatchGroupHandle;
6
+ constructor(group: ParticleBatchGroupHandle, message: string);
7
+ }
3
8
  export declare class ParticleBatchRenderer {
4
9
  private readonly backend;
5
10
  private readonly batches;
11
+ private readonly groups;
12
+ private readonly atlases;
13
+ private readonly onRuntimeDiagnostic?;
6
14
  private quadVB;
7
15
  private quadIB;
8
16
  private nextOrder;
9
17
  private projW;
10
18
  private projH;
11
19
  private projection;
20
+ private backendLost;
21
+ private destroyed;
22
+ private drawOrderDirty;
23
+ private readonly behindDrawOrder;
24
+ private readonly aboveDrawOrder;
25
+ private readonly cssScaleUniform;
12
26
  private readonly uniforms;
13
27
  private readonly command;
14
- constructor(backend: GPUBackend);
28
+ constructor(backend: GPUBackend, onRuntimeDiagnostic?: (event: {
29
+ type: 'particle-group-failure';
30
+ message?: string;
31
+ }) => void);
32
+ createAtlas(descriptor: ParticleAtlasDescriptor): Promise<ParticleAtlasHandle>;
15
33
  createBatch(descriptor: ParticleBatchDescriptor): ParticleBatchHandle;
34
+ createBatchGroup(descriptors: readonly ParticleBatchDescriptor[]): ParticleBatchGroupHandle;
16
35
  get activeCount(): number;
17
- draw(): void;
36
+ private rebuildDrawOrder;
37
+ draw(compositeLayer?: ParticleCompositeLayer): void;
38
+ /** Uploads complete dirty groups inside the active framebuffer transaction. */
39
+ prepareFrame(): void;
40
+ onBackendLoss(): void;
18
41
  destroy(): void;
42
+ private assertAvailable;
43
+ private validateDescriptor;
44
+ private validateEnvelope;
45
+ private cloneEnvelope;
46
+ private createGroupRecord;
47
+ private stageRecord;
48
+ private setRecordTime;
49
+ private setRecordOpacity;
50
+ private updateRecordHeader;
51
+ private syncRecordCssScale;
52
+ private evaluateEnvelope;
53
+ private faultGroup;
54
+ private destroyGroup;
55
+ private destroyRecord;
56
+ private disposeStagedRecord;
57
+ private retainAtlas;
58
+ private releaseAtlasCaller;
59
+ private releaseAtlasRetain;
60
+ private releaseAtlasRef;
19
61
  }
62
+ export {};
@@ -55,6 +55,10 @@ export declare const glsl: {
55
55
  readonly vert: string;
56
56
  readonly frag: string;
57
57
  };
58
+ readonly particleSpriteInstanced: {
59
+ readonly vert: string;
60
+ readonly frag: string;
61
+ };
58
62
  };
59
63
  export declare const wgsl: {
60
64
  readonly svgaBatchPremul: string;
@@ -68,6 +72,7 @@ export declare const wgsl: {
68
72
  readonly clipMask: string;
69
73
  readonly clipMaskInstanced: string;
70
74
  readonly particleInstanced: string;
75
+ readonly particleSpriteInstanced: string;
71
76
  readonly svgaMotionInstancedPremul: string;
72
77
  readonly svgaFrameTableShape: string;
73
78
  readonly svgaFrameTableClipMask: string;
@@ -21,7 +21,7 @@ export type SVGADrawBatchMode = 'multi-slot' | 'per-slot' | 'hybrid-scheduled';
21
21
  /** Why a slot did not use the direct SVGA GPU frame-table path. */
22
22
  export type SVGARenderFallbackReason = 'device-capability' | 'table-too-large-or-budget' | 'table-preparing' | 'upload-failed' | 'slot-overrides' | 'plan-not-ready-or-unsupported' | 'validation-failed' | 'policy-disabled';
23
23
  export type SVGAFrameTimelineFallbackReason = SVGARenderFallbackReason;
24
- export type GiftStageRuntimeDiagnosticType = 'svga-frame-abort' | 'svga-frame-replay-failed' | 'svga-frame-retry' | 'svga-runtime-fallback' | 'gpu-uncaptured-error' | 'backend-lost' | 'observer-error' | 'clip-protocol-error' | 'clip-mesh-invalid' | 'stencil-transaction-failure' | 'backend-operation-rejected' | 'atlas-command-invalid' | 'backend-quarantined';
24
+ export type GiftStageRuntimeDiagnosticType = 'svga-frame-abort' | 'svga-frame-replay-failed' | 'svga-frame-retry' | 'svga-runtime-fallback' | 'gpu-uncaptured-error' | 'backend-lost' | 'observer-error' | 'clip-protocol-error' | 'clip-mesh-invalid' | 'stencil-transaction-failure' | 'backend-operation-rejected' | 'atlas-command-invalid' | 'backend-quarantined' | 'frame-controller-failure' | 'particle-group-failure';
25
25
  /** One production-safe runtime event. GiftStage never uploads these events. */
26
26
  export interface GiftStageRuntimeDiagnosticEvent {
27
27
  type: GiftStageRuntimeDiagnosticType;
@@ -205,7 +205,9 @@ export type ShaderType = 'svga-batch-premul'
205
205
  /** WebGPU / WebGL2: one local-space clip mesh transformed by per-instance affine data. */
206
206
  | 'clip-mask-instanced'
207
207
  /** WebGPU / WebGL2: immutable ballistic particle instances evaluated in the vertex shader. */
208
- | 'particle-instanced';
208
+ | 'particle-instanced'
209
+ /** WebGPU / WebGL2: immutable textured sprite-v2 particles sampled from an explicit atlas. */
210
+ | 'particle-sprite-instanced';
209
211
  export type StencilMode = 'none' | 'write' | 'test' | 'clear';
210
212
  export interface DrawCommand {
211
213
  shader: ShaderType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taole/giftstage",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "description": "High-performance WebGPU/WebGL gift animation player unifying SVGA, VAP, AlphaVideo, and images",
5
5
  "type": "module",
6
6
  "main": "dist/gift-stage.cjs",