@motion-core/motion-gpu 0.4.1 → 0.5.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 (228) hide show
  1. package/README.md +99 -0
  2. package/dist/advanced.d.ts +1 -0
  3. package/dist/advanced.d.ts.map +1 -0
  4. package/dist/advanced.js +14 -6
  5. package/dist/core/advanced.d.ts +1 -0
  6. package/dist/core/advanced.d.ts.map +1 -0
  7. package/dist/core/advanced.js +14 -5
  8. package/dist/core/compute-shader.d.ts +87 -0
  9. package/dist/core/compute-shader.d.ts.map +1 -0
  10. package/dist/core/compute-shader.js +205 -0
  11. package/dist/core/compute-shader.js.map +1 -0
  12. package/dist/core/current-value.d.ts +1 -0
  13. package/dist/core/current-value.d.ts.map +1 -0
  14. package/dist/core/current-value.js +35 -34
  15. package/dist/core/current-value.js.map +1 -0
  16. package/dist/core/error-diagnostics.d.ts +1 -0
  17. package/dist/core/error-diagnostics.d.ts.map +1 -0
  18. package/dist/core/error-diagnostics.js +70 -137
  19. package/dist/core/error-diagnostics.js.map +1 -0
  20. package/dist/core/error-report.d.ts +2 -1
  21. package/dist/core/error-report.d.ts.map +1 -0
  22. package/dist/core/error-report.js +247 -233
  23. package/dist/core/error-report.js.map +1 -0
  24. package/dist/core/frame-registry.d.ts +1 -0
  25. package/dist/core/frame-registry.d.ts.map +1 -0
  26. package/dist/core/frame-registry.js +546 -662
  27. package/dist/core/frame-registry.js.map +1 -0
  28. package/dist/core/index.d.ts +6 -2
  29. package/dist/core/index.d.ts.map +1 -0
  30. package/dist/core/index.js +13 -12
  31. package/dist/core/material-preprocess.d.ts +1 -0
  32. package/dist/core/material-preprocess.d.ts.map +1 -0
  33. package/dist/core/material-preprocess.js +131 -152
  34. package/dist/core/material-preprocess.js.map +1 -0
  35. package/dist/core/material.d.ts +23 -6
  36. package/dist/core/material.d.ts.map +1 -0
  37. package/dist/core/material.js +290 -317
  38. package/dist/core/material.js.map +1 -0
  39. package/dist/core/recompile-policy.d.ts +1 -0
  40. package/dist/core/recompile-policy.d.ts.map +1 -0
  41. package/dist/core/recompile-policy.js +18 -13
  42. package/dist/core/recompile-policy.js.map +1 -0
  43. package/dist/core/render-graph.d.ts +8 -3
  44. package/dist/core/render-graph.d.ts.map +1 -0
  45. package/dist/core/render-graph.js +77 -68
  46. package/dist/core/render-graph.js.map +1 -0
  47. package/dist/core/render-targets.d.ts +1 -0
  48. package/dist/core/render-targets.d.ts.map +1 -0
  49. package/dist/core/render-targets.js +52 -53
  50. package/dist/core/render-targets.js.map +1 -0
  51. package/dist/core/renderer.d.ts +1 -0
  52. package/dist/core/renderer.d.ts.map +1 -0
  53. package/dist/core/renderer.js +1337 -1081
  54. package/dist/core/renderer.js.map +1 -0
  55. package/dist/core/runtime-loop.d.ts +3 -2
  56. package/dist/core/runtime-loop.d.ts.map +1 -0
  57. package/dist/core/runtime-loop.js +353 -362
  58. package/dist/core/runtime-loop.js.map +1 -0
  59. package/dist/core/scheduler-helpers.d.ts +1 -0
  60. package/dist/core/scheduler-helpers.d.ts.map +1 -0
  61. package/dist/core/scheduler-helpers.js +52 -51
  62. package/dist/core/scheduler-helpers.js.map +1 -0
  63. package/dist/core/shader.d.ts +10 -1
  64. package/dist/core/shader.d.ts.map +1 -0
  65. package/dist/core/shader.js +109 -115
  66. package/dist/core/shader.js.map +1 -0
  67. package/dist/core/storage-buffers.d.ts +37 -0
  68. package/dist/core/storage-buffers.d.ts.map +1 -0
  69. package/dist/core/storage-buffers.js +95 -0
  70. package/dist/core/storage-buffers.js.map +1 -0
  71. package/dist/core/texture-loader.d.ts +1 -0
  72. package/dist/core/texture-loader.d.ts.map +1 -0
  73. package/dist/core/texture-loader.js +209 -273
  74. package/dist/core/texture-loader.js.map +1 -0
  75. package/dist/core/textures.d.ts +13 -0
  76. package/dist/core/textures.d.ts.map +1 -0
  77. package/dist/core/textures.js +111 -116
  78. package/dist/core/textures.js.map +1 -0
  79. package/dist/core/types.d.ts +147 -4
  80. package/dist/core/types.d.ts.map +1 -0
  81. package/dist/core/types.js +0 -4
  82. package/dist/core/uniforms.d.ts +1 -0
  83. package/dist/core/uniforms.d.ts.map +1 -0
  84. package/dist/core/uniforms.js +170 -191
  85. package/dist/core/uniforms.js.map +1 -0
  86. package/dist/index.d.ts +1 -0
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +13 -6
  89. package/dist/passes/BlitPass.d.ts +1 -0
  90. package/dist/passes/BlitPass.d.ts.map +1 -0
  91. package/dist/passes/BlitPass.js +23 -18
  92. package/dist/passes/BlitPass.js.map +1 -0
  93. package/dist/passes/ComputePass.d.ts +83 -0
  94. package/dist/passes/ComputePass.d.ts.map +1 -0
  95. package/dist/passes/ComputePass.js +92 -0
  96. package/dist/passes/ComputePass.js.map +1 -0
  97. package/dist/passes/CopyPass.d.ts +1 -0
  98. package/dist/passes/CopyPass.d.ts.map +1 -0
  99. package/dist/passes/CopyPass.js +58 -52
  100. package/dist/passes/CopyPass.js.map +1 -0
  101. package/dist/passes/FullscreenPass.d.ts +1 -0
  102. package/dist/passes/FullscreenPass.d.ts.map +1 -0
  103. package/dist/passes/FullscreenPass.js +127 -130
  104. package/dist/passes/FullscreenPass.js.map +1 -0
  105. package/dist/passes/PingPongComputePass.d.ts +104 -0
  106. package/dist/passes/PingPongComputePass.d.ts.map +1 -0
  107. package/dist/passes/PingPongComputePass.js +132 -0
  108. package/dist/passes/PingPongComputePass.js.map +1 -0
  109. package/dist/passes/ShaderPass.d.ts +1 -0
  110. package/dist/passes/ShaderPass.d.ts.map +1 -0
  111. package/dist/passes/ShaderPass.js +41 -37
  112. package/dist/passes/ShaderPass.js.map +1 -0
  113. package/dist/passes/index.d.ts +3 -0
  114. package/dist/passes/index.d.ts.map +1 -0
  115. package/dist/passes/index.js +6 -3
  116. package/dist/react/FragCanvas.d.ts +3 -2
  117. package/dist/react/FragCanvas.d.ts.map +1 -0
  118. package/dist/react/FragCanvas.js +234 -211
  119. package/dist/react/FragCanvas.js.map +1 -0
  120. package/dist/react/MotionGPUErrorOverlay.d.ts +1 -0
  121. package/dist/react/MotionGPUErrorOverlay.d.ts.map +1 -0
  122. package/dist/react/MotionGPUErrorOverlay.js +200 -14
  123. package/dist/react/MotionGPUErrorOverlay.js.map +1 -0
  124. package/dist/react/Portal.d.ts +1 -0
  125. package/dist/react/Portal.d.ts.map +1 -0
  126. package/dist/react/Portal.js +18 -21
  127. package/dist/react/Portal.js.map +1 -0
  128. package/dist/react/advanced.d.ts +1 -0
  129. package/dist/react/advanced.d.ts.map +1 -0
  130. package/dist/react/advanced.js +14 -6
  131. package/dist/react/frame-context.d.ts +1 -0
  132. package/dist/react/frame-context.d.ts.map +1 -0
  133. package/dist/react/frame-context.js +88 -94
  134. package/dist/react/frame-context.js.map +1 -0
  135. package/dist/react/index.d.ts +6 -2
  136. package/dist/react/index.d.ts.map +1 -0
  137. package/dist/react/index.js +12 -9
  138. package/dist/react/motiongpu-context.d.ts +1 -0
  139. package/dist/react/motiongpu-context.d.ts.map +1 -0
  140. package/dist/react/motiongpu-context.js +18 -15
  141. package/dist/react/motiongpu-context.js.map +1 -0
  142. package/dist/react/use-motiongpu-user-context.d.ts +1 -0
  143. package/dist/react/use-motiongpu-user-context.d.ts.map +1 -0
  144. package/dist/react/use-motiongpu-user-context.js +83 -82
  145. package/dist/react/use-motiongpu-user-context.js.map +1 -0
  146. package/dist/react/use-texture.d.ts +1 -0
  147. package/dist/react/use-texture.d.ts.map +1 -0
  148. package/dist/react/use-texture.js +132 -152
  149. package/dist/react/use-texture.js.map +1 -0
  150. package/dist/svelte/FragCanvas.svelte +2 -2
  151. package/dist/svelte/FragCanvas.svelte.d.ts +3 -2
  152. package/dist/svelte/FragCanvas.svelte.d.ts.map +1 -0
  153. package/dist/svelte/MotionGPUErrorOverlay.svelte +137 -7
  154. package/dist/svelte/MotionGPUErrorOverlay.svelte.d.ts +1 -0
  155. package/dist/svelte/MotionGPUErrorOverlay.svelte.d.ts.map +1 -0
  156. package/dist/svelte/Portal.svelte.d.ts +1 -0
  157. package/dist/svelte/Portal.svelte.d.ts.map +1 -0
  158. package/dist/svelte/advanced.d.ts +1 -0
  159. package/dist/svelte/advanced.d.ts.map +1 -0
  160. package/dist/svelte/advanced.js +13 -6
  161. package/dist/svelte/frame-context.d.ts +1 -0
  162. package/dist/svelte/frame-context.d.ts.map +1 -0
  163. package/dist/svelte/frame-context.js +27 -27
  164. package/dist/svelte/frame-context.js.map +1 -0
  165. package/dist/svelte/index.d.ts +6 -2
  166. package/dist/svelte/index.d.ts.map +1 -0
  167. package/dist/svelte/index.js +12 -9
  168. package/dist/svelte/motiongpu-context.d.ts +1 -0
  169. package/dist/svelte/motiongpu-context.d.ts.map +1 -0
  170. package/dist/svelte/motiongpu-context.js +24 -21
  171. package/dist/svelte/motiongpu-context.js.map +1 -0
  172. package/dist/svelte/use-motiongpu-user-context.d.ts +1 -0
  173. package/dist/svelte/use-motiongpu-user-context.d.ts.map +1 -0
  174. package/dist/svelte/use-motiongpu-user-context.js +69 -70
  175. package/dist/svelte/use-motiongpu-user-context.js.map +1 -0
  176. package/dist/svelte/use-texture.d.ts +1 -0
  177. package/dist/svelte/use-texture.d.ts.map +1 -0
  178. package/dist/svelte/use-texture.js +125 -147
  179. package/dist/svelte/use-texture.js.map +1 -0
  180. package/package.json +12 -7
  181. package/src/lib/advanced.ts +6 -0
  182. package/src/lib/core/advanced.ts +12 -0
  183. package/src/lib/core/compute-shader.ts +326 -0
  184. package/src/lib/core/current-value.ts +64 -0
  185. package/src/lib/core/error-diagnostics.ts +236 -0
  186. package/src/lib/core/error-report.ts +535 -0
  187. package/src/lib/core/frame-registry.ts +1190 -0
  188. package/src/lib/core/index.ts +94 -0
  189. package/src/lib/core/material-preprocess.ts +295 -0
  190. package/src/lib/core/material.ts +748 -0
  191. package/src/lib/core/recompile-policy.ts +31 -0
  192. package/src/lib/core/render-graph.ts +173 -0
  193. package/src/lib/core/render-targets.ts +107 -0
  194. package/src/lib/core/renderer.ts +2161 -0
  195. package/src/lib/core/runtime-loop.ts +537 -0
  196. package/src/lib/core/scheduler-helpers.ts +136 -0
  197. package/src/lib/core/shader.ts +301 -0
  198. package/src/lib/core/storage-buffers.ts +142 -0
  199. package/src/lib/core/texture-loader.ts +482 -0
  200. package/src/lib/core/textures.ts +257 -0
  201. package/src/lib/core/types.ts +743 -0
  202. package/src/lib/core/uniforms.ts +282 -0
  203. package/src/lib/index.ts +6 -0
  204. package/src/lib/passes/BlitPass.ts +54 -0
  205. package/src/lib/passes/ComputePass.ts +136 -0
  206. package/src/lib/passes/CopyPass.ts +80 -0
  207. package/src/lib/passes/FullscreenPass.ts +173 -0
  208. package/src/lib/passes/PingPongComputePass.ts +180 -0
  209. package/src/lib/passes/ShaderPass.ts +89 -0
  210. package/src/lib/passes/index.ts +9 -0
  211. package/src/lib/react/FragCanvas.tsx +345 -0
  212. package/src/lib/react/MotionGPUErrorOverlay.tsx +524 -0
  213. package/src/lib/react/Portal.tsx +34 -0
  214. package/src/lib/react/advanced.ts +36 -0
  215. package/src/lib/react/frame-context.ts +169 -0
  216. package/src/lib/react/index.ts +68 -0
  217. package/src/lib/react/motiongpu-context.ts +88 -0
  218. package/src/lib/react/use-motiongpu-user-context.ts +186 -0
  219. package/src/lib/react/use-texture.ts +233 -0
  220. package/src/lib/svelte/FragCanvas.svelte +249 -0
  221. package/src/lib/svelte/MotionGPUErrorOverlay.svelte +512 -0
  222. package/src/lib/svelte/Portal.svelte +31 -0
  223. package/src/lib/svelte/advanced.ts +32 -0
  224. package/src/lib/svelte/frame-context.ts +87 -0
  225. package/src/lib/svelte/index.ts +68 -0
  226. package/src/lib/svelte/motiongpu-context.ts +97 -0
  227. package/src/lib/svelte/use-motiongpu-user-context.ts +145 -0
  228. package/src/lib/svelte/use-texture.ts +232 -0
@@ -1,365 +1,356 @@
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';
1
+ import { assertUniformValueForType } from "./uniforms.js";
2
+ import { resolveMaterial } from "./material.js";
3
+ import { toMotionGPUErrorReport } from "./error-report.js";
4
+ import { createRenderer } from "./renderer.js";
5
+ import { buildRendererPipelineSignature } from "./recompile-policy.js";
6
+ //#region src/lib/core/runtime-loop.ts
6
7
  function getRendererRetryDelayMs(attempt) {
7
- return Math.min(8000, 250 * 2 ** Math.max(0, attempt - 1));
8
+ return Math.min(8e3, 250 * 2 ** Math.max(0, attempt - 1));
8
9
  }
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
- let activeErrorKey = null;
37
- let errorHistory = [];
38
- const getHistoryLimit = () => {
39
- const value = options.getErrorHistoryLimit?.() ?? 0;
40
- if (!Number.isFinite(value) || value <= 0) {
41
- return 0;
42
- }
43
- return Math.floor(value);
44
- };
45
- const publishErrorHistory = () => {
46
- options.reportErrorHistory?.(errorHistory);
47
- const onErrorHistory = options.getOnErrorHistory?.();
48
- if (!onErrorHistory) {
49
- return;
50
- }
51
- try {
52
- onErrorHistory(errorHistory);
53
- }
54
- catch {
55
- // User-provided error history handlers must not break runtime error recovery.
56
- }
57
- };
58
- const syncErrorHistory = () => {
59
- const limit = getHistoryLimit();
60
- if (limit <= 0) {
61
- if (errorHistory.length === 0) {
62
- return;
63
- }
64
- errorHistory = [];
65
- publishErrorHistory();
66
- return;
67
- }
68
- if (errorHistory.length <= limit) {
69
- return;
70
- }
71
- errorHistory = errorHistory.slice(errorHistory.length - limit);
72
- publishErrorHistory();
73
- };
74
- const setError = (error, phase) => {
75
- const report = toMotionGPUErrorReport(error, phase);
76
- const reportKey = JSON.stringify({
77
- phase: report.phase,
78
- title: report.title,
79
- message: report.message,
80
- rawMessage: report.rawMessage
81
- });
82
- if (activeErrorKey === reportKey) {
83
- return;
84
- }
85
- activeErrorKey = reportKey;
86
- const historyLimit = getHistoryLimit();
87
- if (historyLimit > 0) {
88
- errorHistory = [...errorHistory, report];
89
- if (errorHistory.length > historyLimit) {
90
- errorHistory = errorHistory.slice(errorHistory.length - historyLimit);
91
- }
92
- publishErrorHistory();
93
- }
94
- options.reportError(report);
95
- const onError = options.getOnError();
96
- if (!onError) {
97
- return;
98
- }
99
- try {
100
- onError(report);
101
- }
102
- catch {
103
- // User-provided error handlers must not break runtime error recovery.
104
- }
105
- };
106
- const clearError = () => {
107
- if (activeErrorKey === null) {
108
- return;
109
- }
110
- activeErrorKey = null;
111
- options.reportError(null);
112
- };
113
- const scheduleFrame = () => {
114
- if (isDisposed || frameId !== null) {
115
- return;
116
- }
117
- frameId = requestAnimationFrame(renderFrame);
118
- };
119
- const requestFrame = () => {
120
- scheduleFrame();
121
- };
122
- const invalidate = (token) => {
123
- registry.invalidate(token);
124
- requestFrame();
125
- };
126
- const advance = () => {
127
- registry.advance();
128
- requestFrame();
129
- };
130
- const resetRuntimeMaps = () => {
131
- for (const key of Object.keys(runtimeUniforms)) {
132
- if (!uniformKeySet.has(key)) {
133
- Reflect.deleteProperty(runtimeUniforms, key);
134
- }
135
- }
136
- for (const key of Object.keys(runtimeTextures)) {
137
- if (!textureKeySet.has(key)) {
138
- Reflect.deleteProperty(runtimeTextures, key);
139
- }
140
- }
141
- };
142
- const resetRenderPayloadMaps = () => {
143
- for (const key of Object.keys(renderUniforms)) {
144
- if (!uniformKeySet.has(key)) {
145
- Reflect.deleteProperty(renderUniforms, key);
146
- }
147
- }
148
- for (const key of Object.keys(renderTextures)) {
149
- if (!textureKeySet.has(key)) {
150
- Reflect.deleteProperty(renderTextures, key);
151
- }
152
- }
153
- };
154
- const syncMaterialRuntimeState = (materialState) => {
155
- const signatureChanged = activeMaterialSignature !== materialState.signature;
156
- const defaultsChanged = activeUniforms !== materialState.uniforms || activeTextures !== materialState.textures;
157
- if (!signatureChanged && !defaultsChanged) {
158
- return;
159
- }
160
- activeUniforms = materialState.uniforms;
161
- activeTextures = materialState.textures;
162
- if (!signatureChanged) {
163
- return;
164
- }
165
- uniformKeys = materialState.uniformLayout.entries.map((entry) => entry.name);
166
- uniformTypes = new Map(materialState.uniformLayout.entries.map((entry) => [entry.name, entry.type]));
167
- textureKeys = materialState.textureKeys;
168
- uniformKeySet = new Set(uniformKeys);
169
- textureKeySet = new Set(textureKeys);
170
- resetRuntimeMaps();
171
- resetRenderPayloadMaps();
172
- activeMaterialSignature = materialState.signature;
173
- };
174
- const resolveActiveMaterial = () => {
175
- return resolveMaterial(options.getMaterial());
176
- };
177
- const setUniform = (name, value) => {
178
- if (!uniformKeySet.has(name)) {
179
- throw new Error(`Unknown uniform "${name}". Declare it in material.uniforms first.`);
180
- }
181
- const expectedType = uniformTypes.get(name);
182
- if (!expectedType) {
183
- throw new Error(`Unknown uniform type for "${name}"`);
184
- }
185
- assertUniformValueForType(expectedType, value);
186
- runtimeUniforms[name] = value;
187
- };
188
- const setTexture = (name, value) => {
189
- if (!textureKeySet.has(name)) {
190
- throw new Error(`Unknown texture "${name}". Declare it in material.textures first.`);
191
- }
192
- runtimeTextures[name] = value;
193
- };
194
- const renderFrame = (timestamp) => {
195
- frameId = null;
196
- if (isDisposed) {
197
- return;
198
- }
199
- syncErrorHistory();
200
- let materialState;
201
- try {
202
- materialState = resolveActiveMaterial();
203
- }
204
- catch (error) {
205
- setError(error, 'initialization');
206
- scheduleFrame();
207
- return;
208
- }
209
- shouldContinueAfterFrame = false;
210
- const outputColorSpace = options.getOutputColorSpace();
211
- const rendererSignature = buildRendererPipelineSignature({
212
- materialSignature: materialState.signature,
213
- outputColorSpace
214
- });
215
- syncMaterialRuntimeState(materialState);
216
- if (failedRendererSignature && failedRendererSignature !== rendererSignature) {
217
- failedRendererSignature = null;
218
- failedRendererAttempts = 0;
219
- nextRendererRetryAt = 0;
220
- }
221
- if (!renderer || activeRendererSignature !== rendererSignature) {
222
- if (failedRendererSignature === rendererSignature &&
223
- performance.now() < nextRendererRetryAt) {
224
- scheduleFrame();
225
- return;
226
- }
227
- if (!rendererRebuildPromise) {
228
- rendererRebuildPromise = (async () => {
229
- try {
230
- const nextRenderer = await createRenderer({
231
- canvas: canvasElement,
232
- fragmentWgsl: materialState.fragmentWgsl,
233
- fragmentLineMap: materialState.fragmentLineMap,
234
- fragmentSource: materialState.fragmentSource,
235
- includeSources: materialState.includeSources,
236
- defineBlockSource: materialState.defineBlockSource,
237
- materialSource: materialState.source,
238
- materialSignature: materialState.signature,
239
- uniformLayout: materialState.uniformLayout,
240
- textureKeys: materialState.textureKeys,
241
- textureDefinitions: materialState.textures,
242
- getRenderTargets: options.getRenderTargets,
243
- getPasses: options.getPasses,
244
- outputColorSpace,
245
- getClearColor: options.getClearColor,
246
- getDpr: () => options.dpr.current,
247
- adapterOptions: options.getAdapterOptions(),
248
- deviceDescriptor: options.getDeviceDescriptor()
249
- });
250
- if (isDisposed) {
251
- nextRenderer.destroy();
252
- return;
253
- }
254
- renderer?.destroy();
255
- renderer = nextRenderer;
256
- activeRendererSignature = rendererSignature;
257
- failedRendererSignature = null;
258
- failedRendererAttempts = 0;
259
- nextRendererRetryAt = 0;
260
- clearError();
261
- }
262
- catch (error) {
263
- failedRendererSignature = rendererSignature;
264
- failedRendererAttempts += 1;
265
- const retryDelayMs = getRendererRetryDelayMs(failedRendererAttempts);
266
- nextRendererRetryAt = performance.now() + retryDelayMs;
267
- setError(error, 'initialization');
268
- }
269
- finally {
270
- rendererRebuildPromise = null;
271
- scheduleFrame();
272
- }
273
- })();
274
- }
275
- return;
276
- }
277
- const time = timestamp / 1000;
278
- const rawDelta = Math.max(0, time - previousTime);
279
- const delta = Math.min(rawDelta, options.maxDelta.current);
280
- previousTime = time;
281
- const rect = canvasElement.getBoundingClientRect();
282
- const width = Math.max(0, Math.floor(rect.width));
283
- const height = Math.max(0, Math.floor(rect.height));
284
- if (width !== currentCssWidth || height !== currentCssHeight) {
285
- currentCssWidth = width;
286
- currentCssHeight = height;
287
- size.set({ width, height });
288
- }
289
- try {
290
- registry.run({
291
- time,
292
- delta,
293
- setUniform,
294
- setTexture,
295
- invalidate,
296
- advance,
297
- renderMode: registry.getRenderMode(),
298
- autoRender: registry.getAutoRender(),
299
- canvas: canvasElement
300
- });
301
- const shouldRenderFrame = registry.shouldRender();
302
- shouldContinueAfterFrame =
303
- registry.getRenderMode() === 'always' ||
304
- (registry.getRenderMode() === 'on-demand' && shouldRenderFrame);
305
- if (shouldRenderFrame) {
306
- for (const key of uniformKeys) {
307
- const runtimeValue = runtimeUniforms[key];
308
- renderUniforms[key] =
309
- runtimeValue === undefined ? activeUniforms[key] : runtimeValue;
310
- }
311
- for (const key of textureKeys) {
312
- const runtimeValue = runtimeTextures[key];
313
- renderTextures[key] =
314
- runtimeValue === undefined ? (activeTextures[key]?.source ?? null) : runtimeValue;
315
- }
316
- canvasSize.width = width;
317
- canvasSize.height = height;
318
- renderer.render({
319
- time,
320
- delta,
321
- renderMode: registry.getRenderMode(),
322
- uniforms: renderUniforms,
323
- textures: renderTextures,
324
- canvasSize
325
- });
326
- }
327
- clearError();
328
- }
329
- catch (error) {
330
- setError(error, 'render');
331
- }
332
- finally {
333
- registry.endFrame();
334
- }
335
- if (shouldContinueAfterFrame) {
336
- scheduleFrame();
337
- }
338
- };
339
- (async () => {
340
- try {
341
- const initialMaterial = resolveActiveMaterial();
342
- syncMaterialRuntimeState(initialMaterial);
343
- activeRendererSignature = '';
344
- scheduleFrame();
345
- }
346
- catch (error) {
347
- setError(error, 'initialization');
348
- scheduleFrame();
349
- }
350
- })();
351
- return {
352
- requestFrame,
353
- invalidate,
354
- advance,
355
- destroy: () => {
356
- isDisposed = true;
357
- if (frameId !== null) {
358
- cancelAnimationFrame(frameId);
359
- frameId = null;
360
- }
361
- renderer?.destroy();
362
- registry.clear();
363
- }
364
- };
10
+ function createMotionGPURuntimeLoop(options) {
11
+ const { canvas: canvasElement, registry, size } = options;
12
+ let frameId = null;
13
+ let renderer = null;
14
+ let isDisposed = false;
15
+ let previousTime = performance.now() / 1e3;
16
+ let activeRendererSignature = "";
17
+ let failedRendererSignature = null;
18
+ let failedRendererAttempts = 0;
19
+ let nextRendererRetryAt = 0;
20
+ let rendererRebuildPromise = null;
21
+ const runtimeUniforms = {};
22
+ const runtimeTextures = {};
23
+ let activeUniforms = {};
24
+ let activeTextures = {};
25
+ let uniformKeys = [];
26
+ let uniformKeySet = /* @__PURE__ */ new Set();
27
+ let uniformTypes = /* @__PURE__ */ new Map();
28
+ let textureKeys = [];
29
+ let textureKeySet = /* @__PURE__ */ new Set();
30
+ let activeMaterialSignature = "";
31
+ let currentCssWidth = -1;
32
+ let currentCssHeight = -1;
33
+ const renderUniforms = {};
34
+ const renderTextures = {};
35
+ const canvasSize = {
36
+ width: 0,
37
+ height: 0
38
+ };
39
+ let storageBufferKeys = [];
40
+ let storageBufferKeySet = /* @__PURE__ */ new Set();
41
+ let storageBufferDefinitions = {};
42
+ const pendingStorageWrites = [];
43
+ let shouldContinueAfterFrame = false;
44
+ let activeErrorKey = null;
45
+ let errorHistory = [];
46
+ const getHistoryLimit = () => {
47
+ const value = options.getErrorHistoryLimit?.() ?? 0;
48
+ if (!Number.isFinite(value) || value <= 0) return 0;
49
+ return Math.floor(value);
50
+ };
51
+ const publishErrorHistory = () => {
52
+ options.reportErrorHistory?.(errorHistory);
53
+ const onErrorHistory = options.getOnErrorHistory?.();
54
+ if (!onErrorHistory) return;
55
+ try {
56
+ onErrorHistory(errorHistory);
57
+ } catch {}
58
+ };
59
+ const syncErrorHistory = () => {
60
+ const limit = getHistoryLimit();
61
+ if (limit <= 0) {
62
+ if (errorHistory.length === 0) return;
63
+ errorHistory = [];
64
+ publishErrorHistory();
65
+ return;
66
+ }
67
+ if (errorHistory.length <= limit) return;
68
+ errorHistory = errorHistory.slice(errorHistory.length - limit);
69
+ publishErrorHistory();
70
+ };
71
+ const setError = (error, phase) => {
72
+ const report = toMotionGPUErrorReport(error, phase);
73
+ const reportKey = JSON.stringify({
74
+ phase: report.phase,
75
+ title: report.title,
76
+ message: report.message,
77
+ rawMessage: report.rawMessage
78
+ });
79
+ if (activeErrorKey === reportKey) return;
80
+ activeErrorKey = reportKey;
81
+ const historyLimit = getHistoryLimit();
82
+ if (historyLimit > 0) {
83
+ errorHistory = [...errorHistory, report];
84
+ if (errorHistory.length > historyLimit) errorHistory = errorHistory.slice(errorHistory.length - historyLimit);
85
+ publishErrorHistory();
86
+ }
87
+ options.reportError(report);
88
+ const onError = options.getOnError();
89
+ if (!onError) return;
90
+ try {
91
+ onError(report);
92
+ } catch {}
93
+ };
94
+ const clearError = () => {
95
+ if (activeErrorKey === null) return;
96
+ activeErrorKey = null;
97
+ options.reportError(null);
98
+ };
99
+ const scheduleFrame = () => {
100
+ if (isDisposed || frameId !== null) return;
101
+ frameId = requestAnimationFrame(renderFrame);
102
+ };
103
+ const requestFrame = () => {
104
+ scheduleFrame();
105
+ };
106
+ const invalidate = (token) => {
107
+ registry.invalidate(token);
108
+ requestFrame();
109
+ };
110
+ const advance = () => {
111
+ registry.advance();
112
+ requestFrame();
113
+ };
114
+ const resetRuntimeMaps = () => {
115
+ for (const key of Object.keys(runtimeUniforms)) if (!uniformKeySet.has(key)) Reflect.deleteProperty(runtimeUniforms, key);
116
+ for (const key of Object.keys(runtimeTextures)) if (!textureKeySet.has(key)) Reflect.deleteProperty(runtimeTextures, key);
117
+ };
118
+ const resetRenderPayloadMaps = () => {
119
+ for (const key of Object.keys(renderUniforms)) if (!uniformKeySet.has(key)) Reflect.deleteProperty(renderUniforms, key);
120
+ for (const key of Object.keys(renderTextures)) if (!textureKeySet.has(key)) Reflect.deleteProperty(renderTextures, key);
121
+ };
122
+ const syncMaterialRuntimeState = (materialState) => {
123
+ const signatureChanged = activeMaterialSignature !== materialState.signature;
124
+ const defaultsChanged = activeUniforms !== materialState.uniforms || activeTextures !== materialState.textures;
125
+ if (!signatureChanged && !defaultsChanged) return;
126
+ activeUniforms = materialState.uniforms;
127
+ activeTextures = materialState.textures;
128
+ if (!signatureChanged) return;
129
+ uniformKeys = materialState.uniformLayout.entries.map((entry) => entry.name);
130
+ uniformTypes = new Map(materialState.uniformLayout.entries.map((entry) => [entry.name, entry.type]));
131
+ textureKeys = materialState.textureKeys;
132
+ uniformKeySet = new Set(uniformKeys);
133
+ textureKeySet = new Set(textureKeys);
134
+ storageBufferKeys = materialState.storageBufferKeys;
135
+ storageBufferKeySet = new Set(storageBufferKeys);
136
+ storageBufferDefinitions = options.getMaterial().storageBuffers ?? {};
137
+ resetRuntimeMaps();
138
+ resetRenderPayloadMaps();
139
+ activeMaterialSignature = materialState.signature;
140
+ };
141
+ const resolveActiveMaterial = () => {
142
+ return resolveMaterial(options.getMaterial());
143
+ };
144
+ const setUniform = (name, value) => {
145
+ if (!uniformKeySet.has(name)) throw new Error(`Unknown uniform "${name}". Declare it in material.uniforms first.`);
146
+ const expectedType = uniformTypes.get(name);
147
+ if (!expectedType) throw new Error(`Unknown uniform type for "${name}"`);
148
+ assertUniformValueForType(expectedType, value);
149
+ runtimeUniforms[name] = value;
150
+ };
151
+ const setTexture = (name, value) => {
152
+ if (!textureKeySet.has(name)) throw new Error(`Unknown texture "${name}". Declare it in material.textures first.`);
153
+ runtimeTextures[name] = value;
154
+ };
155
+ const writeStorageBuffer = (name, data, writeOptions) => {
156
+ if (!storageBufferKeySet.has(name)) throw new Error(`Unknown storage buffer "${name}". Declare it in material.storageBuffers first.`);
157
+ const definition = storageBufferDefinitions[name];
158
+ if (!definition) throw new Error(`Missing definition for storage buffer "${name}".`);
159
+ const offset = writeOptions?.offset ?? 0;
160
+ if (offset < 0 || offset + data.byteLength > definition.size) throw new Error(`Storage buffer "${name}" write out of bounds: offset=${offset}, dataSize=${data.byteLength}, bufferSize=${definition.size}.`);
161
+ pendingStorageWrites.push({
162
+ name,
163
+ data,
164
+ offset
165
+ });
166
+ };
167
+ const readStorageBuffer = (name) => {
168
+ if (!storageBufferKeySet.has(name)) throw new Error(`Unknown storage buffer "${name}". Declare it in material.storageBuffers first.`);
169
+ if (!renderer) return Promise.reject(/* @__PURE__ */ new Error(`Cannot read storage buffer "${name}": renderer not initialized.`));
170
+ const gpuBuffer = renderer.getStorageBuffer?.(name);
171
+ if (!gpuBuffer) return Promise.reject(/* @__PURE__ */ new Error(`Storage buffer "${name}" not allocated on GPU.`));
172
+ const device = renderer.getDevice?.();
173
+ if (!device) return Promise.reject(/* @__PURE__ */ new Error("Cannot read storage buffer: GPU device unavailable."));
174
+ const definition = storageBufferDefinitions[name];
175
+ if (!definition) return Promise.reject(/* @__PURE__ */ new Error(`Missing definition for storage buffer "${name}".`));
176
+ const stagingBuffer = device.createBuffer({
177
+ size: definition.size,
178
+ usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
179
+ });
180
+ const commandEncoder = device.createCommandEncoder();
181
+ commandEncoder.copyBufferToBuffer(gpuBuffer, 0, stagingBuffer, 0, definition.size);
182
+ device.queue.submit([commandEncoder.finish()]);
183
+ return stagingBuffer.mapAsync(GPUMapMode.READ).then(() => {
184
+ const result = stagingBuffer.getMappedRange().slice(0);
185
+ stagingBuffer.unmap();
186
+ stagingBuffer.destroy();
187
+ return result;
188
+ });
189
+ };
190
+ const renderFrame = (timestamp) => {
191
+ frameId = null;
192
+ if (isDisposed) return;
193
+ syncErrorHistory();
194
+ let materialState;
195
+ try {
196
+ materialState = resolveActiveMaterial();
197
+ } catch (error) {
198
+ setError(error, "initialization");
199
+ scheduleFrame();
200
+ return;
201
+ }
202
+ shouldContinueAfterFrame = false;
203
+ const outputColorSpace = options.getOutputColorSpace();
204
+ const rendererSignature = buildRendererPipelineSignature({
205
+ materialSignature: materialState.signature,
206
+ outputColorSpace
207
+ });
208
+ syncMaterialRuntimeState(materialState);
209
+ if (failedRendererSignature && failedRendererSignature !== rendererSignature) {
210
+ failedRendererSignature = null;
211
+ failedRendererAttempts = 0;
212
+ nextRendererRetryAt = 0;
213
+ }
214
+ if (!renderer || activeRendererSignature !== rendererSignature) {
215
+ if (failedRendererSignature === rendererSignature && performance.now() < nextRendererRetryAt) {
216
+ scheduleFrame();
217
+ return;
218
+ }
219
+ if (!rendererRebuildPromise) rendererRebuildPromise = (async () => {
220
+ try {
221
+ const nextRenderer = await createRenderer({
222
+ canvas: canvasElement,
223
+ fragmentWgsl: materialState.fragmentWgsl,
224
+ fragmentLineMap: materialState.fragmentLineMap,
225
+ fragmentSource: materialState.fragmentSource,
226
+ includeSources: materialState.includeSources,
227
+ defineBlockSource: materialState.defineBlockSource,
228
+ materialSource: materialState.source,
229
+ materialSignature: materialState.signature,
230
+ uniformLayout: materialState.uniformLayout,
231
+ textureKeys: materialState.textureKeys,
232
+ textureDefinitions: materialState.textures,
233
+ storageBufferKeys: materialState.storageBufferKeys,
234
+ storageBufferDefinitions,
235
+ storageTextureKeys: materialState.storageTextureKeys,
236
+ getRenderTargets: options.getRenderTargets,
237
+ getPasses: options.getPasses,
238
+ outputColorSpace,
239
+ getClearColor: options.getClearColor,
240
+ getDpr: () => options.dpr.current,
241
+ adapterOptions: options.getAdapterOptions(),
242
+ deviceDescriptor: options.getDeviceDescriptor()
243
+ });
244
+ if (isDisposed) {
245
+ nextRenderer.destroy();
246
+ return;
247
+ }
248
+ renderer?.destroy();
249
+ renderer = nextRenderer;
250
+ activeRendererSignature = rendererSignature;
251
+ failedRendererSignature = null;
252
+ failedRendererAttempts = 0;
253
+ nextRendererRetryAt = 0;
254
+ clearError();
255
+ } catch (error) {
256
+ failedRendererSignature = rendererSignature;
257
+ failedRendererAttempts += 1;
258
+ const retryDelayMs = getRendererRetryDelayMs(failedRendererAttempts);
259
+ nextRendererRetryAt = performance.now() + retryDelayMs;
260
+ setError(error, "initialization");
261
+ } finally {
262
+ rendererRebuildPromise = null;
263
+ scheduleFrame();
264
+ }
265
+ })();
266
+ return;
267
+ }
268
+ const time = timestamp / 1e3;
269
+ const rawDelta = Math.max(0, time - previousTime);
270
+ const delta = Math.min(rawDelta, options.maxDelta.current);
271
+ previousTime = time;
272
+ const rect = canvasElement.getBoundingClientRect();
273
+ const width = Math.max(0, Math.floor(rect.width));
274
+ const height = Math.max(0, Math.floor(rect.height));
275
+ if (width !== currentCssWidth || height !== currentCssHeight) {
276
+ currentCssWidth = width;
277
+ currentCssHeight = height;
278
+ size.set({
279
+ width,
280
+ height
281
+ });
282
+ }
283
+ try {
284
+ registry.run({
285
+ time,
286
+ delta,
287
+ setUniform,
288
+ setTexture,
289
+ writeStorageBuffer,
290
+ readStorageBuffer,
291
+ invalidate,
292
+ advance,
293
+ renderMode: registry.getRenderMode(),
294
+ autoRender: registry.getAutoRender(),
295
+ canvas: canvasElement
296
+ });
297
+ const shouldRenderFrame = registry.shouldRender();
298
+ shouldContinueAfterFrame = registry.getRenderMode() === "always" || registry.getRenderMode() === "on-demand" && shouldRenderFrame;
299
+ if (shouldRenderFrame) {
300
+ for (const key of uniformKeys) {
301
+ const runtimeValue = runtimeUniforms[key];
302
+ renderUniforms[key] = runtimeValue === void 0 ? activeUniforms[key] : runtimeValue;
303
+ }
304
+ for (const key of textureKeys) {
305
+ const runtimeValue = runtimeTextures[key];
306
+ renderTextures[key] = runtimeValue === void 0 ? activeTextures[key]?.source ?? null : runtimeValue;
307
+ }
308
+ canvasSize.width = width;
309
+ canvasSize.height = height;
310
+ renderer.render({
311
+ time,
312
+ delta,
313
+ renderMode: registry.getRenderMode(),
314
+ uniforms: renderUniforms,
315
+ textures: renderTextures,
316
+ canvasSize,
317
+ ...pendingStorageWrites.length > 0 ? { pendingStorageWrites: pendingStorageWrites.splice(0) } : {}
318
+ });
319
+ }
320
+ clearError();
321
+ } catch (error) {
322
+ setError(error, "render");
323
+ } finally {
324
+ registry.endFrame();
325
+ }
326
+ if (shouldContinueAfterFrame) scheduleFrame();
327
+ };
328
+ (async () => {
329
+ try {
330
+ syncMaterialRuntimeState(resolveActiveMaterial());
331
+ activeRendererSignature = "";
332
+ scheduleFrame();
333
+ } catch (error) {
334
+ setError(error, "initialization");
335
+ scheduleFrame();
336
+ }
337
+ })();
338
+ return {
339
+ requestFrame,
340
+ invalidate,
341
+ advance,
342
+ destroy: () => {
343
+ isDisposed = true;
344
+ if (frameId !== null) {
345
+ cancelAnimationFrame(frameId);
346
+ frameId = null;
347
+ }
348
+ renderer?.destroy();
349
+ registry.clear();
350
+ }
351
+ };
365
352
  }
353
+ //#endregion
354
+ export { createMotionGPURuntimeLoop };
355
+
356
+ //# sourceMappingURL=runtime-loop.js.map