@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.
Files changed (71) hide show
  1. package/README.md +35 -2
  2. package/dist/core/compute-bindgroup-cache.d.ts +13 -0
  3. package/dist/core/compute-bindgroup-cache.d.ts.map +1 -0
  4. package/dist/core/compute-bindgroup-cache.js +45 -0
  5. package/dist/core/compute-bindgroup-cache.js.map +1 -0
  6. package/dist/core/compute-shader.d.ts +48 -0
  7. package/dist/core/compute-shader.d.ts.map +1 -1
  8. package/dist/core/compute-shader.js +34 -1
  9. package/dist/core/compute-shader.js.map +1 -1
  10. package/dist/core/error-diagnostics.d.ts +8 -1
  11. package/dist/core/error-diagnostics.d.ts.map +1 -1
  12. package/dist/core/error-diagnostics.js +7 -3
  13. package/dist/core/error-diagnostics.js.map +1 -1
  14. package/dist/core/error-report.d.ts.map +1 -1
  15. package/dist/core/error-report.js +19 -1
  16. package/dist/core/error-report.js.map +1 -1
  17. package/dist/core/material.d.ts.map +1 -1
  18. package/dist/core/material.js +2 -1
  19. package/dist/core/material.js.map +1 -1
  20. package/dist/core/pointer.d.ts +96 -0
  21. package/dist/core/pointer.d.ts.map +1 -0
  22. package/dist/core/pointer.js +71 -0
  23. package/dist/core/pointer.js.map +1 -0
  24. package/dist/core/renderer.d.ts.map +1 -1
  25. package/dist/core/renderer.js +150 -85
  26. package/dist/core/renderer.js.map +1 -1
  27. package/dist/core/runtime-loop.d.ts.map +1 -1
  28. package/dist/core/runtime-loop.js +26 -14
  29. package/dist/core/runtime-loop.js.map +1 -1
  30. package/dist/core/shader.d.ts +7 -2
  31. package/dist/core/shader.d.ts.map +1 -1
  32. package/dist/core/shader.js +1 -0
  33. package/dist/core/shader.js.map +1 -1
  34. package/dist/core/textures.d.ts +4 -0
  35. package/dist/core/textures.d.ts.map +1 -1
  36. package/dist/core/textures.js +2 -1
  37. package/dist/core/textures.js.map +1 -1
  38. package/dist/core/types.d.ts +1 -1
  39. package/dist/core/types.d.ts.map +1 -1
  40. package/dist/react/advanced.js +2 -1
  41. package/dist/react/index.d.ts +2 -0
  42. package/dist/react/index.d.ts.map +1 -1
  43. package/dist/react/index.js +2 -1
  44. package/dist/react/use-pointer.d.ts +94 -0
  45. package/dist/react/use-pointer.d.ts.map +1 -0
  46. package/dist/react/use-pointer.js +285 -0
  47. package/dist/react/use-pointer.js.map +1 -0
  48. package/dist/svelte/advanced.js +2 -1
  49. package/dist/svelte/index.d.ts +2 -0
  50. package/dist/svelte/index.d.ts.map +1 -1
  51. package/dist/svelte/index.js +2 -1
  52. package/dist/svelte/use-pointer.d.ts +94 -0
  53. package/dist/svelte/use-pointer.d.ts.map +1 -0
  54. package/dist/svelte/use-pointer.js +292 -0
  55. package/dist/svelte/use-pointer.js.map +1 -0
  56. package/package.json +1 -1
  57. package/src/lib/core/compute-bindgroup-cache.ts +73 -0
  58. package/src/lib/core/compute-shader.ts +86 -0
  59. package/src/lib/core/error-diagnostics.ts +29 -4
  60. package/src/lib/core/error-report.ts +26 -1
  61. package/src/lib/core/material.ts +2 -1
  62. package/src/lib/core/pointer.ts +177 -0
  63. package/src/lib/core/renderer.ts +198 -92
  64. package/src/lib/core/runtime-loop.ts +37 -16
  65. package/src/lib/core/shader.ts +13 -2
  66. package/src/lib/core/textures.ts +6 -1
  67. package/src/lib/core/types.ts +1 -1
  68. package/src/lib/react/index.ts +10 -0
  69. package/src/lib/react/use-pointer.ts +515 -0
  70. package/src/lib/svelte/index.ts +10 -0
  71. 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 = errorHistory.slice(errorHistory.length - limit);
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 = [...errorHistory, report];
163
+ errorHistory.push(report);
151
164
  if (errorHistory.length > historyLimit) {
152
- errorHistory = errorHistory.slice(errorHistory.length - historyLimit);
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 clearError = (): void => {
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
- Reflect.deleteProperty(runtimeUniforms, key);
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
- Reflect.deleteProperty(runtimeTextures, key);
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
- Reflect.deleteProperty(renderUniforms, key);
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
- Reflect.deleteProperty(renderTextures, key);
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
- clearError();
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
- ...(pendingStorageWrites.length > 0
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
- clearError();
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
  }
@@ -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(location: MaterialSourceLocation | null): string | null {
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
  }
@@ -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) {
@@ -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.
@@ -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,