@luma.gl/shadertools 9.3.0-alpha.8 → 9.3.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 (113) hide show
  1. package/dist/dist.dev.js +1020 -191
  2. package/dist/dist.min.js +440 -145
  3. package/dist/index.cjs +915 -173
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/index.d.ts +7 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +4 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/color/normalize-byte-colors.d.ts +23 -0
  10. package/dist/lib/color/normalize-byte-colors.d.ts.map +1 -0
  11. package/dist/lib/color/normalize-byte-colors.js +42 -0
  12. package/dist/lib/color/normalize-byte-colors.js.map +1 -0
  13. package/dist/lib/glsl-utils/shader-utils.js +4 -4
  14. package/dist/lib/glsl-utils/shader-utils.js.map +1 -1
  15. package/dist/lib/shader-assembly/assemble-shaders.d.ts.map +1 -1
  16. package/dist/lib/shader-assembly/assemble-shaders.js +102 -49
  17. package/dist/lib/shader-assembly/assemble-shaders.js.map +1 -1
  18. package/dist/lib/shader-assembly/wgsl-binding-debug.d.ts.map +1 -1
  19. package/dist/lib/shader-assembly/wgsl-binding-debug.js +7 -3
  20. package/dist/lib/shader-assembly/wgsl-binding-debug.js.map +1 -1
  21. package/dist/lib/shader-assembly/wgsl-binding-scan.d.ts +19 -0
  22. package/dist/lib/shader-assembly/wgsl-binding-scan.d.ts.map +1 -0
  23. package/dist/lib/shader-assembly/wgsl-binding-scan.js +151 -0
  24. package/dist/lib/shader-assembly/wgsl-binding-scan.js.map +1 -0
  25. package/dist/lib/shader-generator/glsl/generate-glsl.js +4 -4
  26. package/dist/lib/shader-generator/glsl/generate-glsl.js.map +1 -1
  27. package/dist/lib/shader-module/shader-module-uniform-layout.d.ts +69 -0
  28. package/dist/lib/shader-module/shader-module-uniform-layout.d.ts.map +1 -1
  29. package/dist/lib/shader-module/shader-module-uniform-layout.js +145 -4
  30. package/dist/lib/shader-module/shader-module-uniform-layout.js.map +1 -1
  31. package/dist/modules/color/float-colors.d.ts +26 -0
  32. package/dist/modules/color/float-colors.d.ts.map +1 -0
  33. package/dist/modules/color/float-colors.js +82 -0
  34. package/dist/modules/color/float-colors.js.map +1 -0
  35. package/dist/modules/engine/picking/picking.d.ts +8 -8
  36. package/dist/modules/engine/picking/picking.d.ts.map +1 -1
  37. package/dist/modules/engine/picking/picking.js +13 -15
  38. package/dist/modules/engine/picking/picking.js.map +1 -1
  39. package/dist/modules/engine/project/project.d.ts +1 -1
  40. package/dist/modules/engine/project/project.js +1 -1
  41. package/dist/modules/engine/skin/skin.d.ts +2 -2
  42. package/dist/modules/engine/skin/skin.d.ts.map +1 -1
  43. package/dist/modules/engine/skin/skin.js +1 -1
  44. package/dist/modules/lighting/gouraud-material/gouraud-material.d.ts +1 -0
  45. package/dist/modules/lighting/gouraud-material/gouraud-material.d.ts.map +1 -1
  46. package/dist/modules/lighting/gouraud-material/gouraud-material.js +6 -3
  47. package/dist/modules/lighting/gouraud-material/gouraud-material.js.map +1 -1
  48. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.d.ts +2 -2
  49. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.d.ts.map +1 -1
  50. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.js +2 -2
  51. package/dist/modules/lighting/lights/lighting-glsl.d.ts +1 -1
  52. package/dist/modules/lighting/lights/lighting-glsl.d.ts.map +1 -1
  53. package/dist/modules/lighting/lights/lighting-glsl.js +1 -1
  54. package/dist/modules/lighting/lights/lighting.d.ts +4 -2
  55. package/dist/modules/lighting/lights/lighting.d.ts.map +1 -1
  56. package/dist/modules/lighting/lights/lighting.js +17 -11
  57. package/dist/modules/lighting/lights/lighting.js.map +1 -1
  58. package/dist/modules/lighting/no-material/dirlight.d.ts +3 -3
  59. package/dist/modules/lighting/no-material/dirlight.d.ts.map +1 -1
  60. package/dist/modules/lighting/no-material/dirlight.js +2 -2
  61. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts +2 -2
  62. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts.map +1 -1
  63. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js +138 -35
  64. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js.map +1 -1
  65. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts +1 -1
  66. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts.map +1 -1
  67. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js +139 -35
  68. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js.map +1 -1
  69. package/dist/modules/lighting/pbr-material/pbr-material.d.ts +74 -6
  70. package/dist/modules/lighting/pbr-material/pbr-material.d.ts.map +1 -1
  71. package/dist/modules/lighting/pbr-material/pbr-material.js +70 -2
  72. package/dist/modules/lighting/pbr-material/pbr-material.js.map +1 -1
  73. package/dist/modules/lighting/pbr-material/pbr-projection.js +1 -1
  74. package/dist/modules/lighting/pbr-material/pbr-scene.d.ts +40 -0
  75. package/dist/modules/lighting/pbr-material/pbr-scene.d.ts.map +1 -0
  76. package/dist/modules/lighting/pbr-material/pbr-scene.js +67 -0
  77. package/dist/modules/lighting/pbr-material/pbr-scene.js.map +1 -0
  78. package/dist/modules/lighting/phong-material/phong-material.d.ts +1 -0
  79. package/dist/modules/lighting/phong-material/phong-material.d.ts.map +1 -1
  80. package/dist/modules/lighting/phong-material/phong-material.js +6 -3
  81. package/dist/modules/lighting/phong-material/phong-material.js.map +1 -1
  82. package/dist/modules/lighting/phong-material/phong-shaders-glsl.d.ts +2 -2
  83. package/dist/modules/lighting/phong-material/phong-shaders-glsl.d.ts.map +1 -1
  84. package/dist/modules/lighting/phong-material/phong-shaders-glsl.js +2 -2
  85. package/dist/modules/math/fp64/fp64-arithmetic-glsl.d.ts +1 -1
  86. package/dist/modules/math/fp64/fp64-arithmetic-glsl.d.ts.map +1 -1
  87. package/dist/modules/math/fp64/fp64-arithmetic-glsl.js +1 -1
  88. package/package.json +2 -2
  89. package/src/index.ts +17 -1
  90. package/src/lib/color/normalize-byte-colors.ts +57 -0
  91. package/src/lib/glsl-utils/shader-utils.ts +4 -4
  92. package/src/lib/shader-assembly/assemble-shaders.ts +197 -69
  93. package/src/lib/shader-assembly/wgsl-binding-debug.ts +14 -3
  94. package/src/lib/shader-assembly/wgsl-binding-scan.ts +228 -0
  95. package/src/lib/shader-generator/glsl/generate-glsl.ts +4 -4
  96. package/src/lib/shader-module/shader-module-uniform-layout.ts +236 -10
  97. package/src/modules/color/float-colors.ts +99 -0
  98. package/src/modules/engine/picking/picking.ts +17 -19
  99. package/src/modules/engine/project/project.ts +1 -1
  100. package/src/modules/engine/skin/skin.ts +1 -1
  101. package/src/modules/lighting/gouraud-material/gouraud-material.ts +10 -3
  102. package/src/modules/lighting/lambert-material/lambert-shaders-glsl.ts +2 -2
  103. package/src/modules/lighting/lights/lighting-glsl.ts +1 -1
  104. package/src/modules/lighting/lights/lighting.ts +20 -11
  105. package/src/modules/lighting/no-material/dirlight.ts +2 -2
  106. package/src/modules/lighting/pbr-material/pbr-material-glsl.ts +138 -35
  107. package/src/modules/lighting/pbr-material/pbr-material-wgsl.ts +139 -35
  108. package/src/modules/lighting/pbr-material/pbr-material.ts +110 -3
  109. package/src/modules/lighting/pbr-material/pbr-projection.ts +1 -1
  110. package/src/modules/lighting/pbr-material/pbr-scene.ts +91 -0
  111. package/src/modules/lighting/phong-material/phong-material.ts +10 -3
  112. package/src/modules/lighting/phong-material/phong-shaders-glsl.ts +2 -2
  113. package/src/modules/math/fp64/fp64-arithmetic-glsl.ts +1 -1
@@ -8,23 +8,28 @@ 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 {validateShaderModuleUniformLayout} from '../shader-module/shader-module-uniform-layout';
11
+ import {
12
+ validateShaderModuleUniformLayout,
13
+ warnIfGLSLUniformBlocksAreNotStd140
14
+ } from '../shader-module/shader-module-uniform-layout';
12
15
  import type {ShaderInjection} from './shader-injections';
13
16
  import type {ShaderModule} from '../shader-module/shader-module';
14
17
  import {ShaderHook, normalizeShaderHooks, getShaderHooks} from './shader-hooks';
15
18
  import {assert} from '../utils/assert';
16
19
  import {getShaderInfo} from '../glsl-utils/get-shader-info';
17
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';
18
31
 
19
32
  const INJECT_SHADER_DECLARATIONS = `\n\n${DECLARATION_INJECT_MARKER}\n`;
20
- const MODULE_WGSL_BINDING_DECLARATION_REGEXES = [
21
- /@binding\(\s*(auto|\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g,
22
- /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(auto|\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g
23
- ] as const;
24
- const WGSL_BINDING_DECLARATION_REGEXES = [
25
- /@binding\(\s*(\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g,
26
- /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g
27
- ] as const;
28
33
  const RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT = 100;
29
34
 
30
35
  /**
@@ -249,7 +254,10 @@ export function assembleShaderWGSL(
249
254
 
250
255
  // TODO - hack until shadertool modules support WebGPU
251
256
  const modulesToInject = modules;
252
- const usedBindingsByGroup = getUsedBindingsByGroupFromApplicationWGSL(coreSource);
257
+ const applicationRelocation = relocateWGSLApplicationBindings(coreSource);
258
+ const usedBindingsByGroup = getUsedBindingsByGroupFromApplicationWGSL(
259
+ applicationRelocation.source
260
+ );
253
261
  const reservedBindingKeysByGroup = reserveRegisteredModuleBindings(
254
262
  modulesToInject,
255
263
  options._bindingRegistry,
@@ -299,7 +307,7 @@ export function assembleShaderWGSL(
299
307
  assembledSource += formatWGSLBindingAssignmentComments(bindingAssignments);
300
308
 
301
309
  // Add the version directive and actual source of this shader
302
- assembledSource += coreSource;
310
+ assembledSource += applicationRelocation.source;
303
311
 
304
312
  // Apply any requested shader injections
305
313
  assembledSource = injectShader(assembledSource, stage, mainInjections);
@@ -460,6 +468,10 @@ ${getApplicationDefines(allDefines)}
460
468
  assembledSource = transpileGLSLShader(assembledSource, stage);
461
469
  }
462
470
 
471
+ if (language === 'glsl') {
472
+ warnIfGLSLUniformBlocksAreNotStd140(assembledSource, stage, log);
473
+ }
474
+
463
475
  return assembledSource.trim();
464
476
  }
465
477
 
@@ -568,13 +580,16 @@ type WGSLBindingAssignment = {
568
580
  location: number;
569
581
  };
570
582
 
583
+ type WGSLApplicationRelocationState = {
584
+ sawSupportedBindingDeclaration: boolean;
585
+ };
586
+
571
587
  type WGSLRelocationState = {
572
588
  sawSupportedBindingDeclaration: boolean;
573
589
  nextHintedBindingLocation: number | null;
574
590
  };
575
591
 
576
592
  type WGSLRelocationParams = {
577
- isBindingFirst: boolean;
578
593
  module: ShaderModule;
579
594
  context: BindingRelocationContext;
580
595
  bindingAssignments: WGSLBindingAssignment[];
@@ -584,82 +599,115 @@ type WGSLRelocationParams = {
584
599
  function getUsedBindingsByGroupFromApplicationWGSL(source: string): Map<number, Set<number>> {
585
600
  const usedBindingsByGroup = new Map<number, Set<number>>();
586
601
 
587
- for (const regex of WGSL_BINDING_DECLARATION_REGEXES) {
588
- regex.lastIndex = 0;
589
- let match: RegExpExecArray | null;
590
- while ((match = regex.exec(source))) {
591
- const isBindingFirst = regex === WGSL_BINDING_DECLARATION_REGEXES[0];
592
- const location = Number(match[isBindingFirst ? 1 : 2]);
593
- const group = Number(match[isBindingFirst ? 2 : 1]);
594
- const name = match[4];
595
-
596
- validateApplicationWGSLBinding(group, location, name);
597
- registerUsedBindingLocation(
598
- usedBindingsByGroup,
599
- group,
600
- location,
601
- `application binding "${name}"`
602
- );
603
- }
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
+ );
604
616
  }
605
617
 
606
618
  return usedBindingsByGroup;
607
619
  }
608
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
+
609
666
  function relocateWGSLModuleBindings(
610
667
  moduleSource: string,
611
668
  module: ShaderModule,
612
669
  context: BindingRelocationContext
613
670
  ): {source: string; bindingAssignments: WGSLBindingAssignment[]} {
614
671
  const bindingAssignments: WGSLBindingAssignment[] = [];
672
+ const declarationMatches = getWGSLBindingDeclarationMatches(
673
+ moduleSource,
674
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES
675
+ );
615
676
  const relocationState: WGSLRelocationState = {
616
- sawSupportedBindingDeclaration: false,
677
+ sawSupportedBindingDeclaration: declarationMatches.length > 0,
617
678
  nextHintedBindingLocation:
618
679
  typeof module.firstBindingSlot === 'number' ? module.firstBindingSlot : null
619
680
  };
620
681
 
621
- let relocatedSource = relocateWGSLModuleBindingsWithRegex(
682
+ const relocatedSource = replaceWGSLBindingDeclarationMatches(
622
683
  moduleSource,
623
- MODULE_WGSL_BINDING_DECLARATION_REGEXES[0],
624
- {isBindingFirst: true, module, context, bindingAssignments, relocationState}
625
- );
626
- relocatedSource = relocateWGSLModuleBindingsWithRegex(
627
- relocatedSource,
628
- MODULE_WGSL_BINDING_DECLARATION_REGEXES[1],
629
- {isBindingFirst: false, module, context, bindingAssignments, relocationState}
684
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES,
685
+ declarationMatch =>
686
+ relocateWGSLModuleBindingMatch(declarationMatch, {
687
+ module,
688
+ context,
689
+ bindingAssignments,
690
+ relocationState
691
+ })
630
692
  );
631
693
 
632
- if (moduleSource.includes('@binding(auto)') && !relocationState.sawSupportedBindingDeclaration) {
694
+ if (hasWGSLAutoBinding(moduleSource) && !relocationState.sawSupportedBindingDeclaration) {
633
695
  throw new Error(
634
696
  `Unsupported @binding(auto) declaration form in module "${module.name}". ` +
635
- 'Use "@group(N) @binding(auto) var ..." or "@binding(auto) @group(N) var ..." on a single line.'
697
+ 'Use adjacent "@group(N)" and "@binding(auto)" decorators followed by a bindable "var" declaration.'
636
698
  );
637
699
  }
638
700
 
639
701
  return {source: relocatedSource, bindingAssignments};
640
702
  }
641
703
 
642
- function relocateWGSLModuleBindingsWithRegex(
643
- source: string,
644
- regex: RegExp,
645
- params: WGSLRelocationParams
646
- ): string {
647
- return source.replace(regex, (...replaceArguments) =>
648
- relocateWGSLModuleBindingMatch(replaceArguments, params)
649
- );
650
- }
651
-
652
704
  function relocateWGSLModuleBindingMatch(
653
- replaceArguments: unknown[],
705
+ declarationMatch: WGSLBindingDeclarationMatch,
654
706
  params: WGSLRelocationParams
655
707
  ): string {
656
- const {isBindingFirst, module, context, bindingAssignments, relocationState} = params;
657
- relocationState.sawSupportedBindingDeclaration = true;
708
+ const {module, context, bindingAssignments, relocationState} = params;
658
709
 
659
- const match = replaceArguments[0] as string;
660
- const bindingToken = replaceArguments[isBindingFirst ? 1 : 2] as string;
661
- const groupToken = replaceArguments[isBindingFirst ? 2 : 1] as string;
662
- const name = replaceArguments[4] as string;
710
+ const {match, bindingToken, groupToken, name} = declarationMatch;
663
711
  const group = Number(groupToken);
664
712
 
665
713
  if (bindingToken === 'auto') {
@@ -709,6 +757,30 @@ function relocateWGSLModuleBindingMatch(
709
757
  return match;
710
758
  }
711
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
+
712
784
  function reserveRegisteredModuleBindings(
713
785
  modules: ShaderModule[],
714
786
  bindingRegistry: Map<string, number> | undefined,
@@ -775,16 +847,14 @@ function getModuleWGSLBindingDeclarations(module: ShaderModule): {name: string;
775
847
  const declarations: {name: string; group: number}[] = [];
776
848
  const moduleSource = module.source || '';
777
849
 
778
- for (const regex of MODULE_WGSL_BINDING_DECLARATION_REGEXES) {
779
- regex.lastIndex = 0;
780
- let match: RegExpExecArray | null;
781
- while ((match = regex.exec(moduleSource))) {
782
- const isBindingFirst = regex === MODULE_WGSL_BINDING_DECLARATION_REGEXES[0];
783
- declarations.push({
784
- name: match[4],
785
- group: Number(match[isBindingFirst ? 2 : 1])
786
- });
787
- }
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
+ });
788
858
  }
789
859
 
790
860
  return declarations;
@@ -850,10 +920,45 @@ function allocateAutoBindingLocation(
850
920
  return nextBinding;
851
921
  }
852
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
+
853
937
  function assertNoUnresolvedAutoBindings(source: string): void {
854
- if (/@binding\(\s*auto\s*\)/.test(source)) {
855
- throw new Error('Unresolved @binding(auto) remained in assembled WGSL source.');
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
+ );
856
957
  }
958
+
959
+ throw new Error(
960
+ `Unresolved @binding(auto) remained in assembled WGSL source near "${formatWGSLSourceSnippet(unresolvedBinding.match)}".`
961
+ );
857
962
  }
858
963
 
859
964
  function formatWGSLBindingAssignmentComments(bindingAssignments: WGSLBindingAssignment[]): string {
@@ -873,6 +978,29 @@ function getBindingRegistryKey(group: number, moduleName: string, bindingName: s
873
978
  return `${group}:${moduleName}:${bindingName}`;
874
979
  }
875
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
+
876
1004
  /*
877
1005
  function getHookFunctions(
878
1006
  hookFunctions: Record<string, HookFunction>,
@@ -2,6 +2,8 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ import {WGSL_BINDABLE_VARIABLE_PATTERN, maskWGSLComments} from './wgsl-binding-scan';
6
+
5
7
  type ShaderBindingAssignment = {
6
8
  moduleName: string;
7
9
  name: string;
@@ -10,8 +12,14 @@ type ShaderBindingAssignment = {
10
12
  };
11
13
 
12
14
  const WGSL_BINDING_DEBUG_REGEXES = [
13
- /@binding\(\s*(\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*var(?:<([^>]+)>)?\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([^;]+);/g,
14
- /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(\d+)\s*\)\s*var(?:<([^>]+)>)?\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([^;]+);/g
15
+ new RegExp(
16
+ `@binding\\(\\s*(\\d+)\\s*\\)\\s*@group\\(\\s*(\\d+)\\s*\\)\\s*${WGSL_BINDABLE_VARIABLE_PATTERN}\\s*:\\s*([^;]+);`,
17
+ 'g'
18
+ ),
19
+ new RegExp(
20
+ `@group\\(\\s*(\\d+)\\s*\\)\\s*@binding\\(\\s*(\\d+)\\s*\\)\\s*${WGSL_BINDABLE_VARIABLE_PATTERN}\\s*:\\s*([^;]+);`,
21
+ 'g'
22
+ )
15
23
  ] as const;
16
24
 
17
25
  /** One debug row describing a WGSL binding in the assembled shader source. */
@@ -54,6 +62,7 @@ export function getShaderBindingDebugRowsFromWGSL(
54
62
  source: string,
55
63
  bindingAssignments: ShaderBindingAssignment[] = []
56
64
  ): ShaderBindingDebugRow[] {
65
+ const maskedSource = maskWGSLComments(source);
57
66
  const assignmentMap = new Map<string, string>();
58
67
  for (const bindingAssignment of bindingAssignments) {
59
68
  assignmentMap.set(
@@ -70,7 +79,8 @@ export function getShaderBindingDebugRowsFromWGSL(
70
79
  for (const regex of WGSL_BINDING_DEBUG_REGEXES) {
71
80
  regex.lastIndex = 0;
72
81
  let match: RegExpExecArray | null;
73
- while ((match = regex.exec(source))) {
82
+ match = regex.exec(maskedSource);
83
+ while (match) {
74
84
  const isBindingFirst = regex === WGSL_BINDING_DEBUG_REGEXES[0];
75
85
  const binding = Number(match[isBindingFirst ? 1 : 2]);
76
86
  const group = Number(match[isBindingFirst ? 2 : 1]);
@@ -90,6 +100,7 @@ export function getShaderBindingDebugRowsFromWGSL(
90
100
  resourceType
91
101
  })
92
102
  );
103
+ match = regex.exec(maskedSource);
93
104
  }
94
105
  }
95
106
 
@@ -0,0 +1,228 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ export const WGSL_BINDABLE_VARIABLE_PATTERN =
6
+ '(?:var<\\s*(uniform|storage(?:\\s*,\\s*[A-Za-z_][A-Za-z0-9_]*)?)\\s*>|var)\\s+([A-Za-z_][A-Za-z0-9_]*)';
7
+ const WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN = '\\s*';
8
+
9
+ export const MODULE_WGSL_BINDING_DECLARATION_REGEXES = [
10
+ new RegExp(
11
+ `@binding\\(\\s*(auto|\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`,
12
+ 'g'
13
+ ),
14
+ new RegExp(
15
+ `@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@binding\\(\\s*(auto|\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`,
16
+ 'g'
17
+ )
18
+ ] as const;
19
+
20
+ export const WGSL_BINDING_DECLARATION_REGEXES = [
21
+ new RegExp(
22
+ `@binding\\(\\s*(auto|\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`,
23
+ 'g'
24
+ ),
25
+ new RegExp(
26
+ `@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@binding\\(\\s*(auto|\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`,
27
+ 'g'
28
+ )
29
+ ] as const;
30
+
31
+ export const WGSL_EXPLICIT_BINDING_DECLARATION_REGEXES = [
32
+ new RegExp(
33
+ `@binding\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`,
34
+ 'g'
35
+ ),
36
+ new RegExp(
37
+ `@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@binding\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`,
38
+ 'g'
39
+ )
40
+ ] as const;
41
+
42
+ const WGSL_AUTO_BINDING_DECLARATION_REGEXES = [
43
+ new RegExp(
44
+ `@binding\\(\\s*(auto)\\s*\\)\\s*@group\\(\\s*(\\d+)\\s*\\)\\s*${WGSL_BINDABLE_VARIABLE_PATTERN}`,
45
+ 'g'
46
+ ),
47
+ new RegExp(
48
+ `@group\\(\\s*(\\d+)\\s*\\)\\s*@binding\\(\\s*(auto)\\s*\\)\\s*${WGSL_BINDABLE_VARIABLE_PATTERN}`,
49
+ 'g'
50
+ ),
51
+ new RegExp(
52
+ `@binding\\(\\s*(auto)\\s*\\)\\s*@group\\(\\s*(\\d+)\\s*\\)(?:[\\s\\n\\r]*@[A-Za-z_][^\\n\\r]*)*[\\s\\n\\r]*${WGSL_BINDABLE_VARIABLE_PATTERN}`,
53
+ 'g'
54
+ ),
55
+ new RegExp(
56
+ `@group\\(\\s*(\\d+)\\s*\\)\\s*@binding\\(\\s*(auto)\\s*\\)(?:[\\s\\n\\r]*@[A-Za-z_][^\\n\\r]*)*[\\s\\n\\r]*${WGSL_BINDABLE_VARIABLE_PATTERN}`,
57
+ 'g'
58
+ )
59
+ ] as const;
60
+
61
+ export type WGSLBindingDeclarationMatch = {
62
+ match: string;
63
+ index: number;
64
+ length: number;
65
+ bindingToken: string;
66
+ groupToken: string;
67
+ accessDeclaration?: string;
68
+ name: string;
69
+ };
70
+
71
+ export function maskWGSLComments(source: string): string {
72
+ const maskedCharacters = source.split('');
73
+ let index = 0;
74
+ let blockCommentDepth = 0;
75
+ let inLineComment = false;
76
+ let inString = false;
77
+ let isEscaped = false;
78
+
79
+ while (index < source.length) {
80
+ const character = source[index];
81
+ const nextCharacter = source[index + 1];
82
+
83
+ if (inString) {
84
+ if (isEscaped) {
85
+ isEscaped = false;
86
+ } else if (character === '\\') {
87
+ isEscaped = true;
88
+ } else if (character === '"') {
89
+ inString = false;
90
+ }
91
+ index++;
92
+ continue;
93
+ }
94
+
95
+ if (inLineComment) {
96
+ if (character === '\n' || character === '\r') {
97
+ inLineComment = false;
98
+ } else {
99
+ maskedCharacters[index] = ' ';
100
+ }
101
+ index++;
102
+ continue;
103
+ }
104
+
105
+ if (blockCommentDepth > 0) {
106
+ if (character === '/' && nextCharacter === '*') {
107
+ maskedCharacters[index] = ' ';
108
+ maskedCharacters[index + 1] = ' ';
109
+ blockCommentDepth++;
110
+ index += 2;
111
+ continue;
112
+ }
113
+
114
+ if (character === '*' && nextCharacter === '/') {
115
+ maskedCharacters[index] = ' ';
116
+ maskedCharacters[index + 1] = ' ';
117
+ blockCommentDepth--;
118
+ index += 2;
119
+ continue;
120
+ }
121
+
122
+ if (character !== '\n' && character !== '\r') {
123
+ maskedCharacters[index] = ' ';
124
+ }
125
+ index++;
126
+ continue;
127
+ }
128
+
129
+ if (character === '"') {
130
+ inString = true;
131
+ index++;
132
+ continue;
133
+ }
134
+
135
+ if (character === '/' && nextCharacter === '/') {
136
+ maskedCharacters[index] = ' ';
137
+ maskedCharacters[index + 1] = ' ';
138
+ inLineComment = true;
139
+ index += 2;
140
+ continue;
141
+ }
142
+
143
+ if (character === '/' && nextCharacter === '*') {
144
+ maskedCharacters[index] = ' ';
145
+ maskedCharacters[index + 1] = ' ';
146
+ blockCommentDepth = 1;
147
+ index += 2;
148
+ continue;
149
+ }
150
+
151
+ index++;
152
+ }
153
+
154
+ return maskedCharacters.join('');
155
+ }
156
+
157
+ export function getWGSLBindingDeclarationMatches(
158
+ source: string,
159
+ regexes: readonly RegExp[]
160
+ ): WGSLBindingDeclarationMatch[] {
161
+ const maskedSource = maskWGSLComments(source);
162
+ const matches: WGSLBindingDeclarationMatch[] = [];
163
+
164
+ for (const regex of regexes) {
165
+ regex.lastIndex = 0;
166
+ let match: RegExpExecArray | null;
167
+ match = regex.exec(maskedSource);
168
+ while (match) {
169
+ const isBindingFirst = regex === regexes[0];
170
+ const index = match.index;
171
+ const length = match[0].length;
172
+ matches.push({
173
+ match: source.slice(index, index + length),
174
+ index,
175
+ length,
176
+ bindingToken: match[isBindingFirst ? 1 : 2],
177
+ groupToken: match[isBindingFirst ? 2 : 1],
178
+ accessDeclaration: match[3]?.trim(),
179
+ name: match[4]
180
+ });
181
+ match = regex.exec(maskedSource);
182
+ }
183
+ }
184
+
185
+ return matches.sort((left, right) => left.index - right.index);
186
+ }
187
+
188
+ export function replaceWGSLBindingDeclarationMatches(
189
+ source: string,
190
+ regexes: readonly RegExp[],
191
+ replacer: (match: WGSLBindingDeclarationMatch) => string
192
+ ): string {
193
+ const matches = getWGSLBindingDeclarationMatches(source, regexes);
194
+ if (!matches.length) {
195
+ return source;
196
+ }
197
+
198
+ let relocatedSource = '';
199
+ let lastIndex = 0;
200
+
201
+ for (const match of matches) {
202
+ relocatedSource += source.slice(lastIndex, match.index);
203
+ relocatedSource += replacer(match);
204
+ lastIndex = match.index + match.length;
205
+ }
206
+
207
+ relocatedSource += source.slice(lastIndex);
208
+ return relocatedSource;
209
+ }
210
+
211
+ export function hasWGSLAutoBinding(source: string): boolean {
212
+ return /@binding\(\s*auto\s*\)/.test(maskWGSLComments(source));
213
+ }
214
+
215
+ export function getFirstWGSLAutoBindingDeclarationMatch(
216
+ source: string,
217
+ regexes: readonly RegExp[]
218
+ ): WGSLBindingDeclarationMatch | undefined {
219
+ const autoBindingRegexes =
220
+ regexes === MODULE_WGSL_BINDING_DECLARATION_REGEXES ||
221
+ regexes === WGSL_BINDING_DECLARATION_REGEXES
222
+ ? WGSL_AUTO_BINDING_DECLARATION_REGEXES
223
+ : regexes;
224
+
225
+ return getWGSLBindingDeclarationMatches(source, autoBindingRegexes).find(
226
+ declarationMatch => declarationMatch.bindingToken === 'auto'
227
+ );
228
+ }
@@ -21,11 +21,11 @@ function generateGLSLUniformDeclarations(
21
21
  ): string {
22
22
  const glsl: string[] = [];
23
23
 
24
- // => uniform UniformBlockName {
24
+ // => layout(std140) uniform UniformBlockName {
25
25
  switch (options.uniforms) {
26
26
  case 'scoped-interface-blocks':
27
27
  case 'unscoped-interface-blocks':
28
- glsl.push(`uniform ${capitalize(module.name)} {`);
28
+ glsl.push(`layout(std140) uniform ${capitalize(module.name)} {`);
29
29
  break;
30
30
  case 'uniforms':
31
31
  // ignore
@@ -41,11 +41,11 @@ function generateGLSLUniformDeclarations(
41
41
  const glslUniformType = getGLSLUniformType(uniformFormat as UniformFormat);
42
42
  switch (options.uniforms) {
43
43
  case 'scoped-interface-blocks':
44
- // => uniform UniformBlockName {
44
+ // => layout(std140) uniform UniformBlockName {
45
45
  glsl.push(` ${glslUniformType} ${uniformName};`);
46
46
  break;
47
47
  case 'unscoped-interface-blocks':
48
- // => uniform UniformBlockName {
48
+ // => layout(std140) uniform UniformBlockName {
49
49
  glsl.push(` ${glslUniformType} ${module.name}_${uniformName};`);
50
50
  break;
51
51
  case 'uniforms':