@motion-core/motion-gpu 0.1.0 → 0.2.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 +31 -11
- package/dist/advanced.d.ts +3 -11
- package/dist/advanced.js +3 -6
- package/dist/core/advanced.d.ts +6 -0
- package/dist/core/advanced.js +5 -0
- package/dist/core/current-value.d.ts +23 -0
- package/dist/core/current-value.js +33 -0
- package/dist/core/error-diagnostics.d.ts +1 -1
- package/dist/core/error-report.js +2 -2
- package/dist/{frame-context.d.ts → core/frame-registry.d.ts} +3 -17
- package/dist/{frame-context.js → core/frame-registry.js} +2 -37
- package/dist/core/index.d.ts +19 -0
- package/dist/core/index.js +12 -0
- package/dist/core/material-preprocess.d.ts +1 -1
- package/dist/core/material-preprocess.js +1 -1
- package/dist/core/material.d.ts +4 -4
- package/dist/core/material.js +3 -3
- package/dist/core/recompile-policy.d.ts +1 -1
- package/dist/core/render-graph.d.ts +1 -1
- package/dist/core/render-targets.d.ts +1 -1
- package/dist/core/render-targets.js +1 -1
- package/dist/core/renderer.d.ts +1 -1
- package/dist/core/renderer.js +6 -6
- package/dist/core/runtime-loop.d.ts +31 -0
- package/dist/core/runtime-loop.js +294 -0
- package/dist/{advanced-scheduler.d.ts → core/scheduler-helpers.d.ts} +6 -2
- package/dist/core/shader.d.ts +2 -2
- package/dist/core/shader.js +1 -1
- package/dist/core/texture-loader.d.ts +1 -1
- package/dist/core/textures.d.ts +1 -1
- package/dist/core/textures.js +1 -1
- package/dist/core/uniforms.d.ts +1 -1
- package/dist/index.d.ts +3 -14
- package/dist/index.js +3 -8
- package/dist/passes/BlitPass.d.ts +1 -1
- package/dist/passes/CopyPass.d.ts +1 -1
- package/dist/passes/CopyPass.js +1 -1
- package/dist/passes/ShaderPass.d.ts +1 -1
- package/dist/passes/index.d.ts +3 -3
- package/dist/passes/index.js +3 -3
- package/dist/svelte/FragCanvas.svelte +220 -0
- package/dist/{FragCanvas.svelte.d.ts → svelte/FragCanvas.svelte.d.ts} +3 -3
- package/dist/{MotionGPUErrorOverlay.svelte → svelte/MotionGPUErrorOverlay.svelte} +1 -1
- package/dist/{MotionGPUErrorOverlay.svelte.d.ts → svelte/MotionGPUErrorOverlay.svelte.d.ts} +1 -1
- package/dist/svelte/advanced.d.ts +11 -0
- package/dist/svelte/advanced.js +6 -0
- package/dist/svelte/frame-context.d.ts +14 -0
- package/dist/svelte/frame-context.js +32 -0
- package/dist/svelte/index.d.ts +15 -0
- package/dist/svelte/index.js +9 -0
- package/dist/{motiongpu-context.d.ts → svelte/motiongpu-context.d.ts} +5 -7
- package/dist/{use-motiongpu-user-context.d.ts → svelte/use-motiongpu-user-context.d.ts} +2 -2
- package/dist/{use-motiongpu-user-context.js → svelte/use-motiongpu-user-context.js} +1 -1
- package/dist/{use-texture.d.ts → svelte/use-texture.d.ts} +2 -2
- package/dist/{use-texture.js → svelte/use-texture.js} +2 -2
- package/package.json +25 -5
- package/dist/FragCanvas.svelte +0 -511
- package/dist/current-writable.d.ts +0 -31
- package/dist/current-writable.js +0 -27
- /package/dist/{advanced-scheduler.js → core/scheduler-helpers.js} +0 -0
- /package/dist/{Portal.svelte → svelte/Portal.svelte} +0 -0
- /package/dist/{Portal.svelte.d.ts → svelte/Portal.svelte.d.ts} +0 -0
- /package/dist/{motiongpu-context.js → svelte/motiongpu-context.js} +0 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { resolveMaterial } from './material.js';
|
|
2
|
+
import { toMotionGPUErrorReport } from './error-report.js';
|
|
3
|
+
import { createRenderer } from './renderer.js';
|
|
4
|
+
import { buildRendererPipelineSignature } from './recompile-policy.js';
|
|
5
|
+
import { assertUniformValueForType } from './uniforms.js';
|
|
6
|
+
function getRendererRetryDelayMs(attempt) {
|
|
7
|
+
return Math.min(8000, 250 * 2 ** Math.max(0, attempt - 1));
|
|
8
|
+
}
|
|
9
|
+
export function createMotionGPURuntimeLoop(options) {
|
|
10
|
+
const { canvas: canvasElement, registry, size } = options;
|
|
11
|
+
let frameId = null;
|
|
12
|
+
let renderer = null;
|
|
13
|
+
let isDisposed = false;
|
|
14
|
+
let previousTime = performance.now() / 1000;
|
|
15
|
+
let activeRendererSignature = '';
|
|
16
|
+
let failedRendererSignature = null;
|
|
17
|
+
let failedRendererAttempts = 0;
|
|
18
|
+
let nextRendererRetryAt = 0;
|
|
19
|
+
let rendererRebuildPromise = null;
|
|
20
|
+
const runtimeUniforms = {};
|
|
21
|
+
const runtimeTextures = {};
|
|
22
|
+
let activeUniforms = {};
|
|
23
|
+
let activeTextures = {};
|
|
24
|
+
let uniformKeys = [];
|
|
25
|
+
let uniformKeySet = new Set();
|
|
26
|
+
let uniformTypes = new Map();
|
|
27
|
+
let textureKeys = [];
|
|
28
|
+
let textureKeySet = new Set();
|
|
29
|
+
let activeMaterialSignature = '';
|
|
30
|
+
let currentCssWidth = -1;
|
|
31
|
+
let currentCssHeight = -1;
|
|
32
|
+
const renderUniforms = {};
|
|
33
|
+
const renderTextures = {};
|
|
34
|
+
const canvasSize = { width: 0, height: 0 };
|
|
35
|
+
let shouldContinueAfterFrame = false;
|
|
36
|
+
const setError = (error, phase) => {
|
|
37
|
+
const report = toMotionGPUErrorReport(error, phase);
|
|
38
|
+
options.reportError(report);
|
|
39
|
+
options.getOnError()?.(report);
|
|
40
|
+
};
|
|
41
|
+
const clearError = () => {
|
|
42
|
+
options.reportError(null);
|
|
43
|
+
};
|
|
44
|
+
const scheduleFrame = () => {
|
|
45
|
+
if (isDisposed || frameId !== null) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
frameId = requestAnimationFrame(renderFrame);
|
|
49
|
+
};
|
|
50
|
+
const requestFrame = () => {
|
|
51
|
+
scheduleFrame();
|
|
52
|
+
};
|
|
53
|
+
const invalidate = (token) => {
|
|
54
|
+
registry.invalidate(token);
|
|
55
|
+
requestFrame();
|
|
56
|
+
};
|
|
57
|
+
const advance = () => {
|
|
58
|
+
registry.advance();
|
|
59
|
+
requestFrame();
|
|
60
|
+
};
|
|
61
|
+
const resetRuntimeMaps = () => {
|
|
62
|
+
for (const key of Object.keys(runtimeUniforms)) {
|
|
63
|
+
if (!uniformKeySet.has(key)) {
|
|
64
|
+
Reflect.deleteProperty(runtimeUniforms, key);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const key of Object.keys(runtimeTextures)) {
|
|
68
|
+
if (!textureKeySet.has(key)) {
|
|
69
|
+
Reflect.deleteProperty(runtimeTextures, key);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const resetRenderPayloadMaps = () => {
|
|
74
|
+
for (const key of Object.keys(renderUniforms)) {
|
|
75
|
+
if (!uniformKeySet.has(key)) {
|
|
76
|
+
Reflect.deleteProperty(renderUniforms, key);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
for (const key of Object.keys(renderTextures)) {
|
|
80
|
+
if (!textureKeySet.has(key)) {
|
|
81
|
+
Reflect.deleteProperty(renderTextures, key);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const syncMaterialRuntimeState = (materialState) => {
|
|
86
|
+
const signatureChanged = activeMaterialSignature !== materialState.signature;
|
|
87
|
+
const defaultsChanged = activeUniforms !== materialState.uniforms || activeTextures !== materialState.textures;
|
|
88
|
+
if (!signatureChanged && !defaultsChanged) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
activeUniforms = materialState.uniforms;
|
|
92
|
+
activeTextures = materialState.textures;
|
|
93
|
+
if (!signatureChanged) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
uniformKeys = materialState.uniformLayout.entries.map((entry) => entry.name);
|
|
97
|
+
uniformTypes = new Map(materialState.uniformLayout.entries.map((entry) => [entry.name, entry.type]));
|
|
98
|
+
textureKeys = materialState.textureKeys;
|
|
99
|
+
uniformKeySet = new Set(uniformKeys);
|
|
100
|
+
textureKeySet = new Set(textureKeys);
|
|
101
|
+
resetRuntimeMaps();
|
|
102
|
+
resetRenderPayloadMaps();
|
|
103
|
+
activeMaterialSignature = materialState.signature;
|
|
104
|
+
};
|
|
105
|
+
const resolveActiveMaterial = () => {
|
|
106
|
+
return resolveMaterial(options.getMaterial());
|
|
107
|
+
};
|
|
108
|
+
const setUniform = (name, value) => {
|
|
109
|
+
if (!uniformKeySet.has(name)) {
|
|
110
|
+
throw new Error(`Unknown uniform "${name}". Declare it in material.uniforms first.`);
|
|
111
|
+
}
|
|
112
|
+
const expectedType = uniformTypes.get(name);
|
|
113
|
+
if (!expectedType) {
|
|
114
|
+
throw new Error(`Unknown uniform type for "${name}"`);
|
|
115
|
+
}
|
|
116
|
+
assertUniformValueForType(expectedType, value);
|
|
117
|
+
runtimeUniforms[name] = value;
|
|
118
|
+
};
|
|
119
|
+
const setTexture = (name, value) => {
|
|
120
|
+
if (!textureKeySet.has(name)) {
|
|
121
|
+
throw new Error(`Unknown texture "${name}". Declare it in material.textures first.`);
|
|
122
|
+
}
|
|
123
|
+
runtimeTextures[name] = value;
|
|
124
|
+
};
|
|
125
|
+
const renderFrame = (timestamp) => {
|
|
126
|
+
frameId = null;
|
|
127
|
+
if (isDisposed) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
let materialState;
|
|
131
|
+
try {
|
|
132
|
+
materialState = resolveActiveMaterial();
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
setError(error, 'initialization');
|
|
136
|
+
scheduleFrame();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
shouldContinueAfterFrame = false;
|
|
140
|
+
const outputColorSpace = options.getOutputColorSpace();
|
|
141
|
+
const rendererSignature = buildRendererPipelineSignature({
|
|
142
|
+
materialSignature: materialState.signature,
|
|
143
|
+
outputColorSpace
|
|
144
|
+
});
|
|
145
|
+
syncMaterialRuntimeState(materialState);
|
|
146
|
+
if (failedRendererSignature && failedRendererSignature !== rendererSignature) {
|
|
147
|
+
failedRendererSignature = null;
|
|
148
|
+
failedRendererAttempts = 0;
|
|
149
|
+
nextRendererRetryAt = 0;
|
|
150
|
+
}
|
|
151
|
+
if (!renderer || activeRendererSignature !== rendererSignature) {
|
|
152
|
+
if (failedRendererSignature === rendererSignature &&
|
|
153
|
+
performance.now() < nextRendererRetryAt) {
|
|
154
|
+
scheduleFrame();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!rendererRebuildPromise) {
|
|
158
|
+
rendererRebuildPromise = (async () => {
|
|
159
|
+
try {
|
|
160
|
+
const nextRenderer = await createRenderer({
|
|
161
|
+
canvas: canvasElement,
|
|
162
|
+
fragmentWgsl: materialState.fragmentWgsl,
|
|
163
|
+
fragmentLineMap: materialState.fragmentLineMap,
|
|
164
|
+
fragmentSource: materialState.fragmentSource,
|
|
165
|
+
includeSources: materialState.includeSources,
|
|
166
|
+
defineBlockSource: materialState.defineBlockSource,
|
|
167
|
+
materialSource: materialState.source,
|
|
168
|
+
uniformLayout: materialState.uniformLayout,
|
|
169
|
+
textureKeys: materialState.textureKeys,
|
|
170
|
+
textureDefinitions: materialState.textures,
|
|
171
|
+
getRenderTargets: options.getRenderTargets,
|
|
172
|
+
getPasses: options.getPasses,
|
|
173
|
+
outputColorSpace,
|
|
174
|
+
getClearColor: options.getClearColor,
|
|
175
|
+
getDpr: () => options.dpr.current,
|
|
176
|
+
adapterOptions: options.getAdapterOptions(),
|
|
177
|
+
deviceDescriptor: options.getDeviceDescriptor()
|
|
178
|
+
});
|
|
179
|
+
if (isDisposed) {
|
|
180
|
+
nextRenderer.destroy();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
renderer?.destroy();
|
|
184
|
+
renderer = nextRenderer;
|
|
185
|
+
activeRendererSignature = rendererSignature;
|
|
186
|
+
failedRendererSignature = null;
|
|
187
|
+
failedRendererAttempts = 0;
|
|
188
|
+
nextRendererRetryAt = 0;
|
|
189
|
+
clearError();
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
failedRendererSignature = rendererSignature;
|
|
193
|
+
failedRendererAttempts += 1;
|
|
194
|
+
const retryDelayMs = getRendererRetryDelayMs(failedRendererAttempts);
|
|
195
|
+
nextRendererRetryAt = performance.now() + retryDelayMs;
|
|
196
|
+
setError(error, 'initialization');
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
rendererRebuildPromise = null;
|
|
200
|
+
scheduleFrame();
|
|
201
|
+
}
|
|
202
|
+
})();
|
|
203
|
+
}
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const time = timestamp / 1000;
|
|
207
|
+
const rawDelta = Math.max(0, time - previousTime);
|
|
208
|
+
const delta = Math.min(rawDelta, options.maxDelta.current);
|
|
209
|
+
previousTime = time;
|
|
210
|
+
const rect = canvasElement.getBoundingClientRect();
|
|
211
|
+
const width = Math.max(0, Math.floor(rect.width));
|
|
212
|
+
const height = Math.max(0, Math.floor(rect.height));
|
|
213
|
+
if (width !== currentCssWidth || height !== currentCssHeight) {
|
|
214
|
+
currentCssWidth = width;
|
|
215
|
+
currentCssHeight = height;
|
|
216
|
+
size.set({ width, height });
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
registry.run({
|
|
220
|
+
time,
|
|
221
|
+
delta,
|
|
222
|
+
setUniform,
|
|
223
|
+
setTexture,
|
|
224
|
+
invalidate,
|
|
225
|
+
advance,
|
|
226
|
+
renderMode: registry.getRenderMode(),
|
|
227
|
+
autoRender: registry.getAutoRender(),
|
|
228
|
+
canvas: canvasElement
|
|
229
|
+
});
|
|
230
|
+
const shouldRenderFrame = registry.shouldRender();
|
|
231
|
+
shouldContinueAfterFrame =
|
|
232
|
+
registry.getRenderMode() === 'always' ||
|
|
233
|
+
(registry.getRenderMode() === 'on-demand' && shouldRenderFrame);
|
|
234
|
+
if (shouldRenderFrame) {
|
|
235
|
+
for (const key of uniformKeys) {
|
|
236
|
+
const runtimeValue = runtimeUniforms[key];
|
|
237
|
+
renderUniforms[key] =
|
|
238
|
+
runtimeValue === undefined ? activeUniforms[key] : runtimeValue;
|
|
239
|
+
}
|
|
240
|
+
for (const key of textureKeys) {
|
|
241
|
+
const runtimeValue = runtimeTextures[key];
|
|
242
|
+
renderTextures[key] =
|
|
243
|
+
runtimeValue === undefined ? (activeTextures[key]?.source ?? null) : runtimeValue;
|
|
244
|
+
}
|
|
245
|
+
canvasSize.width = width;
|
|
246
|
+
canvasSize.height = height;
|
|
247
|
+
renderer.render({
|
|
248
|
+
time,
|
|
249
|
+
delta,
|
|
250
|
+
renderMode: registry.getRenderMode(),
|
|
251
|
+
uniforms: renderUniforms,
|
|
252
|
+
textures: renderTextures,
|
|
253
|
+
canvasSize
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
clearError();
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
setError(error, 'render');
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
registry.endFrame();
|
|
263
|
+
}
|
|
264
|
+
if (shouldContinueAfterFrame) {
|
|
265
|
+
scheduleFrame();
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
(async () => {
|
|
269
|
+
try {
|
|
270
|
+
const initialMaterial = resolveActiveMaterial();
|
|
271
|
+
syncMaterialRuntimeState(initialMaterial);
|
|
272
|
+
activeRendererSignature = '';
|
|
273
|
+
scheduleFrame();
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
setError(error, 'initialization');
|
|
277
|
+
scheduleFrame();
|
|
278
|
+
}
|
|
279
|
+
})();
|
|
280
|
+
return {
|
|
281
|
+
requestFrame,
|
|
282
|
+
invalidate,
|
|
283
|
+
advance,
|
|
284
|
+
destroy: () => {
|
|
285
|
+
isDisposed = true;
|
|
286
|
+
if (frameId !== null) {
|
|
287
|
+
cancelAnimationFrame(frameId);
|
|
288
|
+
frameId = null;
|
|
289
|
+
}
|
|
290
|
+
renderer?.destroy();
|
|
291
|
+
registry.clear();
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import type { FrameProfilingSnapshot, FrameRunTimings, FrameScheduleSnapshot
|
|
1
|
+
import type { FrameProfilingSnapshot, FrameRegistry, FrameRunTimings, FrameScheduleSnapshot } from './frame-registry.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Public scheduler control surface shared by framework adapters.
|
|
4
|
+
*/
|
|
5
|
+
export type MotionGPUScheduler = Pick<FrameRegistry, 'createStage' | 'getStage' | 'setDiagnosticsEnabled' | 'getDiagnosticsEnabled' | 'getLastRunTimings' | 'getSchedule' | 'setProfilingEnabled' | 'setProfilingWindow' | 'resetProfiling' | 'getProfilingEnabled' | 'getProfilingWindow' | 'getProfilingSnapshot'>;
|
|
6
|
+
/**
|
|
7
|
+
* Named scheduler presets exposed from advanced entrypoints.
|
|
4
8
|
*/
|
|
5
9
|
export type SchedulerPreset = 'balanced' | 'debug' | 'performance';
|
|
6
10
|
/**
|
package/dist/core/shader.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { MaterialLineMap, MaterialSourceLocation } from './material-preprocess';
|
|
2
|
-
import type { UniformLayout } from './types';
|
|
1
|
+
import type { MaterialLineMap, MaterialSourceLocation } from './material-preprocess.js';
|
|
2
|
+
import type { UniformLayout } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* 1-based map from generated WGSL lines to original material source lines.
|
|
5
5
|
*/
|
package/dist/core/shader.js
CHANGED
package/dist/core/textures.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TextureData, TextureDefinition, TextureDefinitionMap, TextureUpdateMode, TextureValue } from './types';
|
|
1
|
+
import type { TextureData, TextureDefinition, TextureDefinitionMap, TextureUpdateMode, TextureValue } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Texture definition with defaults and normalized numeric limits applied.
|
|
4
4
|
*/
|
package/dist/core/textures.js
CHANGED
package/dist/core/uniforms.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Root package entrypoint.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Framework-agnostic core entrypoint.
|
|
5
5
|
*/
|
|
6
|
-
export
|
|
7
|
-
export { defineMaterial } from './core/material';
|
|
8
|
-
export { BlitPass, CopyPass, ShaderPass } from './passes';
|
|
9
|
-
export { useMotionGPU } from './motiongpu-context';
|
|
10
|
-
export { useFrame } from './frame-context';
|
|
11
|
-
export { useTexture } from './use-texture';
|
|
12
|
-
export type { FrameInvalidationToken, FrameState, OutputColorSpace, RenderPass, RenderPassContext, RenderPassFlags, RenderPassInputSlot, RenderPassOutputSlot, RenderMode, RenderTarget, RenderTargetDefinition, RenderTargetDefinitionMap, TextureData, TextureDefinition, TextureDefinitionMap, TextureUpdateMode, TextureMap, TextureSource, TextureValue, TypedUniform, UniformMat4Value, UniformMap, UniformType, UniformValue } from './core/types';
|
|
13
|
-
export type { LoadedTexture, TextureDecodeOptions, TextureLoadOptions } from './core/texture-loader';
|
|
14
|
-
export type { FragMaterial, FragMaterialInput, MaterialIncludes, MaterialDefineValue, MaterialDefines, TypedMaterialDefineValue } from './core/material';
|
|
15
|
-
export type { MotionGPUContext } from './motiongpu-context';
|
|
16
|
-
export type { UseFrameOptions, UseFrameResult } from './frame-context';
|
|
17
|
-
export type { TextureUrlInput, UseTextureResult } from './use-texture';
|
|
6
|
+
export * from './core/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Root package entrypoint.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Framework-agnostic core entrypoint.
|
|
5
5
|
*/
|
|
6
|
-
export
|
|
7
|
-
export { defineMaterial } from './core/material';
|
|
8
|
-
export { BlitPass, CopyPass, ShaderPass } from './passes';
|
|
9
|
-
export { useMotionGPU } from './motiongpu-context';
|
|
10
|
-
export { useFrame } from './frame-context';
|
|
11
|
-
export { useTexture } from './use-texture';
|
|
6
|
+
export * from './core/index.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RenderPass, RenderPassContext, RenderPassFlags, RenderPassInputSlot, RenderPassOutputSlot } from '../core/types';
|
|
1
|
+
import type { RenderPass, RenderPassContext, RenderPassFlags, RenderPassInputSlot, RenderPassOutputSlot } from '../core/types.js';
|
|
2
2
|
export interface BlitPassOptions extends RenderPassFlags {
|
|
3
3
|
enabled?: boolean;
|
|
4
4
|
needsSwap?: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RenderPass, RenderPassContext, RenderPassFlags, RenderPassInputSlot, RenderPassOutputSlot } from '../core/types';
|
|
1
|
+
import type { RenderPass, RenderPassContext, RenderPassFlags, RenderPassInputSlot, RenderPassOutputSlot } from '../core/types.js';
|
|
2
2
|
export interface CopyPassOptions extends RenderPassFlags {
|
|
3
3
|
enabled?: boolean;
|
|
4
4
|
needsSwap?: boolean;
|
package/dist/passes/CopyPass.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RenderPass, RenderPassContext, RenderPassFlags, RenderPassInputSlot, RenderPassOutputSlot } from '../core/types';
|
|
1
|
+
import type { RenderPass, RenderPassContext, RenderPassFlags, RenderPassInputSlot, RenderPassOutputSlot } from '../core/types.js';
|
|
2
2
|
export interface ShaderPassOptions extends RenderPassFlags {
|
|
3
3
|
fragment: string;
|
|
4
4
|
enabled?: boolean;
|
package/dist/passes/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { BlitPass, type BlitPassOptions } from './BlitPass';
|
|
2
|
-
export { CopyPass, type CopyPassOptions } from './CopyPass';
|
|
3
|
-
export { ShaderPass, type ShaderPassOptions } from './ShaderPass';
|
|
1
|
+
export { BlitPass, type BlitPassOptions } from './BlitPass.js';
|
|
2
|
+
export { CopyPass, type CopyPassOptions } from './CopyPass.js';
|
|
3
|
+
export { ShaderPass, type ShaderPassOptions } from './ShaderPass.js';
|
package/dist/passes/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { BlitPass } from './BlitPass';
|
|
2
|
-
export { CopyPass } from './CopyPass';
|
|
3
|
-
export { ShaderPass } from './ShaderPass';
|
|
1
|
+
export { BlitPass } from './BlitPass.js';
|
|
2
|
+
export { CopyPass } from './CopyPass.js';
|
|
3
|
+
export { ShaderPass } from './ShaderPass.js';
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
import type { FragMaterial } from '../core/material';
|
|
5
|
+
import { createCurrentWritable as currentWritable } from '../core/current-value';
|
|
6
|
+
import { toMotionGPUErrorReport, type MotionGPUErrorReport } from '../core/error-report';
|
|
7
|
+
import MotionGPUErrorOverlay from './MotionGPUErrorOverlay.svelte';
|
|
8
|
+
import { createMotionGPURuntimeLoop } from '../core/runtime-loop';
|
|
9
|
+
import type {
|
|
10
|
+
FrameInvalidationToken,
|
|
11
|
+
OutputColorSpace,
|
|
12
|
+
RenderPass,
|
|
13
|
+
RenderMode,
|
|
14
|
+
RenderTargetDefinitionMap
|
|
15
|
+
} from '../core/types';
|
|
16
|
+
import { provideMotionGPUContext } from './motiongpu-context';
|
|
17
|
+
import { createFrameRegistry, provideFrameRegistry } from './frame-context';
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
material: FragMaterial;
|
|
21
|
+
renderTargets?: RenderTargetDefinitionMap;
|
|
22
|
+
passes?: RenderPass[];
|
|
23
|
+
clearColor?: [number, number, number, number];
|
|
24
|
+
outputColorSpace?: OutputColorSpace;
|
|
25
|
+
renderMode?: RenderMode;
|
|
26
|
+
autoRender?: boolean;
|
|
27
|
+
maxDelta?: number;
|
|
28
|
+
adapterOptions?: GPURequestAdapterOptions;
|
|
29
|
+
deviceDescriptor?: GPUDeviceDescriptor;
|
|
30
|
+
dpr?: number;
|
|
31
|
+
showErrorOverlay?: boolean;
|
|
32
|
+
errorRenderer?: Snippet<[MotionGPUErrorReport]>;
|
|
33
|
+
onError?: (report: MotionGPUErrorReport) => void;
|
|
34
|
+
class?: string;
|
|
35
|
+
style?: string;
|
|
36
|
+
children?: Snippet;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const initialDpr = typeof window === 'undefined' ? 1 : (window.devicePixelRatio ?? 1);
|
|
40
|
+
|
|
41
|
+
let {
|
|
42
|
+
material,
|
|
43
|
+
renderTargets = {},
|
|
44
|
+
passes = [],
|
|
45
|
+
clearColor = [0, 0, 0, 1],
|
|
46
|
+
outputColorSpace = 'srgb',
|
|
47
|
+
renderMode = 'always',
|
|
48
|
+
autoRender = true,
|
|
49
|
+
maxDelta = 0.1,
|
|
50
|
+
adapterOptions = undefined,
|
|
51
|
+
deviceDescriptor = undefined,
|
|
52
|
+
dpr = initialDpr,
|
|
53
|
+
showErrorOverlay = true,
|
|
54
|
+
errorRenderer = undefined,
|
|
55
|
+
onError = undefined,
|
|
56
|
+
class: className = '',
|
|
57
|
+
style = '',
|
|
58
|
+
children
|
|
59
|
+
}: Props = $props();
|
|
60
|
+
|
|
61
|
+
let canvas: HTMLCanvasElement | undefined;
|
|
62
|
+
let errorReport = $state<MotionGPUErrorReport | null>(null);
|
|
63
|
+
|
|
64
|
+
const bindCanvas = (node: HTMLCanvasElement) => {
|
|
65
|
+
canvas = node;
|
|
66
|
+
return {
|
|
67
|
+
destroy: () => {
|
|
68
|
+
if (canvas === node) {
|
|
69
|
+
canvas = undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const registry = createFrameRegistry({ maxDelta: 0.1 });
|
|
76
|
+
provideFrameRegistry(registry);
|
|
77
|
+
let requestFrameSignal: (() => void) | null = null;
|
|
78
|
+
const requestFrame = (): void => {
|
|
79
|
+
requestFrameSignal?.();
|
|
80
|
+
};
|
|
81
|
+
const invalidateFrame = (token?: FrameInvalidationToken): void => {
|
|
82
|
+
registry.invalidate(token);
|
|
83
|
+
requestFrame();
|
|
84
|
+
};
|
|
85
|
+
const advanceFrame = (): void => {
|
|
86
|
+
registry.advance();
|
|
87
|
+
requestFrame();
|
|
88
|
+
};
|
|
89
|
+
const size = currentWritable({ width: 0, height: 0 });
|
|
90
|
+
const dprState = currentWritable(initialDpr, requestFrame);
|
|
91
|
+
const maxDeltaState = currentWritable<number>(0.1, (value) => {
|
|
92
|
+
registry.setMaxDelta(value);
|
|
93
|
+
requestFrame();
|
|
94
|
+
});
|
|
95
|
+
const renderModeState = currentWritable<RenderMode>('always', (value) => {
|
|
96
|
+
registry.setRenderMode(value);
|
|
97
|
+
requestFrame();
|
|
98
|
+
});
|
|
99
|
+
const autoRenderState = currentWritable<boolean>(true, (value) => {
|
|
100
|
+
registry.setAutoRender(value);
|
|
101
|
+
requestFrame();
|
|
102
|
+
});
|
|
103
|
+
const userState = currentWritable<Record<string | symbol, unknown>>({});
|
|
104
|
+
|
|
105
|
+
provideMotionGPUContext({
|
|
106
|
+
get canvas() {
|
|
107
|
+
return canvas;
|
|
108
|
+
},
|
|
109
|
+
size,
|
|
110
|
+
dpr: dprState,
|
|
111
|
+
maxDelta: maxDeltaState,
|
|
112
|
+
renderMode: renderModeState,
|
|
113
|
+
autoRender: autoRenderState,
|
|
114
|
+
user: userState,
|
|
115
|
+
invalidate: () => invalidateFrame(),
|
|
116
|
+
advance: advanceFrame,
|
|
117
|
+
scheduler: {
|
|
118
|
+
createStage: registry.createStage,
|
|
119
|
+
getStage: registry.getStage,
|
|
120
|
+
setDiagnosticsEnabled: registry.setDiagnosticsEnabled,
|
|
121
|
+
getDiagnosticsEnabled: registry.getDiagnosticsEnabled,
|
|
122
|
+
getLastRunTimings: registry.getLastRunTimings,
|
|
123
|
+
getSchedule: registry.getSchedule,
|
|
124
|
+
setProfilingEnabled: registry.setProfilingEnabled,
|
|
125
|
+
setProfilingWindow: registry.setProfilingWindow,
|
|
126
|
+
resetProfiling: registry.resetProfiling,
|
|
127
|
+
getProfilingEnabled: registry.getProfilingEnabled,
|
|
128
|
+
getProfilingWindow: registry.getProfilingWindow,
|
|
129
|
+
getProfilingSnapshot: registry.getProfilingSnapshot
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
$effect(() => {
|
|
134
|
+
renderModeState.set(renderMode);
|
|
135
|
+
requestFrame();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
$effect(() => {
|
|
139
|
+
autoRenderState.set(autoRender);
|
|
140
|
+
requestFrame();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
$effect(() => {
|
|
144
|
+
maxDeltaState.set(maxDelta);
|
|
145
|
+
requestFrame();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
$effect(() => {
|
|
149
|
+
dprState.set(dpr);
|
|
150
|
+
requestFrame();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
onMount(() => {
|
|
154
|
+
if (!canvas) {
|
|
155
|
+
const report = toMotionGPUErrorReport(
|
|
156
|
+
new Error('Canvas element is not available'),
|
|
157
|
+
'initialization'
|
|
158
|
+
);
|
|
159
|
+
errorReport = report;
|
|
160
|
+
onError?.(report);
|
|
161
|
+
return () => registry.clear();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const runtimeLoop = createMotionGPURuntimeLoop({
|
|
165
|
+
canvas,
|
|
166
|
+
registry,
|
|
167
|
+
size,
|
|
168
|
+
dpr: dprState,
|
|
169
|
+
maxDelta: maxDeltaState,
|
|
170
|
+
getMaterial: () => material,
|
|
171
|
+
getRenderTargets: () => renderTargets,
|
|
172
|
+
getPasses: () => passes,
|
|
173
|
+
getClearColor: () => clearColor,
|
|
174
|
+
getOutputColorSpace: () => outputColorSpace,
|
|
175
|
+
getAdapterOptions: () => adapterOptions,
|
|
176
|
+
getDeviceDescriptor: () => deviceDescriptor,
|
|
177
|
+
getOnError: () => onError,
|
|
178
|
+
reportError: (report) => {
|
|
179
|
+
errorReport = report;
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
requestFrameSignal = runtimeLoop.requestFrame;
|
|
183
|
+
|
|
184
|
+
return () => {
|
|
185
|
+
requestFrameSignal = null;
|
|
186
|
+
runtimeLoop.destroy();
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
</script>
|
|
190
|
+
|
|
191
|
+
<div class="motiongpu-canvas-wrap">
|
|
192
|
+
<canvas use:bindCanvas class={className} {style}></canvas>
|
|
193
|
+
{#if showErrorOverlay && errorReport}
|
|
194
|
+
{#if errorRenderer}
|
|
195
|
+
{@render errorRenderer(errorReport)}
|
|
196
|
+
{:else}
|
|
197
|
+
<MotionGPUErrorOverlay report={errorReport} />
|
|
198
|
+
{/if}
|
|
199
|
+
{/if}
|
|
200
|
+
{@render children?.()}
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<style>
|
|
204
|
+
.motiongpu-canvas-wrap {
|
|
205
|
+
position: relative;
|
|
206
|
+
width: 100%;
|
|
207
|
+
height: 100%;
|
|
208
|
+
min-width: 0;
|
|
209
|
+
min-height: 0;
|
|
210
|
+
overflow: hidden;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
canvas {
|
|
214
|
+
position: absolute;
|
|
215
|
+
inset: 0;
|
|
216
|
+
display: block;
|
|
217
|
+
width: 100%;
|
|
218
|
+
height: 100%;
|
|
219
|
+
}
|
|
220
|
+
</style>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
-
import {
|
|
3
|
-
import { type MotionGPUErrorReport } from '
|
|
4
|
-
import type { OutputColorSpace, RenderPass, RenderMode, RenderTargetDefinitionMap } from '
|
|
2
|
+
import type { FragMaterial } from '../core/material';
|
|
3
|
+
import { type MotionGPUErrorReport } from '../core/error-report';
|
|
4
|
+
import type { OutputColorSpace, RenderPass, RenderMode, RenderTargetDefinitionMap } from '../core/types';
|
|
5
5
|
interface Props {
|
|
6
6
|
material: FragMaterial;
|
|
7
7
|
renderTargets?: RenderTargetDefinitionMap;
|