@motion-core/motion-gpu 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -1
- package/dist/core/current-value.js +3 -0
- package/dist/core/error-diagnostics.d.ts +14 -0
- package/dist/core/error-diagnostics.js +41 -1
- package/dist/core/error-report.d.ts +37 -0
- package/dist/core/error-report.js +60 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/material-preprocess.d.ts +5 -5
- package/dist/core/material-preprocess.js +1 -4
- package/dist/core/material.d.ts +32 -23
- package/dist/core/material.js +14 -7
- package/dist/core/renderer.d.ts +10 -0
- package/dist/core/renderer.js +66 -4
- package/dist/core/runtime-loop.d.ts +3 -0
- package/dist/core/runtime-loop.js +72 -1
- package/dist/core/types.d.ts +24 -10
- package/dist/passes/BlitPass.d.ts +6 -27
- package/dist/passes/BlitPass.js +10 -121
- package/dist/passes/FullscreenPass.d.ts +37 -0
- package/dist/passes/FullscreenPass.js +131 -0
- package/dist/passes/ShaderPass.d.ts +6 -26
- package/dist/passes/ShaderPass.js +10 -121
- package/dist/react/FragCanvas.d.ts +26 -0
- package/dist/react/FragCanvas.js +218 -0
- package/dist/react/FragCanvas.tsx +345 -0
- package/dist/react/MotionGPUErrorOverlay.d.ts +6 -0
- package/dist/react/MotionGPUErrorOverlay.js +52 -0
- package/dist/react/MotionGPUErrorOverlay.tsx +129 -0
- package/dist/react/Portal.d.ts +6 -0
- package/dist/react/Portal.js +24 -0
- package/dist/react/Portal.tsx +34 -0
- package/dist/react/advanced.d.ts +11 -0
- package/dist/react/advanced.js +6 -0
- package/dist/react/frame-context.d.ts +14 -0
- package/dist/react/frame-context.js +98 -0
- package/dist/react/index.d.ts +15 -0
- package/dist/react/index.js +9 -0
- package/dist/react/motiongpu-context.d.ts +73 -0
- package/dist/react/motiongpu-context.js +18 -0
- package/dist/react/use-motiongpu-user-context.d.ts +49 -0
- package/dist/react/use-motiongpu-user-context.js +94 -0
- package/dist/react/use-texture.d.ts +40 -0
- package/dist/react/use-texture.js +162 -0
- package/dist/svelte/FragCanvas.svelte +45 -16
- package/dist/svelte/FragCanvas.svelte.d.ts +2 -0
- package/dist/svelte/MotionGPUErrorOverlay.svelte +10 -19
- package/dist/svelte/Portal.svelte +6 -21
- package/dist/svelte/use-motiongpu-user-context.d.ts +9 -1
- package/dist/svelte/use-motiongpu-user-context.js +4 -1
- package/dist/svelte/use-texture.d.ts +11 -2
- package/dist/svelte/use-texture.js +13 -3
- package/package.json +28 -3
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
4
|
[](https://svelte.dev)
|
|
5
|
+
[](https://react.dev)
|
|
5
6
|
[](https://gpuweb.github.io/gpuweb/)
|
|
6
7
|
[](https://www.typescriptlang.org)
|
|
7
8
|
[](https://www.npmjs.com/package/@motion-core/motion-gpu)
|
|
@@ -12,7 +13,7 @@
|
|
|
12
13
|
|
|
13
14
|
**A tiny WebGPU runtime for writing Shadertoy-style fullscreen shaders in pure WGSL.**
|
|
14
15
|
|
|
15
|
-
`@motion-core/motion-gpu` ships a framework-agnostic core plus
|
|
16
|
+
`@motion-core/motion-gpu` ships a framework-agnostic core plus Svelte 5 and React adapters for building fullscreen shader pipelines using WebGPU and WGSL.
|
|
16
17
|
It provides a minimal runtime loop, scheduler, and render graph designed specifically for fragment-driven GPU programs.
|
|
17
18
|
|
|
18
19
|
Unlike general-purpose 3D engines, Motion GPU focuses on a very narrow problem: **running fullscreen fragment shaders and multi-pass GPU pipelines**.
|
|
@@ -119,6 +120,39 @@ Also exports runtime/core types:
|
|
|
119
120
|
|
|
120
121
|
---
|
|
121
122
|
|
|
123
|
+
## React adapter
|
|
124
|
+
|
|
125
|
+
`@motion-core/motion-gpu/react` exposes the runtime API for React:
|
|
126
|
+
|
|
127
|
+
- `FragCanvas`
|
|
128
|
+
- `defineMaterial`
|
|
129
|
+
- `useMotionGPU`
|
|
130
|
+
- `useFrame`
|
|
131
|
+
- `useTexture`
|
|
132
|
+
- `ShaderPass`
|
|
133
|
+
- `BlitPass`
|
|
134
|
+
- `CopyPass`
|
|
135
|
+
|
|
136
|
+
Also exports runtime/core types:
|
|
137
|
+
|
|
138
|
+
- uniforms
|
|
139
|
+
- textures
|
|
140
|
+
- render passes
|
|
141
|
+
- scheduler
|
|
142
|
+
- loader types
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
`@motion-core/motion-gpu/react/advanced` re-exports everything above, plus:
|
|
147
|
+
|
|
148
|
+
- `useMotionGPUUserContext`
|
|
149
|
+
- `useSetMotionGPUUserContext`
|
|
150
|
+
- `setMotionGPUUserContext`
|
|
151
|
+
- `applySchedulerPreset`
|
|
152
|
+
- `captureSchedulerDebugSnapshot`
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
122
156
|
## Framework-agnostic core
|
|
123
157
|
|
|
124
158
|
`@motion-core/motion-gpu` (and explicit alias `@motion-core/motion-gpu/core`) exposes adapter-building primitives:
|
|
@@ -144,6 +178,7 @@ Also exports runtime/core types:
|
|
|
144
178
|
# Requirements
|
|
145
179
|
|
|
146
180
|
- Svelte 5 is required only for the Svelte adapter entrypoints (`/svelte`, `/svelte/advanced`)
|
|
181
|
+
- React 18+ is required only for the React adapter entrypoints (`/react`, `/react/advanced`)
|
|
147
182
|
- A browser/runtime with WebGPU support
|
|
148
183
|
- Secure context (`https://` or `localhost`)
|
|
149
184
|
|
|
@@ -157,6 +192,12 @@ npm i @motion-core/motion-gpu
|
|
|
157
192
|
|
|
158
193
|
---
|
|
159
194
|
|
|
195
|
+
# AI Documentation
|
|
196
|
+
|
|
197
|
+
MotionGPU documentation is also available for AI tools via [Context7](https://context7.com/motion-core/motion-gpu).
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
160
201
|
# Quick Start
|
|
161
202
|
|
|
162
203
|
## 1. Create a material and render it
|
|
@@ -182,6 +223,30 @@ fn frag(uv: vec2f) -> vec4f {
|
|
|
182
223
|
|
|
183
224
|
---
|
|
184
225
|
|
|
226
|
+
### React equivalent
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
import { FragCanvas, defineMaterial } from '@motion-core/motion-gpu/react';
|
|
230
|
+
|
|
231
|
+
const material = defineMaterial({
|
|
232
|
+
fragment: `
|
|
233
|
+
fn frag(uv: vec2f) -> vec4f {
|
|
234
|
+
return vec4f(uv.x, uv.y, 0.25, 1.0);
|
|
235
|
+
}
|
|
236
|
+
`
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
export function App() {
|
|
240
|
+
return (
|
|
241
|
+
<div style={{ width: '100vw', height: '100vh' }}>
|
|
242
|
+
<FragCanvas material={material} />
|
|
243
|
+
</div>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
185
250
|
## 2. Add animated uniforms via `useFrame`
|
|
186
251
|
|
|
187
252
|
```svelte
|
|
@@ -219,6 +284,18 @@ fn frag(uv: vec2f) -> vec4f {
|
|
|
219
284
|
</script>
|
|
220
285
|
```
|
|
221
286
|
|
|
287
|
+
```tsx
|
|
288
|
+
import { useFrame } from '@motion-core/motion-gpu/react';
|
|
289
|
+
|
|
290
|
+
export function Runtime() {
|
|
291
|
+
useFrame((state) => {
|
|
292
|
+
state.setUniform('uTime', state.time);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
222
299
|
---
|
|
223
300
|
|
|
224
301
|
# Core Runtime Model
|
|
@@ -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.
|
|
@@ -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
|
}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export { createMotionGPURuntimeLoop } from './runtime-loop.js';
|
|
|
11
11
|
export { loadTexturesFromUrls } from './texture-loader.js';
|
|
12
12
|
export { BlitPass, CopyPass, ShaderPass } from '../passes/index.js';
|
|
13
13
|
export type { CurrentReadable, CurrentWritable, Subscribable } from './current-value.js';
|
|
14
|
-
export type { MotionGPUErrorPhase, MotionGPUErrorReport, MotionGPUErrorSource, MotionGPUErrorSourceLine } from './error-report.js';
|
|
14
|
+
export type { MotionGPUErrorCode, MotionGPUErrorContext, MotionGPUErrorPhase, MotionGPUErrorReport, MotionGPUErrorSeverity, MotionGPUErrorSource, MotionGPUErrorSourceLine } from './error-report.js';
|
|
15
15
|
export type { FrameCallback, FrameKey, FrameProfilingSnapshot, FrameRegistry, FrameRunTimings, FrameScheduleSnapshot, FrameStage, FrameStageCallback, FrameTask, FrameTaskInvalidation, FrameTaskInvalidationToken, FrameTimingStats, UseFrameOptions, UseFrameResult } from './frame-registry.js';
|
|
16
16
|
export type { FragMaterial, FragMaterialInput, MaterialDefineValue, MaterialDefines, MaterialIncludes, ResolvedMaterial, TypedMaterialDefineValue } from './material.js';
|
|
17
17
|
export type { MotionGPURuntimeLoop, MotionGPURuntimeLoopOptions } from './runtime-loop.js';
|
|
@@ -44,11 +44,11 @@ export interface PreprocessedMaterialFragment {
|
|
|
44
44
|
/**
|
|
45
45
|
* Validates and normalizes define entries.
|
|
46
46
|
*/
|
|
47
|
-
export declare function normalizeDefines(defines: MaterialDefines | undefined): MaterialDefines
|
|
47
|
+
export declare function normalizeDefines<TDefineKey extends string>(defines: MaterialDefines<TDefineKey> | undefined): MaterialDefines<TDefineKey>;
|
|
48
48
|
/**
|
|
49
49
|
* Validates include map identifiers and source chunks.
|
|
50
50
|
*/
|
|
51
|
-
export declare function normalizeIncludes(includes: MaterialIncludes | undefined): MaterialIncludes
|
|
51
|
+
export declare function normalizeIncludes<TIncludeKey extends string>(includes: MaterialIncludes<TIncludeKey> | undefined): MaterialIncludes<TIncludeKey>;
|
|
52
52
|
/**
|
|
53
53
|
* Converts one define declaration to WGSL `const`.
|
|
54
54
|
*/
|
|
@@ -56,8 +56,8 @@ export declare function toDefineLine(key: string, value: MaterialDefineValue): s
|
|
|
56
56
|
/**
|
|
57
57
|
* Preprocesses material fragment with deterministic define/include expansion and line mapping.
|
|
58
58
|
*/
|
|
59
|
-
export declare function preprocessMaterialFragment(input: {
|
|
59
|
+
export declare function preprocessMaterialFragment<TDefineKey extends string, TIncludeKey extends string>(input: {
|
|
60
60
|
fragment: string;
|
|
61
|
-
defines?: MaterialDefines
|
|
62
|
-
includes?: MaterialIncludes
|
|
61
|
+
defines?: MaterialDefines<TDefineKey>;
|
|
62
|
+
includes?: MaterialIncludes<TIncludeKey>;
|
|
63
63
|
}): PreprocessedMaterialFragment;
|
|
@@ -44,10 +44,7 @@ export function normalizeDefines(defines) {
|
|
|
44
44
|
continue;
|
|
45
45
|
}
|
|
46
46
|
const normalized = normalizeTypedDefine(name, value);
|
|
47
|
-
resolved[name] = Object.freeze(
|
|
48
|
-
type: normalized.type,
|
|
49
|
-
value: normalized.value
|
|
50
|
-
});
|
|
47
|
+
resolved[name] = Object.freeze(normalized);
|
|
51
48
|
}
|
|
52
49
|
return resolved;
|
|
53
50
|
}
|
package/dist/core/material.d.ts
CHANGED
|
@@ -5,16 +5,25 @@ import type { TextureDefinitionMap, UniformMap } from './types.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* Typed compile-time define declaration.
|
|
7
7
|
*/
|
|
8
|
-
export
|
|
8
|
+
export type TypedMaterialDefineValue = {
|
|
9
9
|
/**
|
|
10
10
|
* WGSL scalar type.
|
|
11
11
|
*/
|
|
12
|
-
type: 'bool'
|
|
12
|
+
type: 'bool';
|
|
13
13
|
/**
|
|
14
14
|
* Literal value for the selected WGSL type.
|
|
15
15
|
*/
|
|
16
|
-
value: boolean
|
|
17
|
-
}
|
|
16
|
+
value: boolean;
|
|
17
|
+
} | {
|
|
18
|
+
/**
|
|
19
|
+
* WGSL scalar type.
|
|
20
|
+
*/
|
|
21
|
+
type: 'f32' | 'i32' | 'u32';
|
|
22
|
+
/**
|
|
23
|
+
* Literal value for the selected WGSL type.
|
|
24
|
+
*/
|
|
25
|
+
value: number;
|
|
26
|
+
};
|
|
18
27
|
/**
|
|
19
28
|
* Allowed value types for WGSL `const` define injection.
|
|
20
29
|
*/
|
|
@@ -22,15 +31,15 @@ export type MaterialDefineValue = boolean | number | TypedMaterialDefineValue;
|
|
|
22
31
|
/**
|
|
23
32
|
* Define map keyed by uniform-compatible identifier names.
|
|
24
33
|
*/
|
|
25
|
-
export type MaterialDefines = Record<
|
|
34
|
+
export type MaterialDefines<TKey extends string = string> = Record<TKey, MaterialDefineValue>;
|
|
26
35
|
/**
|
|
27
36
|
* Include map keyed by include identifier used in `#include <name>` directives.
|
|
28
37
|
*/
|
|
29
|
-
export type MaterialIncludes = Record<
|
|
38
|
+
export type MaterialIncludes<TKey extends string = string> = Record<TKey, string>;
|
|
30
39
|
/**
|
|
31
40
|
* External material input accepted by {@link defineMaterial}.
|
|
32
41
|
*/
|
|
33
|
-
export interface FragMaterialInput {
|
|
42
|
+
export interface FragMaterialInput<TUniformKey extends string = string, TTextureKey extends string = string, TDefineKey extends string = string, TIncludeKey extends string = string> {
|
|
34
43
|
/**
|
|
35
44
|
* User WGSL source containing `frag(uv: vec2f) -> vec4f`.
|
|
36
45
|
*/
|
|
@@ -38,24 +47,24 @@ export interface FragMaterialInput {
|
|
|
38
47
|
/**
|
|
39
48
|
* Initial uniform values.
|
|
40
49
|
*/
|
|
41
|
-
uniforms?: UniformMap
|
|
50
|
+
uniforms?: UniformMap<TUniformKey>;
|
|
42
51
|
/**
|
|
43
52
|
* Texture definitions keyed by texture uniform name.
|
|
44
53
|
*/
|
|
45
|
-
textures?: TextureDefinitionMap
|
|
54
|
+
textures?: TextureDefinitionMap<TTextureKey>;
|
|
46
55
|
/**
|
|
47
56
|
* Optional compile-time define constants injected into WGSL.
|
|
48
57
|
*/
|
|
49
|
-
defines?: MaterialDefines
|
|
58
|
+
defines?: MaterialDefines<TDefineKey>;
|
|
50
59
|
/**
|
|
51
60
|
* Optional WGSL include chunks used by `#include <name>` directives.
|
|
52
61
|
*/
|
|
53
|
-
includes?: MaterialIncludes
|
|
62
|
+
includes?: MaterialIncludes<TIncludeKey>;
|
|
54
63
|
}
|
|
55
64
|
/**
|
|
56
65
|
* Normalized and immutable material declaration consumed by `FragCanvas`.
|
|
57
66
|
*/
|
|
58
|
-
export interface FragMaterial {
|
|
67
|
+
export interface FragMaterial<TUniformKey extends string = string, TTextureKey extends string = string, TDefineKey extends string = string, TIncludeKey extends string = string> {
|
|
59
68
|
/**
|
|
60
69
|
* User WGSL source containing `frag(uv: vec2f) -> vec4f`.
|
|
61
70
|
*/
|
|
@@ -63,24 +72,24 @@ export interface FragMaterial {
|
|
|
63
72
|
/**
|
|
64
73
|
* Initial uniform values.
|
|
65
74
|
*/
|
|
66
|
-
readonly uniforms: Readonly<UniformMap
|
|
75
|
+
readonly uniforms: Readonly<UniformMap<TUniformKey>>;
|
|
67
76
|
/**
|
|
68
77
|
* Texture definitions keyed by texture uniform name.
|
|
69
78
|
*/
|
|
70
|
-
readonly textures: Readonly<TextureDefinitionMap
|
|
79
|
+
readonly textures: Readonly<TextureDefinitionMap<TTextureKey>>;
|
|
71
80
|
/**
|
|
72
81
|
* Optional compile-time define constants injected into WGSL.
|
|
73
82
|
*/
|
|
74
|
-
readonly defines: Readonly<MaterialDefines
|
|
83
|
+
readonly defines: Readonly<MaterialDefines<TDefineKey>>;
|
|
75
84
|
/**
|
|
76
85
|
* Optional WGSL include chunks used by `#include <name>` directives.
|
|
77
86
|
*/
|
|
78
|
-
readonly includes: Readonly<MaterialIncludes
|
|
87
|
+
readonly includes: Readonly<MaterialIncludes<TIncludeKey>>;
|
|
79
88
|
}
|
|
80
89
|
/**
|
|
81
90
|
* Fully resolved, immutable material snapshot used for renderer creation/caching.
|
|
82
91
|
*/
|
|
83
|
-
export interface ResolvedMaterial {
|
|
92
|
+
export interface ResolvedMaterial<TUniformKey extends string = string, TTextureKey extends string = string, TIncludeKey extends string = string> {
|
|
84
93
|
/**
|
|
85
94
|
* Final fragment WGSL after define injection.
|
|
86
95
|
*/
|
|
@@ -92,11 +101,11 @@ export interface ResolvedMaterial {
|
|
|
92
101
|
/**
|
|
93
102
|
* Cloned uniforms.
|
|
94
103
|
*/
|
|
95
|
-
uniforms: UniformMap
|
|
104
|
+
uniforms: UniformMap<TUniformKey>;
|
|
96
105
|
/**
|
|
97
106
|
* Cloned texture definitions.
|
|
98
107
|
*/
|
|
99
|
-
textures: TextureDefinitionMap
|
|
108
|
+
textures: TextureDefinitionMap<TTextureKey>;
|
|
100
109
|
/**
|
|
101
110
|
* Resolved packed uniform layout.
|
|
102
111
|
*/
|
|
@@ -104,7 +113,7 @@ export interface ResolvedMaterial {
|
|
|
104
113
|
/**
|
|
105
114
|
* Sorted texture keys.
|
|
106
115
|
*/
|
|
107
|
-
textureKeys:
|
|
116
|
+
textureKeys: TTextureKey[];
|
|
108
117
|
/**
|
|
109
118
|
* Deterministic JSON signature for cache invalidation.
|
|
110
119
|
*/
|
|
@@ -116,7 +125,7 @@ export interface ResolvedMaterial {
|
|
|
116
125
|
/**
|
|
117
126
|
* Normalized include sources map.
|
|
118
127
|
*/
|
|
119
|
-
includeSources: MaterialIncludes
|
|
128
|
+
includeSources: MaterialIncludes<TIncludeKey>;
|
|
120
129
|
/**
|
|
121
130
|
* Deterministic define block source used for diagnostics mapping.
|
|
122
131
|
*/
|
|
@@ -147,11 +156,11 @@ export declare function applyMaterialDefines(fragment: string, defines: Material
|
|
|
147
156
|
* @param input - User material declaration.
|
|
148
157
|
* @returns Frozen material object safe to share and cache.
|
|
149
158
|
*/
|
|
150
|
-
export declare function defineMaterial(input: FragMaterialInput): FragMaterial
|
|
159
|
+
export declare function defineMaterial<TUniformKey extends string = string, TTextureKey extends string = string, TDefineKey extends string = string, TIncludeKey extends string = string>(input: FragMaterialInput<TUniformKey, TTextureKey, TDefineKey, TIncludeKey>): FragMaterial<TUniformKey, TTextureKey, TDefineKey, TIncludeKey>;
|
|
151
160
|
/**
|
|
152
161
|
* Resolves a material to renderer-ready data and a deterministic signature.
|
|
153
162
|
*
|
|
154
163
|
* @param material - Material input created via {@link defineMaterial}.
|
|
155
164
|
* @returns Resolved material with packed uniform layout, sorted texture keys and cache signature.
|
|
156
165
|
*/
|
|
157
|
-
export declare function resolveMaterial(material: FragMaterial): ResolvedMaterial
|
|
166
|
+
export declare function resolveMaterial<TUniformKey extends string = string, TTextureKey extends string = string, TDefineKey extends string = string, TIncludeKey extends string = string>(material: FragMaterial<TUniformKey, TTextureKey, TDefineKey, TIncludeKey>): ResolvedMaterial<TUniformKey, TTextureKey, TIncludeKey>;
|
package/dist/core/material.js
CHANGED
|
@@ -6,12 +6,17 @@ import { normalizeDefines, normalizeIncludes, preprocessMaterialFragment, toDefi
|
|
|
6
6
|
*/
|
|
7
7
|
const FRAGMENT_FUNCTION_SIGNATURE_PATTERN = /\bfn\s+frag\s*\(\s*([^)]*?)\s*\)\s*->\s*([A-Za-z_][A-Za-z0-9_<>\s]*)\s*(?:\{|$)/m;
|
|
8
8
|
const FRAGMENT_FUNCTION_NAME_PATTERN = /\bfn\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/g;
|
|
9
|
-
/**
|
|
10
|
-
* Cache of resolved material snapshots keyed by immutable material instance.
|
|
11
|
-
*/
|
|
12
9
|
const resolvedMaterialCache = new WeakMap();
|
|
13
10
|
const preprocessedFragmentCache = new WeakMap();
|
|
14
11
|
const materialSourceMetadataCache = new WeakMap();
|
|
12
|
+
function getCachedResolvedMaterial(material) {
|
|
13
|
+
const cached = resolvedMaterialCache.get(material);
|
|
14
|
+
if (!cached) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
// Invariant: the cache key is the same material object used to produce this resolved payload.
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
15
20
|
const STACK_TRACE_CHROME_PATTERN = /^\s*at\s+(?:(.*?)\s+\()?(.+?):(\d+):(\d+)\)?$/;
|
|
16
21
|
const STACK_TRACE_FIREFOX_PATTERN = /^(.*?)@(.+?):(\d+):(\d+)$/;
|
|
17
22
|
function getPathBasename(path) {
|
|
@@ -204,9 +209,11 @@ function resolveTextures(textures) {
|
|
|
204
209
|
const resolved = {};
|
|
205
210
|
for (const [name, definition] of Object.entries(textures ?? {})) {
|
|
206
211
|
assertUniformName(name);
|
|
212
|
+
const source = definition?.source;
|
|
213
|
+
const normalizedSource = cloneTextureValue(source);
|
|
207
214
|
const clonedDefinition = {
|
|
208
215
|
...(definition ?? {}),
|
|
209
|
-
source:
|
|
216
|
+
...(source !== undefined ? { source: normalizedSource } : {})
|
|
210
217
|
};
|
|
211
218
|
resolved[name] = Object.freeze(clonedDefinition);
|
|
212
219
|
}
|
|
@@ -296,8 +303,8 @@ export function defineMaterial(input) {
|
|
|
296
303
|
const source = Object.freeze(resolveSourceMetadata(undefined));
|
|
297
304
|
const preprocessed = preprocessMaterialFragment({
|
|
298
305
|
fragment,
|
|
299
|
-
defines
|
|
300
|
-
includes
|
|
306
|
+
defines,
|
|
307
|
+
includes
|
|
301
308
|
});
|
|
302
309
|
const material = Object.freeze({
|
|
303
310
|
fragment,
|
|
@@ -318,7 +325,7 @@ export function defineMaterial(input) {
|
|
|
318
325
|
*/
|
|
319
326
|
export function resolveMaterial(material) {
|
|
320
327
|
assertDefinedMaterial(material);
|
|
321
|
-
const cached =
|
|
328
|
+
const cached = getCachedResolvedMaterial(material);
|
|
322
329
|
if (cached) {
|
|
323
330
|
return cached;
|
|
324
331
|
}
|