@motion-core/motion-gpu 0.5.0 → 0.7.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 +35 -2
- package/dist/core/compute-bindgroup-cache.d.ts +13 -0
- package/dist/core/compute-bindgroup-cache.d.ts.map +1 -0
- package/dist/core/compute-bindgroup-cache.js +45 -0
- package/dist/core/compute-bindgroup-cache.js.map +1 -0
- package/dist/core/compute-shader.d.ts +48 -0
- package/dist/core/compute-shader.d.ts.map +1 -1
- package/dist/core/compute-shader.js +34 -1
- package/dist/core/compute-shader.js.map +1 -1
- package/dist/core/error-diagnostics.d.ts +8 -1
- package/dist/core/error-diagnostics.d.ts.map +1 -1
- package/dist/core/error-diagnostics.js +7 -3
- package/dist/core/error-diagnostics.js.map +1 -1
- package/dist/core/error-report.d.ts.map +1 -1
- package/dist/core/error-report.js +19 -1
- package/dist/core/error-report.js.map +1 -1
- package/dist/core/material.d.ts.map +1 -1
- package/dist/core/material.js +2 -1
- package/dist/core/material.js.map +1 -1
- package/dist/core/pointer.d.ts +96 -0
- package/dist/core/pointer.d.ts.map +1 -0
- package/dist/core/pointer.js +71 -0
- package/dist/core/pointer.js.map +1 -0
- package/dist/core/renderer.d.ts.map +1 -1
- package/dist/core/renderer.js +150 -85
- package/dist/core/renderer.js.map +1 -1
- package/dist/core/runtime-loop.d.ts.map +1 -1
- package/dist/core/runtime-loop.js +26 -14
- package/dist/core/runtime-loop.js.map +1 -1
- package/dist/core/shader.d.ts +7 -2
- package/dist/core/shader.d.ts.map +1 -1
- package/dist/core/shader.js +1 -0
- package/dist/core/shader.js.map +1 -1
- package/dist/core/textures.d.ts +4 -0
- package/dist/core/textures.d.ts.map +1 -1
- package/dist/core/textures.js +2 -1
- package/dist/core/textures.js.map +1 -1
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/react/advanced.js +2 -1
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +2 -1
- package/dist/react/use-pointer.d.ts +94 -0
- package/dist/react/use-pointer.d.ts.map +1 -0
- package/dist/react/use-pointer.js +285 -0
- package/dist/react/use-pointer.js.map +1 -0
- package/dist/svelte/advanced.js +2 -1
- package/dist/svelte/index.d.ts +2 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +2 -1
- package/dist/svelte/use-pointer.d.ts +94 -0
- package/dist/svelte/use-pointer.d.ts.map +1 -0
- package/dist/svelte/use-pointer.js +292 -0
- package/dist/svelte/use-pointer.js.map +1 -0
- package/package.json +1 -1
- package/src/lib/core/compute-bindgroup-cache.ts +73 -0
- package/src/lib/core/compute-shader.ts +86 -0
- package/src/lib/core/error-diagnostics.ts +29 -4
- package/src/lib/core/error-report.ts +26 -1
- package/src/lib/core/material.ts +2 -1
- package/src/lib/core/pointer.ts +177 -0
- package/src/lib/core/renderer.ts +198 -92
- package/src/lib/core/runtime-loop.ts +37 -16
- package/src/lib/core/shader.ts +13 -2
- package/src/lib/core/textures.ts +6 -1
- package/src/lib/core/types.ts +1 -1
- package/src/lib/react/index.ts +10 -0
- package/src/lib/react/use-pointer.ts +515 -0
- package/src/lib/svelte/index.ts +10 -0
- package/src/lib/svelte/use-pointer.ts +507 -0
|
@@ -54,6 +54,8 @@ function getRendererRetryDelayMs(attempt: number): number {
|
|
|
54
54
|
return Math.min(8000, 250 * 2 ** Math.max(0, attempt - 1));
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
const ERROR_CLEAR_GRACE_MS = 750;
|
|
58
|
+
|
|
57
59
|
export function createMotionGPURuntimeLoop(
|
|
58
60
|
options: MotionGPURuntimeLoopOptions
|
|
59
61
|
): MotionGPURuntimeLoop {
|
|
@@ -90,6 +92,16 @@ export function createMotionGPURuntimeLoop(
|
|
|
90
92
|
let shouldContinueAfterFrame = false;
|
|
91
93
|
let activeErrorKey: string | null = null;
|
|
92
94
|
let errorHistory: MotionGPUErrorReport[] = [];
|
|
95
|
+
let errorClearReadyAtMs = 0;
|
|
96
|
+
let lastFrameTimestampMs = performance.now();
|
|
97
|
+
|
|
98
|
+
const resolveNowMs = (nowMs?: number): number => {
|
|
99
|
+
if (typeof nowMs === 'number' && Number.isFinite(nowMs)) {
|
|
100
|
+
return nowMs;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return lastFrameTimestampMs;
|
|
104
|
+
};
|
|
93
105
|
|
|
94
106
|
const getHistoryLimit = (): number => {
|
|
95
107
|
const value = options.getErrorHistoryLimit?.() ?? 0;
|
|
@@ -129,12 +141,13 @@ export function createMotionGPURuntimeLoop(
|
|
|
129
141
|
return;
|
|
130
142
|
}
|
|
131
143
|
|
|
132
|
-
errorHistory
|
|
144
|
+
errorHistory.splice(0, errorHistory.length - limit);
|
|
133
145
|
publishErrorHistory();
|
|
134
146
|
};
|
|
135
147
|
|
|
136
|
-
const setError = (error: unknown, phase: MotionGPUErrorPhase): void => {
|
|
148
|
+
const setError = (error: unknown, phase: MotionGPUErrorPhase, nowMs?: number): void => {
|
|
137
149
|
const report = toMotionGPUErrorReport(error, phase);
|
|
150
|
+
errorClearReadyAtMs = resolveNowMs(nowMs) + ERROR_CLEAR_GRACE_MS;
|
|
138
151
|
const reportKey = JSON.stringify({
|
|
139
152
|
phase: report.phase,
|
|
140
153
|
title: report.title,
|
|
@@ -147,9 +160,9 @@ export function createMotionGPURuntimeLoop(
|
|
|
147
160
|
activeErrorKey = reportKey;
|
|
148
161
|
const historyLimit = getHistoryLimit();
|
|
149
162
|
if (historyLimit > 0) {
|
|
150
|
-
errorHistory
|
|
163
|
+
errorHistory.push(report);
|
|
151
164
|
if (errorHistory.length > historyLimit) {
|
|
152
|
-
errorHistory
|
|
165
|
+
errorHistory.splice(0, errorHistory.length - historyLimit);
|
|
153
166
|
}
|
|
154
167
|
publishErrorHistory();
|
|
155
168
|
}
|
|
@@ -166,12 +179,16 @@ export function createMotionGPURuntimeLoop(
|
|
|
166
179
|
}
|
|
167
180
|
};
|
|
168
181
|
|
|
169
|
-
const
|
|
182
|
+
const maybeClearError = (nowMs?: number): void => {
|
|
170
183
|
if (activeErrorKey === null) {
|
|
171
184
|
return;
|
|
172
185
|
}
|
|
186
|
+
if (resolveNowMs(nowMs) < errorClearReadyAtMs) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
173
189
|
|
|
174
190
|
activeErrorKey = null;
|
|
191
|
+
errorClearReadyAtMs = 0;
|
|
175
192
|
options.reportError(null);
|
|
176
193
|
};
|
|
177
194
|
|
|
@@ -200,13 +217,13 @@ export function createMotionGPURuntimeLoop(
|
|
|
200
217
|
const resetRuntimeMaps = (): void => {
|
|
201
218
|
for (const key of Object.keys(runtimeUniforms)) {
|
|
202
219
|
if (!uniformKeySet.has(key)) {
|
|
203
|
-
|
|
220
|
+
delete runtimeUniforms[key];
|
|
204
221
|
}
|
|
205
222
|
}
|
|
206
223
|
|
|
207
224
|
for (const key of Object.keys(runtimeTextures)) {
|
|
208
225
|
if (!textureKeySet.has(key)) {
|
|
209
|
-
|
|
226
|
+
delete runtimeTextures[key];
|
|
210
227
|
}
|
|
211
228
|
}
|
|
212
229
|
};
|
|
@@ -214,13 +231,13 @@ export function createMotionGPURuntimeLoop(
|
|
|
214
231
|
const resetRenderPayloadMaps = (): void => {
|
|
215
232
|
for (const key of Object.keys(renderUniforms)) {
|
|
216
233
|
if (!uniformKeySet.has(key)) {
|
|
217
|
-
|
|
234
|
+
delete renderUniforms[key];
|
|
218
235
|
}
|
|
219
236
|
}
|
|
220
237
|
|
|
221
238
|
for (const key of Object.keys(renderTextures)) {
|
|
222
239
|
if (!textureKeySet.has(key)) {
|
|
223
|
-
|
|
240
|
+
delete renderTextures[key];
|
|
224
241
|
}
|
|
225
242
|
}
|
|
226
243
|
};
|
|
@@ -345,13 +362,14 @@ export function createMotionGPURuntimeLoop(
|
|
|
345
362
|
if (isDisposed) {
|
|
346
363
|
return;
|
|
347
364
|
}
|
|
365
|
+
lastFrameTimestampMs = timestamp;
|
|
348
366
|
syncErrorHistory();
|
|
349
367
|
|
|
350
368
|
let materialState: ResolvedMaterial;
|
|
351
369
|
try {
|
|
352
370
|
materialState = resolveActiveMaterial();
|
|
353
371
|
} catch (error) {
|
|
354
|
-
setError(error, 'initialization');
|
|
372
|
+
setError(error, 'initialization', timestamp);
|
|
355
373
|
scheduleFrame();
|
|
356
374
|
return;
|
|
357
375
|
}
|
|
@@ -418,7 +436,7 @@ export function createMotionGPURuntimeLoop(
|
|
|
418
436
|
failedRendererSignature = null;
|
|
419
437
|
failedRendererAttempts = 0;
|
|
420
438
|
nextRendererRetryAt = 0;
|
|
421
|
-
|
|
439
|
+
maybeClearError(performance.now());
|
|
422
440
|
} catch (error) {
|
|
423
441
|
failedRendererSignature = rendererSignature;
|
|
424
442
|
failedRendererAttempts += 1;
|
|
@@ -490,15 +508,18 @@ export function createMotionGPURuntimeLoop(
|
|
|
490
508
|
uniforms: renderUniforms,
|
|
491
509
|
textures: renderTextures,
|
|
492
510
|
canvasSize,
|
|
493
|
-
|
|
494
|
-
? { pendingStorageWrites: pendingStorageWrites.splice(0) }
|
|
495
|
-
: {})
|
|
511
|
+
pendingStorageWrites: pendingStorageWrites.length > 0 ? pendingStorageWrites : undefined
|
|
496
512
|
});
|
|
513
|
+
// Clear in-place after synchronous render() completes — avoids
|
|
514
|
+
// the splice(0) copy and eliminates the conditional spread object.
|
|
515
|
+
if (pendingStorageWrites.length > 0) {
|
|
516
|
+
pendingStorageWrites.length = 0;
|
|
517
|
+
}
|
|
497
518
|
}
|
|
498
519
|
|
|
499
|
-
|
|
520
|
+
maybeClearError(timestamp);
|
|
500
521
|
} catch (error) {
|
|
501
|
-
setError(error, 'render');
|
|
522
|
+
setError(error, 'render', timestamp);
|
|
502
523
|
} finally {
|
|
503
524
|
registry.endFrame();
|
|
504
525
|
}
|
package/src/lib/core/shader.ts
CHANGED
|
@@ -2,6 +2,11 @@ import { assertUniformName } from './uniforms.js';
|
|
|
2
2
|
import type { MaterialLineMap, MaterialSourceLocation } from './material-preprocess.js';
|
|
3
3
|
import type { StorageBufferType, UniformLayout } from './types.js';
|
|
4
4
|
|
|
5
|
+
type ComputeShaderSourceLocation = {
|
|
6
|
+
kind: 'compute';
|
|
7
|
+
line: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
5
10
|
/**
|
|
6
11
|
* Fallback uniform field used when no custom uniforms are provided.
|
|
7
12
|
*/
|
|
@@ -146,7 +151,7 @@ function buildFragmentOutput(keepAliveExpression: string, enableSrgbTransform: b
|
|
|
146
151
|
/**
|
|
147
152
|
* 1-based map from generated WGSL lines to original material source lines.
|
|
148
153
|
*/
|
|
149
|
-
export type ShaderLineMap = Array<MaterialSourceLocation | null>;
|
|
154
|
+
export type ShaderLineMap = Array<(MaterialSourceLocation | ComputeShaderSourceLocation) | null>;
|
|
150
155
|
|
|
151
156
|
/**
|
|
152
157
|
* Result of shader source generation with line mapping metadata.
|
|
@@ -284,7 +289,9 @@ export function buildShaderSourceWithMap(
|
|
|
284
289
|
/**
|
|
285
290
|
* Converts source location metadata to user-facing diagnostics label.
|
|
286
291
|
*/
|
|
287
|
-
export function formatShaderSourceLocation(
|
|
292
|
+
export function formatShaderSourceLocation(
|
|
293
|
+
location: (MaterialSourceLocation | ComputeShaderSourceLocation) | null
|
|
294
|
+
): string | null {
|
|
288
295
|
if (!location) {
|
|
289
296
|
return null;
|
|
290
297
|
}
|
|
@@ -297,5 +304,9 @@ export function formatShaderSourceLocation(location: MaterialSourceLocation | nu
|
|
|
297
304
|
return `include <${location.include}> line ${location.line}`;
|
|
298
305
|
}
|
|
299
306
|
|
|
307
|
+
if (location.kind === 'compute') {
|
|
308
|
+
return `compute line ${location.line}`;
|
|
309
|
+
}
|
|
310
|
+
|
|
300
311
|
return `define "${location.define}" line ${location.line}`;
|
|
301
312
|
}
|
package/src/lib/core/textures.ts
CHANGED
|
@@ -59,6 +59,10 @@ export interface NormalizedTextureDefinition {
|
|
|
59
59
|
* Whether this texture is a storage texture (writable by compute).
|
|
60
60
|
*/
|
|
61
61
|
storage: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Whether this texture should be exposed as a fragment-stage sampled binding.
|
|
64
|
+
*/
|
|
65
|
+
fragmentVisible: boolean;
|
|
62
66
|
/**
|
|
63
67
|
* Explicit width for storage textures. Undefined when derived from source.
|
|
64
68
|
*/
|
|
@@ -115,7 +119,8 @@ export function normalizeTextureDefinition(
|
|
|
115
119
|
filter: definition?.filter ?? DEFAULT_TEXTURE_FILTER,
|
|
116
120
|
addressModeU: definition?.addressModeU ?? DEFAULT_TEXTURE_ADDRESS_MODE,
|
|
117
121
|
addressModeV: definition?.addressModeV ?? DEFAULT_TEXTURE_ADDRESS_MODE,
|
|
118
|
-
storage: isStorage
|
|
122
|
+
storage: isStorage,
|
|
123
|
+
fragmentVisible: definition?.fragmentVisible ?? true
|
|
119
124
|
};
|
|
120
125
|
|
|
121
126
|
if (definition?.width !== undefined) {
|
package/src/lib/core/types.ts
CHANGED
|
@@ -726,7 +726,7 @@ export interface Renderer {
|
|
|
726
726
|
width: number;
|
|
727
727
|
height: number;
|
|
728
728
|
};
|
|
729
|
-
pendingStorageWrites?: PendingStorageWrite[];
|
|
729
|
+
pendingStorageWrites?: PendingStorageWrite[] | undefined;
|
|
730
730
|
}) => void;
|
|
731
731
|
/**
|
|
732
732
|
* Returns the GPU buffer for a named storage buffer, if allocated.
|
package/src/lib/react/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ export {
|
|
|
12
12
|
} from '../passes/index.js';
|
|
13
13
|
export { useMotionGPU } from './motiongpu-context.js';
|
|
14
14
|
export { useFrame } from './frame-context.js';
|
|
15
|
+
export { usePointer } from './use-pointer.js';
|
|
15
16
|
export { useTexture } from './use-texture.js';
|
|
16
17
|
export type {
|
|
17
18
|
FrameInvalidationToken,
|
|
@@ -56,6 +57,15 @@ export type {
|
|
|
56
57
|
} from '../core/material.js';
|
|
57
58
|
export type { MotionGPUContext } from './motiongpu-context.js';
|
|
58
59
|
export type { UseFrameOptions, UseFrameResult } from './frame-context.js';
|
|
60
|
+
export type {
|
|
61
|
+
PointerClick,
|
|
62
|
+
PointerFrameRequestMode,
|
|
63
|
+
PointerKind,
|
|
64
|
+
PointerPoint,
|
|
65
|
+
PointerState,
|
|
66
|
+
UsePointerOptions,
|
|
67
|
+
UsePointerResult
|
|
68
|
+
} from './use-pointer.js';
|
|
59
69
|
export type { TextureUrlInput, UseTextureResult } from './use-texture.js';
|
|
60
70
|
export type {
|
|
61
71
|
StorageBufferAccess,
|