@motion-core/motion-gpu 0.1.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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +325 -0
  3. package/dist/FragCanvas.svelte +511 -0
  4. package/dist/FragCanvas.svelte.d.ts +26 -0
  5. package/dist/MotionGPUErrorOverlay.svelte +394 -0
  6. package/dist/MotionGPUErrorOverlay.svelte.d.ts +7 -0
  7. package/dist/Portal.svelte +46 -0
  8. package/dist/Portal.svelte.d.ts +8 -0
  9. package/dist/advanced-scheduler.d.ts +44 -0
  10. package/dist/advanced-scheduler.js +58 -0
  11. package/dist/advanced.d.ts +14 -0
  12. package/dist/advanced.js +9 -0
  13. package/dist/core/error-diagnostics.d.ts +40 -0
  14. package/dist/core/error-diagnostics.js +111 -0
  15. package/dist/core/error-report.d.ts +67 -0
  16. package/dist/core/error-report.js +190 -0
  17. package/dist/core/material-preprocess.d.ts +63 -0
  18. package/dist/core/material-preprocess.js +166 -0
  19. package/dist/core/material.d.ts +157 -0
  20. package/dist/core/material.js +358 -0
  21. package/dist/core/recompile-policy.d.ts +27 -0
  22. package/dist/core/recompile-policy.js +15 -0
  23. package/dist/core/render-graph.d.ts +55 -0
  24. package/dist/core/render-graph.js +73 -0
  25. package/dist/core/render-targets.d.ts +39 -0
  26. package/dist/core/render-targets.js +63 -0
  27. package/dist/core/renderer.d.ts +9 -0
  28. package/dist/core/renderer.js +1097 -0
  29. package/dist/core/shader.d.ts +42 -0
  30. package/dist/core/shader.js +196 -0
  31. package/dist/core/texture-loader.d.ts +129 -0
  32. package/dist/core/texture-loader.js +295 -0
  33. package/dist/core/textures.d.ts +114 -0
  34. package/dist/core/textures.js +136 -0
  35. package/dist/core/types.d.ts +523 -0
  36. package/dist/core/types.js +4 -0
  37. package/dist/core/uniforms.d.ts +48 -0
  38. package/dist/core/uniforms.js +222 -0
  39. package/dist/current-writable.d.ts +31 -0
  40. package/dist/current-writable.js +27 -0
  41. package/dist/frame-context.d.ts +287 -0
  42. package/dist/frame-context.js +731 -0
  43. package/dist/index.d.ts +17 -0
  44. package/dist/index.js +11 -0
  45. package/dist/motiongpu-context.d.ts +77 -0
  46. package/dist/motiongpu-context.js +26 -0
  47. package/dist/passes/BlitPass.d.ts +32 -0
  48. package/dist/passes/BlitPass.js +158 -0
  49. package/dist/passes/CopyPass.d.ts +25 -0
  50. package/dist/passes/CopyPass.js +53 -0
  51. package/dist/passes/ShaderPass.d.ts +40 -0
  52. package/dist/passes/ShaderPass.js +182 -0
  53. package/dist/passes/index.d.ts +3 -0
  54. package/dist/passes/index.js +3 -0
  55. package/dist/use-motiongpu-user-context.d.ts +35 -0
  56. package/dist/use-motiongpu-user-context.js +74 -0
  57. package/dist/use-texture.d.ts +35 -0
  58. package/dist/use-texture.js +147 -0
  59. package/package.json +94 -0
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Valid WGSL identifier pattern used for uniform and texture keys.
3
+ */
4
+ const IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
5
+ /**
6
+ * Rounds a value up to the nearest multiple of `alignment`.
7
+ */
8
+ function roundUp(value, alignment) {
9
+ return Math.ceil(value / alignment) * alignment;
10
+ }
11
+ /**
12
+ * Returns WGSL std140-like alignment and size metadata for a uniform type.
13
+ */
14
+ function getTypeLayout(type) {
15
+ switch (type) {
16
+ case 'f32':
17
+ return { alignment: 4, size: 4 };
18
+ case 'vec2f':
19
+ return { alignment: 8, size: 8 };
20
+ case 'vec3f':
21
+ return { alignment: 16, size: 12 };
22
+ case 'vec4f':
23
+ return { alignment: 16, size: 16 };
24
+ case 'mat4x4f':
25
+ return { alignment: 16, size: 64 };
26
+ default:
27
+ throw new Error(`Unsupported uniform type: ${type}`);
28
+ }
29
+ }
30
+ /**
31
+ * Type guard for explicitly typed uniform objects.
32
+ */
33
+ function isTypedUniformValue(value) {
34
+ return typeof value === 'object' && value !== null && 'type' in value && 'value' in value;
35
+ }
36
+ /**
37
+ * Validates numeric tuple input with a fixed length.
38
+ */
39
+ function isTuple(value, size) {
40
+ return (Array.isArray(value) &&
41
+ value.length === size &&
42
+ value.every((entry) => typeof entry === 'number' && Number.isFinite(entry)));
43
+ }
44
+ /**
45
+ * Type guard for accepted 4x4 matrix uniform values.
46
+ */
47
+ function isMat4Value(value) {
48
+ if (value instanceof Float32Array) {
49
+ return value.length === 16;
50
+ }
51
+ return (Array.isArray(value) &&
52
+ value.length === 16 &&
53
+ value.every((entry) => typeof entry === 'number' && Number.isFinite(entry)));
54
+ }
55
+ /**
56
+ * Asserts that a name can be safely used as a WGSL identifier.
57
+ *
58
+ * @param name - Candidate uniform/texture name.
59
+ * @throws {Error} When the identifier is invalid.
60
+ */
61
+ export function assertUniformName(name) {
62
+ if (!IDENTIFIER_PATTERN.test(name)) {
63
+ throw new Error(`Invalid uniform name: ${name}`);
64
+ }
65
+ }
66
+ /**
67
+ * Infers the WGSL type tag from a runtime uniform value.
68
+ *
69
+ * @param value - Uniform input value.
70
+ * @returns Inferred uniform type.
71
+ * @throws {Error} When the value does not match any supported shape.
72
+ */
73
+ export function inferUniformType(value) {
74
+ if (isTypedUniformValue(value)) {
75
+ return value.type;
76
+ }
77
+ if (typeof value === 'number') {
78
+ return 'f32';
79
+ }
80
+ if (Array.isArray(value)) {
81
+ if (value.length === 2) {
82
+ return 'vec2f';
83
+ }
84
+ if (value.length === 3) {
85
+ return 'vec3f';
86
+ }
87
+ if (value.length === 4) {
88
+ return 'vec4f';
89
+ }
90
+ }
91
+ throw new Error('Uniform value must resolve to f32, vec2f, vec3f, vec4f or mat4x4f');
92
+ }
93
+ /**
94
+ * Validates that a uniform value matches an explicit uniform type declaration.
95
+ *
96
+ * @param type - Declared WGSL type.
97
+ * @param value - Runtime value to validate.
98
+ * @throws {Error} When the value shape is incompatible with the declared type.
99
+ */
100
+ export function assertUniformValueForType(type, value) {
101
+ const input = isTypedUniformValue(value) ? value.value : value;
102
+ if (type === 'f32') {
103
+ if (typeof input !== 'number' || !Number.isFinite(input)) {
104
+ throw new Error('Uniform f32 value must be a finite number');
105
+ }
106
+ return;
107
+ }
108
+ if (type === 'vec2f') {
109
+ if (!isTuple(input, 2)) {
110
+ throw new Error('Uniform vec2f value must be a tuple with 2 numbers');
111
+ }
112
+ return;
113
+ }
114
+ if (type === 'vec3f') {
115
+ if (!isTuple(input, 3)) {
116
+ throw new Error('Uniform vec3f value must be a tuple with 3 numbers');
117
+ }
118
+ return;
119
+ }
120
+ if (type === 'vec4f') {
121
+ if (!isTuple(input, 4)) {
122
+ throw new Error('Uniform vec4f value must be a tuple with 4 numbers');
123
+ }
124
+ return;
125
+ }
126
+ if (!isMat4Value(input)) {
127
+ throw new Error('Uniform mat4x4f value must contain 16 numbers');
128
+ }
129
+ }
130
+ /**
131
+ * Resolves a deterministic packed uniform buffer layout from a uniform map.
132
+ *
133
+ * @param uniforms - Input uniform definitions.
134
+ * @returns Sorted layout with byte offsets and final buffer byte length.
135
+ */
136
+ export function resolveUniformLayout(uniforms) {
137
+ const names = Object.keys(uniforms).sort();
138
+ let offset = 0;
139
+ const entries = [];
140
+ const byName = {};
141
+ for (const name of names) {
142
+ assertUniformName(name);
143
+ const type = inferUniformType(uniforms[name]);
144
+ const { alignment, size } = getTypeLayout(type);
145
+ offset = roundUp(offset, alignment);
146
+ const entry = {
147
+ name,
148
+ type,
149
+ offset,
150
+ size
151
+ };
152
+ entries.push(entry);
153
+ byName[name] = entry;
154
+ offset += size;
155
+ }
156
+ const byteLength = Math.max(16, roundUp(offset, 16));
157
+ return { entries, byName, byteLength };
158
+ }
159
+ /**
160
+ * Writes one validated uniform value directly into the output float buffer.
161
+ */
162
+ function writeUniformValue(type, value, data, base) {
163
+ const input = isTypedUniformValue(value) ? value.value : value;
164
+ assertUniformValueForType(type, value);
165
+ if (type === 'f32') {
166
+ data[base] = input;
167
+ return;
168
+ }
169
+ if (type === 'mat4x4f') {
170
+ const matrix = input;
171
+ if (matrix instanceof Float32Array) {
172
+ for (let index = 0; index < 16; index += 1) {
173
+ data[base + index] = matrix[index] ?? 0;
174
+ }
175
+ return;
176
+ }
177
+ for (let index = 0; index < 16; index += 1) {
178
+ data[base + index] = matrix[index] ?? 0;
179
+ }
180
+ return;
181
+ }
182
+ const tuple = input;
183
+ const length = type === 'vec2f' ? 2 : type === 'vec3f' ? 3 : 4;
184
+ for (let index = 0; index < length; index += 1) {
185
+ data[base + index] = tuple[index] ?? 0;
186
+ }
187
+ }
188
+ /**
189
+ * Packs uniforms into a newly allocated `Float32Array`.
190
+ *
191
+ * @param uniforms - Uniform values to pack.
192
+ * @param layout - Target layout definition.
193
+ * @returns Packed float buffer sized to `layout.byteLength`.
194
+ */
195
+ export function packUniforms(uniforms, layout) {
196
+ const data = new Float32Array(layout.byteLength / 4);
197
+ packUniformsInto(uniforms, layout, data);
198
+ return data;
199
+ }
200
+ /**
201
+ * Packs uniforms into an existing output buffer and zeroes missing values.
202
+ *
203
+ * @param uniforms - Uniform values to pack.
204
+ * @param layout - Target layout metadata.
205
+ * @param data - Destination float buffer.
206
+ * @throws {Error} When `data` size does not match the required layout size.
207
+ */
208
+ export function packUniformsInto(uniforms, layout, data) {
209
+ const requiredLength = layout.byteLength / 4;
210
+ if (data.length !== requiredLength) {
211
+ throw new Error(`Uniform output buffer size mismatch. Expected ${requiredLength}, got ${data.length}`);
212
+ }
213
+ data.fill(0);
214
+ for (const entry of layout.entries) {
215
+ const raw = uniforms[entry.name];
216
+ if (raw === undefined) {
217
+ continue;
218
+ }
219
+ const base = entry.offset / 4;
220
+ writeUniformValue(entry.type, raw, data, base);
221
+ }
222
+ }
@@ -0,0 +1,31 @@
1
+ import { type Readable } from 'svelte/store';
2
+ /**
3
+ * Readable store with synchronous access to the latest value.
4
+ */
5
+ export interface CurrentReadable<T> extends Readable<T> {
6
+ /**
7
+ * Latest store value.
8
+ */
9
+ readonly current: T;
10
+ }
11
+ /**
12
+ * Writable extension of {@link CurrentReadable}.
13
+ */
14
+ export interface CurrentWritable<T> extends CurrentReadable<T> {
15
+ /**
16
+ * Sets next value.
17
+ */
18
+ set: (value: T) => void;
19
+ /**
20
+ * Updates value based on previous value.
21
+ */
22
+ update: (updater: (value: T) => T) => void;
23
+ }
24
+ /**
25
+ * Creates a writable store that exposes the current value without subscription.
26
+ *
27
+ * @param initialValue - Initial store value.
28
+ * @param onChange - Optional side-effect callback invoked on every `set`.
29
+ * @returns Current-aware writable store.
30
+ */
31
+ export declare function currentWritable<T>(initialValue: T, onChange?: (value: T) => void): CurrentWritable<T>;
@@ -0,0 +1,27 @@
1
+ import { writable } from 'svelte/store';
2
+ /**
3
+ * Creates a writable store that exposes the current value without subscription.
4
+ *
5
+ * @param initialValue - Initial store value.
6
+ * @param onChange - Optional side-effect callback invoked on every `set`.
7
+ * @returns Current-aware writable store.
8
+ */
9
+ export function currentWritable(initialValue, onChange) {
10
+ let current = initialValue;
11
+ const store = writable(initialValue);
12
+ const set = (value) => {
13
+ current = value;
14
+ store.set(value);
15
+ onChange?.(value);
16
+ };
17
+ return {
18
+ get current() {
19
+ return current;
20
+ },
21
+ subscribe: store.subscribe,
22
+ set,
23
+ update(updater) {
24
+ set(updater(current));
25
+ }
26
+ };
27
+ }
@@ -0,0 +1,287 @@
1
+ import { type Readable } from 'svelte/store';
2
+ import type { FrameInvalidationToken, FrameState, RenderMode } from './core/types';
3
+ /**
4
+ * Per-frame callback executed by the frame scheduler.
5
+ */
6
+ export type FrameCallback = (state: FrameState) => void;
7
+ /**
8
+ * Stable key type used to identify frame tasks and stages.
9
+ */
10
+ export type FrameKey = string | symbol;
11
+ /**
12
+ * Public metadata describing a registered frame task.
13
+ */
14
+ export interface FrameTask {
15
+ key: FrameKey;
16
+ stage: FrameKey;
17
+ }
18
+ /**
19
+ * Public metadata describing a frame stage.
20
+ */
21
+ export interface FrameStage {
22
+ key: FrameKey;
23
+ }
24
+ /**
25
+ * Stage callback allowing custom orchestration around task execution.
26
+ */
27
+ export type FrameStageCallback = (state: FrameState, runTasks: () => void) => void;
28
+ /**
29
+ * Options controlling task registration and scheduling behavior.
30
+ */
31
+ export interface UseFrameOptions {
32
+ /**
33
+ * Whether task starts in active state.
34
+ *
35
+ * @default true
36
+ */
37
+ autoStart?: boolean;
38
+ /**
39
+ * Whether task execution invalidates frame automatically.
40
+ *
41
+ * @default true
42
+ */
43
+ autoInvalidate?: boolean;
44
+ /**
45
+ * Explicit task invalidation policy.
46
+ */
47
+ invalidation?: FrameTaskInvalidation;
48
+ /**
49
+ * Stage to register task in.
50
+ *
51
+ * If omitted, main stage is used unless inferred from task dependencies.
52
+ */
53
+ stage?: FrameKey | FrameStage;
54
+ /**
55
+ * Task dependencies that should run after this task.
56
+ */
57
+ before?: (FrameKey | FrameTask) | (FrameKey | FrameTask)[];
58
+ /**
59
+ * Task dependencies that should run before this task.
60
+ */
61
+ after?: (FrameKey | FrameTask) | (FrameKey | FrameTask)[];
62
+ /**
63
+ * Dynamic predicate controlling whether the task is currently active.
64
+ */
65
+ running?: () => boolean;
66
+ }
67
+ /**
68
+ * Invalidation token value or resolver.
69
+ */
70
+ export type FrameTaskInvalidationToken = FrameInvalidationToken | (() => FrameInvalidationToken | null | undefined);
71
+ /**
72
+ * Explicit task invalidation policy.
73
+ */
74
+ export type FrameTaskInvalidation = 'never' | 'always' | {
75
+ mode?: 'never' | 'always';
76
+ token?: FrameTaskInvalidationToken;
77
+ } | {
78
+ mode: 'on-change';
79
+ token: FrameTaskInvalidationToken;
80
+ };
81
+ /**
82
+ * Handle returned by `useFrame` registration.
83
+ */
84
+ export interface UseFrameResult {
85
+ /**
86
+ * Registered task metadata.
87
+ */
88
+ task: FrameTask;
89
+ /**
90
+ * Starts task execution.
91
+ */
92
+ start: () => void;
93
+ /**
94
+ * Stops task execution.
95
+ */
96
+ stop: () => void;
97
+ /**
98
+ * Readable flag representing effective running state.
99
+ */
100
+ started: Readable<boolean>;
101
+ }
102
+ /**
103
+ * Snapshot of the resolved stage/task execution order.
104
+ */
105
+ export interface FrameScheduleSnapshot {
106
+ stages: Array<{
107
+ key: string;
108
+ tasks: string[];
109
+ }>;
110
+ }
111
+ /**
112
+ * Optional scheduler diagnostics payload captured for the last run.
113
+ */
114
+ export interface FrameRunTimings {
115
+ total: number;
116
+ stages: Record<string, {
117
+ duration: number;
118
+ tasks: Record<string, number>;
119
+ }>;
120
+ }
121
+ /**
122
+ * Aggregated timing statistics for stage/task profiling.
123
+ */
124
+ export interface FrameTimingStats {
125
+ last: number;
126
+ avg: number;
127
+ min: number;
128
+ max: number;
129
+ count: number;
130
+ }
131
+ /**
132
+ * Profiling snapshot aggregated from the configured history window.
133
+ */
134
+ export interface FrameProfilingSnapshot {
135
+ window: number;
136
+ frameCount: number;
137
+ lastFrame: FrameRunTimings | null;
138
+ total: FrameTimingStats;
139
+ stages: Record<string, {
140
+ timings: FrameTimingStats;
141
+ tasks: Record<string, FrameTimingStats>;
142
+ }>;
143
+ }
144
+ /**
145
+ * Internal registration payload including unsubscribe callback.
146
+ */
147
+ interface RegisteredFrameTask extends UseFrameResult {
148
+ unsubscribe: () => void;
149
+ }
150
+ /**
151
+ * Runtime registry that stores frame tasks/stages and drives render scheduling.
152
+ */
153
+ export interface FrameRegistry {
154
+ /**
155
+ * Registers a frame callback in the scheduler.
156
+ */
157
+ register: (keyOrCallback: FrameKey | FrameCallback, callbackOrOptions?: FrameCallback | UseFrameOptions, maybeOptions?: UseFrameOptions) => RegisteredFrameTask;
158
+ /**
159
+ * Executes one scheduler run.
160
+ */
161
+ run: (state: FrameState) => void;
162
+ /**
163
+ * Marks frame as invalidated for `on-demand` mode.
164
+ */
165
+ invalidate: (token?: FrameInvalidationToken) => void;
166
+ /**
167
+ * Requests a single render in `manual` mode.
168
+ */
169
+ advance: () => void;
170
+ /**
171
+ * Returns whether renderer should submit a frame now.
172
+ */
173
+ shouldRender: () => boolean;
174
+ /**
175
+ * Resets one-frame invalidation/advance flags.
176
+ */
177
+ endFrame: () => void;
178
+ /**
179
+ * Sets render scheduling mode.
180
+ */
181
+ setRenderMode: (mode: RenderMode) => void;
182
+ /**
183
+ * Enables or disables automatic rendering entirely.
184
+ */
185
+ setAutoRender: (enabled: boolean) => void;
186
+ /**
187
+ * Sets maximum allowed delta passed to frame tasks.
188
+ */
189
+ setMaxDelta: (value: number) => void;
190
+ /**
191
+ * Enables/disables frame profiling.
192
+ */
193
+ setProfilingEnabled: (enabled: boolean) => void;
194
+ /**
195
+ * Sets profiling history window (in frames).
196
+ */
197
+ setProfilingWindow: (window: number) => void;
198
+ /**
199
+ * Clears collected profiling samples.
200
+ */
201
+ resetProfiling: () => void;
202
+ /**
203
+ * Enables/disables diagnostics capture.
204
+ */
205
+ setDiagnosticsEnabled: (enabled: boolean) => void;
206
+ /**
207
+ * Returns current render mode.
208
+ */
209
+ getRenderMode: () => RenderMode;
210
+ /**
211
+ * Returns whether automatic rendering is enabled.
212
+ */
213
+ getAutoRender: () => boolean;
214
+ /**
215
+ * Returns current max delta clamp.
216
+ */
217
+ getMaxDelta: () => number;
218
+ /**
219
+ * Returns profiling toggle state.
220
+ */
221
+ getProfilingEnabled: () => boolean;
222
+ /**
223
+ * Returns active profiling history window (in frames).
224
+ */
225
+ getProfilingWindow: () => number;
226
+ /**
227
+ * Returns aggregated profiling snapshot.
228
+ */
229
+ getProfilingSnapshot: () => FrameProfilingSnapshot | null;
230
+ /**
231
+ * Returns diagnostics toggle state.
232
+ */
233
+ getDiagnosticsEnabled: () => boolean;
234
+ /**
235
+ * Returns last run timings snapshot when diagnostics are enabled.
236
+ */
237
+ getLastRunTimings: () => FrameRunTimings | null;
238
+ /**
239
+ * Returns dependency-sorted schedule snapshot.
240
+ */
241
+ getSchedule: () => FrameScheduleSnapshot;
242
+ /**
243
+ * Creates or updates a stage.
244
+ */
245
+ createStage: (key: FrameKey, options?: {
246
+ before?: (FrameKey | FrameStage) | (FrameKey | FrameStage)[];
247
+ after?: (FrameKey | FrameStage) | (FrameKey | FrameStage)[];
248
+ callback?: FrameStageCallback | null;
249
+ }) => FrameStage;
250
+ /**
251
+ * Reads stage metadata by key.
252
+ */
253
+ getStage: (key: FrameKey) => FrameStage | undefined;
254
+ /**
255
+ * Removes all tasks from all stages.
256
+ */
257
+ clear: () => void;
258
+ }
259
+ /**
260
+ * Creates a frame registry used by `FragCanvas` and `useFrame`.
261
+ *
262
+ * @param options - Initial scheduler options.
263
+ * @returns Mutable frame registry instance.
264
+ */
265
+ export declare function createFrameRegistry(options?: {
266
+ renderMode?: RenderMode;
267
+ autoRender?: boolean;
268
+ maxDelta?: number;
269
+ profilingEnabled?: boolean;
270
+ profilingWindow?: number;
271
+ diagnosticsEnabled?: boolean;
272
+ }): FrameRegistry;
273
+ /**
274
+ * Provides a frame registry through Svelte context.
275
+ *
276
+ * @param registry - Registry to provide.
277
+ */
278
+ export declare function provideFrameRegistry(registry: FrameRegistry): void;
279
+ /**
280
+ * Registers a frame callback using an auto-generated task key.
281
+ */
282
+ export declare function useFrame(callback: FrameCallback, options?: UseFrameOptions): UseFrameResult;
283
+ /**
284
+ * Registers a frame callback with an explicit task key.
285
+ */
286
+ export declare function useFrame(key: FrameKey, callback: FrameCallback, options?: UseFrameOptions): UseFrameResult;
287
+ export {};