@motion-core/motion-gpu 0.4.1 → 0.4.2

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 (205) hide show
  1. package/dist/advanced.d.ts +1 -0
  2. package/dist/advanced.d.ts.map +1 -0
  3. package/dist/advanced.js +12 -6
  4. package/dist/core/advanced.d.ts +1 -0
  5. package/dist/core/advanced.d.ts.map +1 -0
  6. package/dist/core/advanced.js +12 -5
  7. package/dist/core/current-value.d.ts +1 -0
  8. package/dist/core/current-value.d.ts.map +1 -0
  9. package/dist/core/current-value.js +35 -34
  10. package/dist/core/current-value.js.map +1 -0
  11. package/dist/core/error-diagnostics.d.ts +1 -0
  12. package/dist/core/error-diagnostics.d.ts.map +1 -0
  13. package/dist/core/error-diagnostics.js +70 -137
  14. package/dist/core/error-diagnostics.js.map +1 -0
  15. package/dist/core/error-report.d.ts +1 -0
  16. package/dist/core/error-report.d.ts.map +1 -0
  17. package/dist/core/error-report.js +184 -233
  18. package/dist/core/error-report.js.map +1 -0
  19. package/dist/core/frame-registry.d.ts +1 -0
  20. package/dist/core/frame-registry.d.ts.map +1 -0
  21. package/dist/core/frame-registry.js +546 -662
  22. package/dist/core/frame-registry.js.map +1 -0
  23. package/dist/core/index.d.ts +1 -0
  24. package/dist/core/index.d.ts.map +1 -0
  25. package/dist/core/index.js +11 -12
  26. package/dist/core/material-preprocess.d.ts +1 -0
  27. package/dist/core/material-preprocess.d.ts.map +1 -0
  28. package/dist/core/material-preprocess.js +128 -151
  29. package/dist/core/material-preprocess.js.map +1 -0
  30. package/dist/core/material.d.ts +1 -0
  31. package/dist/core/material.d.ts.map +1 -0
  32. package/dist/core/material.js +263 -317
  33. package/dist/core/material.js.map +1 -0
  34. package/dist/core/recompile-policy.d.ts +1 -0
  35. package/dist/core/recompile-policy.d.ts.map +1 -0
  36. package/dist/core/recompile-policy.js +18 -13
  37. package/dist/core/recompile-policy.js.map +1 -0
  38. package/dist/core/render-graph.d.ts +1 -0
  39. package/dist/core/render-graph.d.ts.map +1 -0
  40. package/dist/core/render-graph.js +61 -68
  41. package/dist/core/render-graph.js.map +1 -0
  42. package/dist/core/render-targets.d.ts +1 -0
  43. package/dist/core/render-targets.d.ts.map +1 -0
  44. package/dist/core/render-targets.js +52 -53
  45. package/dist/core/render-targets.js.map +1 -0
  46. package/dist/core/renderer.d.ts +1 -0
  47. package/dist/core/renderer.d.ts.map +1 -0
  48. package/dist/core/renderer.js +942 -1081
  49. package/dist/core/renderer.js.map +1 -0
  50. package/dist/core/runtime-loop.d.ts +1 -0
  51. package/dist/core/runtime-loop.d.ts.map +1 -0
  52. package/dist/core/runtime-loop.js +305 -362
  53. package/dist/core/runtime-loop.js.map +1 -0
  54. package/dist/core/scheduler-helpers.d.ts +1 -0
  55. package/dist/core/scheduler-helpers.d.ts.map +1 -0
  56. package/dist/core/scheduler-helpers.js +52 -51
  57. package/dist/core/scheduler-helpers.js.map +1 -0
  58. package/dist/core/shader.d.ts +1 -0
  59. package/dist/core/shader.d.ts.map +1 -0
  60. package/dist/core/shader.js +92 -117
  61. package/dist/core/shader.js.map +1 -0
  62. package/dist/core/texture-loader.d.ts +1 -0
  63. package/dist/core/texture-loader.d.ts.map +1 -0
  64. package/dist/core/texture-loader.js +205 -273
  65. package/dist/core/texture-loader.js.map +1 -0
  66. package/dist/core/textures.d.ts +1 -0
  67. package/dist/core/textures.d.ts.map +1 -0
  68. package/dist/core/textures.js +106 -116
  69. package/dist/core/textures.js.map +1 -0
  70. package/dist/core/types.d.ts +1 -0
  71. package/dist/core/types.d.ts.map +1 -0
  72. package/dist/core/types.js +0 -4
  73. package/dist/core/uniforms.d.ts +1 -0
  74. package/dist/core/uniforms.d.ts.map +1 -0
  75. package/dist/core/uniforms.js +170 -191
  76. package/dist/core/uniforms.js.map +1 -0
  77. package/dist/index.d.ts +1 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +11 -6
  80. package/dist/passes/BlitPass.d.ts +1 -0
  81. package/dist/passes/BlitPass.d.ts.map +1 -0
  82. package/dist/passes/BlitPass.js +23 -18
  83. package/dist/passes/BlitPass.js.map +1 -0
  84. package/dist/passes/CopyPass.d.ts +1 -0
  85. package/dist/passes/CopyPass.d.ts.map +1 -0
  86. package/dist/passes/CopyPass.js +58 -52
  87. package/dist/passes/CopyPass.js.map +1 -0
  88. package/dist/passes/FullscreenPass.d.ts +1 -0
  89. package/dist/passes/FullscreenPass.d.ts.map +1 -0
  90. package/dist/passes/FullscreenPass.js +127 -130
  91. package/dist/passes/FullscreenPass.js.map +1 -0
  92. package/dist/passes/ShaderPass.d.ts +1 -0
  93. package/dist/passes/ShaderPass.d.ts.map +1 -0
  94. package/dist/passes/ShaderPass.js +40 -37
  95. package/dist/passes/ShaderPass.js.map +1 -0
  96. package/dist/passes/index.d.ts +1 -0
  97. package/dist/passes/index.d.ts.map +1 -0
  98. package/dist/passes/index.js +4 -3
  99. package/dist/react/FragCanvas.d.ts +1 -0
  100. package/dist/react/FragCanvas.d.ts.map +1 -0
  101. package/dist/react/FragCanvas.js +234 -211
  102. package/dist/react/FragCanvas.js.map +1 -0
  103. package/dist/react/MotionGPUErrorOverlay.d.ts +1 -0
  104. package/dist/react/MotionGPUErrorOverlay.d.ts.map +1 -0
  105. package/dist/react/MotionGPUErrorOverlay.js +96 -13
  106. package/dist/react/MotionGPUErrorOverlay.js.map +1 -0
  107. package/dist/react/Portal.d.ts +1 -0
  108. package/dist/react/Portal.d.ts.map +1 -0
  109. package/dist/react/Portal.js +18 -21
  110. package/dist/react/Portal.js.map +1 -0
  111. package/dist/react/advanced.d.ts +1 -0
  112. package/dist/react/advanced.d.ts.map +1 -0
  113. package/dist/react/advanced.js +12 -6
  114. package/dist/react/frame-context.d.ts +1 -0
  115. package/dist/react/frame-context.d.ts.map +1 -0
  116. package/dist/react/frame-context.js +88 -94
  117. package/dist/react/frame-context.js.map +1 -0
  118. package/dist/react/index.d.ts +1 -0
  119. package/dist/react/index.d.ts.map +1 -0
  120. package/dist/react/index.js +10 -9
  121. package/dist/react/motiongpu-context.d.ts +1 -0
  122. package/dist/react/motiongpu-context.d.ts.map +1 -0
  123. package/dist/react/motiongpu-context.js +18 -15
  124. package/dist/react/motiongpu-context.js.map +1 -0
  125. package/dist/react/use-motiongpu-user-context.d.ts +1 -0
  126. package/dist/react/use-motiongpu-user-context.d.ts.map +1 -0
  127. package/dist/react/use-motiongpu-user-context.js +83 -82
  128. package/dist/react/use-motiongpu-user-context.js.map +1 -0
  129. package/dist/react/use-texture.d.ts +1 -0
  130. package/dist/react/use-texture.d.ts.map +1 -0
  131. package/dist/react/use-texture.js +132 -152
  132. package/dist/react/use-texture.js.map +1 -0
  133. package/dist/svelte/FragCanvas.svelte.d.ts +1 -0
  134. package/dist/svelte/FragCanvas.svelte.d.ts.map +1 -0
  135. package/dist/svelte/MotionGPUErrorOverlay.svelte.d.ts +1 -0
  136. package/dist/svelte/MotionGPUErrorOverlay.svelte.d.ts.map +1 -0
  137. package/dist/svelte/Portal.svelte.d.ts +1 -0
  138. package/dist/svelte/Portal.svelte.d.ts.map +1 -0
  139. package/dist/svelte/advanced.d.ts +1 -0
  140. package/dist/svelte/advanced.d.ts.map +1 -0
  141. package/dist/svelte/advanced.js +11 -6
  142. package/dist/svelte/frame-context.d.ts +1 -0
  143. package/dist/svelte/frame-context.d.ts.map +1 -0
  144. package/dist/svelte/frame-context.js +27 -27
  145. package/dist/svelte/frame-context.js.map +1 -0
  146. package/dist/svelte/index.d.ts +1 -0
  147. package/dist/svelte/index.d.ts.map +1 -0
  148. package/dist/svelte/index.js +10 -9
  149. package/dist/svelte/motiongpu-context.d.ts +1 -0
  150. package/dist/svelte/motiongpu-context.d.ts.map +1 -0
  151. package/dist/svelte/motiongpu-context.js +24 -21
  152. package/dist/svelte/motiongpu-context.js.map +1 -0
  153. package/dist/svelte/use-motiongpu-user-context.d.ts +1 -0
  154. package/dist/svelte/use-motiongpu-user-context.d.ts.map +1 -0
  155. package/dist/svelte/use-motiongpu-user-context.js +69 -70
  156. package/dist/svelte/use-motiongpu-user-context.js.map +1 -0
  157. package/dist/svelte/use-texture.d.ts +1 -0
  158. package/dist/svelte/use-texture.d.ts.map +1 -0
  159. package/dist/svelte/use-texture.js +125 -147
  160. package/dist/svelte/use-texture.js.map +1 -0
  161. package/package.json +12 -7
  162. package/src/lib/advanced.ts +6 -0
  163. package/src/lib/core/advanced.ts +12 -0
  164. package/src/lib/core/current-value.ts +64 -0
  165. package/src/lib/core/error-diagnostics.ts +236 -0
  166. package/src/lib/core/error-report.ts +406 -0
  167. package/src/lib/core/frame-registry.ts +1189 -0
  168. package/src/lib/core/index.ts +77 -0
  169. package/src/lib/core/material-preprocess.ts +284 -0
  170. package/src/lib/core/material.ts +667 -0
  171. package/src/lib/core/recompile-policy.ts +31 -0
  172. package/src/lib/core/render-graph.ts +143 -0
  173. package/src/lib/core/render-targets.ts +107 -0
  174. package/src/lib/core/renderer.ts +1547 -0
  175. package/src/lib/core/runtime-loop.ts +458 -0
  176. package/src/lib/core/scheduler-helpers.ts +136 -0
  177. package/src/lib/core/shader.ts +258 -0
  178. package/src/lib/core/texture-loader.ts +476 -0
  179. package/src/lib/core/textures.ts +235 -0
  180. package/src/lib/core/types.ts +582 -0
  181. package/src/lib/core/uniforms.ts +282 -0
  182. package/src/lib/index.ts +6 -0
  183. package/src/lib/passes/BlitPass.ts +54 -0
  184. package/src/lib/passes/CopyPass.ts +80 -0
  185. package/src/lib/passes/FullscreenPass.ts +173 -0
  186. package/src/lib/passes/ShaderPass.ts +88 -0
  187. package/src/lib/passes/index.ts +3 -0
  188. package/src/lib/react/FragCanvas.tsx +345 -0
  189. package/src/lib/react/MotionGPUErrorOverlay.tsx +392 -0
  190. package/src/lib/react/Portal.tsx +34 -0
  191. package/src/lib/react/advanced.ts +36 -0
  192. package/src/lib/react/frame-context.ts +169 -0
  193. package/src/lib/react/index.ts +51 -0
  194. package/src/lib/react/motiongpu-context.ts +88 -0
  195. package/src/lib/react/use-motiongpu-user-context.ts +186 -0
  196. package/src/lib/react/use-texture.ts +233 -0
  197. package/src/lib/svelte/FragCanvas.svelte +249 -0
  198. package/src/lib/svelte/MotionGPUErrorOverlay.svelte +382 -0
  199. package/src/lib/svelte/Portal.svelte +31 -0
  200. package/src/lib/svelte/advanced.ts +32 -0
  201. package/src/lib/svelte/frame-context.ts +87 -0
  202. package/src/lib/svelte/index.ts +51 -0
  203. package/src/lib/svelte/motiongpu-context.ts +97 -0
  204. package/src/lib/svelte/use-motiongpu-user-context.ts +145 -0
  205. package/src/lib/svelte/use-texture.ts +232 -0
@@ -1,157 +1,135 @@
1
- import { onDestroy } from 'svelte';
2
- import { createCurrentWritable as currentWritable } from '../core/current-value.js';
3
- import { isAbortError, loadTexturesFromUrls } from '../core/texture-loader.js';
4
- import { toMotionGPUErrorReport } from '../core/error-report.js';
1
+ import { toMotionGPUErrorReport } from "../core/error-report.js";
2
+ import { createCurrentWritable } from "../core/current-value.js";
3
+ import { isAbortError, loadTexturesFromUrls } from "../core/texture-loader.js";
4
+ import { onDestroy } from "svelte";
5
+ //#region src/lib/svelte/use-texture.ts
5
6
  /**
6
- * Normalizes unknown thrown values to an `Error` instance.
7
- */
7
+ * Normalizes unknown thrown values to an `Error` instance.
8
+ */
8
9
  function toError(error) {
9
- if (error instanceof Error) {
10
- return error;
11
- }
12
- return new Error('Unknown texture loading error');
10
+ if (error instanceof Error) return error;
11
+ return /* @__PURE__ */ new Error("Unknown texture loading error");
13
12
  }
14
13
  /**
15
- * Releases GPU-side resources for a list of loaded textures.
16
- */
14
+ * Releases GPU-side resources for a list of loaded textures.
15
+ */
17
16
  function disposeTextures(list) {
18
- for (const texture of list ?? []) {
19
- texture.dispose();
20
- }
17
+ for (const texture of list ?? []) texture.dispose();
21
18
  }
22
19
  function mergeAbortSignals(primary, secondary) {
23
- if (!secondary) {
24
- return {
25
- signal: primary,
26
- dispose: () => { }
27
- };
28
- }
29
- if (typeof AbortSignal.any === 'function') {
30
- return {
31
- signal: AbortSignal.any([primary, secondary]),
32
- dispose: () => { }
33
- };
34
- }
35
- const fallback = new AbortController();
36
- let disposed = false;
37
- const cleanup = () => {
38
- if (disposed) {
39
- return;
40
- }
41
- disposed = true;
42
- primary.removeEventListener('abort', abort);
43
- secondary.removeEventListener('abort', abort);
44
- };
45
- const abort = () => fallback.abort();
46
- primary.addEventListener('abort', abort, { once: true });
47
- secondary.addEventListener('abort', abort, { once: true });
48
- return {
49
- signal: fallback.signal,
50
- dispose: cleanup
51
- };
20
+ if (!secondary) return {
21
+ signal: primary,
22
+ dispose: () => {}
23
+ };
24
+ if (typeof AbortSignal.any === "function") return {
25
+ signal: AbortSignal.any([primary, secondary]),
26
+ dispose: () => {}
27
+ };
28
+ const fallback = new AbortController();
29
+ let disposed = false;
30
+ const cleanup = () => {
31
+ if (disposed) return;
32
+ disposed = true;
33
+ primary.removeEventListener("abort", abort);
34
+ secondary.removeEventListener("abort", abort);
35
+ };
36
+ const abort = () => fallback.abort();
37
+ primary.addEventListener("abort", abort, { once: true });
38
+ secondary.addEventListener("abort", abort, { once: true });
39
+ return {
40
+ signal: fallback.signal,
41
+ dispose: cleanup
42
+ };
52
43
  }
53
44
  /**
54
- * Loads textures from URLs and exposes reactive loading/error state.
55
- *
56
- * @param urlInput - URLs array or lazy URL provider.
57
- * @param optionsInput - Loader options object or lazy options provider.
58
- * @returns Reactive texture loading state with reload support.
59
- */
60
- export function useTexture(urlInput, optionsInput = {}) {
61
- const textures = currentWritable(null);
62
- const loading = currentWritable(true);
63
- const error = currentWritable(null);
64
- const errorReport = currentWritable(null);
65
- let disposed = false;
66
- let requestVersion = 0;
67
- let activeController = null;
68
- let runningLoad = null;
69
- let reloadQueued = false;
70
- const getUrls = typeof urlInput === 'function' ? urlInput : () => urlInput;
71
- const getOptions = typeof optionsInput === 'function'
72
- ? optionsInput
73
- : () => optionsInput;
74
- const executeLoad = async () => {
75
- if (disposed) {
76
- return;
77
- }
78
- const version = ++requestVersion;
79
- const controller = new AbortController();
80
- activeController = controller;
81
- loading.set(true);
82
- error.set(null);
83
- errorReport.set(null);
84
- const previous = textures.current;
85
- const options = getOptions() ?? {};
86
- const mergedSignal = mergeAbortSignals(controller.signal, options.signal);
87
- try {
88
- const loaded = await loadTexturesFromUrls(getUrls(), {
89
- ...options,
90
- signal: mergedSignal.signal
91
- });
92
- if (disposed || version !== requestVersion) {
93
- disposeTextures(loaded);
94
- return;
95
- }
96
- textures.set(loaded);
97
- disposeTextures(previous);
98
- }
99
- catch (nextError) {
100
- if (disposed || version !== requestVersion) {
101
- return;
102
- }
103
- if (isAbortError(nextError)) {
104
- return;
105
- }
106
- disposeTextures(previous);
107
- textures.set(null);
108
- const normalizedError = toError(nextError);
109
- error.set(normalizedError);
110
- errorReport.set(toMotionGPUErrorReport(normalizedError, 'initialization'));
111
- }
112
- finally {
113
- if (!disposed && version === requestVersion) {
114
- loading.set(false);
115
- }
116
- if (activeController === controller) {
117
- activeController = null;
118
- }
119
- mergedSignal.dispose();
120
- }
121
- };
122
- const runLoadLoop = async () => {
123
- do {
124
- reloadQueued = false;
125
- await executeLoad();
126
- } while (reloadQueued && !disposed);
127
- };
128
- const load = () => {
129
- activeController?.abort();
130
- if (runningLoad) {
131
- reloadQueued = true;
132
- return runningLoad;
133
- }
134
- const pending = runLoadLoop();
135
- const trackedPending = pending.finally(() => {
136
- if (runningLoad === trackedPending) {
137
- runningLoad = null;
138
- }
139
- });
140
- runningLoad = trackedPending;
141
- return trackedPending;
142
- };
143
- void load();
144
- onDestroy(() => {
145
- disposed = true;
146
- requestVersion += 1;
147
- activeController?.abort();
148
- disposeTextures(textures.current);
149
- });
150
- return {
151
- textures,
152
- loading,
153
- error,
154
- errorReport,
155
- reload: load
156
- };
45
+ * Loads textures from URLs and exposes reactive loading/error state.
46
+ *
47
+ * @param urlInput - URLs array or lazy URL provider.
48
+ * @param optionsInput - Loader options object or lazy options provider.
49
+ * @returns Reactive texture loading state with reload support.
50
+ */
51
+ function useTexture(urlInput, optionsInput = {}) {
52
+ const textures = createCurrentWritable(null);
53
+ const loading = createCurrentWritable(true);
54
+ const error = createCurrentWritable(null);
55
+ const errorReport = createCurrentWritable(null);
56
+ let disposed = false;
57
+ let requestVersion = 0;
58
+ let activeController = null;
59
+ let runningLoad = null;
60
+ let reloadQueued = false;
61
+ const getUrls = typeof urlInput === "function" ? urlInput : () => urlInput;
62
+ const getOptions = typeof optionsInput === "function" ? optionsInput : () => optionsInput;
63
+ const executeLoad = async () => {
64
+ if (disposed) return;
65
+ const version = ++requestVersion;
66
+ const controller = new AbortController();
67
+ activeController = controller;
68
+ loading.set(true);
69
+ error.set(null);
70
+ errorReport.set(null);
71
+ const previous = textures.current;
72
+ const options = getOptions() ?? {};
73
+ const mergedSignal = mergeAbortSignals(controller.signal, options.signal);
74
+ try {
75
+ const loaded = await loadTexturesFromUrls(getUrls(), {
76
+ ...options,
77
+ signal: mergedSignal.signal
78
+ });
79
+ if (disposed || version !== requestVersion) {
80
+ disposeTextures(loaded);
81
+ return;
82
+ }
83
+ textures.set(loaded);
84
+ disposeTextures(previous);
85
+ } catch (nextError) {
86
+ if (disposed || version !== requestVersion) return;
87
+ if (isAbortError(nextError)) return;
88
+ disposeTextures(previous);
89
+ textures.set(null);
90
+ const normalizedError = toError(nextError);
91
+ error.set(normalizedError);
92
+ errorReport.set(toMotionGPUErrorReport(normalizedError, "initialization"));
93
+ } finally {
94
+ if (!disposed && version === requestVersion) loading.set(false);
95
+ if (activeController === controller) activeController = null;
96
+ mergedSignal.dispose();
97
+ }
98
+ };
99
+ const runLoadLoop = async () => {
100
+ do {
101
+ reloadQueued = false;
102
+ await executeLoad();
103
+ } while (reloadQueued && !disposed);
104
+ };
105
+ const load = () => {
106
+ activeController?.abort();
107
+ if (runningLoad) {
108
+ reloadQueued = true;
109
+ return runningLoad;
110
+ }
111
+ const trackedPending = runLoadLoop().finally(() => {
112
+ if (runningLoad === trackedPending) runningLoad = null;
113
+ });
114
+ runningLoad = trackedPending;
115
+ return trackedPending;
116
+ };
117
+ load();
118
+ onDestroy(() => {
119
+ disposed = true;
120
+ requestVersion += 1;
121
+ activeController?.abort();
122
+ disposeTextures(textures.current);
123
+ });
124
+ return {
125
+ textures,
126
+ loading,
127
+ error,
128
+ errorReport,
129
+ reload: load
130
+ };
157
131
  }
132
+ //#endregion
133
+ export { useTexture };
134
+
135
+ //# sourceMappingURL=use-texture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-texture.js","names":[],"sources":["../../src/lib/svelte/use-texture.ts"],"sourcesContent":["import { onDestroy } from 'svelte';\nimport {\n\tcreateCurrentWritable as currentWritable,\n\ttype CurrentReadable\n} from '../core/current-value.js';\nimport {\n\tisAbortError,\n\tloadTexturesFromUrls,\n\ttype LoadedTexture,\n\ttype TextureLoadOptions\n} from '../core/texture-loader.js';\nimport { toMotionGPUErrorReport, type MotionGPUErrorReport } from '../core/error-report.js';\n\n/**\n * Reactive state returned by {@link useTexture}.\n */\nexport interface UseTextureResult {\n\t/**\n\t * Loaded textures or `null` when unavailable/failed.\n\t */\n\ttextures: CurrentReadable<LoadedTexture[] | null>;\n\t/**\n\t * `true` while an active load request is running.\n\t */\n\tloading: CurrentReadable<boolean>;\n\t/**\n\t * Last loading error.\n\t */\n\terror: CurrentReadable<Error | null>;\n\t/**\n\t * Last loading error normalized to MotionGPU diagnostics report shape.\n\t */\n\terrorReport: CurrentReadable<MotionGPUErrorReport | null>;\n\t/**\n\t * Reloads all textures using current URL input.\n\t */\n\treload: () => Promise<void>;\n}\n\n/**\n * Supported URL input variants for `useTexture`.\n */\nexport type TextureUrlInput = string[] | (() => string[]);\n\n/**\n * Supported options input variants for `useTexture`.\n */\nexport type TextureOptionsInput = TextureLoadOptions | (() => TextureLoadOptions);\n\n/**\n * Normalizes unknown thrown values to an `Error` instance.\n */\nfunction toError(error: unknown): Error {\n\tif (error instanceof Error) {\n\t\treturn error;\n\t}\n\n\treturn new Error('Unknown texture loading error');\n}\n\n/**\n * Releases GPU-side resources for a list of loaded textures.\n */\nfunction disposeTextures(list: LoadedTexture[] | null): void {\n\tfor (const texture of list ?? []) {\n\t\ttexture.dispose();\n\t}\n}\n\ninterface MergedAbortSignal {\n\tsignal: AbortSignal;\n\tdispose: () => void;\n}\n\nfunction mergeAbortSignals(\n\tprimary: AbortSignal,\n\tsecondary: AbortSignal | undefined\n): MergedAbortSignal {\n\tif (!secondary) {\n\t\treturn {\n\t\t\tsignal: primary,\n\t\t\tdispose: () => {}\n\t\t};\n\t}\n\n\tif (typeof AbortSignal.any === 'function') {\n\t\treturn {\n\t\t\tsignal: AbortSignal.any([primary, secondary]),\n\t\t\tdispose: () => {}\n\t\t};\n\t}\n\n\tconst fallback = new AbortController();\n\tlet disposed = false;\n\tconst cleanup = (): void => {\n\t\tif (disposed) {\n\t\t\treturn;\n\t\t}\n\t\tdisposed = true;\n\t\tprimary.removeEventListener('abort', abort);\n\t\tsecondary.removeEventListener('abort', abort);\n\t};\n\tconst abort = (): void => fallback.abort();\n\n\tprimary.addEventListener('abort', abort, { once: true });\n\tsecondary.addEventListener('abort', abort, { once: true });\n\n\treturn {\n\t\tsignal: fallback.signal,\n\t\tdispose: cleanup\n\t};\n}\n\n/**\n * Loads textures from URLs and exposes reactive loading/error state.\n *\n * @param urlInput - URLs array or lazy URL provider.\n * @param optionsInput - Loader options object or lazy options provider.\n * @returns Reactive texture loading state with reload support.\n */\nexport function useTexture(\n\turlInput: TextureUrlInput,\n\toptionsInput: TextureOptionsInput = {}\n): UseTextureResult {\n\tconst textures = currentWritable<LoadedTexture[] | null>(null);\n\tconst loading = currentWritable(true);\n\tconst error = currentWritable<Error | null>(null);\n\tconst errorReport = currentWritable<MotionGPUErrorReport | null>(null);\n\tlet disposed = false;\n\tlet requestVersion = 0;\n\tlet activeController: AbortController | null = null;\n\tlet runningLoad: Promise<void> | null = null;\n\tlet reloadQueued = false;\n\tconst getUrls = typeof urlInput === 'function' ? urlInput : () => urlInput;\n\tconst getOptions =\n\t\ttypeof optionsInput === 'function'\n\t\t\t? (optionsInput as () => TextureLoadOptions)\n\t\t\t: () => optionsInput;\n\n\tconst executeLoad = async (): Promise<void> => {\n\t\tif (disposed) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst version = ++requestVersion;\n\t\tconst controller = new AbortController();\n\t\tactiveController = controller;\n\t\tloading.set(true);\n\t\terror.set(null);\n\t\terrorReport.set(null);\n\n\t\tconst previous = textures.current;\n\t\tconst options = getOptions() ?? {};\n\t\tconst mergedSignal = mergeAbortSignals(controller.signal, options.signal);\n\t\ttry {\n\t\t\tconst loaded = await loadTexturesFromUrls(getUrls(), {\n\t\t\t\t...options,\n\t\t\t\tsignal: mergedSignal.signal\n\t\t\t});\n\t\t\tif (disposed || version !== requestVersion) {\n\t\t\t\tdisposeTextures(loaded);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttextures.set(loaded);\n\t\t\tdisposeTextures(previous);\n\t\t} catch (nextError) {\n\t\t\tif (disposed || version !== requestVersion) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (isAbortError(nextError)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tdisposeTextures(previous);\n\t\t\ttextures.set(null);\n\t\t\tconst normalizedError = toError(nextError);\n\t\t\terror.set(normalizedError);\n\t\t\terrorReport.set(toMotionGPUErrorReport(normalizedError, 'initialization'));\n\t\t} finally {\n\t\t\tif (!disposed && version === requestVersion) {\n\t\t\t\tloading.set(false);\n\t\t\t}\n\t\t\tif (activeController === controller) {\n\t\t\t\tactiveController = null;\n\t\t\t}\n\t\t\tmergedSignal.dispose();\n\t\t}\n\t};\n\n\tconst runLoadLoop = async (): Promise<void> => {\n\t\tdo {\n\t\t\treloadQueued = false;\n\t\t\tawait executeLoad();\n\t\t} while (reloadQueued && !disposed);\n\t};\n\n\tconst load = (): Promise<void> => {\n\t\tactiveController?.abort();\n\t\tif (runningLoad) {\n\t\t\treloadQueued = true;\n\t\t\treturn runningLoad;\n\t\t}\n\n\t\tconst pending = runLoadLoop();\n\t\tconst trackedPending = pending.finally(() => {\n\t\t\tif (runningLoad === trackedPending) {\n\t\t\t\trunningLoad = null;\n\t\t\t}\n\t\t});\n\t\trunningLoad = trackedPending;\n\t\treturn trackedPending;\n\t};\n\n\tvoid load();\n\n\tonDestroy(() => {\n\t\tdisposed = true;\n\t\trequestVersion += 1;\n\t\tactiveController?.abort();\n\t\tdisposeTextures(textures.current);\n\t});\n\n\treturn {\n\t\ttextures,\n\t\tloading,\n\t\terror,\n\t\terrorReport,\n\t\treload: load\n\t};\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motion-core/motion-gpu",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Framework-agnostic WebGPU runtime for fullscreen WGSL shaders with explicit Svelte and React adapter entrypoints.",
5
5
  "keywords": [
6
6
  "svelte",
@@ -66,11 +66,16 @@
66
66
  }
67
67
  },
68
68
  "files": [
69
- "dist"
69
+ "dist",
70
+ "!dist/**/*.test.*",
71
+ "!dist/**/*.spec.*",
72
+ "src/lib",
73
+ "!src/lib/**/*.test.*",
74
+ "!src/lib/**/*.spec.*"
70
75
  ],
71
76
  "scripts": {
72
- "build": "svelte-package -i src/lib -o dist && node ./scripts/build/transpile-react-tsx.mjs && node ./scripts/build/patch-webgpu-types-dts.mjs",
73
- "build:watch": "svelte-package -i src/lib -o dist -w --preserve-output",
77
+ "build": "vite build --config ./vite.package.config.ts",
78
+ "build:watch": "vite build --watch --config ./vite.package.config.ts",
74
79
  "prepack": "bun run build",
75
80
  "check": "svelte-check --tsconfig ./tsconfig.json && publint",
76
81
  "test": "vitest run",
@@ -113,8 +118,7 @@
113
118
  "@eslint/compat": "^2.0.2",
114
119
  "@eslint/js": "^10.0.1",
115
120
  "@playwright/test": "^1.58.2",
116
- "@sveltejs/package": "^2.5.0",
117
- "@sveltejs/vite-plugin-svelte": "^6.2.4",
121
+ "@sveltejs/vite-plugin-svelte": "^7.0.0",
118
122
  "@testing-library/react": "^16.3.0",
119
123
  "@testing-library/svelte": "^5.2.8",
120
124
  "@types/react": "^19.2.2",
@@ -133,10 +137,11 @@
133
137
  "react": "^19.2.0",
134
138
  "react-dom": "^19.2.0",
135
139
  "svelte": "^5.51.0",
140
+ "svelte2tsx": "^0.7.51",
136
141
  "svelte-check": "^4.3.6",
137
142
  "typescript": "^5.9.3",
138
143
  "typescript-eslint": "^8.54.0",
139
- "vite": "^7.3.1",
144
+ "vite": "^8.0.1",
140
145
  "vitest": "^4.0.18"
141
146
  }
142
147
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Root advanced package entrypoint.
3
+ *
4
+ * Framework-agnostic advanced core entrypoint.
5
+ */
6
+ export * from './core/advanced.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Framework-agnostic advanced MotionGPU core entrypoint.
3
+ */
4
+ export * from './index.js';
5
+ export { applySchedulerPreset, captureSchedulerDebugSnapshot } from './scheduler-helpers.js';
6
+ export type {
7
+ ApplySchedulerPresetOptions,
8
+ MotionGPUScheduler,
9
+ SchedulerDebugSnapshot,
10
+ SchedulerPreset,
11
+ SchedulerPresetConfig
12
+ } from './scheduler-helpers.js';
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Minimal subscribe contract used by MotionGPU core.
3
+ */
4
+ export interface Subscribable<T> {
5
+ subscribe: (run: (value: T) => void) => () => void;
6
+ }
7
+
8
+ /**
9
+ * Readable value with synchronous access to the latest value.
10
+ */
11
+ export interface CurrentReadable<T> extends Subscribable<T> {
12
+ readonly current: T;
13
+ }
14
+
15
+ /**
16
+ * Writable extension of {@link CurrentReadable}.
17
+ */
18
+ export interface CurrentWritable<T> extends CurrentReadable<T> {
19
+ set: (value: T) => void;
20
+ update: (updater: (value: T) => T) => void;
21
+ }
22
+
23
+ /**
24
+ * Creates a writable value with immediate subscription semantics.
25
+ */
26
+ export function createCurrentWritable<T>(
27
+ initialValue: T,
28
+ onChange?: (value: T) => void
29
+ ): CurrentWritable<T> {
30
+ let current = initialValue;
31
+ const subscribers = new Set<(value: T) => void>();
32
+
33
+ const notify = (value: T): void => {
34
+ for (const run of subscribers) {
35
+ run(value);
36
+ }
37
+ };
38
+
39
+ const set = (value: T): void => {
40
+ if (Object.is(current, value)) {
41
+ return;
42
+ }
43
+ current = value;
44
+ notify(value);
45
+ onChange?.(value);
46
+ };
47
+
48
+ return {
49
+ get current() {
50
+ return current;
51
+ },
52
+ subscribe(run) {
53
+ subscribers.add(run);
54
+ run(current);
55
+ return () => {
56
+ subscribers.delete(run);
57
+ };
58
+ },
59
+ set,
60
+ update(updater) {
61
+ set(updater(current));
62
+ }
63
+ };
64
+ }
@@ -0,0 +1,236 @@
1
+ import type { MaterialSourceLocation } from './material-preprocess.js';
2
+
3
+ /**
4
+ * Source metadata for material declaration callsite.
5
+ */
6
+ export interface MaterialSourceMetadata {
7
+ component?: string;
8
+ file?: string;
9
+ line?: number;
10
+ column?: number;
11
+ functionName?: string;
12
+ }
13
+
14
+ /**
15
+ * One WGSL compiler diagnostic enriched with source-location metadata.
16
+ */
17
+ export interface ShaderCompilationDiagnostic {
18
+ generatedLine: number;
19
+ message: string;
20
+ linePos?: number;
21
+ lineLength?: number;
22
+ sourceLocation: MaterialSourceLocation | null;
23
+ }
24
+
25
+ /**
26
+ * Runtime context snapshot captured for shader compilation diagnostics.
27
+ */
28
+ export interface ShaderCompilationRuntimeContext {
29
+ materialSignature?: string;
30
+ passGraph?: {
31
+ passCount: number;
32
+ enabledPassCount: number;
33
+ inputs: string[];
34
+ outputs: string[];
35
+ };
36
+ activeRenderTargets: string[];
37
+ }
38
+
39
+ /**
40
+ * Structured payload attached to WGSL compilation errors.
41
+ */
42
+ export interface ShaderCompilationDiagnosticsPayload {
43
+ kind: 'shader-compilation';
44
+ diagnostics: ShaderCompilationDiagnostic[];
45
+ fragmentSource: string;
46
+ includeSources: Record<string, string>;
47
+ defineBlockSource?: string;
48
+ materialSource: MaterialSourceMetadata | null;
49
+ runtimeContext?: ShaderCompilationRuntimeContext;
50
+ }
51
+
52
+ type MotionGPUErrorWithDiagnostics = Error & {
53
+ motiongpuDiagnostics?: unknown;
54
+ };
55
+
56
+ function isMaterialSourceMetadata(value: unknown): value is MaterialSourceMetadata {
57
+ if (value === null || typeof value !== 'object') {
58
+ return false;
59
+ }
60
+
61
+ const record = value as Record<string, unknown>;
62
+ if (record.component !== undefined && typeof record.component !== 'string') {
63
+ return false;
64
+ }
65
+ if (record.file !== undefined && typeof record.file !== 'string') {
66
+ return false;
67
+ }
68
+ if (record.functionName !== undefined && typeof record.functionName !== 'string') {
69
+ return false;
70
+ }
71
+ if (record.line !== undefined && typeof record.line !== 'number') {
72
+ return false;
73
+ }
74
+ if (record.column !== undefined && typeof record.column !== 'number') {
75
+ return false;
76
+ }
77
+
78
+ return true;
79
+ }
80
+
81
+ function isMaterialSourceLocation(value: unknown): value is MaterialSourceLocation | null {
82
+ if (value === null) {
83
+ return true;
84
+ }
85
+
86
+ if (typeof value !== 'object') {
87
+ return false;
88
+ }
89
+
90
+ const record = value as Record<string, unknown>;
91
+ const kind = record.kind;
92
+ if (kind !== 'fragment' && kind !== 'include' && kind !== 'define') {
93
+ return false;
94
+ }
95
+
96
+ return typeof record.line === 'number';
97
+ }
98
+
99
+ function isShaderCompilationDiagnostic(value: unknown): value is ShaderCompilationDiagnostic {
100
+ if (value === null || typeof value !== 'object') {
101
+ return false;
102
+ }
103
+
104
+ const record = value as Record<string, unknown>;
105
+ if (typeof record.generatedLine !== 'number') {
106
+ return false;
107
+ }
108
+ if (typeof record.message !== 'string') {
109
+ return false;
110
+ }
111
+ if (record.linePos !== undefined && typeof record.linePos !== 'number') {
112
+ return false;
113
+ }
114
+ if (record.lineLength !== undefined && typeof record.lineLength !== 'number') {
115
+ return false;
116
+ }
117
+ if (!isMaterialSourceLocation(record.sourceLocation)) {
118
+ return false;
119
+ }
120
+
121
+ return true;
122
+ }
123
+
124
+ function isStringArray(value: unknown): value is string[] {
125
+ return Array.isArray(value) && value.every((entry) => typeof entry === 'string');
126
+ }
127
+
128
+ function isShaderCompilationRuntimeContext(
129
+ value: unknown
130
+ ): value is ShaderCompilationRuntimeContext {
131
+ if (value === null || typeof value !== 'object') {
132
+ return false;
133
+ }
134
+
135
+ const record = value as Record<string, unknown>;
136
+ if (record.materialSignature !== undefined && typeof record.materialSignature !== 'string') {
137
+ return false;
138
+ }
139
+ if (!isStringArray(record.activeRenderTargets)) {
140
+ return false;
141
+ }
142
+ const passGraph = record.passGraph;
143
+ if (passGraph === undefined) {
144
+ return true;
145
+ }
146
+ if (passGraph === null || typeof passGraph !== 'object') {
147
+ return false;
148
+ }
149
+
150
+ const passGraphRecord = passGraph as Record<string, unknown>;
151
+ if (typeof passGraphRecord.passCount !== 'number') {
152
+ return false;
153
+ }
154
+ if (typeof passGraphRecord.enabledPassCount !== 'number') {
155
+ return false;
156
+ }
157
+ if (!isStringArray(passGraphRecord.inputs) || !isStringArray(passGraphRecord.outputs)) {
158
+ return false;
159
+ }
160
+
161
+ return true;
162
+ }
163
+
164
+ /**
165
+ * Attaches structured diagnostics payload to an Error.
166
+ */
167
+ export function attachShaderCompilationDiagnostics(
168
+ error: Error,
169
+ payload: ShaderCompilationDiagnosticsPayload
170
+ ): Error {
171
+ (error as MotionGPUErrorWithDiagnostics).motiongpuDiagnostics = payload;
172
+ return error;
173
+ }
174
+
175
+ /**
176
+ * Extracts structured diagnostics payload from unknown error value.
177
+ */
178
+ export function getShaderCompilationDiagnostics(
179
+ error: unknown
180
+ ): ShaderCompilationDiagnosticsPayload | null {
181
+ if (!(error instanceof Error)) {
182
+ return null;
183
+ }
184
+
185
+ const payload = (error as MotionGPUErrorWithDiagnostics).motiongpuDiagnostics;
186
+ if (payload === null || typeof payload !== 'object') {
187
+ return null;
188
+ }
189
+
190
+ const record = payload as Record<string, unknown>;
191
+ if (record.kind !== 'shader-compilation') {
192
+ return null;
193
+ }
194
+ if (
195
+ !Array.isArray(record.diagnostics) ||
196
+ !record.diagnostics.every(isShaderCompilationDiagnostic)
197
+ ) {
198
+ return null;
199
+ }
200
+ if (typeof record.fragmentSource !== 'string') {
201
+ return null;
202
+ }
203
+ if (record.defineBlockSource !== undefined && typeof record.defineBlockSource !== 'string') {
204
+ return null;
205
+ }
206
+ if (record.includeSources === null || typeof record.includeSources !== 'object') {
207
+ return null;
208
+ }
209
+ const includeSources = record.includeSources as Record<string, unknown>;
210
+ if (Object.values(includeSources).some((value) => typeof value !== 'string')) {
211
+ return null;
212
+ }
213
+ if (record.materialSource !== null && !isMaterialSourceMetadata(record.materialSource)) {
214
+ return null;
215
+ }
216
+ if (
217
+ record.runtimeContext !== undefined &&
218
+ !isShaderCompilationRuntimeContext(record.runtimeContext)
219
+ ) {
220
+ return null;
221
+ }
222
+
223
+ return {
224
+ kind: 'shader-compilation',
225
+ diagnostics: record.diagnostics as ShaderCompilationDiagnostic[],
226
+ fragmentSource: record.fragmentSource,
227
+ includeSources: includeSources as Record<string, string>,
228
+ ...(record.defineBlockSource !== undefined
229
+ ? { defineBlockSource: record.defineBlockSource as string }
230
+ : {}),
231
+ materialSource: (record.materialSource ?? null) as MaterialSourceMetadata | null,
232
+ ...(record.runtimeContext !== undefined
233
+ ? { runtimeContext: record.runtimeContext as ShaderCompilationRuntimeContext }
234
+ : {})
235
+ };
236
+ }