@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
@@ -0,0 +1,301 @@
1
+ import { assertUniformName } from './uniforms.js';
2
+ import type { MaterialLineMap, MaterialSourceLocation } from './material-preprocess.js';
3
+ import type { StorageBufferType, UniformLayout } from './types.js';
4
+
5
+ /**
6
+ * Fallback uniform field used when no custom uniforms are provided.
7
+ */
8
+ const DEFAULT_UNIFORM_FIELD = 'motiongpu_unused: vec4f,';
9
+
10
+ /**
11
+ * Builds WGSL struct fields for user uniforms.
12
+ */
13
+ function buildUniformStruct(layout: UniformLayout): string {
14
+ if (layout.entries.length === 0) {
15
+ return DEFAULT_UNIFORM_FIELD;
16
+ }
17
+
18
+ return layout.entries
19
+ .map((entry) => {
20
+ assertUniformName(entry.name);
21
+ return `${entry.name}: ${entry.type},`;
22
+ })
23
+ .join('\n\t');
24
+ }
25
+
26
+ /**
27
+ * Builds a numeric expression that references one uniform value to keep bindings alive.
28
+ */
29
+ function getKeepAliveExpression(layout: UniformLayout): string {
30
+ if (layout.entries.length === 0) {
31
+ return 'motiongpuUniforms.motiongpu_unused.x';
32
+ }
33
+
34
+ const [firstEntry] = layout.entries;
35
+ if (!firstEntry) {
36
+ return 'motiongpuUniforms.motiongpu_unused.x';
37
+ }
38
+
39
+ if (firstEntry.type === 'f32') {
40
+ return `motiongpuUniforms.${firstEntry.name}`;
41
+ }
42
+
43
+ if (firstEntry.type === 'mat4x4f') {
44
+ return `motiongpuUniforms.${firstEntry.name}[0].x`;
45
+ }
46
+
47
+ return `motiongpuUniforms.${firstEntry.name}.x`;
48
+ }
49
+
50
+ /**
51
+ * Builds texture sampler/texture binding declarations.
52
+ */
53
+ function buildTextureBindings(textureKeys: string[]): string {
54
+ if (textureKeys.length === 0) {
55
+ return '';
56
+ }
57
+
58
+ const declarations: string[] = [];
59
+
60
+ for (let index = 0; index < textureKeys.length; index += 1) {
61
+ const key = textureKeys[index];
62
+ if (key === undefined) {
63
+ continue;
64
+ }
65
+
66
+ assertUniformName(key);
67
+ const binding = 2 + index * 2;
68
+ declarations.push(`@group(0) @binding(${binding}) var ${key}Sampler: sampler;`);
69
+ declarations.push(`@group(0) @binding(${binding + 1}) var ${key}: texture_2d<f32>;`);
70
+ }
71
+
72
+ return declarations.join('\n');
73
+ }
74
+
75
+ /**
76
+ * Builds read-only storage buffer bindings for fragment shader.
77
+ */
78
+ function buildFragmentStorageBufferBindings(
79
+ storageBufferKeys: string[],
80
+ definitions: Record<string, { type: StorageBufferType }>
81
+ ): string {
82
+ if (storageBufferKeys.length === 0) {
83
+ return '';
84
+ }
85
+
86
+ const declarations: string[] = [];
87
+
88
+ for (let index = 0; index < storageBufferKeys.length; index += 1) {
89
+ const key = storageBufferKeys[index];
90
+ if (key === undefined) {
91
+ continue;
92
+ }
93
+
94
+ const definition = definitions[key];
95
+ if (!definition) {
96
+ continue;
97
+ }
98
+
99
+ declarations.push(
100
+ `@group(1) @binding(${index}) var<storage, read> ${key}: ${definition.type};`
101
+ );
102
+ }
103
+
104
+ return declarations.join('\n');
105
+ }
106
+
107
+ /**
108
+ * Optionally returns helper WGSL for linear-to-sRGB conversion.
109
+ */
110
+ function buildColorTransformHelpers(enableSrgbTransform: boolean): string {
111
+ if (!enableSrgbTransform) {
112
+ return '';
113
+ }
114
+
115
+ return `
116
+ fn motiongpuLinearToSrgb(linearColor: vec3f) -> vec3f {
117
+ let cutoff = vec3f(0.0031308);
118
+ let lower = linearColor * 12.92;
119
+ let higher = vec3f(1.055) * pow(linearColor, vec3f(1.0 / 2.4)) - vec3f(0.055);
120
+ return select(lower, higher, linearColor > cutoff);
121
+ }
122
+ `;
123
+ }
124
+
125
+ /**
126
+ * Builds fragment output code with optional color-space conversion.
127
+ */
128
+ function buildFragmentOutput(keepAliveExpression: string, enableSrgbTransform: boolean): string {
129
+ if (enableSrgbTransform) {
130
+ return `
131
+ let fragColor = frag(in.uv);
132
+ let motiongpuKeepAlive = ${keepAliveExpression};
133
+ let motiongpuLinear = vec4f(fragColor.rgb + motiongpuKeepAlive * 0.0, fragColor.a);
134
+ let motiongpuSrgb = motiongpuLinearToSrgb(max(motiongpuLinear.rgb, vec3f(0.0)));
135
+ return vec4f(motiongpuSrgb, motiongpuLinear.a);
136
+ `;
137
+ }
138
+
139
+ return `
140
+ let fragColor = frag(in.uv);
141
+ let motiongpuKeepAlive = ${keepAliveExpression};
142
+ return vec4f(fragColor.rgb + motiongpuKeepAlive * 0.0, fragColor.a);
143
+ `;
144
+ }
145
+
146
+ /**
147
+ * 1-based map from generated WGSL lines to original material source lines.
148
+ */
149
+ export type ShaderLineMap = Array<MaterialSourceLocation | null>;
150
+
151
+ /**
152
+ * Result of shader source generation with line mapping metadata.
153
+ */
154
+ export interface BuiltShaderSource {
155
+ /**
156
+ * Full WGSL source code.
157
+ */
158
+ code: string;
159
+ /**
160
+ * 1-based generated-line map to material source locations.
161
+ */
162
+ lineMap: ShaderLineMap;
163
+ }
164
+
165
+ /**
166
+ * Assembles complete WGSL shader source used by the fullscreen renderer pipeline.
167
+ *
168
+ * @param fragmentWgsl - User fragment shader code containing `frag(uv: vec2f) -> vec4f`.
169
+ * @param uniformLayout - Resolved uniform layout.
170
+ * @param textureKeys - Sorted texture keys.
171
+ * @param options - Shader build options.
172
+ * @returns Complete WGSL source for vertex + fragment stages.
173
+ */
174
+ export function buildShaderSource(
175
+ fragmentWgsl: string,
176
+ uniformLayout: UniformLayout,
177
+ textureKeys: string[] = [],
178
+ options?: {
179
+ convertLinearToSrgb?: boolean;
180
+ storageBufferKeys?: string[];
181
+ storageBufferDefinitions?: Record<string, { type: StorageBufferType }>;
182
+ }
183
+ ): string {
184
+ const uniformFields = buildUniformStruct(uniformLayout);
185
+ const keepAliveExpression = getKeepAliveExpression(uniformLayout);
186
+ const textureBindings = buildTextureBindings(textureKeys);
187
+ const enableSrgbTransform = options?.convertLinearToSrgb ?? false;
188
+ const colorTransformHelpers = buildColorTransformHelpers(enableSrgbTransform);
189
+ const fragmentOutput = buildFragmentOutput(keepAliveExpression, enableSrgbTransform);
190
+ const storageBufferBindings = buildFragmentStorageBufferBindings(
191
+ options?.storageBufferKeys ?? [],
192
+ options?.storageBufferDefinitions ?? {}
193
+ );
194
+
195
+ return `
196
+ struct MotionGPUFrame {
197
+ time: f32,
198
+ delta: f32,
199
+ resolution: vec2f,
200
+ };
201
+
202
+ struct MotionGPUUniforms {
203
+ ${uniformFields}
204
+ };
205
+
206
+ @group(0) @binding(0) var<uniform> motiongpuFrame: MotionGPUFrame;
207
+ @group(0) @binding(1) var<uniform> motiongpuUniforms: MotionGPUUniforms;
208
+ ${textureBindings}
209
+ ${storageBufferBindings ? '\n' + storageBufferBindings : ''}
210
+ ${colorTransformHelpers}
211
+
212
+ struct MotionGPUVertexOut {
213
+ @builtin(position) position: vec4f,
214
+ @location(0) uv: vec2f,
215
+ };
216
+
217
+ @vertex
218
+ fn motiongpuVertex(@builtin(vertex_index) index: u32) -> MotionGPUVertexOut {
219
+ var positions = array<vec2f, 3>(
220
+ vec2f(-1.0, -3.0),
221
+ vec2f(-1.0, 1.0),
222
+ vec2f(3.0, 1.0)
223
+ );
224
+
225
+ let position = positions[index];
226
+ var out: MotionGPUVertexOut;
227
+ out.position = vec4f(position, 0.0, 1.0);
228
+ out.uv = (position + vec2f(1.0, 1.0)) * 0.5;
229
+ return out;
230
+ }
231
+
232
+ ${fragmentWgsl}
233
+
234
+ @fragment
235
+ fn motiongpuFragment(in: MotionGPUVertexOut) -> @location(0) vec4f {
236
+ ${fragmentOutput}
237
+ }
238
+ `;
239
+ }
240
+
241
+ /**
242
+ * Assembles complete WGSL shader source with material-source line mapping metadata.
243
+ */
244
+ export function buildShaderSourceWithMap(
245
+ fragmentWgsl: string,
246
+ uniformLayout: UniformLayout,
247
+ textureKeys: string[] = [],
248
+ options?: {
249
+ convertLinearToSrgb?: boolean;
250
+ fragmentLineMap?: MaterialLineMap;
251
+ storageBufferKeys?: string[];
252
+ storageBufferDefinitions?: Record<string, { type: StorageBufferType }>;
253
+ }
254
+ ): BuiltShaderSource {
255
+ const code = buildShaderSource(fragmentWgsl, uniformLayout, textureKeys, options);
256
+ const fragmentStartIndex = code.indexOf(fragmentWgsl);
257
+ const lineCount = code.split('\n').length;
258
+ const lineMap: ShaderLineMap = new Array(lineCount + 1).fill(null);
259
+
260
+ if (fragmentStartIndex === -1) {
261
+ return {
262
+ code,
263
+ lineMap
264
+ };
265
+ }
266
+
267
+ const fragmentStartLine = code.slice(0, fragmentStartIndex).split('\n').length;
268
+ const fragmentLineCount = fragmentWgsl.split('\n').length;
269
+
270
+ for (let line = 0; line < fragmentLineCount; line += 1) {
271
+ const generatedLine = fragmentStartLine + line;
272
+ lineMap[generatedLine] = options?.fragmentLineMap?.[line + 1] ?? {
273
+ kind: 'fragment',
274
+ line: line + 1
275
+ };
276
+ }
277
+
278
+ return {
279
+ code,
280
+ lineMap
281
+ };
282
+ }
283
+
284
+ /**
285
+ * Converts source location metadata to user-facing diagnostics label.
286
+ */
287
+ export function formatShaderSourceLocation(location: MaterialSourceLocation | null): string | null {
288
+ if (!location) {
289
+ return null;
290
+ }
291
+
292
+ if (location.kind === 'fragment') {
293
+ return `fragment line ${location.line}`;
294
+ }
295
+
296
+ if (location.kind === 'include') {
297
+ return `include <${location.include}> line ${location.line}`;
298
+ }
299
+
300
+ return `define "${location.define}" line ${location.line}`;
301
+ }
@@ -0,0 +1,142 @@
1
+ import { assertUniformName } from './uniforms.js';
2
+ import type {
3
+ StorageBufferDefinition,
4
+ StorageBufferDefinitionMap,
5
+ StorageBufferType
6
+ } from './types.js';
7
+
8
+ /**
9
+ * Valid WGSL storage buffer element types.
10
+ */
11
+ const VALID_STORAGE_BUFFER_TYPES: ReadonlySet<string> = new Set<StorageBufferType>([
12
+ 'array<f32>',
13
+ 'array<vec2f>',
14
+ 'array<vec3f>',
15
+ 'array<vec4f>',
16
+ 'array<u32>',
17
+ 'array<i32>',
18
+ 'array<vec4u>',
19
+ 'array<vec4i>'
20
+ ]);
21
+
22
+ /**
23
+ * Storage-compatible texture formats for `texture_storage_2d`.
24
+ */
25
+ export const STORAGE_TEXTURE_FORMATS: ReadonlySet<GPUTextureFormat> = new Set([
26
+ 'r32float',
27
+ 'r32sint',
28
+ 'r32uint',
29
+ 'rg32float',
30
+ 'rg32sint',
31
+ 'rg32uint',
32
+ 'rgba8unorm',
33
+ 'rgba8snorm',
34
+ 'rgba8uint',
35
+ 'rgba8sint',
36
+ 'rgba16float',
37
+ 'rgba16uint',
38
+ 'rgba16sint',
39
+ 'rgba32float',
40
+ 'rgba32uint',
41
+ 'rgba32sint',
42
+ 'bgra8unorm'
43
+ ] as GPUTextureFormat[]);
44
+
45
+ /**
46
+ * Validates a single storage buffer definition.
47
+ *
48
+ * @param name - Buffer identifier.
49
+ * @param definition - Storage buffer definition to validate.
50
+ * @throws {Error} When any field is invalid.
51
+ */
52
+ export function assertStorageBufferDefinition(
53
+ name: string,
54
+ definition: StorageBufferDefinition
55
+ ): void {
56
+ assertUniformName(name);
57
+
58
+ if (!Number.isFinite(definition.size) || definition.size <= 0) {
59
+ throw new Error(
60
+ `Storage buffer "${name}" size must be a finite number greater than 0, got ${definition.size}`
61
+ );
62
+ }
63
+
64
+ if (definition.size % 4 !== 0) {
65
+ throw new Error(
66
+ `Storage buffer "${name}" size must be a multiple of 4, got ${definition.size}`
67
+ );
68
+ }
69
+
70
+ if (!VALID_STORAGE_BUFFER_TYPES.has(definition.type)) {
71
+ throw new Error(
72
+ `Storage buffer "${name}" has unknown type "${definition.type}". Supported types: ${[...VALID_STORAGE_BUFFER_TYPES].join(', ')}`
73
+ );
74
+ }
75
+
76
+ if (
77
+ definition.access !== undefined &&
78
+ definition.access !== 'read' &&
79
+ definition.access !== 'read-write'
80
+ ) {
81
+ throw new Error(
82
+ `Storage buffer "${name}" has invalid access mode "${definition.access}". Use 'read' or 'read-write'.`
83
+ );
84
+ }
85
+
86
+ if (definition.initialData !== undefined) {
87
+ if (definition.initialData.byteLength > definition.size) {
88
+ throw new Error(
89
+ `Storage buffer "${name}" initialData byte length (${definition.initialData.byteLength}) exceeds buffer size (${definition.size})`
90
+ );
91
+ }
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Validates and returns sorted storage buffer keys.
97
+ *
98
+ * @param definitions - Storage buffer definition map.
99
+ * @returns Lexicographically sorted buffer keys.
100
+ */
101
+ export function resolveStorageBufferKeys(definitions: StorageBufferDefinitionMap): string[] {
102
+ const keys = Object.keys(definitions).sort();
103
+ for (const key of keys) {
104
+ const definition = definitions[key];
105
+ if (definition) {
106
+ assertStorageBufferDefinition(key, definition);
107
+ }
108
+ }
109
+ return keys;
110
+ }
111
+
112
+ /**
113
+ * Normalizes a storage buffer definition with defaults applied.
114
+ *
115
+ * @param definition - Raw definition.
116
+ * @returns Normalized definition with access default.
117
+ */
118
+ export function normalizeStorageBufferDefinition(
119
+ definition: StorageBufferDefinition
120
+ ): Required<Pick<StorageBufferDefinition, 'size' | 'type' | 'access'>> {
121
+ return {
122
+ size: definition.size,
123
+ type: definition.type,
124
+ access: definition.access ?? 'read-write'
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Validates that a texture format is storage-compatible.
130
+ *
131
+ * @param name - Texture identifier.
132
+ * @param format - GPU texture format.
133
+ * @throws {Error} When format is not storage-compatible.
134
+ */
135
+ export function assertStorageTextureFormat(name: string, format: GPUTextureFormat): void {
136
+ if (!STORAGE_TEXTURE_FORMATS.has(format)) {
137
+ throw new Error(
138
+ `Texture "${name}" with storage:true requires a storage-compatible format, but got "${format}". ` +
139
+ `Supported formats: ${[...STORAGE_TEXTURE_FORMATS].join(', ')}`
140
+ );
141
+ }
142
+ }