@luma.gl/shadertools 9.2.6 → 9.3.0-alpha.11

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 (182) hide show
  1. package/dist/dist.dev.js +5388 -6438
  2. package/dist/dist.min.js +2264 -322
  3. package/dist/index.cjs +3601 -566
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/index.d.ts +12 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +5 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/glsl-utils/shader-utils.js +4 -4
  10. package/dist/lib/glsl-utils/shader-utils.js.map +1 -1
  11. package/dist/lib/preprocessor/preprocessor.d.ts.map +1 -1
  12. package/dist/lib/preprocessor/preprocessor.js +35 -8
  13. package/dist/lib/preprocessor/preprocessor.js.map +1 -1
  14. package/dist/lib/shader-assembler.d.ts +10 -0
  15. package/dist/lib/shader-assembler.d.ts.map +1 -1
  16. package/dist/lib/shader-assembler.js +20 -3
  17. package/dist/lib/shader-assembler.js.map +1 -1
  18. package/dist/lib/shader-assembly/assemble-shaders.d.ts +23 -2
  19. package/dist/lib/shader-assembly/assemble-shaders.d.ts.map +1 -1
  20. package/dist/lib/shader-assembly/assemble-shaders.js +265 -12
  21. package/dist/lib/shader-assembly/assemble-shaders.js.map +1 -1
  22. package/dist/lib/shader-assembly/wgsl-binding-debug.d.ts +37 -0
  23. package/dist/lib/shader-assembly/wgsl-binding-debug.d.ts.map +1 -0
  24. package/dist/lib/shader-assembly/wgsl-binding-debug.js +144 -0
  25. package/dist/lib/shader-assembly/wgsl-binding-debug.js.map +1 -0
  26. package/dist/lib/shader-assembly/wgsl-binding-scan.d.ts +19 -0
  27. package/dist/lib/shader-assembly/wgsl-binding-scan.d.ts.map +1 -0
  28. package/dist/lib/shader-assembly/wgsl-binding-scan.js +151 -0
  29. package/dist/lib/shader-assembly/wgsl-binding-scan.js.map +1 -0
  30. package/dist/lib/shader-generator/glsl/generate-glsl.js +7 -4
  31. package/dist/lib/shader-generator/glsl/generate-glsl.js.map +1 -1
  32. package/dist/lib/shader-generator/wgsl/generate-wgsl.d.ts.map +1 -1
  33. package/dist/lib/shader-generator/wgsl/generate-wgsl.js +3 -0
  34. package/dist/lib/shader-generator/wgsl/generate-wgsl.js.map +1 -1
  35. package/dist/lib/shader-module/shader-module-uniform-layout.d.ts +91 -0
  36. package/dist/lib/shader-module/shader-module-uniform-layout.d.ts.map +1 -0
  37. package/dist/lib/shader-module/shader-module-uniform-layout.js +253 -0
  38. package/dist/lib/shader-module/shader-module-uniform-layout.js.map +1 -0
  39. package/dist/lib/shader-module/shader-module.d.ts +12 -6
  40. package/dist/lib/shader-module/shader-module.d.ts.map +1 -1
  41. package/dist/lib/shader-module/shader-module.js.map +1 -1
  42. package/dist/lib/utils/assert.d.ts.map +1 -1
  43. package/dist/lib/utils/assert.js +3 -1
  44. package/dist/lib/utils/assert.js.map +1 -1
  45. package/dist/lib/utils/uniform-types.d.ts +11 -7
  46. package/dist/lib/utils/uniform-types.d.ts.map +1 -1
  47. package/dist/modules/engine/picking/picking.d.ts +5 -2
  48. package/dist/modules/engine/picking/picking.d.ts.map +1 -1
  49. package/dist/modules/engine/picking/picking.js +5 -2
  50. package/dist/modules/engine/picking/picking.js.map +1 -1
  51. package/dist/modules/engine/project/project.d.ts +1 -1
  52. package/dist/modules/engine/project/project.js +1 -1
  53. package/dist/modules/engine/skin/skin.d.ts +30 -0
  54. package/dist/modules/engine/skin/skin.d.ts.map +1 -0
  55. package/dist/modules/engine/skin/skin.js +86 -0
  56. package/dist/modules/engine/skin/skin.js.map +1 -0
  57. package/dist/modules/lighting/gouraud-material/gouraud-material.d.ts +1 -0
  58. package/dist/modules/lighting/gouraud-material/gouraud-material.d.ts.map +1 -1
  59. package/dist/modules/lighting/gouraud-material/gouraud-material.js +3 -0
  60. package/dist/modules/lighting/gouraud-material/gouraud-material.js.map +1 -1
  61. package/dist/modules/lighting/ibl/ibl.d.ts +26 -0
  62. package/dist/modules/lighting/ibl/ibl.d.ts.map +1 -0
  63. package/dist/modules/lighting/ibl/ibl.js +33 -0
  64. package/dist/modules/lighting/ibl/ibl.js.map +1 -0
  65. package/dist/modules/lighting/lambert-material/lambert-material.d.ts +10 -0
  66. package/dist/modules/lighting/lambert-material/lambert-material.d.ts.map +1 -0
  67. package/dist/modules/lighting/lambert-material/lambert-material.js +33 -0
  68. package/dist/modules/lighting/lambert-material/lambert-material.js.map +1 -0
  69. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.d.ts +3 -0
  70. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.d.ts.map +1 -0
  71. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.js +60 -0
  72. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.js.map +1 -0
  73. package/dist/modules/lighting/lambert-material/lambert-shaders-wgsl.d.ts +2 -0
  74. package/dist/modules/lighting/lambert-material/lambert-shaders-wgsl.d.ts.map +1 -0
  75. package/dist/modules/lighting/lambert-material/lambert-shaders-wgsl.js +73 -0
  76. package/dist/modules/lighting/lambert-material/lambert-shaders-wgsl.js.map +1 -0
  77. package/dist/modules/lighting/lights/lighting-glsl.d.ts +1 -1
  78. package/dist/modules/lighting/lights/lighting-glsl.d.ts.map +1 -1
  79. package/dist/modules/lighting/lights/lighting-glsl.js +44 -38
  80. package/dist/modules/lighting/lights/lighting-glsl.js.map +1 -1
  81. package/dist/modules/lighting/lights/lighting-wgsl.d.ts +1 -1
  82. package/dist/modules/lighting/lights/lighting-wgsl.d.ts.map +1 -1
  83. package/dist/modules/lighting/lights/lighting-wgsl.js +46 -18
  84. package/dist/modules/lighting/lights/lighting-wgsl.js.map +1 -1
  85. package/dist/modules/lighting/lights/lighting.d.ts +104 -62
  86. package/dist/modules/lighting/lights/lighting.d.ts.map +1 -1
  87. package/dist/modules/lighting/lights/lighting.js +107 -68
  88. package/dist/modules/lighting/lights/lighting.js.map +1 -1
  89. package/dist/modules/lighting/no-material/dirlight.d.ts +8 -3
  90. package/dist/modules/lighting/no-material/dirlight.d.ts.map +1 -1
  91. package/dist/modules/lighting/no-material/dirlight.js +5 -3
  92. package/dist/modules/lighting/no-material/dirlight.js.map +1 -1
  93. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts +2 -2
  94. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts.map +1 -1
  95. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js +694 -38
  96. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js.map +1 -1
  97. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts +2 -2
  98. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts.map +1 -1
  99. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js +956 -109
  100. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js.map +1 -1
  101. package/dist/modules/lighting/pbr-material/pbr-material.d.ts +242 -43
  102. package/dist/modules/lighting/pbr-material/pbr-material.d.ts.map +1 -1
  103. package/dist/modules/lighting/pbr-material/pbr-material.js +178 -2
  104. package/dist/modules/lighting/pbr-material/pbr-material.js.map +1 -1
  105. package/dist/modules/lighting/pbr-material/pbr-projection.d.ts.map +1 -1
  106. package/dist/modules/lighting/pbr-material/pbr-projection.js +14 -2
  107. package/dist/modules/lighting/pbr-material/pbr-projection.js.map +1 -1
  108. package/dist/modules/lighting/pbr-material/pbr-scene.d.ts +40 -0
  109. package/dist/modules/lighting/pbr-material/pbr-scene.d.ts.map +1 -0
  110. package/dist/modules/lighting/pbr-material/pbr-scene.js +67 -0
  111. package/dist/modules/lighting/pbr-material/pbr-scene.js.map +1 -0
  112. package/dist/modules/lighting/phong-material/phong-material.d.ts +1 -0
  113. package/dist/modules/lighting/phong-material/phong-material.d.ts.map +1 -1
  114. package/dist/modules/lighting/phong-material/phong-material.js +4 -0
  115. package/dist/modules/lighting/phong-material/phong-material.js.map +1 -1
  116. package/dist/modules/lighting/phong-material/phong-shaders-glsl.d.ts +2 -2
  117. package/dist/modules/lighting/phong-material/phong-shaders-glsl.d.ts.map +1 -1
  118. package/dist/modules/lighting/phong-material/phong-shaders-glsl.js +17 -6
  119. package/dist/modules/lighting/phong-material/phong-shaders-glsl.js.map +1 -1
  120. package/dist/modules/lighting/phong-material/phong-shaders-wgsl.d.ts +1 -40
  121. package/dist/modules/lighting/phong-material/phong-shaders-wgsl.d.ts.map +1 -1
  122. package/dist/modules/lighting/phong-material/phong-shaders-wgsl.js +71 -76
  123. package/dist/modules/lighting/phong-material/phong-shaders-wgsl.js.map +1 -1
  124. package/dist/modules/math/fp64/fp64-arithmetic-glsl.d.ts +1 -1
  125. package/dist/modules/math/fp64/fp64-arithmetic-glsl.d.ts.map +1 -1
  126. package/dist/modules/math/fp64/fp64-arithmetic-glsl.js +42 -11
  127. package/dist/modules/math/fp64/fp64-arithmetic-glsl.js.map +1 -1
  128. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.d.ts +2 -0
  129. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.d.ts.map +1 -0
  130. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.js +212 -0
  131. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.js.map +1 -0
  132. package/dist/modules/math/fp64/fp64.d.ts +1 -0
  133. package/dist/modules/math/fp64/fp64.d.ts.map +1 -1
  134. package/dist/modules/math/fp64/fp64.js +8 -2
  135. package/dist/modules/math/fp64/fp64.js.map +1 -1
  136. package/dist/modules/math/random/random.d.ts +1 -1
  137. package/dist/modules/math/random/random.d.ts.map +1 -1
  138. package/dist/modules/math/random/random.js +2 -3
  139. package/dist/modules/math/random/random.js.map +1 -1
  140. package/package.json +4 -5
  141. package/src/index.ts +37 -6
  142. package/src/lib/glsl-utils/shader-utils.ts +4 -4
  143. package/src/lib/preprocessor/preprocessor.ts +44 -8
  144. package/src/lib/shader-assembler.ts +25 -3
  145. package/src/lib/shader-assembly/assemble-shaders.ts +506 -13
  146. package/src/lib/shader-assembly/wgsl-binding-debug.ts +227 -0
  147. package/src/lib/shader-assembly/wgsl-binding-scan.ts +228 -0
  148. package/src/lib/shader-generator/glsl/generate-glsl.ts +11 -5
  149. package/src/lib/shader-generator/wgsl/generate-wgsl.ts +6 -0
  150. package/src/lib/shader-module/shader-module-uniform-layout.ts +420 -0
  151. package/src/lib/shader-module/shader-module.ts +17 -7
  152. package/src/lib/utils/assert.ts +3 -1
  153. package/src/lib/utils/uniform-types.ts +24 -9
  154. package/src/modules/engine/picking/picking.ts +5 -2
  155. package/src/modules/engine/project/project.ts +1 -1
  156. package/src/modules/engine/skin/skin.ts +114 -0
  157. package/src/modules/lighting/gouraud-material/gouraud-material.ts +4 -0
  158. package/src/modules/lighting/ibl/ibl.ts +44 -0
  159. package/src/modules/lighting/lambert-material/lambert-material.ts +42 -0
  160. package/src/modules/lighting/lambert-material/lambert-shaders-glsl.ts +61 -0
  161. package/src/modules/lighting/lambert-material/lambert-shaders-wgsl.ts +73 -0
  162. package/src/modules/lighting/lights/lighting-glsl.ts +44 -38
  163. package/src/modules/lighting/lights/lighting-wgsl.ts +46 -18
  164. package/src/modules/lighting/lights/lighting.ts +198 -99
  165. package/src/modules/lighting/no-material/dirlight.ts +5 -3
  166. package/src/modules/lighting/pbr-material/pbr-material-glsl.ts +694 -38
  167. package/src/modules/lighting/pbr-material/pbr-material-wgsl.ts +956 -109
  168. package/src/modules/lighting/pbr-material/pbr-material.ts +294 -7
  169. package/src/modules/lighting/pbr-material/pbr-projection.ts +15 -2
  170. package/src/modules/lighting/pbr-material/pbr-scene.ts +91 -0
  171. package/src/modules/lighting/phong-material/phong-material.ts +5 -0
  172. package/src/modules/lighting/phong-material/phong-shaders-glsl.ts +17 -6
  173. package/src/modules/lighting/phong-material/phong-shaders-wgsl.ts +71 -77
  174. package/src/modules/math/fp64/fp64-arithmetic-glsl.ts +42 -11
  175. package/src/modules/math/fp64/fp64-arithmetic-wgsl.ts +212 -0
  176. package/src/modules/math/fp64/fp64.ts +9 -3
  177. package/src/modules/math/random/random.ts +2 -3
  178. package/dist/lib/wgsl/get-shader-layout-wgsl.d.ts +0 -8
  179. package/dist/lib/wgsl/get-shader-layout-wgsl.d.ts.map +0 -1
  180. package/dist/lib/wgsl/get-shader-layout-wgsl.js +0 -95
  181. package/dist/lib/wgsl/get-shader-layout-wgsl.js.map +0 -1
  182. package/src/lib/wgsl/get-shader-layout-wgsl.ts +0 -105
@@ -8,13 +8,29 @@ import {getPlatformShaderDefines} from './platform-defines';
8
8
  import {injectShader, DECLARATION_INJECT_MARKER} from './shader-injections';
9
9
  import {transpileGLSLShader} from '../shader-transpiler/transpile-glsl-shader';
10
10
  import {checkShaderModuleDeprecations} from '../shader-module/shader-module';
11
+ import {
12
+ validateShaderModuleUniformLayout,
13
+ warnIfGLSLUniformBlocksAreNotStd140
14
+ } from '../shader-module/shader-module-uniform-layout';
11
15
  import type {ShaderInjection} from './shader-injections';
12
16
  import type {ShaderModule} from '../shader-module/shader-module';
13
17
  import {ShaderHook, normalizeShaderHooks, getShaderHooks} from './shader-hooks';
14
18
  import {assert} from '../utils/assert';
15
19
  import {getShaderInfo} from '../glsl-utils/get-shader-info';
20
+ import {getShaderBindingDebugRowsFromWGSL, type ShaderBindingDebugRow} from './wgsl-binding-debug';
21
+ import {
22
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES,
23
+ WGSL_BINDING_DECLARATION_REGEXES,
24
+ WGSL_EXPLICIT_BINDING_DECLARATION_REGEXES,
25
+ getFirstWGSLAutoBindingDeclarationMatch,
26
+ getWGSLBindingDeclarationMatches,
27
+ hasWGSLAutoBinding,
28
+ replaceWGSLBindingDeclarationMatches,
29
+ type WGSLBindingDeclarationMatch
30
+ } from './wgsl-binding-scan';
16
31
 
17
32
  const INJECT_SHADER_DECLARATIONS = `\n\n${DECLARATION_INJECT_MARKER}\n`;
33
+ const RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT = 100;
18
34
 
19
35
  /**
20
36
  * Precision prologue to inject before functions are injected in shader
@@ -78,6 +94,8 @@ type AssembleStageOptions = {
78
94
  prologue?: boolean;
79
95
  /** logger object */
80
96
  log?: any;
97
+ /** @internal Stable per-assembler WGSL binding assignments. */
98
+ _bindingRegistry?: Map<string, number>;
81
99
  };
82
100
 
83
101
  export type HookFunction = {hook: string; header: string; footer: string; signature?: string};
@@ -94,21 +112,28 @@ export function assembleWGSLShader(
94
112
  options: AssembleShaderOptions & {
95
113
  /** Single WGSL shader */
96
114
  source: string;
115
+ /** @internal Stable per-assembler WGSL binding assignments. */
116
+ _bindingRegistry?: Map<string, number>;
97
117
  }
98
118
  ): {
99
119
  source: string;
100
120
  getUniforms: GetUniformsFunc;
121
+ bindingAssignments: {moduleName: string; name: string; group: number; location: number}[];
122
+ bindingTable: ShaderBindingDebugRow[];
101
123
  } {
102
124
  const modules = getShaderModuleDependencies(options.modules || []);
125
+ const {source, bindingAssignments} = assembleShaderWGSL(options.platformInfo, {
126
+ ...options,
127
+ source: options.source,
128
+ stage: 'vertex',
129
+ modules
130
+ });
103
131
 
104
132
  return {
105
- source: assembleShaderWGSL(options.platformInfo, {
106
- ...options,
107
- source: options.source,
108
- stage: 'vertex',
109
- modules
110
- }),
111
- getUniforms: assembleGetUniforms(modules)
133
+ source,
134
+ getUniforms: assembleGetUniforms(modules),
135
+ bindingAssignments,
136
+ bindingTable: getShaderBindingDebugRowsFromWGSL(source, bindingAssignments)
112
137
  };
113
138
  }
114
139
 
@@ -155,7 +180,10 @@ export function assembleGLSLShaderPair(
155
180
  * @param options
156
181
  * @returns
157
182
  */
158
- export function assembleShaderWGSL(platformInfo: PlatformInfo, options: AssembleStageOptions) {
183
+ export function assembleShaderWGSL(
184
+ platformInfo: PlatformInfo,
185
+ options: AssembleStageOptions
186
+ ): {source: string; bindingAssignments: WGSLBindingAssignment[]} {
159
187
  const {
160
188
  // id,
161
189
  source,
@@ -226,12 +254,32 @@ export function assembleShaderWGSL(platformInfo: PlatformInfo, options: Assemble
226
254
 
227
255
  // TODO - hack until shadertool modules support WebGPU
228
256
  const modulesToInject = modules;
257
+ const applicationRelocation = relocateWGSLApplicationBindings(coreSource);
258
+ const usedBindingsByGroup = getUsedBindingsByGroupFromApplicationWGSL(
259
+ applicationRelocation.source
260
+ );
261
+ const reservedBindingKeysByGroup = reserveRegisteredModuleBindings(
262
+ modulesToInject,
263
+ options._bindingRegistry,
264
+ usedBindingsByGroup
265
+ );
266
+ const bindingAssignments: WGSLBindingAssignment[] = [];
229
267
 
230
268
  for (const module of modulesToInject) {
231
269
  if (log) {
232
270
  checkShaderModuleDeprecations(module, coreSource, log);
233
271
  }
234
- const moduleSource = getShaderModuleSource(module, 'wgsl');
272
+ const relocation = relocateWGSLModuleBindings(
273
+ getShaderModuleSource(module, 'wgsl', log),
274
+ module,
275
+ {
276
+ usedBindingsByGroup,
277
+ bindingRegistry: options._bindingRegistry,
278
+ reservedBindingKeysByGroup
279
+ }
280
+ );
281
+ bindingAssignments.push(...relocation.bindingAssignments);
282
+ const moduleSource = relocation.source;
235
283
  // Add the module source, and a #define that declares it presence
236
284
  assembledSource += moduleSource;
237
285
 
@@ -256,14 +304,17 @@ export function assembleShaderWGSL(platformInfo: PlatformInfo, options: Assemble
256
304
  assembledSource = injectShader(assembledSource, stage, declInjections);
257
305
 
258
306
  assembledSource += getShaderHooks(hookFunctionMap[stage], hookInjections);
307
+ assembledSource += formatWGSLBindingAssignmentComments(bindingAssignments);
259
308
 
260
309
  // Add the version directive and actual source of this shader
261
- assembledSource += coreSource;
310
+ assembledSource += applicationRelocation.source;
262
311
 
263
312
  // Apply any requested shader injections
264
313
  assembledSource = injectShader(assembledSource, stage, mainInjections);
265
314
 
266
- return assembledSource;
315
+ assertNoUnresolvedAutoBindings(assembledSource);
316
+
317
+ return {source: assembledSource, bindingAssignments};
267
318
  }
268
319
 
269
320
  /**
@@ -379,7 +430,7 @@ ${getApplicationDefines(allDefines)}
379
430
  if (log) {
380
431
  checkShaderModuleDeprecations(module, coreSource, log);
381
432
  }
382
- const moduleSource = getShaderModuleSource(module, stage);
433
+ const moduleSource = getShaderModuleSource(module, stage, log);
383
434
  // Add the module source, and a #define that declares it presence
384
435
  assembledSource += moduleSource;
385
436
 
@@ -417,6 +468,10 @@ ${getApplicationDefines(allDefines)}
417
468
  assembledSource = transpileGLSLShader(assembledSource, stage);
418
469
  }
419
470
 
471
+ if (language === 'glsl') {
472
+ warnIfGLSLUniformBlocksAreNotStd140(assembledSource, stage, log);
473
+ }
474
+
420
475
  return assembledSource.trim();
421
476
  }
422
477
 
@@ -476,7 +531,8 @@ function getApplicationDefines(defines: Record<string, boolean> = {}): string {
476
531
  /** Extracts the source code chunk for the specified shader type from the named shader module */
477
532
  export function getShaderModuleSource(
478
533
  module: ShaderModule,
479
- stage: 'vertex' | 'fragment' | 'wgsl'
534
+ stage: 'vertex' | 'fragment' | 'wgsl',
535
+ log?: any
480
536
  ): string {
481
537
  let moduleSource;
482
538
  switch (stage) {
@@ -496,6 +552,9 @@ export function getShaderModuleSource(
496
552
  if (!module.name) {
497
553
  throw new Error('Shader module must have a name');
498
554
  }
555
+
556
+ validateShaderModuleUniformLayout(module, stage, {log});
557
+
499
558
  const moduleName = module.name.toUpperCase().replace(/[^0-9a-z]/gi, '_');
500
559
  let source = `\
501
560
  // ----- MODULE ${module.name} ---------------
@@ -508,6 +567,440 @@ export function getShaderModuleSource(
508
567
  return source;
509
568
  }
510
569
 
570
+ type BindingRelocationContext = {
571
+ usedBindingsByGroup: Map<number, Set<number>>;
572
+ bindingRegistry?: Map<string, number>;
573
+ reservedBindingKeysByGroup: Map<number, Map<number, string>>;
574
+ };
575
+
576
+ type WGSLBindingAssignment = {
577
+ moduleName: string;
578
+ name: string;
579
+ group: number;
580
+ location: number;
581
+ };
582
+
583
+ type WGSLApplicationRelocationState = {
584
+ sawSupportedBindingDeclaration: boolean;
585
+ };
586
+
587
+ type WGSLRelocationState = {
588
+ sawSupportedBindingDeclaration: boolean;
589
+ nextHintedBindingLocation: number | null;
590
+ };
591
+
592
+ type WGSLRelocationParams = {
593
+ module: ShaderModule;
594
+ context: BindingRelocationContext;
595
+ bindingAssignments: WGSLBindingAssignment[];
596
+ relocationState: WGSLRelocationState;
597
+ };
598
+
599
+ function getUsedBindingsByGroupFromApplicationWGSL(source: string): Map<number, Set<number>> {
600
+ const usedBindingsByGroup = new Map<number, Set<number>>();
601
+
602
+ for (const match of getWGSLBindingDeclarationMatches(
603
+ source,
604
+ WGSL_EXPLICIT_BINDING_DECLARATION_REGEXES
605
+ )) {
606
+ const location = Number(match.bindingToken);
607
+ const group = Number(match.groupToken);
608
+
609
+ validateApplicationWGSLBinding(group, location, match.name);
610
+ registerUsedBindingLocation(
611
+ usedBindingsByGroup,
612
+ group,
613
+ location,
614
+ `application binding "${match.name}"`
615
+ );
616
+ }
617
+
618
+ return usedBindingsByGroup;
619
+ }
620
+
621
+ function relocateWGSLApplicationBindings(source: string): {source: string} {
622
+ const declarationMatches = getWGSLBindingDeclarationMatches(
623
+ source,
624
+ WGSL_BINDING_DECLARATION_REGEXES
625
+ );
626
+ const usedBindingsByGroup = new Map<number, Set<number>>();
627
+
628
+ for (const declarationMatch of declarationMatches) {
629
+ if (declarationMatch.bindingToken === 'auto') {
630
+ continue;
631
+ }
632
+
633
+ const location = Number(declarationMatch.bindingToken);
634
+ const group = Number(declarationMatch.groupToken);
635
+
636
+ validateApplicationWGSLBinding(group, location, declarationMatch.name);
637
+ registerUsedBindingLocation(
638
+ usedBindingsByGroup,
639
+ group,
640
+ location,
641
+ `application binding "${declarationMatch.name}"`
642
+ );
643
+ }
644
+
645
+ const relocationState: WGSLApplicationRelocationState = {
646
+ sawSupportedBindingDeclaration: declarationMatches.length > 0
647
+ };
648
+
649
+ const relocatedSource = replaceWGSLBindingDeclarationMatches(
650
+ source,
651
+ WGSL_BINDING_DECLARATION_REGEXES,
652
+ declarationMatch =>
653
+ relocateWGSLApplicationBindingMatch(declarationMatch, usedBindingsByGroup, relocationState)
654
+ );
655
+
656
+ if (hasWGSLAutoBinding(source) && !relocationState.sawSupportedBindingDeclaration) {
657
+ throw new Error(
658
+ 'Unsupported @binding(auto) declaration form in application WGSL. ' +
659
+ 'Use adjacent "@group(N)" and "@binding(auto)" decorators followed by a bindable "var" declaration.'
660
+ );
661
+ }
662
+
663
+ return {source: relocatedSource};
664
+ }
665
+
666
+ function relocateWGSLModuleBindings(
667
+ moduleSource: string,
668
+ module: ShaderModule,
669
+ context: BindingRelocationContext
670
+ ): {source: string; bindingAssignments: WGSLBindingAssignment[]} {
671
+ const bindingAssignments: WGSLBindingAssignment[] = [];
672
+ const declarationMatches = getWGSLBindingDeclarationMatches(
673
+ moduleSource,
674
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES
675
+ );
676
+ const relocationState: WGSLRelocationState = {
677
+ sawSupportedBindingDeclaration: declarationMatches.length > 0,
678
+ nextHintedBindingLocation:
679
+ typeof module.firstBindingSlot === 'number' ? module.firstBindingSlot : null
680
+ };
681
+
682
+ const relocatedSource = replaceWGSLBindingDeclarationMatches(
683
+ moduleSource,
684
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES,
685
+ declarationMatch =>
686
+ relocateWGSLModuleBindingMatch(declarationMatch, {
687
+ module,
688
+ context,
689
+ bindingAssignments,
690
+ relocationState
691
+ })
692
+ );
693
+
694
+ if (hasWGSLAutoBinding(moduleSource) && !relocationState.sawSupportedBindingDeclaration) {
695
+ throw new Error(
696
+ `Unsupported @binding(auto) declaration form in module "${module.name}". ` +
697
+ 'Use adjacent "@group(N)" and "@binding(auto)" decorators followed by a bindable "var" declaration.'
698
+ );
699
+ }
700
+
701
+ return {source: relocatedSource, bindingAssignments};
702
+ }
703
+
704
+ function relocateWGSLModuleBindingMatch(
705
+ declarationMatch: WGSLBindingDeclarationMatch,
706
+ params: WGSLRelocationParams
707
+ ): string {
708
+ const {module, context, bindingAssignments, relocationState} = params;
709
+
710
+ const {match, bindingToken, groupToken, name} = declarationMatch;
711
+ const group = Number(groupToken);
712
+
713
+ if (bindingToken === 'auto') {
714
+ const registryKey = getBindingRegistryKey(group, module.name, name);
715
+ const registryLocation = context.bindingRegistry?.get(registryKey);
716
+ const location =
717
+ registryLocation !== undefined
718
+ ? registryLocation
719
+ : relocationState.nextHintedBindingLocation === null
720
+ ? allocateAutoBindingLocation(group, context.usedBindingsByGroup)
721
+ : allocateAutoBindingLocation(
722
+ group,
723
+ context.usedBindingsByGroup,
724
+ relocationState.nextHintedBindingLocation
725
+ );
726
+ validateModuleWGSLBinding(module.name, group, location, name);
727
+ if (
728
+ registryLocation !== undefined &&
729
+ claimReservedBindingLocation(context.reservedBindingKeysByGroup, group, location, registryKey)
730
+ ) {
731
+ bindingAssignments.push({moduleName: module.name, name, group, location});
732
+ return match.replace(/@binding\(\s*auto\s*\)/, `@binding(${location})`);
733
+ }
734
+ registerUsedBindingLocation(
735
+ context.usedBindingsByGroup,
736
+ group,
737
+ location,
738
+ `module "${module.name}" binding "${name}"`
739
+ );
740
+ context.bindingRegistry?.set(registryKey, location);
741
+ bindingAssignments.push({moduleName: module.name, name, group, location});
742
+ if (relocationState.nextHintedBindingLocation !== null && registryLocation === undefined) {
743
+ relocationState.nextHintedBindingLocation = location + 1;
744
+ }
745
+ return match.replace(/@binding\(\s*auto\s*\)/, `@binding(${location})`);
746
+ }
747
+
748
+ const location = Number(bindingToken);
749
+ validateModuleWGSLBinding(module.name, group, location, name);
750
+ registerUsedBindingLocation(
751
+ context.usedBindingsByGroup,
752
+ group,
753
+ location,
754
+ `module "${module.name}" binding "${name}"`
755
+ );
756
+ bindingAssignments.push({moduleName: module.name, name, group, location});
757
+ return match;
758
+ }
759
+
760
+ function relocateWGSLApplicationBindingMatch(
761
+ declarationMatch: WGSLBindingDeclarationMatch,
762
+ usedBindingsByGroup: Map<number, Set<number>>,
763
+ relocationState: WGSLApplicationRelocationState
764
+ ): string {
765
+ const {match, bindingToken, groupToken, name} = declarationMatch;
766
+ const group = Number(groupToken);
767
+
768
+ if (bindingToken === 'auto') {
769
+ const location = allocateApplicationAutoBindingLocation(group, usedBindingsByGroup);
770
+ validateApplicationWGSLBinding(group, location, name);
771
+ registerUsedBindingLocation(
772
+ usedBindingsByGroup,
773
+ group,
774
+ location,
775
+ `application binding "${name}"`
776
+ );
777
+ return match.replace(/@binding\(\s*auto\s*\)/, `@binding(${location})`);
778
+ }
779
+
780
+ relocationState.sawSupportedBindingDeclaration = true;
781
+ return match;
782
+ }
783
+
784
+ function reserveRegisteredModuleBindings(
785
+ modules: ShaderModule[],
786
+ bindingRegistry: Map<string, number> | undefined,
787
+ usedBindingsByGroup: Map<number, Set<number>>
788
+ ): Map<number, Map<number, string>> {
789
+ const reservedBindingKeysByGroup = new Map<number, Map<number, string>>();
790
+ if (!bindingRegistry) {
791
+ return reservedBindingKeysByGroup;
792
+ }
793
+
794
+ for (const module of modules) {
795
+ for (const binding of getModuleWGSLBindingDeclarations(module)) {
796
+ const registryKey = getBindingRegistryKey(binding.group, module.name, binding.name);
797
+ const location = bindingRegistry.get(registryKey);
798
+ if (location !== undefined) {
799
+ const reservedBindingKeys =
800
+ reservedBindingKeysByGroup.get(binding.group) || new Map<number, string>();
801
+ const existingReservation = reservedBindingKeys.get(location);
802
+ if (existingReservation && existingReservation !== registryKey) {
803
+ throw new Error(
804
+ `Duplicate WGSL binding reservation for modules "${existingReservation}" and "${registryKey}": group ${binding.group}, binding ${location}.`
805
+ );
806
+ }
807
+
808
+ registerUsedBindingLocation(
809
+ usedBindingsByGroup,
810
+ binding.group,
811
+ location,
812
+ `registered module binding "${registryKey}"`
813
+ );
814
+ reservedBindingKeys.set(location, registryKey);
815
+ reservedBindingKeysByGroup.set(binding.group, reservedBindingKeys);
816
+ }
817
+ }
818
+ }
819
+
820
+ return reservedBindingKeysByGroup;
821
+ }
822
+
823
+ function claimReservedBindingLocation(
824
+ reservedBindingKeysByGroup: Map<number, Map<number, string>>,
825
+ group: number,
826
+ location: number,
827
+ registryKey: string
828
+ ): boolean {
829
+ const reservedBindingKeys = reservedBindingKeysByGroup.get(group);
830
+ if (!reservedBindingKeys) {
831
+ return false;
832
+ }
833
+
834
+ const reservedKey = reservedBindingKeys.get(location);
835
+ if (!reservedKey) {
836
+ return false;
837
+ }
838
+ if (reservedKey !== registryKey) {
839
+ throw new Error(
840
+ `Registered module binding "${registryKey}" collided with "${reservedKey}": group ${group}, binding ${location}.`
841
+ );
842
+ }
843
+ return true;
844
+ }
845
+
846
+ function getModuleWGSLBindingDeclarations(module: ShaderModule): {name: string; group: number}[] {
847
+ const declarations: {name: string; group: number}[] = [];
848
+ const moduleSource = module.source || '';
849
+
850
+ for (const match of getWGSLBindingDeclarationMatches(
851
+ moduleSource,
852
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES
853
+ )) {
854
+ declarations.push({
855
+ name: match.name,
856
+ group: Number(match.groupToken)
857
+ });
858
+ }
859
+
860
+ return declarations;
861
+ }
862
+
863
+ function validateApplicationWGSLBinding(group: number, location: number, name: string): void {
864
+ if (group === 0 && location >= RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT) {
865
+ throw new Error(
866
+ `Application binding "${name}" in group 0 uses reserved binding ${location}. ` +
867
+ `Application-owned explicit group-0 bindings must stay below ${RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT}.`
868
+ );
869
+ }
870
+ }
871
+
872
+ function validateModuleWGSLBinding(
873
+ moduleName: string,
874
+ group: number,
875
+ location: number,
876
+ name: string
877
+ ): void {
878
+ if (group === 0 && location < RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT) {
879
+ throw new Error(
880
+ `Module "${moduleName}" binding "${name}" in group 0 uses reserved application binding ${location}. ` +
881
+ `Module-owned explicit group-0 bindings must be ${RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT} or higher.`
882
+ );
883
+ }
884
+ }
885
+
886
+ function registerUsedBindingLocation(
887
+ usedBindingsByGroup: Map<number, Set<number>>,
888
+ group: number,
889
+ location: number,
890
+ label: string
891
+ ): void {
892
+ const usedBindings = usedBindingsByGroup.get(group) || new Set<number>();
893
+ if (usedBindings.has(location)) {
894
+ throw new Error(
895
+ `Duplicate WGSL binding assignment for ${label}: group ${group}, binding ${location}.`
896
+ );
897
+ }
898
+ usedBindings.add(location);
899
+ usedBindingsByGroup.set(group, usedBindings);
900
+ }
901
+
902
+ function allocateAutoBindingLocation(
903
+ group: number,
904
+ usedBindingsByGroup: Map<number, Set<number>>,
905
+ preferredBindingLocation?: number
906
+ ): number {
907
+ const usedBindings = usedBindingsByGroup.get(group) || new Set<number>();
908
+ let nextBinding =
909
+ preferredBindingLocation ??
910
+ (group === 0
911
+ ? RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT
912
+ : usedBindings.size > 0
913
+ ? Math.max(...usedBindings) + 1
914
+ : 0);
915
+
916
+ while (usedBindings.has(nextBinding)) {
917
+ nextBinding++;
918
+ }
919
+
920
+ return nextBinding;
921
+ }
922
+
923
+ function allocateApplicationAutoBindingLocation(
924
+ group: number,
925
+ usedBindingsByGroup: Map<number, Set<number>>
926
+ ): number {
927
+ const usedBindings = usedBindingsByGroup.get(group) || new Set<number>();
928
+ let nextBinding = 0;
929
+
930
+ while (usedBindings.has(nextBinding)) {
931
+ nextBinding++;
932
+ }
933
+
934
+ return nextBinding;
935
+ }
936
+
937
+ function assertNoUnresolvedAutoBindings(source: string): void {
938
+ const unresolvedBinding = getFirstWGSLAutoBindingDeclarationMatch(
939
+ source,
940
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES
941
+ );
942
+ if (!unresolvedBinding) {
943
+ return;
944
+ }
945
+
946
+ const moduleName = getWGSLModuleNameAtIndex(source, unresolvedBinding.index);
947
+ if (moduleName) {
948
+ throw new Error(
949
+ `Unresolved @binding(auto) for module "${moduleName}" binding "${unresolvedBinding.name}" remained in assembled WGSL source.`
950
+ );
951
+ }
952
+
953
+ if (isInApplicationWGSLSection(source, unresolvedBinding.index)) {
954
+ throw new Error(
955
+ `Unresolved @binding(auto) for application binding "${unresolvedBinding.name}" remained in assembled WGSL source.`
956
+ );
957
+ }
958
+
959
+ throw new Error(
960
+ `Unresolved @binding(auto) remained in assembled WGSL source near "${formatWGSLSourceSnippet(unresolvedBinding.match)}".`
961
+ );
962
+ }
963
+
964
+ function formatWGSLBindingAssignmentComments(bindingAssignments: WGSLBindingAssignment[]): string {
965
+ if (bindingAssignments.length === 0) {
966
+ return '';
967
+ }
968
+
969
+ let source = '// ----- MODULE WGSL BINDING ASSIGNMENTS ---------------\n';
970
+ for (const bindingAssignment of bindingAssignments) {
971
+ source += `// ${bindingAssignment.moduleName}.${bindingAssignment.name} -> @group(${bindingAssignment.group}) @binding(${bindingAssignment.location})\n`;
972
+ }
973
+ source += '\n';
974
+ return source;
975
+ }
976
+
977
+ function getBindingRegistryKey(group: number, moduleName: string, bindingName: string): string {
978
+ return `${group}:${moduleName}:${bindingName}`;
979
+ }
980
+
981
+ function getWGSLModuleNameAtIndex(source: string, index: number): string | undefined {
982
+ const moduleHeaderRegex = /^\/\/ ----- MODULE ([^\n]+) ---------------$/gm;
983
+ let moduleName: string | undefined;
984
+ let match: RegExpExecArray | null;
985
+
986
+ match = moduleHeaderRegex.exec(source);
987
+ while (match && match.index <= index) {
988
+ moduleName = match[1];
989
+ match = moduleHeaderRegex.exec(source);
990
+ }
991
+
992
+ return moduleName;
993
+ }
994
+
995
+ function isInApplicationWGSLSection(source: string, index: number): boolean {
996
+ const injectionMarkerIndex = source.indexOf(INJECT_SHADER_DECLARATIONS);
997
+ return injectionMarkerIndex >= 0 ? index > injectionMarkerIndex : true;
998
+ }
999
+
1000
+ function formatWGSLSourceSnippet(source: string): string {
1001
+ return source.replace(/\s+/g, ' ').trim();
1002
+ }
1003
+
511
1004
  /*
512
1005
  function getHookFunctions(
513
1006
  hookFunctions: Record<string, HookFunction>,