@motion-core/motion-gpu 0.1.0 → 0.3.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 (70) hide show
  1. package/README.md +37 -11
  2. package/dist/advanced.d.ts +3 -11
  3. package/dist/advanced.js +3 -6
  4. package/dist/core/advanced.d.ts +6 -0
  5. package/dist/core/advanced.js +5 -0
  6. package/dist/core/current-value.d.ts +23 -0
  7. package/dist/core/current-value.js +36 -0
  8. package/dist/core/error-diagnostics.d.ts +15 -1
  9. package/dist/core/error-diagnostics.js +41 -1
  10. package/dist/core/error-report.d.ts +37 -0
  11. package/dist/core/error-report.js +62 -3
  12. package/dist/{frame-context.d.ts → core/frame-registry.d.ts} +3 -17
  13. package/dist/{frame-context.js → core/frame-registry.js} +2 -37
  14. package/dist/core/index.d.ts +19 -0
  15. package/dist/core/index.js +12 -0
  16. package/dist/core/material-preprocess.d.ts +1 -1
  17. package/dist/core/material-preprocess.js +1 -1
  18. package/dist/core/material.d.ts +4 -4
  19. package/dist/core/material.js +3 -3
  20. package/dist/core/recompile-policy.d.ts +1 -1
  21. package/dist/core/render-graph.d.ts +1 -1
  22. package/dist/core/render-targets.d.ts +1 -1
  23. package/dist/core/render-targets.js +1 -1
  24. package/dist/core/renderer.d.ts +11 -1
  25. package/dist/core/renderer.js +72 -10
  26. package/dist/core/runtime-loop.d.ts +34 -0
  27. package/dist/core/runtime-loop.js +365 -0
  28. package/dist/{advanced-scheduler.d.ts → core/scheduler-helpers.d.ts} +6 -2
  29. package/dist/core/shader.d.ts +2 -2
  30. package/dist/core/shader.js +1 -1
  31. package/dist/core/texture-loader.d.ts +1 -1
  32. package/dist/core/textures.d.ts +1 -1
  33. package/dist/core/textures.js +1 -1
  34. package/dist/core/types.d.ts +4 -0
  35. package/dist/core/uniforms.d.ts +1 -1
  36. package/dist/index.d.ts +3 -14
  37. package/dist/index.js +3 -8
  38. package/dist/passes/BlitPass.d.ts +6 -27
  39. package/dist/passes/BlitPass.js +10 -121
  40. package/dist/passes/CopyPass.d.ts +1 -1
  41. package/dist/passes/CopyPass.js +1 -1
  42. package/dist/passes/FullscreenPass.d.ts +37 -0
  43. package/dist/passes/FullscreenPass.js +131 -0
  44. package/dist/passes/ShaderPass.d.ts +6 -26
  45. package/dist/passes/ShaderPass.js +10 -121
  46. package/dist/passes/index.d.ts +3 -3
  47. package/dist/passes/index.js +3 -3
  48. package/dist/svelte/FragCanvas.svelte +263 -0
  49. package/dist/{FragCanvas.svelte.d.ts → svelte/FragCanvas.svelte.d.ts} +5 -3
  50. package/dist/{MotionGPUErrorOverlay.svelte → svelte/MotionGPUErrorOverlay.svelte} +11 -20
  51. package/dist/{MotionGPUErrorOverlay.svelte.d.ts → svelte/MotionGPUErrorOverlay.svelte.d.ts} +1 -1
  52. package/dist/svelte/advanced.d.ts +11 -0
  53. package/dist/svelte/advanced.js +6 -0
  54. package/dist/svelte/frame-context.d.ts +14 -0
  55. package/dist/svelte/frame-context.js +32 -0
  56. package/dist/svelte/index.d.ts +15 -0
  57. package/dist/svelte/index.js +9 -0
  58. package/dist/{motiongpu-context.d.ts → svelte/motiongpu-context.d.ts} +5 -7
  59. package/dist/{use-motiongpu-user-context.d.ts → svelte/use-motiongpu-user-context.d.ts} +2 -2
  60. package/dist/{use-motiongpu-user-context.js → svelte/use-motiongpu-user-context.js} +1 -1
  61. package/dist/{use-texture.d.ts → svelte/use-texture.d.ts} +7 -2
  62. package/dist/{use-texture.js → svelte/use-texture.js} +9 -3
  63. package/package.json +25 -5
  64. package/dist/FragCanvas.svelte +0 -511
  65. package/dist/current-writable.d.ts +0 -31
  66. package/dist/current-writable.js +0 -27
  67. /package/dist/{advanced-scheduler.js → core/scheduler-helpers.js} +0 -0
  68. /package/dist/{Portal.svelte → svelte/Portal.svelte} +0 -0
  69. /package/dist/{Portal.svelte.d.ts → svelte/Portal.svelte.d.ts} +0 -0
  70. /package/dist/{motiongpu-context.js → svelte/motiongpu-context.js} +0 -0
package/README.md CHANGED
@@ -12,8 +12,8 @@
12
12
 
13
13
  **A tiny WebGPU runtime for writing Shadertoy-style fullscreen shaders in pure WGSL.**
14
14
 
15
- `@motion-core/motion-gpu` is a Svelte 5 package for building fullscreen shader pipelines using WebGPU and WGSL.
16
- It provides a minimal runtime, scheduler, and render graph designed specifically for fragment-driven GPU programs.
15
+ `@motion-core/motion-gpu` ships a framework-agnostic core plus a Svelte 5 adapter for building fullscreen shader pipelines using WebGPU and WGSL.
16
+ It provides a minimal runtime loop, scheduler, and render graph designed specifically for fragment-driven GPU programs.
17
17
 
18
18
  Unlike general-purpose 3D engines, Motion GPU focuses on a very narrow problem: **running fullscreen fragment shaders and multi-pass GPU pipelines**.
19
19
 
@@ -87,9 +87,9 @@ Motion GPU follows a simple three-step flow:
87
87
 
88
88
  # Entrypoints
89
89
 
90
- ## Root (`@motion-core/motion-gpu`)
90
+ ## Svelte adapter
91
91
 
92
- Primary runtime API:
92
+ `@motion-core/motion-gpu/svelte` exposes the runtime API for Svelte:
93
93
 
94
94
  - `FragCanvas`
95
95
  - `defineMaterial`
@@ -110,9 +110,7 @@ Also exports runtime/core types:
110
110
 
111
111
  ---
112
112
 
113
- ## Advanced (`@motion-core/motion-gpu/advanced`)
114
-
115
- Re-exports everything from root, plus:
113
+ `@motion-core/motion-gpu/svelte/advanced` re-exports everything above, plus:
116
114
 
117
115
  - `useMotionGPUUserContext`
118
116
  - `setMotionGPUUserContext`
@@ -121,9 +119,31 @@ Re-exports everything from root, plus:
121
119
 
122
120
  ---
123
121
 
122
+ ## Framework-agnostic core
123
+
124
+ `@motion-core/motion-gpu` (and explicit alias `@motion-core/motion-gpu/core`) exposes adapter-building primitives:
125
+
126
+ - `defineMaterial`
127
+ - `resolveMaterial`
128
+ - `createCurrentWritable`
129
+ - `createFrameRegistry`
130
+ - `createMotionGPURuntimeLoop`
131
+ - `loadTexturesFromUrls`
132
+ - `toMotionGPUErrorReport`
133
+ - `ShaderPass`
134
+ - `BlitPass`
135
+ - `CopyPass`
136
+
137
+ `@motion-core/motion-gpu/advanced` (and explicit alias `@motion-core/motion-gpu/core/advanced`) re-exports core plus:
138
+
139
+ - `applySchedulerPreset`
140
+ - `captureSchedulerDebugSnapshot`
141
+
142
+ ---
143
+
124
144
  # Requirements
125
145
 
126
- - Svelte 5 (`peerDependency: svelte ^5`)
146
+ - Svelte 5 is required only for the Svelte adapter entrypoints (`/svelte`, `/svelte/advanced`)
127
147
  - A browser/runtime with WebGPU support
128
148
  - Secure context (`https://` or `localhost`)
129
149
 
@@ -137,6 +157,12 @@ npm i @motion-core/motion-gpu
137
157
 
138
158
  ---
139
159
 
160
+ # AI Documentation
161
+
162
+ MotionGPU documentation is also available for AI tools via [Context7](https://context7.com/motion-core/motion-gpu).
163
+
164
+ ---
165
+
140
166
  # Quick Start
141
167
 
142
168
  ## 1. Create a material and render it
@@ -144,7 +170,7 @@ npm i @motion-core/motion-gpu
144
170
  ```svelte
145
171
  <!-- App.svelte -->
146
172
  <script lang="ts">
147
- import { FragCanvas, defineMaterial } from '@motion-core/motion-gpu';
173
+ import { FragCanvas, defineMaterial } from '@motion-core/motion-gpu/svelte';
148
174
 
149
175
  const material = defineMaterial({
150
176
  fragment: `
@@ -167,7 +193,7 @@ fn frag(uv: vec2f) -> vec4f {
167
193
  ```svelte
168
194
  <!-- App.svelte -->
169
195
  <script lang="ts">
170
- import { FragCanvas, defineMaterial } from '@motion-core/motion-gpu';
196
+ import { FragCanvas, defineMaterial } from '@motion-core/motion-gpu/svelte';
171
197
  import Runtime from './Runtime.svelte';
172
198
 
173
199
  const material = defineMaterial({
@@ -191,7 +217,7 @@ fn frag(uv: vec2f) -> vec4f {
191
217
  ```svelte
192
218
  <!-- Runtime.svelte -->
193
219
  <script lang="ts">
194
- import { useFrame } from '@motion-core/motion-gpu';
220
+ import { useFrame } from '@motion-core/motion-gpu/svelte';
195
221
 
196
222
  useFrame((state) => {
197
223
  state.setUniform('uTime', state.time);
@@ -1,14 +1,6 @@
1
1
  /**
2
- * Advanced MotionGPU entrypoint.
2
+ * Root advanced package entrypoint.
3
3
  *
4
- * Includes power-user hooks and diagnostics-oriented types that are not part of the
5
- * minimal root API surface.
4
+ * Framework-agnostic advanced core entrypoint.
6
5
  */
7
- export * from './index';
8
- export { applySchedulerPreset, captureSchedulerDebugSnapshot } from './advanced-scheduler';
9
- export { setMotionGPUUserContext, useMotionGPUUserContext } from './use-motiongpu-user-context';
10
- export type { ApplySchedulerPresetOptions, SchedulerDebugSnapshot, SchedulerPreset, SchedulerPresetConfig } from './advanced-scheduler';
11
- export type { MotionGPUUserContext, MotionGPUUserNamespace } from './motiongpu-context';
12
- export type { FrameProfilingSnapshot, FrameKey, FrameTaskInvalidation, FrameTaskInvalidationToken, FrameRunTimings, FrameScheduleSnapshot, FrameStage, FrameStageCallback, FrameTimingStats, FrameTask } from './frame-context';
13
- export type { SetMotionGPUUserContextOptions } from './use-motiongpu-user-context';
14
- export type { RenderPassContext, RenderTarget, UniformLayout, UniformLayoutEntry } from './core/types';
6
+ export * from './core/advanced.js';
package/dist/advanced.js CHANGED
@@ -1,9 +1,6 @@
1
1
  /**
2
- * Advanced MotionGPU entrypoint.
2
+ * Root advanced package entrypoint.
3
3
  *
4
- * Includes power-user hooks and diagnostics-oriented types that are not part of the
5
- * minimal root API surface.
4
+ * Framework-agnostic advanced core entrypoint.
6
5
  */
7
- export * from './index';
8
- export { applySchedulerPreset, captureSchedulerDebugSnapshot } from './advanced-scheduler';
9
- export { setMotionGPUUserContext, useMotionGPUUserContext } from './use-motiongpu-user-context';
6
+ export * from './core/advanced.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Framework-agnostic advanced MotionGPU core entrypoint.
3
+ */
4
+ export * from './index.js';
5
+ export { applySchedulerPreset, captureSchedulerDebugSnapshot } from './scheduler-helpers.js';
6
+ export type { ApplySchedulerPresetOptions, MotionGPUScheduler, SchedulerDebugSnapshot, SchedulerPreset, SchedulerPresetConfig } from './scheduler-helpers.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Framework-agnostic advanced MotionGPU core entrypoint.
3
+ */
4
+ export * from './index.js';
5
+ export { applySchedulerPreset, captureSchedulerDebugSnapshot } from './scheduler-helpers.js';
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Minimal subscribe contract used by MotionGPU core.
3
+ */
4
+ export interface Subscribable<T> {
5
+ subscribe: (run: (value: T) => void) => () => void;
6
+ }
7
+ /**
8
+ * Readable value with synchronous access to the latest value.
9
+ */
10
+ export interface CurrentReadable<T> extends Subscribable<T> {
11
+ readonly current: T;
12
+ }
13
+ /**
14
+ * Writable extension of {@link CurrentReadable}.
15
+ */
16
+ export interface CurrentWritable<T> extends CurrentReadable<T> {
17
+ set: (value: T) => void;
18
+ update: (updater: (value: T) => T) => void;
19
+ }
20
+ /**
21
+ * Creates a writable value with immediate subscription semantics.
22
+ */
23
+ export declare function createCurrentWritable<T>(initialValue: T, onChange?: (value: T) => void): CurrentWritable<T>;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Creates a writable value with immediate subscription semantics.
3
+ */
4
+ export function createCurrentWritable(initialValue, onChange) {
5
+ let current = initialValue;
6
+ const subscribers = new Set();
7
+ const notify = (value) => {
8
+ for (const run of subscribers) {
9
+ run(value);
10
+ }
11
+ };
12
+ const set = (value) => {
13
+ if (Object.is(current, value)) {
14
+ return;
15
+ }
16
+ current = value;
17
+ notify(value);
18
+ onChange?.(value);
19
+ };
20
+ return {
21
+ get current() {
22
+ return current;
23
+ },
24
+ subscribe(run) {
25
+ subscribers.add(run);
26
+ run(current);
27
+ return () => {
28
+ subscribers.delete(run);
29
+ };
30
+ },
31
+ set,
32
+ update(updater) {
33
+ set(updater(current));
34
+ }
35
+ };
36
+ }
@@ -1,4 +1,4 @@
1
- import type { MaterialSourceLocation } from './material-preprocess';
1
+ import type { MaterialSourceLocation } from './material-preprocess.js';
2
2
  /**
3
3
  * Source metadata for material declaration callsite.
4
4
  */
@@ -19,6 +19,19 @@ export interface ShaderCompilationDiagnostic {
19
19
  lineLength?: number;
20
20
  sourceLocation: MaterialSourceLocation | null;
21
21
  }
22
+ /**
23
+ * Runtime context snapshot captured for shader compilation diagnostics.
24
+ */
25
+ export interface ShaderCompilationRuntimeContext {
26
+ materialSignature?: string;
27
+ passGraph?: {
28
+ passCount: number;
29
+ enabledPassCount: number;
30
+ inputs: string[];
31
+ outputs: string[];
32
+ };
33
+ activeRenderTargets: string[];
34
+ }
22
35
  /**
23
36
  * Structured payload attached to WGSL compilation errors.
24
37
  */
@@ -29,6 +42,7 @@ export interface ShaderCompilationDiagnosticsPayload {
29
42
  includeSources: Record<string, string>;
30
43
  defineBlockSource?: string;
31
44
  materialSource: MaterialSourceMetadata | null;
45
+ runtimeContext?: ShaderCompilationRuntimeContext;
32
46
  }
33
47
  /**
34
48
  * Attaches structured diagnostics payload to an Error.
@@ -56,6 +56,39 @@ function isShaderCompilationDiagnostic(value) {
56
56
  }
57
57
  return true;
58
58
  }
59
+ function isStringArray(value) {
60
+ return Array.isArray(value) && value.every((entry) => typeof entry === 'string');
61
+ }
62
+ function isShaderCompilationRuntimeContext(value) {
63
+ if (value === null || typeof value !== 'object') {
64
+ return false;
65
+ }
66
+ const record = value;
67
+ if (record.materialSignature !== undefined && typeof record.materialSignature !== 'string') {
68
+ return false;
69
+ }
70
+ if (!isStringArray(record.activeRenderTargets)) {
71
+ return false;
72
+ }
73
+ const passGraph = record.passGraph;
74
+ if (passGraph === undefined) {
75
+ return true;
76
+ }
77
+ if (passGraph === null || typeof passGraph !== 'object') {
78
+ return false;
79
+ }
80
+ const passGraphRecord = passGraph;
81
+ if (typeof passGraphRecord.passCount !== 'number') {
82
+ return false;
83
+ }
84
+ if (typeof passGraphRecord.enabledPassCount !== 'number') {
85
+ return false;
86
+ }
87
+ if (!isStringArray(passGraphRecord.inputs) || !isStringArray(passGraphRecord.outputs)) {
88
+ return false;
89
+ }
90
+ return true;
91
+ }
59
92
  /**
60
93
  * Attaches structured diagnostics payload to an Error.
61
94
  */
@@ -98,6 +131,10 @@ export function getShaderCompilationDiagnostics(error) {
98
131
  if (record.materialSource !== null && !isMaterialSourceMetadata(record.materialSource)) {
99
132
  return null;
100
133
  }
134
+ if (record.runtimeContext !== undefined &&
135
+ !isShaderCompilationRuntimeContext(record.runtimeContext)) {
136
+ return null;
137
+ }
101
138
  return {
102
139
  kind: 'shader-compilation',
103
140
  diagnostics: record.diagnostics,
@@ -106,6 +143,9 @@ export function getShaderCompilationDiagnostics(error) {
106
143
  ...(record.defineBlockSource !== undefined
107
144
  ? { defineBlockSource: record.defineBlockSource }
108
145
  : {}),
109
- materialSource: (record.materialSource ?? null)
146
+ materialSource: (record.materialSource ?? null),
147
+ ...(record.runtimeContext !== undefined
148
+ ? { runtimeContext: record.runtimeContext }
149
+ : {})
110
150
  };
111
151
  }
@@ -2,6 +2,14 @@
2
2
  * Runtime phase in which an error occurred.
3
3
  */
4
4
  export type MotionGPUErrorPhase = 'initialization' | 'render';
5
+ /**
6
+ * Stable machine-readable error category code.
7
+ */
8
+ export type MotionGPUErrorCode = 'WEBGPU_UNAVAILABLE' | 'WEBGPU_ADAPTER_UNAVAILABLE' | 'WEBGPU_CONTEXT_UNAVAILABLE' | 'WGSL_COMPILATION_FAILED' | 'WEBGPU_DEVICE_LOST' | 'WEBGPU_UNCAPTURED_ERROR' | 'BIND_GROUP_MISMATCH' | 'TEXTURE_USAGE_INVALID' | 'TEXTURE_REQUEST_FAILED' | 'TEXTURE_DECODE_UNAVAILABLE' | 'TEXTURE_REQUEST_ABORTED' | 'MOTIONGPU_RUNTIME_ERROR';
9
+ /**
10
+ * Severity level for user-facing diagnostics.
11
+ */
12
+ export type MotionGPUErrorSeverity = 'error' | 'fatal';
5
13
  /**
6
14
  * One source-code line displayed in diagnostics snippet.
7
15
  */
@@ -20,10 +28,35 @@ export interface MotionGPUErrorSource {
20
28
  column?: number;
21
29
  snippet: MotionGPUErrorSourceLine[];
22
30
  }
31
+ /**
32
+ * Optional runtime context captured with diagnostics payload.
33
+ */
34
+ export interface MotionGPUErrorContext {
35
+ materialSignature?: string;
36
+ passGraph?: {
37
+ passCount: number;
38
+ enabledPassCount: number;
39
+ inputs: string[];
40
+ outputs: string[];
41
+ };
42
+ activeRenderTargets: string[];
43
+ }
23
44
  /**
24
45
  * Structured error payload used by UI diagnostics.
25
46
  */
26
47
  export interface MotionGPUErrorReport {
48
+ /**
49
+ * Stable machine-readable category code.
50
+ */
51
+ code: MotionGPUErrorCode;
52
+ /**
53
+ * Severity level used by diagnostics UIs and telemetry.
54
+ */
55
+ severity: MotionGPUErrorSeverity;
56
+ /**
57
+ * Whether runtime may recover without full renderer re-creation.
58
+ */
59
+ recoverable: boolean;
27
60
  /**
28
61
  * Short category title.
29
62
  */
@@ -56,6 +89,10 @@ export interface MotionGPUErrorReport {
56
89
  * Optional source context for shader-related diagnostics.
57
90
  */
58
91
  source: MotionGPUErrorSource | null;
92
+ /**
93
+ * Optional runtime context snapshot (material/pass graph/render targets).
94
+ */
95
+ context: MotionGPUErrorContext | null;
59
96
  }
60
97
  /**
61
98
  * Converts unknown errors to a consistent, display-ready error report.
@@ -1,5 +1,5 @@
1
- import { getShaderCompilationDiagnostics } from './error-diagnostics';
2
- import { formatShaderSourceLocation } from './shader';
1
+ import { getShaderCompilationDiagnostics } from './error-diagnostics.js';
2
+ import { formatShaderSourceLocation } from './shader.js';
3
3
  /**
4
4
  * Splits multi-line values into trimmed non-empty lines.
5
5
  */
@@ -98,53 +98,107 @@ function formatDiagnosticMessage(entry) {
98
98
  function classifyErrorMessage(message) {
99
99
  if (message.includes('WebGPU is not available in this browser')) {
100
100
  return {
101
+ code: 'WEBGPU_UNAVAILABLE',
102
+ severity: 'fatal',
103
+ recoverable: false,
101
104
  title: 'WebGPU unavailable',
102
105
  hint: 'Use a browser with WebGPU enabled (latest Chrome/Edge/Safari TP) and secure context.'
103
106
  };
104
107
  }
105
108
  if (message.includes('Unable to acquire WebGPU adapter')) {
106
109
  return {
110
+ code: 'WEBGPU_ADAPTER_UNAVAILABLE',
111
+ severity: 'fatal',
112
+ recoverable: false,
107
113
  title: 'WebGPU adapter unavailable',
108
114
  hint: 'GPU adapter request failed. Check browser permissions, flags and device support.'
109
115
  };
110
116
  }
111
117
  if (message.includes('Canvas does not support webgpu context')) {
112
118
  return {
119
+ code: 'WEBGPU_CONTEXT_UNAVAILABLE',
120
+ severity: 'error',
121
+ recoverable: true,
113
122
  title: 'Canvas cannot create WebGPU context',
114
123
  hint: 'Make sure this canvas is attached to DOM and not using an unsupported context option.'
115
124
  };
116
125
  }
117
126
  if (message.includes('WGSL compilation failed')) {
118
127
  return {
128
+ code: 'WGSL_COMPILATION_FAILED',
129
+ severity: 'error',
130
+ recoverable: true,
119
131
  title: 'WGSL compilation failed',
120
132
  hint: 'Check WGSL line numbers below and verify struct/binding/function signatures.'
121
133
  };
122
134
  }
123
135
  if (message.includes('WebGPU device lost') || message.includes('Device Lost')) {
124
136
  return {
137
+ code: 'WEBGPU_DEVICE_LOST',
138
+ severity: 'fatal',
139
+ recoverable: false,
125
140
  title: 'WebGPU device lost',
126
141
  hint: 'GPU device/context was lost. Recreate the renderer and check OS/GPU stability.'
127
142
  };
128
143
  }
129
144
  if (message.includes('WebGPU uncaptured error')) {
130
145
  return {
146
+ code: 'WEBGPU_UNCAPTURED_ERROR',
147
+ severity: 'error',
148
+ recoverable: true,
131
149
  title: 'WebGPU uncaptured error',
132
150
  hint: 'A GPU command failed asynchronously. Review details and validate resource/state usage.'
133
151
  };
134
152
  }
135
153
  if (message.includes('CreateBindGroup') || message.includes('bind group layout')) {
136
154
  return {
155
+ code: 'BIND_GROUP_MISMATCH',
156
+ severity: 'error',
157
+ recoverable: true,
137
158
  title: 'Bind group mismatch',
138
159
  hint: 'Bindings in shader and runtime resources are out of sync. Verify uniforms/textures layout.'
139
160
  };
140
161
  }
141
162
  if (message.includes('Destination texture needs to have CopyDst')) {
142
163
  return {
164
+ code: 'TEXTURE_USAGE_INVALID',
165
+ severity: 'error',
166
+ recoverable: true,
143
167
  title: 'Invalid texture usage flags',
144
168
  hint: 'Texture used as upload destination must include CopyDst (and often RenderAttachment).'
145
169
  };
146
170
  }
171
+ if (message.includes('Texture request failed')) {
172
+ return {
173
+ code: 'TEXTURE_REQUEST_FAILED',
174
+ severity: 'error',
175
+ recoverable: true,
176
+ title: 'Texture request failed',
177
+ hint: 'Verify texture URL, CORS policy and response status before retrying.'
178
+ };
179
+ }
180
+ if (message.includes('createImageBitmap is not available in this runtime')) {
181
+ return {
182
+ code: 'TEXTURE_DECODE_UNAVAILABLE',
183
+ severity: 'fatal',
184
+ recoverable: false,
185
+ title: 'Texture decode unavailable',
186
+ hint: 'Runtime lacks createImageBitmap support. Use a browser/runtime with image bitmap decoding.'
187
+ };
188
+ }
189
+ if (message.toLowerCase().includes('texture request was aborted')) {
190
+ return {
191
+ code: 'TEXTURE_REQUEST_ABORTED',
192
+ severity: 'error',
193
+ recoverable: true,
194
+ title: 'Texture request aborted',
195
+ hint: 'Texture load was cancelled. Retry the request when source inputs stabilize.'
196
+ };
197
+ }
147
198
  return {
199
+ code: 'MOTIONGPU_RUNTIME_ERROR',
200
+ severity: 'error',
201
+ recoverable: true,
148
202
  title: 'MotionGPU render error',
149
203
  hint: 'Review technical details below. If issue persists, isolate shader/uniform/texture changes.'
150
204
  };
@@ -167,6 +221,7 @@ export function toMotionGPUErrorReport(error, phase) {
167
221
  const defaultMessage = rawLines[0] ?? rawMessage;
168
222
  const defaultDetails = rawLines.slice(1);
169
223
  const source = buildSourceFromDiagnostics(error);
224
+ const context = shaderDiagnostics?.runtimeContext ?? null;
170
225
  const message = shaderDiagnostics && shaderDiagnostics.diagnostics[0]
171
226
  ? formatDiagnosticMessage(shaderDiagnostics.diagnostics[0])
172
227
  : defaultMessage;
@@ -178,6 +233,9 @@ export function toMotionGPUErrorReport(error, phase) {
178
233
  : [];
179
234
  const classification = classifyErrorMessage(rawMessage);
180
235
  return {
236
+ code: classification.code,
237
+ severity: classification.severity,
238
+ recoverable: classification.recoverable,
181
239
  title: classification.title,
182
240
  message,
183
241
  hint: classification.hint,
@@ -185,6 +243,7 @@ export function toMotionGPUErrorReport(error, phase) {
185
243
  stack,
186
244
  rawMessage,
187
245
  phase,
188
- source
246
+ source,
247
+ context
189
248
  };
190
249
  }
@@ -1,5 +1,5 @@
1
- import { type Readable } from 'svelte/store';
2
- import type { FrameInvalidationToken, FrameState, RenderMode } from './core/types';
1
+ import { type Subscribable } from './current-value.js';
2
+ import type { FrameInvalidationToken, FrameState, RenderMode } from './types.js';
3
3
  /**
4
4
  * Per-frame callback executed by the frame scheduler.
5
5
  */
@@ -97,7 +97,7 @@ export interface UseFrameResult {
97
97
  /**
98
98
  * Readable flag representing effective running state.
99
99
  */
100
- started: Readable<boolean>;
100
+ started: Subscribable<boolean>;
101
101
  }
102
102
  /**
103
103
  * Snapshot of the resolved stage/task execution order.
@@ -270,18 +270,4 @@ export declare function createFrameRegistry(options?: {
270
270
  profilingWindow?: number;
271
271
  diagnosticsEnabled?: boolean;
272
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
273
  export {};
@@ -1,9 +1,4 @@
1
- import { getContext, onDestroy, setContext } from 'svelte';
2
- import { writable } from 'svelte/store';
3
- /**
4
- * Svelte context key for the active frame registry.
5
- */
6
- const FRAME_CONTEXT_KEY = Symbol('motiongpu.frame-context');
1
+ import { createCurrentWritable } from './current-value.js';
7
2
  /**
8
3
  * Default stage key used when task stage is not explicitly specified.
9
4
  */
@@ -492,7 +487,7 @@ export function createFrameRegistry(options) {
492
487
  ? toStageKey(taskOptions.stage)
493
488
  : (inferredStage?.stage ?? MAIN_STAGE_KEY);
494
489
  const stage = ensureStage(stageKey);
495
- const startedWritable = writable(taskOptions.autoStart ?? true);
490
+ const startedWritable = createCurrentWritable(taskOptions.autoStart ?? true);
496
491
  const internalTask = {
497
492
  task: { key, stage: stage.key },
498
493
  callback,
@@ -699,33 +694,3 @@ export function createFrameRegistry(options) {
699
694
  }
700
695
  };
701
696
  }
702
- /**
703
- * Provides a frame registry through Svelte context.
704
- *
705
- * @param registry - Registry to provide.
706
- */
707
- export function provideFrameRegistry(registry) {
708
- setContext(FRAME_CONTEXT_KEY, registry);
709
- }
710
- /**
711
- * Registers a callback in the active frame registry and auto-unsubscribes on destroy.
712
- *
713
- * @returns Frame task handle for start/stop control.
714
- * @throws {Error} When used outside `<FragCanvas>`.
715
- */
716
- export function useFrame(keyOrCallback, callbackOrOptions, maybeOptions) {
717
- const registry = getContext(FRAME_CONTEXT_KEY);
718
- if (!registry) {
719
- throw new Error('useFrame must be used inside <FragCanvas>');
720
- }
721
- const registration = typeof keyOrCallback === 'function'
722
- ? registry.register(keyOrCallback, callbackOrOptions)
723
- : registry.register(keyOrCallback, callbackOrOptions, maybeOptions);
724
- onDestroy(registration.unsubscribe);
725
- return {
726
- task: registration.task,
727
- start: registration.start,
728
- stop: registration.stop,
729
- started: registration.started
730
- };
731
- }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Framework-agnostic MotionGPU core entrypoint.
3
+ *
4
+ * This surface is intended for building framework adapters (Svelte/React/Vue).
5
+ */
6
+ export { defineMaterial, resolveMaterial } from './material.js';
7
+ export { toMotionGPUErrorReport } from './error-report.js';
8
+ export { createCurrentWritable } from './current-value.js';
9
+ export { createFrameRegistry } from './frame-registry.js';
10
+ export { createMotionGPURuntimeLoop } from './runtime-loop.js';
11
+ export { loadTexturesFromUrls } from './texture-loader.js';
12
+ export { BlitPass, CopyPass, ShaderPass } from '../passes/index.js';
13
+ export type { CurrentReadable, CurrentWritable, Subscribable } from './current-value.js';
14
+ export type { MotionGPUErrorCode, MotionGPUErrorContext, MotionGPUErrorPhase, MotionGPUErrorReport, MotionGPUErrorSeverity, MotionGPUErrorSource, MotionGPUErrorSourceLine } from './error-report.js';
15
+ export type { FrameCallback, FrameKey, FrameProfilingSnapshot, FrameRegistry, FrameRunTimings, FrameScheduleSnapshot, FrameStage, FrameStageCallback, FrameTask, FrameTaskInvalidation, FrameTaskInvalidationToken, FrameTimingStats, UseFrameOptions, UseFrameResult } from './frame-registry.js';
16
+ export type { FragMaterial, FragMaterialInput, MaterialDefineValue, MaterialDefines, MaterialIncludes, ResolvedMaterial, TypedMaterialDefineValue } from './material.js';
17
+ export type { MotionGPURuntimeLoop, MotionGPURuntimeLoopOptions } from './runtime-loop.js';
18
+ export type { LoadedTexture, TextureDecodeOptions, TextureLoadOptions } from './texture-loader.js';
19
+ export type { FrameInvalidationToken, FrameState, OutputColorSpace, RenderPass, RenderPassContext, RenderPassFlags, RenderPassInputSlot, RenderPassOutputSlot, RenderMode, RenderTarget, RenderTargetDefinition, RenderTargetDefinitionMap, TextureData, TextureDefinition, TextureDefinitionMap, TextureMap, TextureSource, TextureUpdateMode, TextureValue, TypedUniform, UniformLayout, UniformLayoutEntry, UniformMap, UniformMat4Value, UniformType, UniformValue } from './types.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Framework-agnostic MotionGPU core entrypoint.
3
+ *
4
+ * This surface is intended for building framework adapters (Svelte/React/Vue).
5
+ */
6
+ export { defineMaterial, resolveMaterial } from './material.js';
7
+ export { toMotionGPUErrorReport } from './error-report.js';
8
+ export { createCurrentWritable } from './current-value.js';
9
+ export { createFrameRegistry } from './frame-registry.js';
10
+ export { createMotionGPURuntimeLoop } from './runtime-loop.js';
11
+ export { loadTexturesFromUrls } from './texture-loader.js';
12
+ export { BlitPass, CopyPass, ShaderPass } from '../passes/index.js';
@@ -1,4 +1,4 @@
1
- import type { MaterialDefineValue, MaterialDefines, MaterialIncludes } from './material';
1
+ import type { MaterialDefineValue, MaterialDefines, MaterialIncludes } from './material.js';
2
2
  /**
3
3
  * Source location metadata for one generated fragment line.
4
4
  */
@@ -1,4 +1,4 @@
1
- import { assertUniformName } from './uniforms';
1
+ import { assertUniformName } from './uniforms.js';
2
2
  const INCLUDE_DIRECTIVE_PATTERN = /^\s*#include\s+<([A-Za-z_][A-Za-z0-9_]*)>\s*$/;
3
3
  function normalizeTypedDefine(name, define) {
4
4
  const value = define.value;
@@ -1,7 +1,7 @@
1
- import type { MaterialSourceMetadata } from './error-diagnostics';
2
- import { resolveUniformLayout } from './uniforms';
3
- import { type MaterialLineMap } from './material-preprocess';
4
- import type { TextureDefinitionMap, UniformMap } from './types';
1
+ import type { MaterialSourceMetadata } from './error-diagnostics.js';
2
+ import { resolveUniformLayout } from './uniforms.js';
3
+ import { type MaterialLineMap } from './material-preprocess.js';
4
+ import type { TextureDefinitionMap, UniformMap } from './types.js';
5
5
  /**
6
6
  * Typed compile-time define declaration.
7
7
  */