@luma.gl/shadertools 9.3.6 → 9.4.0-alpha.1

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 +6555 -2373
  2. package/dist/dist.min.js +2984 -237
  3. package/dist/index.cjs +4356 -268
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/index.d.ts +13 -3
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +6 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/preprocessor/preprocessor.d.ts +11 -1
  10. package/dist/lib/preprocessor/preprocessor.d.ts.map +1 -1
  11. package/dist/lib/preprocessor/preprocessor.js +47 -2
  12. package/dist/lib/preprocessor/preprocessor.js.map +1 -1
  13. package/dist/lib/shader-assembler.d.ts +10 -10
  14. package/dist/lib/shader-assembler.d.ts.map +1 -1
  15. package/dist/lib/shader-assembler.js +36 -20
  16. package/dist/lib/shader-assembler.js.map +1 -1
  17. package/dist/lib/shader-assembly/assemble-shaders.d.ts +28 -7
  18. package/dist/lib/shader-assembly/assemble-shaders.d.ts.map +1 -1
  19. package/dist/lib/shader-assembly/assemble-shaders.js +133 -15
  20. package/dist/lib/shader-assembly/assemble-shaders.js.map +1 -1
  21. package/dist/lib/shader-assembly/platform-info.d.ts +2 -0
  22. package/dist/lib/shader-assembly/platform-info.d.ts.map +1 -1
  23. package/dist/lib/shader-assembly/shader-hooks.d.ts +1 -1
  24. package/dist/lib/shader-assembly/shader-hooks.d.ts.map +1 -1
  25. package/dist/lib/shader-assembly/shader-hooks.js +6 -3
  26. package/dist/lib/shader-assembly/shader-hooks.js.map +1 -1
  27. package/dist/lib/shader-assembly/shader-injections.d.ts +4 -1
  28. package/dist/lib/shader-assembly/shader-injections.d.ts.map +1 -1
  29. package/dist/lib/shader-assembly/shader-injections.js +68 -11
  30. package/dist/lib/shader-assembly/shader-injections.js.map +1 -1
  31. package/dist/lib/shader-assembly/shader-plugin-varyings.d.ts +15 -0
  32. package/dist/lib/shader-assembly/shader-plugin-varyings.d.ts.map +1 -0
  33. package/dist/lib/shader-assembly/shader-plugin-varyings.js +412 -0
  34. package/dist/lib/shader-assembly/shader-plugin-varyings.js.map +1 -0
  35. package/dist/lib/shader-assembly/shader-plugin-vertex-inputs.d.ts +12 -0
  36. package/dist/lib/shader-assembly/shader-plugin-vertex-inputs.d.ts.map +1 -0
  37. package/dist/lib/shader-assembly/shader-plugin-vertex-inputs.js +237 -0
  38. package/dist/lib/shader-assembly/shader-plugin-vertex-inputs.js.map +1 -0
  39. package/dist/lib/shader-module/shader-module.d.ts +3 -0
  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/shader-module/shader-pass-pipeline.d.ts +14 -0
  43. package/dist/lib/shader-module/shader-pass-pipeline.d.ts.map +1 -0
  44. package/dist/lib/shader-module/shader-pass-pipeline.js +5 -0
  45. package/dist/lib/shader-module/shader-pass-pipeline.js.map +1 -0
  46. package/dist/lib/shader-module/shader-pass.d.ts +21 -4
  47. package/dist/lib/shader-module/shader-pass.d.ts.map +1 -1
  48. package/dist/lib/shader-plugin.d.ts +68 -0
  49. package/dist/lib/shader-plugin.d.ts.map +1 -0
  50. package/dist/lib/shader-plugin.js +98 -0
  51. package/dist/lib/shader-plugin.js.map +1 -0
  52. package/dist/lib/utils/assert.js +1 -1
  53. package/dist/lib/utils/assert.js.map +1 -1
  54. package/dist/modules/color/float-colors.d.ts +111 -5
  55. package/dist/modules/color/float-colors.d.ts.map +1 -1
  56. package/dist/modules/color/float-colors.js +200 -34
  57. package/dist/modules/color/float-colors.js.map +1 -1
  58. package/dist/modules/engine/clip/clip.d.ts +12 -0
  59. package/dist/modules/engine/clip/clip.d.ts.map +1 -0
  60. package/dist/modules/engine/clip/clip.js +129 -0
  61. package/dist/modules/engine/clip/clip.js.map +1 -0
  62. package/dist/modules/engine/filter/filter.d.ts +12 -0
  63. package/dist/modules/engine/filter/filter.d.ts.map +1 -0
  64. package/dist/modules/engine/filter/filter.js +88 -0
  65. package/dist/modules/engine/filter/filter.js.map +1 -0
  66. package/dist/modules/geospatial/dggs/dggs-wgsl.d.ts +3 -0
  67. package/dist/modules/geospatial/dggs/dggs-wgsl.d.ts.map +1 -0
  68. package/dist/modules/geospatial/dggs/dggs-wgsl.js +1892 -0
  69. package/dist/modules/geospatial/dggs/dggs-wgsl.js.map +1 -0
  70. package/dist/modules/geospatial/dggs/dggs.d.ts +6 -0
  71. package/dist/modules/geospatial/dggs/dggs.d.ts.map +1 -0
  72. package/dist/modules/geospatial/dggs/dggs.js +10 -0
  73. package/dist/modules/geospatial/dggs/dggs.js.map +1 -0
  74. package/dist/modules/lighting/water-material/water-material.d.ts +28 -0
  75. package/dist/modules/lighting/water-material/water-material.d.ts.map +1 -0
  76. package/dist/modules/lighting/water-material/water-material.js +179 -0
  77. package/dist/modules/lighting/water-material/water-material.js.map +1 -0
  78. package/dist/modules/lighting/water-material/water-shaders-glsl.d.ts +3 -0
  79. package/dist/modules/lighting/water-material/water-shaders-glsl.d.ts.map +1 -0
  80. package/dist/modules/lighting/water-material/water-shaders-glsl.js +232 -0
  81. package/dist/modules/lighting/water-material/water-shaders-glsl.js.map +1 -0
  82. package/dist/modules/lighting/water-material/water-shaders-wgsl.d.ts +2 -0
  83. package/dist/modules/lighting/water-material/water-shaders-wgsl.d.ts.map +1 -0
  84. package/dist/modules/lighting/water-material/water-shaders-wgsl.js +230 -0
  85. package/dist/modules/lighting/water-material/water-shaders-wgsl.js.map +1 -0
  86. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.d.ts +2 -1
  87. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.d.ts.map +1 -1
  88. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.js +268 -0
  89. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.js.map +1 -1
  90. package/package.json +2 -2
  91. package/src/index.ts +50 -3
  92. package/src/lib/preprocessor/preprocessor.ts +68 -3
  93. package/src/lib/shader-assembler.ts +49 -20
  94. package/src/lib/shader-assembly/assemble-shaders.ts +242 -30
  95. package/src/lib/shader-assembly/platform-info.ts +2 -0
  96. package/src/lib/shader-assembly/shader-hooks.ts +7 -3
  97. package/src/lib/shader-assembly/shader-injections.ts +96 -11
  98. package/src/lib/shader-assembly/shader-plugin-varyings.ts +524 -0
  99. package/src/lib/shader-assembly/shader-plugin-vertex-inputs.ts +279 -0
  100. package/src/lib/shader-module/shader-module.ts +3 -0
  101. package/src/lib/shader-module/shader-pass-pipeline.ts +19 -0
  102. package/src/lib/shader-module/shader-pass.ts +29 -4
  103. package/src/lib/shader-plugin.ts +214 -0
  104. package/src/lib/utils/assert.ts +1 -1
  105. package/src/modules/color/float-colors.ts +272 -37
  106. package/src/modules/engine/clip/clip.ts +149 -0
  107. package/src/modules/engine/filter/filter.ts +107 -0
  108. package/src/modules/geospatial/dggs/dggs-wgsl.ts +1892 -0
  109. package/src/modules/geospatial/dggs/dggs.ts +12 -0
  110. package/src/modules/lighting/water-material/water-material.ts +234 -0
  111. package/src/modules/lighting/water-material/water-shaders-glsl.ts +233 -0
  112. package/src/modules/lighting/water-material/water-shaders-wgsl.ts +230 -0
  113. package/src/modules/math/fp64/fp64-arithmetic-wgsl.ts +268 -0
@@ -18,6 +18,17 @@ import {ShaderHook, normalizeShaderHooks, getShaderHooks} from './shader-hooks';
18
18
  import {assert} from '../utils/assert';
19
19
  import {getShaderInfo} from '../glsl-utils/get-shader-info';
20
20
  import {getShaderBindingDebugRowsFromWGSL, type ShaderBindingDebugRow} from './wgsl-binding-debug';
21
+ import {preprocess} from '../preprocessor/preprocessor';
22
+ import type {AttributeShaderType} from '@luma.gl/core';
23
+ import type {ResolvedShaderPluginVarying} from '../shader-plugin';
24
+ import {
25
+ assembleShaderPluginVertexInputsWGSL,
26
+ getShaderPluginVertexInputDeclarationsGLSL
27
+ } from './shader-plugin-vertex-inputs';
28
+ import {
29
+ assembleShaderPluginVaryingsGLSL,
30
+ assembleShaderPluginVaryingsWGSL
31
+ } from './shader-plugin-varyings';
21
32
  import {
22
33
  MODULE_WGSL_BINDING_DECLARATION_REGEXES,
23
34
  WGSL_BINDING_DECLARATION_REGEXES,
@@ -40,9 +51,7 @@ const FRAGMENT_SHADER_PROLOGUE = /* glsl */ `\
40
51
  precision highp float;
41
52
  `;
42
53
 
43
- /**
44
- * Options for `ShaderAssembler.assembleShaders()`
45
- */
54
+ /** Props accepted by `ShaderAssembler` assembly methods. */
46
55
  export type AssembleShaderProps = AssembleShaderOptions & {
47
56
  platformInfo: PlatformInfo;
48
57
  /** WGSL: single shader source. */
@@ -53,6 +62,7 @@ export type AssembleShaderProps = AssembleShaderOptions & {
53
62
  fs?: string | null;
54
63
  };
55
64
 
65
+ /** Shared shader assembly options. */
56
66
  export type AssembleShaderOptions = {
57
67
  /** information about the platform (which shader language & version, extensions etc.) */
58
68
  platformInfo: PlatformInfo;
@@ -60,14 +70,24 @@ export type AssembleShaderOptions = {
60
70
  id?: string;
61
71
  /** Modules to be injected */
62
72
  modules?: ShaderModule[];
63
- /** Defines to be injected */
64
- defines?: Record<string, boolean>;
73
+ /** Boolean or numeric preprocessor defines evaluated before shader assembly. */
74
+ defines?: Record<string, boolean | number>;
65
75
  /** GLSL only: Overrides to be injected. In WGSL these are supplied during Pipeline creation time */
66
76
  constants?: Record<string, number>;
67
77
  /** Hook functions */
68
78
  hookFunctions?: (ShaderHook | string)[];
69
79
  /** Code injections */
70
80
  inject?: Record<string, string | ShaderInjection>;
81
+ /** Ordered code injections contributed by ShaderPlugin descriptors. */
82
+ pluginInjections?: Record<string, ShaderInjection[]>;
83
+ /** Vertex inputs contributed by ShaderPlugin descriptors. */
84
+ pluginVertexInputs?: Record<string, AttributeShaderType>;
85
+ /** Cross-stage varyings contributed by ShaderPlugin descriptors. */
86
+ pluginVaryings?: Record<string, ResolvedShaderPluginVarying>;
87
+ /** WGSL vertex entry point selected by the render pipeline. */
88
+ vertexEntryPoint?: string;
89
+ /** WGSL fragment entry point selected by the render pipeline. */
90
+ fragmentEntryPoint?: string;
71
91
  /** Whether to inject prologue */
72
92
  prologue?: boolean;
73
93
  /** logger object */
@@ -82,14 +102,24 @@ type AssembleStageOptions = {
82
102
  stage: 'vertex' | 'fragment';
83
103
  /** Modules to be injected */
84
104
  modules: any[];
85
- /** Defines to be injected */
86
- defines?: Record<string, boolean>;
105
+ /** Boolean or numeric preprocessor defines evaluated before shader assembly. */
106
+ defines?: Record<string, boolean | number>;
87
107
  /** GLSL only: Overrides to be injected. In WGSL these are supplied during Pipeline creation time */
88
108
  constants?: Record<string, number>;
89
109
  /** Hook functions */
90
110
  hookFunctions?: (ShaderHook | string)[];
91
111
  /** Code injections */
92
112
  inject?: Record<string, string | ShaderInjection>;
113
+ /** Ordered code injections contributed by ShaderPlugin descriptors. */
114
+ pluginInjections?: Record<string, ShaderInjection[]>;
115
+ /** Vertex inputs contributed by ShaderPlugin descriptors. */
116
+ pluginVertexInputs?: Record<string, AttributeShaderType>;
117
+ /** Cross-stage varyings contributed by ShaderPlugin descriptors. */
118
+ pluginVaryings?: Record<string, ResolvedShaderPluginVarying>;
119
+ /** WGSL vertex entry point selected by the render pipeline. */
120
+ vertexEntryPoint?: string;
121
+ /** WGSL fragment entry point selected by the render pipeline. */
122
+ fragmentEntryPoint?: string;
93
123
  /** Whether to inject prologue */
94
124
  prologue?: boolean;
95
125
  /** logger object */
@@ -190,8 +220,14 @@ export function assembleShaderWGSL(
190
220
  stage,
191
221
  modules,
192
222
  // defines = {},
223
+ defines = {},
193
224
  hookFunctions = [],
194
225
  inject = {},
226
+ pluginInjections = {},
227
+ pluginVertexInputs = {},
228
+ pluginVaryings = {},
229
+ vertexEntryPoint = 'vertexMain',
230
+ fragmentEntryPoint = 'fragmentMain',
195
231
  log
196
232
  } = options;
197
233
 
@@ -200,7 +236,19 @@ export function assembleShaderWGSL(
200
236
  // const isVertex = type === 'vs';
201
237
  // const sourceLines = source.split('\n');
202
238
 
203
- const coreSource = source;
239
+ const preprocessedSource = preprocess(source, {defines});
240
+ const pluginVertexInputAssembly = assembleShaderPluginVertexInputsWGSL(
241
+ preprocessedSource,
242
+ vertexEntryPoint,
243
+ pluginVertexInputs
244
+ );
245
+ const pluginVaryingAssembly = assembleShaderPluginVaryingsWGSL(
246
+ pluginVertexInputAssembly.source,
247
+ vertexEntryPoint,
248
+ fragmentEntryPoint,
249
+ pluginVaryings
250
+ );
251
+ const coreSource = pluginVaryingAssembly.source;
204
252
 
205
253
  // Combine Module and Application Defines
206
254
  // const allDefines = {};
@@ -230,6 +278,8 @@ export function assembleShaderWGSL(
230
278
  const declInjections: Record<string, ShaderInjection[]> = {};
231
279
  const mainInjections: Record<string, ShaderInjection[]> = {};
232
280
 
281
+ appendInjections(pluginInjections, hookInjections, declInjections, mainInjections);
282
+
233
283
  for (const key in inject) {
234
284
  const injection =
235
285
  typeof inject[key] === 'string' ? {injection: inject[key], order: 0} : inject[key];
@@ -251,6 +301,13 @@ export function assembleShaderWGSL(
251
301
  mainInjections[key] = [injection as any];
252
302
  }
253
303
  }
304
+ appendGeneratedVertexInputInjections(
305
+ pluginVertexInputAssembly.declarations,
306
+ pluginVertexInputAssembly.initialization,
307
+ declInjections,
308
+ mainInjections
309
+ );
310
+ appendGeneratedVaryingInjections(pluginVaryingAssembly, declInjections, mainInjections);
254
311
 
255
312
  // TODO - hack until shadertool modules support WebGPU
256
313
  const modulesToInject = modules;
@@ -261,7 +318,8 @@ export function assembleShaderWGSL(
261
318
  const reservedBindingKeysByGroup = reserveRegisteredModuleBindings(
262
319
  modulesToInject,
263
320
  options._bindingRegistry,
264
- usedBindingsByGroup
321
+ usedBindingsByGroup,
322
+ defines
265
323
  );
266
324
  const bindingAssignments: WGSLBindingAssignment[] = [];
267
325
 
@@ -269,21 +327,20 @@ export function assembleShaderWGSL(
269
327
  if (log) {
270
328
  checkShaderModuleDeprecations(module, coreSource, log);
271
329
  }
272
- const relocation = relocateWGSLModuleBindings(
273
- getShaderModuleSource(module, 'wgsl', log),
274
- module,
275
- {
276
- usedBindingsByGroup,
277
- bindingRegistry: options._bindingRegistry,
278
- reservedBindingKeysByGroup
279
- }
280
- );
330
+ const preprocessedModuleSource = preprocess(getShaderModuleSource(module, 'wgsl', log), {
331
+ defines
332
+ });
333
+ const relocation = relocateWGSLModuleBindings(preprocessedModuleSource, module, {
334
+ usedBindingsByGroup,
335
+ bindingRegistry: options._bindingRegistry,
336
+ reservedBindingKeysByGroup
337
+ });
281
338
  bindingAssignments.push(...relocation.bindingAssignments);
282
- const moduleSource = relocation.source;
339
+ const relocatedModuleSource = relocation.source;
283
340
  // Add the module source, and a #define that declares it presence
284
- assembledSource += moduleSource;
341
+ assembledSource += relocatedModuleSource;
285
342
 
286
- const injections = module.injections?.[stage] || {};
343
+ const injections = getWGSLModuleInjections(module);
287
344
  for (const key in injections) {
288
345
  const match = /^(v|f)s:#([\w-]+)$/.exec(key);
289
346
  if (match) {
@@ -301,16 +358,26 @@ export function assembleShaderWGSL(
301
358
  // For injectShader
302
359
  assembledSource += INJECT_SHADER_DECLARATIONS;
303
360
 
304
- assembledSource = injectShader(assembledSource, stage, declInjections);
361
+ assembledSource = injectShader(
362
+ assembledSource,
363
+ stage,
364
+ getWGSLDeclarationInjections(declInjections),
365
+ false,
366
+ 'wgsl',
367
+ {vertex: vertexEntryPoint, fragment: fragmentEntryPoint}
368
+ );
305
369
 
306
- assembledSource += getShaderHooks(hookFunctionMap[stage], hookInjections);
370
+ assembledSource += getWGSLShaderHooks(hookFunctionMap, hookInjections);
307
371
  assembledSource += formatWGSLBindingAssignmentComments(bindingAssignments);
308
372
 
309
373
  // Add the version directive and actual source of this shader
310
374
  assembledSource += applicationRelocation.source;
311
375
 
312
376
  // Apply any requested shader injections
313
- assembledSource = injectShader(assembledSource, stage, mainInjections);
377
+ assembledSource = injectShader(assembledSource, stage, mainInjections, false, 'wgsl', {
378
+ vertex: vertexEntryPoint,
379
+ fragment: fragmentEntryPoint
380
+ });
314
381
 
315
382
  assertNoUnresolvedAutoBindings(assembledSource);
316
383
 
@@ -332,9 +399,12 @@ function assembleShaderGLSL(
332
399
  language?: 'glsl' | 'wgsl';
333
400
  stage: 'vertex' | 'fragment';
334
401
  modules: ShaderModule[];
335
- defines?: Record<string, boolean>;
402
+ defines?: Record<string, boolean | number>;
336
403
  hookFunctions?: any[];
337
404
  inject?: Record<string, string | ShaderInjection>;
405
+ pluginInjections?: Record<string, ShaderInjection[]>;
406
+ pluginVertexInputs?: Record<string, AttributeShaderType>;
407
+ pluginVaryings?: Record<string, ResolvedShaderPluginVarying>;
338
408
  prologue?: boolean;
339
409
  log?: any;
340
410
  }
@@ -347,6 +417,9 @@ function assembleShaderGLSL(
347
417
  defines = {},
348
418
  hookFunctions = [],
349
419
  inject = {},
420
+ pluginInjections = {},
421
+ pluginVertexInputs = {},
422
+ pluginVaryings = {},
350
423
  prologue = true,
351
424
  log
352
425
  } = options;
@@ -404,6 +477,8 @@ ${getApplicationDefines(allDefines)}
404
477
  const declInjections: Record<string, ShaderInjection[]> = {};
405
478
  const mainInjections: Record<string, ShaderInjection[]> = {};
406
479
 
480
+ appendInjections(pluginInjections, hookInjections, declInjections, mainInjections);
481
+
407
482
  for (const key in inject) {
408
483
  const injection: ShaderInjection =
409
484
  typeof inject[key] === 'string' ? {injection: inject[key], order: 0} : inject[key];
@@ -425,6 +500,32 @@ ${getApplicationDefines(allDefines)}
425
500
  mainInjections[key] = [injection];
426
501
  }
427
502
  }
503
+ if (stage === 'vertex') {
504
+ const declarations = getShaderPluginVertexInputDeclarationsGLSL(coreSource, pluginVertexInputs);
505
+ if (declarations) {
506
+ declInjections['vs:#decl'] = declInjections['vs:#decl'] || [];
507
+ declInjections['vs:#decl'].push({
508
+ injection: declarations,
509
+ order: Number.MIN_SAFE_INTEGER
510
+ });
511
+ }
512
+ }
513
+ const pluginVaryingAssembly = assembleShaderPluginVaryingsGLSL(coreSource, stage, pluginVaryings);
514
+ if (pluginVaryingAssembly.declarations) {
515
+ const declarationTarget = stage === 'vertex' ? 'vs:#decl' : 'fs:#decl';
516
+ declInjections[declarationTarget] = declInjections[declarationTarget] || [];
517
+ declInjections[declarationTarget].push({
518
+ injection: pluginVaryingAssembly.declarations,
519
+ order: Number.MIN_SAFE_INTEGER
520
+ });
521
+ }
522
+ if (pluginVaryingAssembly.initialization) {
523
+ mainInjections['vs:#main-start'] = mainInjections['vs:#main-start'] || [];
524
+ mainInjections['vs:#main-start'].push({
525
+ injection: pluginVaryingAssembly.initialization,
526
+ order: Number.MIN_SAFE_INTEGER
527
+ });
528
+ }
428
529
 
429
530
  for (const module of modules) {
430
531
  if (log) {
@@ -496,6 +597,113 @@ export function assembleGetUniforms(modules: ShaderModule[]) {
496
597
  };
497
598
  }
498
599
 
600
+ function appendInjections(
601
+ injections: Record<string, ShaderInjection[]>,
602
+ hookInjections: Record<string, ShaderInjection[]>,
603
+ declInjections: Record<string, ShaderInjection[]>,
604
+ mainInjections: Record<string, ShaderInjection[]>
605
+ ): void {
606
+ for (const key in injections) {
607
+ const match = /^(v|f)s:(#)?([\w-]+)$/.exec(key);
608
+ if (match) {
609
+ const hash = match[2];
610
+ const name = match[3];
611
+ const injectionType = hash
612
+ ? name === 'decl'
613
+ ? declInjections
614
+ : mainInjections
615
+ : hookInjections;
616
+ injectionType[key] = injectionType[key] || [];
617
+ injectionType[key].push(...injections[key]);
618
+ } else {
619
+ mainInjections[key] = mainInjections[key] || [];
620
+ mainInjections[key].push(...injections[key]);
621
+ }
622
+ }
623
+ }
624
+
625
+ function appendGeneratedVertexInputInjections(
626
+ declarations: string,
627
+ initialization: string,
628
+ declarationInjections: Record<string, ShaderInjection[]>,
629
+ mainInjections: Record<string, ShaderInjection[]>
630
+ ): void {
631
+ if (declarations) {
632
+ declarationInjections['vs:#decl'] = declarationInjections['vs:#decl'] || [];
633
+ declarationInjections['vs:#decl'].push({
634
+ injection: declarations,
635
+ order: Number.MIN_SAFE_INTEGER
636
+ });
637
+ }
638
+ if (initialization) {
639
+ mainInjections['vs:#main-start'] = mainInjections['vs:#main-start'] || [];
640
+ mainInjections['vs:#main-start'].push({
641
+ injection: initialization,
642
+ order: Number.MIN_SAFE_INTEGER
643
+ });
644
+ }
645
+ }
646
+
647
+ function appendGeneratedVaryingInjections(
648
+ assembly: {
649
+ declarations: string;
650
+ vertexInitialization: string;
651
+ fragmentInitialization: string;
652
+ },
653
+ declarationInjections: Record<string, ShaderInjection[]>,
654
+ mainInjections: Record<string, ShaderInjection[]>
655
+ ): void {
656
+ if (assembly.declarations) {
657
+ declarationInjections['vs:#decl'] = declarationInjections['vs:#decl'] || [];
658
+ declarationInjections['vs:#decl'].push({
659
+ injection: assembly.declarations,
660
+ order: Number.MIN_SAFE_INTEGER
661
+ });
662
+ }
663
+ if (assembly.vertexInitialization) {
664
+ mainInjections['vs:#main-start'] = mainInjections['vs:#main-start'] || [];
665
+ mainInjections['vs:#main-start'].push({
666
+ injection: assembly.vertexInitialization,
667
+ order: Number.MIN_SAFE_INTEGER
668
+ });
669
+ }
670
+ if (assembly.fragmentInitialization) {
671
+ mainInjections['fs:#main-start'] = mainInjections['fs:#main-start'] || [];
672
+ mainInjections['fs:#main-start'].push({
673
+ injection: assembly.fragmentInitialization,
674
+ order: Number.MIN_SAFE_INTEGER
675
+ });
676
+ }
677
+ }
678
+
679
+ function getWGSLModuleInjections(module: ShaderModule): Record<string, ShaderInjection> {
680
+ return {
681
+ ...(module.instance?.normalizedInjections.vertex || {}),
682
+ ...(module.instance?.normalizedInjections.fragment || {})
683
+ };
684
+ }
685
+
686
+ function getWGSLDeclarationInjections(
687
+ declarationInjections: Record<string, ShaderInjection[]>
688
+ ): Record<string, ShaderInjection[]> {
689
+ const unifiedDeclarations = [
690
+ ...(declarationInjections['vs:#decl'] || []),
691
+ ...(declarationInjections['fs:#decl'] || [])
692
+ ];
693
+
694
+ return unifiedDeclarations.length ? {'vs:#decl': unifiedDeclarations} : {};
695
+ }
696
+
697
+ function getWGSLShaderHooks(
698
+ hookFunctionMap: ReturnType<typeof normalizeShaderHooks>,
699
+ hookInjections: Record<string, ShaderInjection[]>
700
+ ): string {
701
+ return (
702
+ getShaderHooks(hookFunctionMap.vertex, hookInjections, 'wgsl') +
703
+ getShaderHooks(hookFunctionMap.fragment, hookInjections, 'wgsl')
704
+ );
705
+ }
706
+
499
707
  /**
500
708
  * NOTE: Removed as id injection defeated caching of shaders
501
709
  *
@@ -517,7 +725,7 @@ export function assembleGetUniforms(modules: ShaderModule[]) {
517
725
  */
518
726
 
519
727
  /** Generates application defines from an object of key value pairs */
520
- function getApplicationDefines(defines: Record<string, boolean> = {}): string {
728
+ function getApplicationDefines(defines: Record<string, boolean | number> = {}): string {
521
729
  let sourceText = '';
522
730
  for (const define in defines) {
523
731
  const value = defines[define];
@@ -784,7 +992,8 @@ function relocateWGSLApplicationBindingMatch(
784
992
  function reserveRegisteredModuleBindings(
785
993
  modules: ShaderModule[],
786
994
  bindingRegistry: Map<string, number> | undefined,
787
- usedBindingsByGroup: Map<number, Set<number>>
995
+ usedBindingsByGroup: Map<number, Set<number>>,
996
+ defines: Record<string, boolean | number>
788
997
  ): Map<number, Map<number, string>> {
789
998
  const reservedBindingKeysByGroup = new Map<number, Map<number, string>>();
790
999
  if (!bindingRegistry) {
@@ -792,7 +1001,7 @@ function reserveRegisteredModuleBindings(
792
1001
  }
793
1002
 
794
1003
  for (const module of modules) {
795
- for (const binding of getModuleWGSLBindingDeclarations(module)) {
1004
+ for (const binding of getModuleWGSLBindingDeclarations(module, defines)) {
796
1005
  const registryKey = getBindingRegistryKey(binding.group, module.name, binding.name);
797
1006
  const location = bindingRegistry.get(registryKey);
798
1007
  if (location !== undefined) {
@@ -843,9 +1052,12 @@ function claimReservedBindingLocation(
843
1052
  return true;
844
1053
  }
845
1054
 
846
- function getModuleWGSLBindingDeclarations(module: ShaderModule): {name: string; group: number}[] {
1055
+ function getModuleWGSLBindingDeclarations(
1056
+ module: ShaderModule,
1057
+ defines: Record<string, boolean | number>
1058
+ ): {name: string; group: number}[] {
847
1059
  const declarations: {name: string; group: number}[] = [];
848
- const moduleSource = module.source || '';
1060
+ const moduleSource = preprocess(module.source || '', {defines});
849
1061
 
850
1062
  for (const match of getWGSLBindingDeclarationMatches(
851
1063
  moduleSource,
@@ -16,6 +16,8 @@ export type PlatformInfo = {
16
16
  shaderLanguageVersion: 100 | 300;
17
17
  /** string identifying current GPU */
18
18
  gpu: string;
19
+ /** Device limits exposed to shader preprocessing decisions such as platform capability defines. */
20
+ limits?: Record<string, number | undefined>;
19
21
  /** Feature strings for feature detection */
20
22
  features: Set<string>;
21
23
  };
@@ -39,12 +39,14 @@ export type ShaderHooks = {
39
39
  /** Generate hook source code */
40
40
  export function getShaderHooks(
41
41
  hookFunctions: Record<string, ShaderHook>,
42
- hookInjections: Record<string, ShaderInjection[]>
42
+ hookInjections: Record<string, ShaderInjection[]>,
43
+ shaderLanguage: 'glsl' | 'wgsl' = 'glsl'
43
44
  ): string {
44
45
  let result = '';
45
46
  for (const hookName in hookFunctions) {
46
47
  const hookFunction = hookFunctions[hookName];
47
- result += `void ${hookFunction.signature} {\n`;
48
+ const functionPrefix = shaderLanguage === 'wgsl' ? 'fn' : 'void';
49
+ result += `${functionPrefix} ${hookFunction.signature} {\n`;
48
50
  if (hookFunction.header) {
49
51
  result += ` ${hookFunction.header}`;
50
52
  }
@@ -82,7 +84,9 @@ export function normalizeShaderHooks(hookFunctions: (string | ShaderHook)[]): Sh
82
84
  hook = hookFunction;
83
85
  }
84
86
  hook = hook.trim();
85
- const [shaderStage, signature] = hook.split(':');
87
+ const stageSeparatorIndex = hook.indexOf(':');
88
+ const shaderStage = hook.slice(0, stageSeparatorIndex);
89
+ const signature = hook.slice(stageSeparatorIndex + 1);
86
90
  const name = hook.replace(/\(.+/, '');
87
91
  const normalizedHook: ShaderHook = Object.assign(opts, {signature});
88
92
  switch (shaderStage) {
@@ -83,7 +83,9 @@ export function injectShader(
83
83
  source: string,
84
84
  stage: 'vertex' | 'fragment',
85
85
  inject: Record<string, ShaderInjection[]>,
86
- injectStandardStubs = false
86
+ injectStandardStubs = false,
87
+ language: 'glsl' | 'wgsl' = 'glsl',
88
+ entryPoints: {vertex?: string; fragment?: string} = {}
87
89
  ): string {
88
90
  const isVertex = stage === 'vertex';
89
91
 
@@ -98,38 +100,56 @@ export function injectShader(
98
100
  switch (key) {
99
101
  // declarations are injected before the main function
100
102
  case 'vs:#decl':
101
- if (isVertex) {
103
+ if (language === 'wgsl' || isVertex) {
102
104
  source = source.replace(DECLARATION_INJECT_MARKER, fragmentString);
103
105
  }
104
106
  break;
105
107
  // inject code at the beginning of the main function
106
108
  case 'vs:#main-start':
107
- if (isVertex) {
108
- source = source.replace(REGEX_START_OF_MAIN, (match: string) => match + fragmentString);
109
+ if (language === 'wgsl' || isVertex) {
110
+ source =
111
+ language === 'wgsl'
112
+ ? injectWGSLStageMain(source, 'vertex', fragmentString, 'start', entryPoints.vertex)
113
+ : source.replace(REGEX_START_OF_MAIN, (match: string) => match + fragmentString);
109
114
  }
110
115
  break;
111
116
  // inject code at the end of main function
112
117
  case 'vs:#main-end':
113
- if (isVertex) {
114
- source = source.replace(REGEX_END_OF_MAIN, (match: string) => fragmentString + match);
118
+ if (language === 'wgsl' || isVertex) {
119
+ source =
120
+ language === 'wgsl'
121
+ ? injectWGSLStageMain(source, 'vertex', fragmentString, 'end', entryPoints.vertex)
122
+ : source.replace(REGEX_END_OF_MAIN, (match: string) => fragmentString + match);
115
123
  }
116
124
  break;
117
125
  // declarations are injected before the main function
118
126
  case 'fs:#decl':
119
- if (!isVertex) {
127
+ if (language === 'wgsl' || !isVertex) {
120
128
  source = source.replace(DECLARATION_INJECT_MARKER, fragmentString);
121
129
  }
122
130
  break;
123
131
  // inject code at the beginning of the main function
124
132
  case 'fs:#main-start':
125
- if (!isVertex) {
126
- source = source.replace(REGEX_START_OF_MAIN, (match: string) => match + fragmentString);
133
+ if (language === 'wgsl' || !isVertex) {
134
+ source =
135
+ language === 'wgsl'
136
+ ? injectWGSLStageMain(
137
+ source,
138
+ 'fragment',
139
+ fragmentString,
140
+ 'start',
141
+ entryPoints.fragment
142
+ )
143
+ : source.replace(REGEX_START_OF_MAIN, (match: string) => match + fragmentString);
127
144
  }
128
145
  break;
129
146
  // inject code at the end of main function
130
147
  case 'fs:#main-end':
131
- if (!isVertex) {
132
- source = source.replace(REGEX_END_OF_MAIN, (match: string) => fragmentString + match);
148
+ if (language === 'wgsl' || !isVertex) {
149
+ source =
150
+ language === 'wgsl'
151
+ ? injectWGSLStageMain(source, 'fragment', fragmentString, 'end', entryPoints.fragment)
152
+ : source.replace(REGEX_END_OF_MAIN, (match: string) => fragmentString + match);
133
153
  }
134
154
  break;
135
155
 
@@ -163,3 +183,68 @@ export function combineInjects(injects: any[]): Record<string, string> {
163
183
  });
164
184
  return result;
165
185
  }
186
+
187
+ function injectWGSLStageMain(
188
+ source: string,
189
+ stage: 'vertex' | 'fragment',
190
+ fragmentString: string,
191
+ position: 'start' | 'end',
192
+ entryPoint?: string
193
+ ): string {
194
+ const bodyRange = getWGSLStageFunctionBodyRange(source, stage, entryPoint);
195
+ if (!bodyRange) {
196
+ return source;
197
+ }
198
+
199
+ if (position === 'start') {
200
+ const insertionIndex = bodyRange.openBraceIndex + 1;
201
+ return `${source.slice(0, insertionIndex)}\n${fragmentString}${source.slice(insertionIndex)}`;
202
+ }
203
+
204
+ return `${source.slice(0, bodyRange.closeBraceIndex)}${fragmentString}${source.slice(
205
+ bodyRange.closeBraceIndex
206
+ )}`;
207
+ }
208
+
209
+ function getWGSLStageFunctionBodyRange(
210
+ source: string,
211
+ stage: 'vertex' | 'fragment',
212
+ entryPoint?: string
213
+ ): {openBraceIndex: number; closeBraceIndex: number} | null {
214
+ const stageAttribute = stage === 'vertex' ? '@vertex' : '@fragment';
215
+ const stageIndex = source.indexOf(stageAttribute);
216
+ if (stageIndex < 0) {
217
+ return null;
218
+ }
219
+
220
+ const functionIndex = entryPoint
221
+ ? source.search(new RegExp(`\\bfn\\s+${escapeRegExp(entryPoint)}\\s*\\(`))
222
+ : source.indexOf('fn', stageIndex);
223
+ if (functionIndex < 0) {
224
+ return null;
225
+ }
226
+
227
+ const openBraceIndex = source.indexOf('{', functionIndex);
228
+ if (openBraceIndex < 0) {
229
+ return null;
230
+ }
231
+
232
+ let braceDepth = 0;
233
+ for (let index = openBraceIndex; index < source.length; index++) {
234
+ const character = source[index];
235
+ if (character === '{') {
236
+ braceDepth++;
237
+ } else if (character === '}') {
238
+ braceDepth--;
239
+ if (braceDepth === 0) {
240
+ return {openBraceIndex, closeBraceIndex: index};
241
+ }
242
+ }
243
+ }
244
+
245
+ return null;
246
+ }
247
+
248
+ function escapeRegExp(value: string): string {
249
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
250
+ }