@motion-core/motion-gpu 0.7.0 → 0.8.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 +40 -34
- package/dist/motion-gpu.css +295 -0
- package/dist/vue/FragCanvas.js +8 -0
- package/dist/vue/FragCanvas.js.map +1 -0
- package/dist/vue/FragCanvas.vue.d.ts +49 -0
- package/dist/vue/FragCanvas.vue.d.ts.map +1 -0
- package/dist/vue/FragCanvas.vue_vue_type_script_setup_true_lang.js +228 -0
- package/dist/vue/FragCanvas.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/vue/MotionGPUErrorOverlay.js +8 -0
- package/dist/vue/MotionGPUErrorOverlay.js.map +1 -0
- package/dist/vue/MotionGPUErrorOverlay.vue.d.ts +8 -0
- package/dist/vue/MotionGPUErrorOverlay.vue.d.ts.map +1 -0
- package/dist/vue/MotionGPUErrorOverlay.vue_vue_type_script_setup_true_lang.js +166 -0
- package/dist/vue/MotionGPUErrorOverlay.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/vue/Portal.js +7 -0
- package/dist/vue/Portal.js.map +1 -0
- package/dist/vue/Portal.vue.d.ts +18 -0
- package/dist/vue/Portal.vue.d.ts.map +1 -0
- package/dist/vue/Portal.vue_vue_type_script_setup_true_lang.js +29 -0
- package/dist/vue/Portal.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/vue/advanced.d.ts +12 -0
- package/dist/vue/advanced.d.ts.map +1 -0
- package/dist/vue/advanced.js +15 -0
- package/dist/vue/frame-context.d.ts +22 -0
- package/dist/vue/frame-context.d.ts.map +1 -0
- package/dist/vue/frame-context.js +38 -0
- package/dist/vue/frame-context.js.map +1 -0
- package/dist/vue/index.d.ts +21 -0
- package/dist/vue/index.d.ts.map +1 -0
- package/dist/vue/index.js +14 -0
- package/dist/vue/motiongpu-context.d.ts +81 -0
- package/dist/vue/motiongpu-context.d.ts.map +1 -0
- package/dist/vue/motiongpu-context.js +29 -0
- package/dist/vue/motiongpu-context.js.map +1 -0
- package/dist/vue/shims-vue.d.js +0 -0
- package/dist/vue/use-motiongpu-user-context.d.ts +44 -0
- package/dist/vue/use-motiongpu-user-context.d.ts.map +1 -0
- package/dist/vue/use-motiongpu-user-context.js +76 -0
- package/dist/vue/use-motiongpu-user-context.js.map +1 -0
- package/dist/vue/use-pointer.d.ts +94 -0
- package/dist/vue/use-pointer.d.ts.map +1 -0
- package/dist/vue/use-pointer.js +298 -0
- package/dist/vue/use-pointer.js.map +1 -0
- package/dist/vue/use-texture.d.ts +45 -0
- package/dist/vue/use-texture.d.ts.map +1 -0
- package/dist/vue/use-texture.js +135 -0
- package/dist/vue/use-texture.js.map +1 -0
- package/package.json +25 -7
- package/src/lib/vue/FragCanvas.vue +294 -0
- package/src/lib/vue/MotionGPUErrorOverlay.vue +518 -0
- package/src/lib/vue/Portal.vue +46 -0
- package/src/lib/vue/advanced.ts +32 -0
- package/src/lib/vue/frame-context.ts +96 -0
- package/src/lib/vue/index.ts +78 -0
- package/src/lib/vue/motiongpu-context.ts +97 -0
- package/src/lib/vue/shims-vue.d.ts +6 -0
- package/src/lib/vue/use-motiongpu-user-context.ts +145 -0
- package/src/lib/vue/use-pointer.ts +514 -0
- package/src/lib/vue/use-texture.ts +232 -0
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
4
|
[](https://svelte.dev)
|
|
5
5
|
[](https://react.dev)
|
|
6
|
+
[](https://vuejs.org)
|
|
6
7
|
[](https://gpuweb.github.io/gpuweb/)
|
|
7
8
|
[](https://www.typescriptlang.org)
|
|
8
9
|
[](https://www.npmjs.com/package/@motion-core/motion-gpu)
|
|
@@ -13,7 +14,7 @@
|
|
|
13
14
|
|
|
14
15
|
**A tiny WebGPU runtime for writing Shadertoy-style fullscreen shaders in pure WGSL.**
|
|
15
16
|
|
|
16
|
-
`@motion-core/motion-gpu` ships a framework-agnostic core plus Svelte 5 and
|
|
17
|
+
`@motion-core/motion-gpu` ships a framework-agnostic core plus Svelte 5, React, and Vue adapters for building fullscreen shader pipelines using WebGPU and WGSL.
|
|
17
18
|
It provides a minimal runtime loop, scheduler, and render graph designed specifically for fragment-driven GPU programs.
|
|
18
19
|
|
|
19
20
|
Unlike general-purpose 3D engines, Motion GPU focuses on a very narrow problem: **running fullscreen fragment shaders and multi-pass GPU pipelines**.
|
|
@@ -65,7 +66,7 @@ Motion GPU follows a simple three-step flow:
|
|
|
65
66
|
|
|
66
67
|
1. Define an immutable material with `defineMaterial(...)`.
|
|
67
68
|
2. Render it with `<FragCanvas />`.
|
|
68
|
-
3. Drive runtime updates with `useFrame(...)`, `useMotionGPU()`,
|
|
69
|
+
3. Drive runtime updates with `useFrame(...)`, `useMotionGPU()`, and `useTexture(...)`.
|
|
69
70
|
|
|
70
71
|
---
|
|
71
72
|
|
|
@@ -163,6 +164,41 @@ Also exports runtime/core types:
|
|
|
163
164
|
|
|
164
165
|
---
|
|
165
166
|
|
|
167
|
+
## Vue adapter
|
|
168
|
+
|
|
169
|
+
`@motion-core/motion-gpu/vue` exposes the runtime API for Vue:
|
|
170
|
+
|
|
171
|
+
- `FragCanvas`
|
|
172
|
+
- `defineMaterial`
|
|
173
|
+
- `useMotionGPU`
|
|
174
|
+
- `useFrame`
|
|
175
|
+
- `usePointer`
|
|
176
|
+
- `useTexture`
|
|
177
|
+
- `ShaderPass`
|
|
178
|
+
- `BlitPass`
|
|
179
|
+
- `CopyPass`
|
|
180
|
+
- `ComputePass`
|
|
181
|
+
- `PingPongComputePass`
|
|
182
|
+
|
|
183
|
+
Also exports runtime/core types:
|
|
184
|
+
|
|
185
|
+
- uniforms
|
|
186
|
+
- textures
|
|
187
|
+
- render passes
|
|
188
|
+
- scheduler
|
|
189
|
+
- loader types
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
`@motion-core/motion-gpu/vue/advanced` re-exports everything above, plus:
|
|
194
|
+
|
|
195
|
+
- `useMotionGPUUserContext`
|
|
196
|
+
- `setMotionGPUUserContext`
|
|
197
|
+
- `applySchedulerPreset`
|
|
198
|
+
- `captureSchedulerDebugSnapshot`
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
166
202
|
## Framework-agnostic core
|
|
167
203
|
|
|
168
204
|
`@motion-core/motion-gpu` (and explicit alias `@motion-core/motion-gpu/core`) exposes adapter-building primitives:
|
|
@@ -191,6 +227,7 @@ Also exports runtime/core types:
|
|
|
191
227
|
|
|
192
228
|
- Svelte 5 is required only for the Svelte adapter entrypoints (`/svelte`, `/svelte/advanced`)
|
|
193
229
|
- React 18+ is required only for the React adapter entrypoints (`/react`, `/react/advanced`)
|
|
230
|
+
- Vue 3.5+ is required only for the Vue adapter entrypoints (`/vue`, `/vue/advanced`)
|
|
194
231
|
- A browser/runtime with WebGPU support
|
|
195
232
|
- Secure context (`https://` or `localhost`)
|
|
196
233
|
|
|
@@ -310,37 +347,6 @@ export function Runtime() {
|
|
|
310
347
|
|
|
311
348
|
---
|
|
312
349
|
|
|
313
|
-
## 2b. Add pointer-driven uniforms via `usePointer`
|
|
314
|
-
|
|
315
|
-
```svelte
|
|
316
|
-
<!-- Runtime.svelte -->
|
|
317
|
-
<script lang="ts">
|
|
318
|
-
import { useFrame, usePointer } from '@motion-core/motion-gpu/svelte';
|
|
319
|
-
|
|
320
|
-
const pointer = usePointer();
|
|
321
|
-
|
|
322
|
-
useFrame((state) => {
|
|
323
|
-
state.setUniform('uMouse', pointer.state.current.uv);
|
|
324
|
-
});
|
|
325
|
-
</script>
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
```tsx
|
|
329
|
-
import { useFrame, usePointer } from '@motion-core/motion-gpu/react';
|
|
330
|
-
|
|
331
|
-
export function Runtime() {
|
|
332
|
-
const pointer = usePointer();
|
|
333
|
-
|
|
334
|
-
useFrame((state) => {
|
|
335
|
-
state.setUniform('uMouse', pointer.state.current.uv);
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
return null;
|
|
339
|
-
}
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
---
|
|
343
|
-
|
|
344
350
|
## 3. Add a GPU compute pass
|
|
345
351
|
|
|
346
352
|
```svelte
|
|
@@ -475,7 +481,7 @@ fn frag(uv: vec2f) -> vec4f
|
|
|
475
481
|
fn shade(inputColor: vec4f, uv: vec2f) -> vec4f
|
|
476
482
|
```
|
|
477
483
|
|
|
478
|
-
3. `useFrame()
|
|
484
|
+
3. `useFrame()` and `useMotionGPU()` must be called inside `<FragCanvas>` subtree.
|
|
479
485
|
|
|
480
486
|
4. You can only set uniforms/textures that were declared in `defineMaterial(...)`.
|
|
481
487
|
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
|
|
2
|
+
.motiongpu-error-overlay {
|
|
3
|
+
--motiongpu-base-hue: var(--base-hue, 265);
|
|
4
|
+
--motiongpu-color-background: oklch(0.2178 0.0056 var(--motiongpu-base-hue));
|
|
5
|
+
--motiongpu-color-background-muted: oklch(0.261 0.007 var(--motiongpu-base-hue));
|
|
6
|
+
--motiongpu-color-foreground: oklch(1 0 0);
|
|
7
|
+
--motiongpu-color-foreground-muted: oklch(0.6699 0.0081 var(--motiongpu-base-hue));
|
|
8
|
+
--motiongpu-color-card: var(--motiongpu-color-background);
|
|
9
|
+
--motiongpu-color-accent: oklch(0.6996 0.181959 44.4414);
|
|
10
|
+
--motiongpu-color-accent-secondary: oklch(0.5096 0.131959 44.4414);
|
|
11
|
+
--motiongpu-color-border: oklch(0.928 0.013 var(--motiongpu-base-hue) / 0.05);
|
|
12
|
+
--motiongpu-color-white-fixed: oklch(1 0 0);
|
|
13
|
+
--motiongpu-shadow-card: var(
|
|
14
|
+
--shadow-2xl,
|
|
15
|
+
0px 1px 1px -0.5px rgba(0, 0, 0, 0.06),
|
|
16
|
+
0px 3px 3px -1.5px rgba(0, 0, 0, 0.06),
|
|
17
|
+
0px 6px 6px -3px rgba(0, 0, 0, 0.06),
|
|
18
|
+
0px 12px 12px -6px rgba(0, 0, 0, 0.06),
|
|
19
|
+
0px 24px 24px -12px rgba(0, 0, 0, 0.05),
|
|
20
|
+
0px 48px 48px -24px rgba(0, 0, 0, 0.06)
|
|
21
|
+
);
|
|
22
|
+
--motiongpu-radius-md: var(--radius-md, 0.5rem);
|
|
23
|
+
--motiongpu-radius-lg: var(--radius-lg, 0.75rem);
|
|
24
|
+
--motiongpu-radius-xl: var(--radius-xl, 1rem);
|
|
25
|
+
--motiongpu-font-sans: var(--font-sans, 'Inter', 'Segoe UI', 'Helvetica Neue', Arial, sans-serif);
|
|
26
|
+
--motiongpu-font-mono: var(--font-mono, 'SFMono-Regular', 'Menlo', 'Consolas', monospace);
|
|
27
|
+
position: fixed;
|
|
28
|
+
inset: 0;
|
|
29
|
+
display: grid;
|
|
30
|
+
place-items: center;
|
|
31
|
+
padding: clamp(0.75rem, 1.4vw, 1.5rem);
|
|
32
|
+
background: rgba(0, 0, 0, 0.8);
|
|
33
|
+
backdrop-filter: blur(10px);
|
|
34
|
+
z-index: 2147483647;
|
|
35
|
+
font-family: var(--motiongpu-font-sans);
|
|
36
|
+
color-scheme: dark;
|
|
37
|
+
}
|
|
38
|
+
.motiongpu-error-dialog {
|
|
39
|
+
width: min(52rem, calc(100vw - 1.5rem));
|
|
40
|
+
max-height: min(84vh, 44rem);
|
|
41
|
+
overflow: auto;
|
|
42
|
+
margin: 0;
|
|
43
|
+
padding: 1.1rem;
|
|
44
|
+
border: 1px solid var(--motiongpu-color-border);
|
|
45
|
+
border-radius: var(--motiongpu-radius-xl);
|
|
46
|
+
max-width: calc(100vw - 1.5rem);
|
|
47
|
+
box-sizing: border-box;
|
|
48
|
+
font-size: 0.875rem;
|
|
49
|
+
font-weight: 400;
|
|
50
|
+
line-height: 1.45;
|
|
51
|
+
background: var(--motiongpu-color-card);
|
|
52
|
+
color: var(--motiongpu-color-foreground);
|
|
53
|
+
box-shadow: var(--motiongpu-shadow-card);
|
|
54
|
+
}
|
|
55
|
+
.motiongpu-error-header {
|
|
56
|
+
display: grid;
|
|
57
|
+
gap: 0.55rem;
|
|
58
|
+
padding-bottom: 0.9rem;
|
|
59
|
+
border-bottom: 1px solid var(--motiongpu-color-border);
|
|
60
|
+
}
|
|
61
|
+
.motiongpu-error-header-top {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: flex-start;
|
|
64
|
+
gap: 0.75rem;
|
|
65
|
+
}
|
|
66
|
+
.motiongpu-error-badges {
|
|
67
|
+
display: inline-flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
gap: 0.4rem;
|
|
70
|
+
flex-wrap: wrap;
|
|
71
|
+
}
|
|
72
|
+
.motiongpu-error-badge-wrap {
|
|
73
|
+
display: inline-flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
gap: 0.4rem;
|
|
76
|
+
width: fit-content;
|
|
77
|
+
padding: 0.18rem;
|
|
78
|
+
border-radius: 999px;
|
|
79
|
+
border: 1px solid var(--motiongpu-color-border);
|
|
80
|
+
background: var(--motiongpu-color-background-muted);
|
|
81
|
+
}
|
|
82
|
+
.motiongpu-error-badge {
|
|
83
|
+
display: inline-flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
margin: 0;
|
|
86
|
+
padding: 0.22rem 0.56rem;
|
|
87
|
+
border-radius: 999px;
|
|
88
|
+
font-size: 0.66rem;
|
|
89
|
+
letter-spacing: 0.08em;
|
|
90
|
+
line-height: 1;
|
|
91
|
+
font-weight: 500;
|
|
92
|
+
text-transform: uppercase;
|
|
93
|
+
color: var(--motiongpu-color-white-fixed);
|
|
94
|
+
background: linear-gradient(
|
|
95
|
+
180deg,
|
|
96
|
+
var(--motiongpu-color-accent) 0%,
|
|
97
|
+
var(--motiongpu-color-accent-secondary) 100%
|
|
98
|
+
);
|
|
99
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.24);
|
|
100
|
+
}
|
|
101
|
+
.motiongpu-error-recoverable {
|
|
102
|
+
margin: 0;
|
|
103
|
+
font-size: 0.67rem;
|
|
104
|
+
line-height: 1.2;
|
|
105
|
+
letter-spacing: 0.06em;
|
|
106
|
+
text-transform: uppercase;
|
|
107
|
+
color: var(--motiongpu-color-foreground-muted);
|
|
108
|
+
}
|
|
109
|
+
.motiongpu-error-recoverable span {
|
|
110
|
+
font-family: var(--motiongpu-font-mono);
|
|
111
|
+
color: var(--motiongpu-color-foreground);
|
|
112
|
+
}
|
|
113
|
+
.motiongpu-error-title {
|
|
114
|
+
margin: 0;
|
|
115
|
+
font-size: clamp(1.02rem, 1vw + 0.72rem, 1.32rem);
|
|
116
|
+
font-weight: 500;
|
|
117
|
+
line-height: 1.18;
|
|
118
|
+
letter-spacing: -0.02em;
|
|
119
|
+
text-wrap: balance;
|
|
120
|
+
color: var(--motiongpu-color-foreground);
|
|
121
|
+
}
|
|
122
|
+
.motiongpu-error-body {
|
|
123
|
+
display: grid;
|
|
124
|
+
gap: 0.62rem;
|
|
125
|
+
margin-top: 0.92rem;
|
|
126
|
+
}
|
|
127
|
+
.motiongpu-error-message {
|
|
128
|
+
margin: 0;
|
|
129
|
+
padding: 0.72rem 0.78rem;
|
|
130
|
+
border: 1px solid color-mix(in oklch, var(--motiongpu-color-accent) 28%, transparent);
|
|
131
|
+
border-radius: var(--motiongpu-radius-md);
|
|
132
|
+
background: color-mix(in oklch, var(--motiongpu-color-accent) 10%, transparent);
|
|
133
|
+
font-size: 0.82rem;
|
|
134
|
+
line-height: 1.4;
|
|
135
|
+
font-weight: 400;
|
|
136
|
+
color: var(--motiongpu-color-foreground);
|
|
137
|
+
}
|
|
138
|
+
.motiongpu-error-hint {
|
|
139
|
+
margin: 0;
|
|
140
|
+
font-size: 0.82rem;
|
|
141
|
+
line-height: 1.45;
|
|
142
|
+
font-weight: 400;
|
|
143
|
+
color: var(--motiongpu-color-foreground-muted);
|
|
144
|
+
}
|
|
145
|
+
.motiongpu-error-sections {
|
|
146
|
+
display: grid;
|
|
147
|
+
gap: 0.62rem;
|
|
148
|
+
margin-top: 0.95rem;
|
|
149
|
+
}
|
|
150
|
+
.motiongpu-error-source {
|
|
151
|
+
display: grid;
|
|
152
|
+
gap: 0.48rem;
|
|
153
|
+
margin-top: 0.96rem;
|
|
154
|
+
}
|
|
155
|
+
.motiongpu-error-source-title {
|
|
156
|
+
margin: 0;
|
|
157
|
+
font-size: 0.8rem;
|
|
158
|
+
font-weight: 500;
|
|
159
|
+
line-height: 1.3;
|
|
160
|
+
letter-spacing: 0.045em;
|
|
161
|
+
text-transform: uppercase;
|
|
162
|
+
color: var(--motiongpu-color-foreground);
|
|
163
|
+
}
|
|
164
|
+
.motiongpu-error-source-frame {
|
|
165
|
+
border: 1px solid var(--motiongpu-color-border);
|
|
166
|
+
border-radius: var(--motiongpu-radius-lg);
|
|
167
|
+
overflow: hidden;
|
|
168
|
+
background: var(--motiongpu-color-background-muted);
|
|
169
|
+
}
|
|
170
|
+
.motiongpu-error-source-tabs {
|
|
171
|
+
display: flex;
|
|
172
|
+
align-items: stretch;
|
|
173
|
+
border-bottom: 1px solid var(--motiongpu-color-border);
|
|
174
|
+
background: var(--motiongpu-color-background);
|
|
175
|
+
}
|
|
176
|
+
.motiongpu-error-source-tab {
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
padding: 0.5rem 0.68rem;
|
|
180
|
+
font-size: 0.76rem;
|
|
181
|
+
font-weight: 400;
|
|
182
|
+
line-height: 1.2;
|
|
183
|
+
color: var(--motiongpu-color-foreground-muted);
|
|
184
|
+
border-right: 1px solid var(--motiongpu-color-border);
|
|
185
|
+
}
|
|
186
|
+
.motiongpu-error-source-tab-active {
|
|
187
|
+
color: var(--motiongpu-color-foreground);
|
|
188
|
+
background: var(--motiongpu-color-background-muted);
|
|
189
|
+
}
|
|
190
|
+
.motiongpu-error-source-tab-spacer {
|
|
191
|
+
flex: 1 1 auto;
|
|
192
|
+
}
|
|
193
|
+
.motiongpu-error-source-snippet {
|
|
194
|
+
display: grid;
|
|
195
|
+
background: var(--motiongpu-color-background-muted);
|
|
196
|
+
}
|
|
197
|
+
.motiongpu-error-source-row {
|
|
198
|
+
display: grid;
|
|
199
|
+
grid-template-columns: 2rem minmax(0, 1fr);
|
|
200
|
+
align-items: start;
|
|
201
|
+
gap: 0.42rem;
|
|
202
|
+
padding: 0.2rem 0.68rem;
|
|
203
|
+
}
|
|
204
|
+
.motiongpu-error-source-row-active {
|
|
205
|
+
background: color-mix(in oklch, var(--motiongpu-color-accent) 10%, transparent);
|
|
206
|
+
}
|
|
207
|
+
.motiongpu-error-source-line {
|
|
208
|
+
font-family: var(--motiongpu-font-mono);
|
|
209
|
+
font-size: 0.77rem;
|
|
210
|
+
font-weight: 400;
|
|
211
|
+
line-height: 1.3;
|
|
212
|
+
font-variant-numeric: tabular-nums;
|
|
213
|
+
font-feature-settings: 'tnum' 1;
|
|
214
|
+
border-right: 1px solid var(--motiongpu-color-border);
|
|
215
|
+
color: var(--motiongpu-color-foreground-muted);
|
|
216
|
+
text-align: left;
|
|
217
|
+
}
|
|
218
|
+
.motiongpu-error-source-code {
|
|
219
|
+
font-family: var(--motiongpu-font-mono);
|
|
220
|
+
font-size: 0.77rem;
|
|
221
|
+
font-weight: 400;
|
|
222
|
+
line-height: 1.3;
|
|
223
|
+
color: var(--motiongpu-color-foreground);
|
|
224
|
+
white-space: pre-wrap;
|
|
225
|
+
word-break: break-word;
|
|
226
|
+
}
|
|
227
|
+
.motiongpu-error-details {
|
|
228
|
+
border: 1px solid var(--motiongpu-color-border);
|
|
229
|
+
border-radius: var(--motiongpu-radius-lg);
|
|
230
|
+
overflow: hidden;
|
|
231
|
+
background: var(--motiongpu-color-background);
|
|
232
|
+
}
|
|
233
|
+
.motiongpu-error-details summary {
|
|
234
|
+
cursor: pointer;
|
|
235
|
+
padding: 0.56rem 0.68rem;
|
|
236
|
+
font-size: 0.7rem;
|
|
237
|
+
letter-spacing: 0.07em;
|
|
238
|
+
line-height: 1.2;
|
|
239
|
+
font-weight: 500;
|
|
240
|
+
text-transform: uppercase;
|
|
241
|
+
color: var(--motiongpu-color-foreground);
|
|
242
|
+
}
|
|
243
|
+
.motiongpu-error-details[open] summary {
|
|
244
|
+
border-bottom: 1px solid var(--motiongpu-color-border);
|
|
245
|
+
}
|
|
246
|
+
.motiongpu-error-details pre {
|
|
247
|
+
margin: 0;
|
|
248
|
+
padding: 0.62rem 0.68rem;
|
|
249
|
+
white-space: pre-wrap;
|
|
250
|
+
word-break: break-word;
|
|
251
|
+
overflow: auto;
|
|
252
|
+
background: var(--motiongpu-color-background-muted);
|
|
253
|
+
font-size: 0.74rem;
|
|
254
|
+
line-height: 1.4;
|
|
255
|
+
font-weight: 400;
|
|
256
|
+
color: var(--motiongpu-color-foreground);
|
|
257
|
+
font-family: var(--motiongpu-font-mono);
|
|
258
|
+
}
|
|
259
|
+
@media (max-width: 42rem) {
|
|
260
|
+
.motiongpu-error-overlay {
|
|
261
|
+
padding: 0.62rem;
|
|
262
|
+
}
|
|
263
|
+
.motiongpu-error-dialog {
|
|
264
|
+
padding: 0.85rem;
|
|
265
|
+
}
|
|
266
|
+
.motiongpu-error-title {
|
|
267
|
+
font-size: 1.02rem;
|
|
268
|
+
}
|
|
269
|
+
.motiongpu-error-header-top {
|
|
270
|
+
flex-direction: column;
|
|
271
|
+
align-items: flex-start;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
@media (prefers-reduced-motion: reduce) {
|
|
275
|
+
.motiongpu-error-overlay {
|
|
276
|
+
backdrop-filter: none;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.motiongpu-canvas-wrap {
|
|
281
|
+
position: relative;
|
|
282
|
+
width: 100%;
|
|
283
|
+
height: 100%;
|
|
284
|
+
min-width: 0;
|
|
285
|
+
min-height: 0;
|
|
286
|
+
overflow: hidden;
|
|
287
|
+
}
|
|
288
|
+
.motiongpu-canvas-wrap > canvas {
|
|
289
|
+
position: absolute;
|
|
290
|
+
inset: 0;
|
|
291
|
+
display: block;
|
|
292
|
+
width: 100%;
|
|
293
|
+
height: 100%;
|
|
294
|
+
}
|
|
295
|
+
/*$vite$:1*/
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import FragCanvas_vue_vue_type_script_setup_true_lang_default from "./FragCanvas.vue_vue_type_script_setup_true_lang.js";
|
|
2
|
+
/* empty css */
|
|
3
|
+
//#region src/lib/vue/FragCanvas.vue
|
|
4
|
+
var FragCanvas_default = FragCanvas_vue_vue_type_script_setup_true_lang_default;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { FragCanvas_default as default };
|
|
7
|
+
|
|
8
|
+
//# sourceMappingURL=FragCanvas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FragCanvas.js","names":[],"sources":["../../src/lib/vue/FragCanvas.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { MotionGPUErrorReport } from '../core/error-report.js';\nimport type { FragMaterial } from '../core/material.js';\nimport type {\n\tAnyPass,\n\tOutputColorSpace,\n\tRenderMode,\n\tRenderTargetDefinitionMap\n} from '../core/types.js';\n\nexport interface FragCanvasProps {\n\tmaterial: FragMaterial;\n\trenderTargets?: RenderTargetDefinitionMap;\n\tpasses?: AnyPass[];\n\tclearColor?: [number, number, number, number];\n\toutputColorSpace?: OutputColorSpace;\n\trenderMode?: RenderMode;\n\tautoRender?: boolean;\n\tmaxDelta?: number;\n\tadapterOptions?: GPURequestAdapterOptions;\n\tdeviceDescriptor?: GPUDeviceDescriptor;\n\tdpr?: number;\n\tshowErrorOverlay?: boolean;\n\tonError?: (report: MotionGPUErrorReport) => void;\n\terrorHistoryLimit?: number;\n\tonErrorHistory?: (history: MotionGPUErrorReport[]) => void;\n\tcanvasClass?: string;\n\tcanvasStyle?: string | Record<string, string | number>;\n}\n\nconst initialDpr = typeof window === 'undefined' ? 1 : (window.devicePixelRatio ?? 1);\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, shallowRef, useTemplateRef, watch } from 'vue';\nimport { createCurrentWritable as currentWritable } from '../core/current-value.js';\nimport { toMotionGPUErrorReport } from '../core/error-report.js';\nimport { createFrameRegistry } from '../core/frame-registry.js';\nimport { createMotionGPURuntimeLoop } from '../core/runtime-loop.js';\nimport { provideFrameRegistry } from './frame-context.js';\nimport { provideMotionGPUContext } from './motiongpu-context.js';\nimport MotionGPUErrorOverlay from './MotionGPUErrorOverlay.vue';\n\nconst props = withDefaults(defineProps<FragCanvasProps>(), {\n\trenderTargets: () => ({}),\n\tpasses: () => [],\n\tclearColor: () => [0, 0, 0, 1] as [number, number, number, number],\n\toutputColorSpace: 'srgb',\n\trenderMode: 'always',\n\tautoRender: true,\n\tmaxDelta: 0.1,\n\tdpr: () => initialDpr,\n\tshowErrorOverlay: true,\n\terrorHistoryLimit: 0,\n\tcanvasClass: ''\n});\n\nconst wrapperStyle = Object.freeze({\n\tposition: 'relative',\n\twidth: '100%',\n\theight: '100%',\n\tminWidth: '0',\n\tminHeight: '0',\n\toverflow: 'hidden'\n});\n\nconst baseCanvasStyle = Object.freeze({\n\tposition: 'absolute',\n\tinset: '0',\n\tdisplay: 'block',\n\twidth: '100%',\n\theight: '100%'\n});\n\nconst resolvedCanvasStyle = computed(() => [baseCanvasStyle, props.canvasStyle]);\n\ndefineSlots<{\n\tdefault(): unknown;\n\terrorRenderer(props: { report: MotionGPUErrorReport }): unknown;\n}>();\n\nconst canvasRef = useTemplateRef<HTMLCanvasElement>('canvasEl');\nconst errorReport = shallowRef<MotionGPUErrorReport | null>(null);\nconst errorHistory = shallowRef<MotionGPUErrorReport[]>([]);\n\nconst registry = createFrameRegistry({ maxDelta: 0.1 });\nprovideFrameRegistry(registry);\n\nlet requestFrameSignal: (() => void) | null = null;\nlet runtimeLoopHandle: ReturnType<typeof createMotionGPURuntimeLoop> | null = null;\nconst requestFrame = (): void => {\n\trequestFrameSignal?.();\n};\nconst invalidateFrame = (): void => {\n\tregistry.invalidate();\n\trequestFrame();\n};\nconst advanceFrame = (): void => {\n\tregistry.advance();\n\trequestFrame();\n};\n\nconst size = currentWritable({ width: 0, height: 0 });\nconst dprState = currentWritable<number>(initialDpr, requestFrame);\nconst maxDeltaState = currentWritable<number>(0.1, (value) => {\n\tregistry.setMaxDelta(value);\n\trequestFrame();\n});\nconst renderModeState = currentWritable<RenderMode>('always', (value) => {\n\tregistry.setRenderMode(value);\n\trequestFrame();\n});\nconst autoRenderState = currentWritable<boolean>(true, (value) => {\n\tregistry.setAutoRender(value);\n\trequestFrame();\n});\nconst userState = currentWritable<Record<string | symbol, unknown>>({});\n\nprovideMotionGPUContext({\n\tget canvas() {\n\t\treturn canvasRef.value ?? undefined;\n\t},\n\tsize,\n\tdpr: dprState,\n\tmaxDelta: maxDeltaState,\n\trenderMode: renderModeState,\n\tautoRender: autoRenderState,\n\tuser: userState,\n\tinvalidate: invalidateFrame,\n\tadvance: advanceFrame,\n\tscheduler: {\n\t\tcreateStage: registry.createStage,\n\t\tgetStage: registry.getStage,\n\t\tsetDiagnosticsEnabled: registry.setDiagnosticsEnabled,\n\t\tgetDiagnosticsEnabled: registry.getDiagnosticsEnabled,\n\t\tgetLastRunTimings: registry.getLastRunTimings,\n\t\tgetSchedule: registry.getSchedule,\n\t\tsetProfilingEnabled: registry.setProfilingEnabled,\n\t\tsetProfilingWindow: registry.setProfilingWindow,\n\t\tresetProfiling: registry.resetProfiling,\n\t\tgetProfilingEnabled: registry.getProfilingEnabled,\n\t\tgetProfilingWindow: registry.getProfilingWindow,\n\t\tgetProfilingSnapshot: registry.getProfilingSnapshot\n\t}\n});\n\n/**\n * Normalizes the user-supplied error history limit to a non-negative integer.\n */\nfunction getNormalizedErrorHistoryLimit(value: number): number {\n\tif (!Number.isFinite(value) || value <= 0) {\n\t\treturn 0;\n\t}\n\n\treturn Math.floor(value);\n}\n\nwatch(\n\t() => props.renderMode,\n\t(value) => {\n\t\trenderModeState.set(value);\n\t}\n);\n\nwatch(\n\t() => props.autoRender,\n\t(value) => {\n\t\tautoRenderState.set(value);\n\t}\n);\n\nwatch(\n\t() => props.maxDelta,\n\t(value) => {\n\t\tmaxDeltaState.set(value);\n\t}\n);\n\nwatch(\n\t() => props.dpr,\n\t(value) => {\n\t\tdprState.set(value);\n\t}\n);\n\nwatch([() => errorHistory.value, () => props.errorHistoryLimit], ([history, rawLimit]) => {\n\tconst limit = getNormalizedErrorHistoryLimit(rawLimit);\n\tif (limit <= 0) {\n\t\tif (history.length === 0) {\n\t\t\treturn;\n\t\t}\n\t\terrorHistory.value = [];\n\t\tprops.onErrorHistory?.([]);\n\t\treturn;\n\t}\n\n\tif (history.length <= limit) {\n\t\treturn;\n\t}\n\n\tconst trimmed = history.slice(history.length - limit);\n\terrorHistory.value = trimmed;\n\tprops.onErrorHistory?.(trimmed);\n});\n\nonMounted(() => {\n\trenderModeState.set(props.renderMode);\n\tautoRenderState.set(props.autoRender);\n\tmaxDeltaState.set(props.maxDelta);\n\tdprState.set(props.dpr);\n\trequestFrame();\n\n\tconst canvas = canvasRef.value;\n\tif (!canvas) {\n\t\tconst report = toMotionGPUErrorReport(\n\t\t\tnew Error('Canvas element is not available'),\n\t\t\t'initialization'\n\t\t);\n\t\terrorReport.value = report;\n\t\tconst historyLimit = getNormalizedErrorHistoryLimit(props.errorHistoryLimit);\n\t\tif (historyLimit > 0) {\n\t\t\tconst nextHistory = [report].slice(-historyLimit);\n\t\t\terrorHistory.value = nextHistory;\n\t\t\tprops.onErrorHistory?.(nextHistory);\n\t\t}\n\t\tprops.onError?.(report);\n\t\treturn;\n\t}\n\n\tconst runtimeLoop = createMotionGPURuntimeLoop({\n\t\tcanvas,\n\t\tregistry,\n\t\tsize,\n\t\tdpr: dprState,\n\t\tmaxDelta: maxDeltaState,\n\t\tgetMaterial: () => props.material,\n\t\tgetRenderTargets: () => props.renderTargets,\n\t\tgetPasses: () => props.passes,\n\t\tgetClearColor: () => props.clearColor,\n\t\tgetOutputColorSpace: () => props.outputColorSpace,\n\t\tgetAdapterOptions: () => props.adapterOptions,\n\t\tgetDeviceDescriptor: () => props.deviceDescriptor,\n\t\tgetOnError: () => props.onError,\n\t\tgetErrorHistoryLimit: () => props.errorHistoryLimit,\n\t\tgetOnErrorHistory: () => props.onErrorHistory,\n\t\treportErrorHistory: (history) => {\n\t\t\terrorHistory.value = history;\n\t\t},\n\t\treportError: (report) => {\n\t\t\terrorReport.value = report;\n\t\t}\n\t});\n\truntimeLoopHandle = runtimeLoop;\n\trequestFrameSignal = runtimeLoop.requestFrame;\n});\n\nonBeforeUnmount(() => {\n\trequestFrameSignal = null;\n\truntimeLoopHandle?.destroy();\n\truntimeLoopHandle = null;\n\tregistry.clear();\n});\n</script>\n\n<template>\n\t<div class=\"motiongpu-canvas-wrap\" :style=\"wrapperStyle\">\n\t\t<canvas ref=\"canvasEl\" :class=\"canvasClass\" :style=\"resolvedCanvasStyle\"></canvas>\n\t\t<template v-if=\"showErrorOverlay && errorReport\">\n\t\t\t<slot name=\"errorRenderer\" :report=\"errorReport\">\n\t\t\t\t<MotionGPUErrorOverlay :report=\"errorReport\" />\n\t\t\t</slot>\n\t\t</template>\n\t\t<slot />\n\t</div>\n</template>\n\n<style>\n.motiongpu-canvas-wrap {\n\tposition: relative;\n\twidth: 100%;\n\theight: 100%;\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: hidden;\n}\n\n.motiongpu-canvas-wrap > canvas {\n\tposition: absolute;\n\tinset: 0;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n}\n</style>\n"],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/// <reference types="@webgpu/types" />
|
|
2
|
+
import type { MotionGPUErrorReport } from '../core/error-report.js';
|
|
3
|
+
import type { FragMaterial } from '../core/material.js';
|
|
4
|
+
import type { AnyPass, OutputColorSpace, RenderMode, RenderTargetDefinitionMap } from '../core/types.js';
|
|
5
|
+
export interface FragCanvasProps {
|
|
6
|
+
material: FragMaterial;
|
|
7
|
+
renderTargets?: RenderTargetDefinitionMap;
|
|
8
|
+
passes?: AnyPass[];
|
|
9
|
+
clearColor?: [number, number, number, number];
|
|
10
|
+
outputColorSpace?: OutputColorSpace;
|
|
11
|
+
renderMode?: RenderMode;
|
|
12
|
+
autoRender?: boolean;
|
|
13
|
+
maxDelta?: number;
|
|
14
|
+
adapterOptions?: GPURequestAdapterOptions;
|
|
15
|
+
deviceDescriptor?: GPUDeviceDescriptor;
|
|
16
|
+
dpr?: number;
|
|
17
|
+
showErrorOverlay?: boolean;
|
|
18
|
+
onError?: (report: MotionGPUErrorReport) => void;
|
|
19
|
+
errorHistoryLimit?: number;
|
|
20
|
+
onErrorHistory?: (history: MotionGPUErrorReport[]) => void;
|
|
21
|
+
canvasClass?: string;
|
|
22
|
+
canvasStyle?: string | Record<string, string | number>;
|
|
23
|
+
}
|
|
24
|
+
declare const _default: typeof __VLS_export;
|
|
25
|
+
export default _default;
|
|
26
|
+
declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<FragCanvasProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FragCanvasProps> & Readonly<{}>, {
|
|
27
|
+
clearColor: [number, number, number, number];
|
|
28
|
+
renderMode: RenderMode;
|
|
29
|
+
renderTargets: RenderTargetDefinitionMap;
|
|
30
|
+
passes: AnyPass[];
|
|
31
|
+
outputColorSpace: OutputColorSpace;
|
|
32
|
+
autoRender: boolean;
|
|
33
|
+
maxDelta: number;
|
|
34
|
+
dpr: number;
|
|
35
|
+
showErrorOverlay: boolean;
|
|
36
|
+
errorHistoryLimit: number;
|
|
37
|
+
canvasClass: string;
|
|
38
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
|
|
39
|
+
default(): unknown;
|
|
40
|
+
errorRenderer(props: {
|
|
41
|
+
report: MotionGPUErrorReport;
|
|
42
|
+
}): unknown;
|
|
43
|
+
}>;
|
|
44
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
45
|
+
new (): {
|
|
46
|
+
$slots: S;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=FragCanvas.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FragCanvas.vue.d.ts","sourceRoot":"","sources":["../../src/lib/vue/FragCanvas.vue"],"names":[],"mappings":";AAmTA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EACX,OAAO,EACP,gBAAgB,EAChB,UAAU,EACV,yBAAyB,EACzB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,YAAY,CAAC;IACvB,aAAa,CAAC,EAAE,yBAAyB,CAAC;IAC1C,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,wBAAwB,CAAC;IAC1C,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,KAAK,IAAI,CAAC;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CACvD;wBAGoB,OAAO,YAAY;AAAxC,wBAAyC;AACzC,QAAA,MAAM,YAAY;gBAlBJ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;gBAEhC,UAAU;mBAJP,yBAAyB;YAChC,OAAO,EAAE;sBAEC,gBAAgB;gBAEtB,OAAO;cACT,MAAM;SAGX,MAAM;sBACO,OAAO;uBAEN,MAAM;iBAEZ,MAAM;;eA0CT,OAAO;yBACG;QAAE,MAAM,EAAE,oBAAoB,CAAA;KAAE,GAAG,OAAO;EA8P5D,CAAC;AAWL,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|