@luma.gl/shaders 10.0.0-alpha.8 → 10.0.0-alpha.9

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 (59) hide show
  1. package/dist/dist.dev.js +189 -268
  2. package/dist/dist.min.js +119 -119
  3. package/dist/glsl.cjs.map +2 -2
  4. package/dist/index.cjs.map +3 -3
  5. package/dist/index.d.ts +3 -3
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/shader-assembler-base.cjs +3 -4
  9. package/dist/lib/shader-assembler-base.d.ts +4 -5
  10. package/dist/lib/shader-assembler-base.d.ts.map +1 -1
  11. package/dist/lib/shader-assembler-base.js +3 -4
  12. package/dist/lib/shader-assembler-base.js.map +1 -1
  13. package/dist/lib/shader-assembly/assemble-glsl-shaders.cjs +5 -16
  14. package/dist/lib/shader-assembly/assemble-glsl-shaders.d.ts +4 -4
  15. package/dist/lib/shader-assembly/assemble-glsl-shaders.d.ts.map +1 -1
  16. package/dist/lib/shader-assembly/assemble-glsl-shaders.js +13 -18
  17. package/dist/lib/shader-assembly/assemble-glsl-shaders.js.map +1 -1
  18. package/dist/lib/shader-assembly/assemble-wgsl-shaders.cjs +5 -24
  19. package/dist/lib/shader-assembly/assemble-wgsl-shaders.d.ts +5 -6
  20. package/dist/lib/shader-assembly/assemble-wgsl-shaders.d.ts.map +1 -1
  21. package/dist/lib/shader-assembly/assemble-wgsl-shaders.js +13 -26
  22. package/dist/lib/shader-assembly/assemble-wgsl-shaders.js.map +1 -1
  23. package/dist/lib/shader-assembly/shader-assembly-shared.d.ts +2 -2
  24. package/dist/lib/shader-assembly/shader-assembly-shared.d.ts.map +1 -1
  25. package/dist/lib/shader-assembly/shader-assembly-shared.js.map +1 -1
  26. package/dist/lib/shader-assembly/shader-assembly-types.d.ts +7 -7
  27. package/dist/lib/shader-assembly/shader-assembly-types.d.ts.map +1 -1
  28. package/dist/lib/shader-assembly/shader-hooks.cjs +17 -36
  29. package/dist/lib/shader-assembly/shader-hooks.d.ts +34 -32
  30. package/dist/lib/shader-assembly/shader-hooks.d.ts.map +1 -1
  31. package/dist/lib/shader-assembly/shader-hooks.js +20 -44
  32. package/dist/lib/shader-assembly/shader-hooks.js.map +1 -1
  33. package/dist/lib/shader-assembly/shader-injections.cjs +26 -2
  34. package/dist/lib/shader-assembly/shader-injections.d.ts +20 -7
  35. package/dist/lib/shader-assembly/shader-injections.d.ts.map +1 -1
  36. package/dist/lib/shader-assembly/shader-injections.js +24 -0
  37. package/dist/lib/shader-assembly/shader-injections.js.map +1 -1
  38. package/dist/lib/shader-module/shader-module.cjs +3 -9
  39. package/dist/lib/shader-module/shader-module.d.ts +4 -8
  40. package/dist/lib/shader-module/shader-module.d.ts.map +1 -1
  41. package/dist/lib/shader-module/shader-module.js +3 -6
  42. package/dist/lib/shader-module/shader-module.js.map +1 -1
  43. package/dist/lib/shader-plugin.cjs +13 -38
  44. package/dist/lib/shader-plugin.d.ts +5 -30
  45. package/dist/lib/shader-plugin.d.ts.map +1 -1
  46. package/dist/lib/shader-plugin.js +17 -40
  47. package/dist/lib/shader-plugin.js.map +1 -1
  48. package/dist/wgsl.cjs.map +2 -2
  49. package/package.json +2 -2
  50. package/src/index.ts +5 -6
  51. package/src/lib/shader-assembler-base.ts +5 -6
  52. package/src/lib/shader-assembly/assemble-glsl-shaders.ts +25 -25
  53. package/src/lib/shader-assembly/assemble-wgsl-shaders.ts +27 -42
  54. package/src/lib/shader-assembly/shader-assembly-shared.ts +5 -5
  55. package/src/lib/shader-assembly/shader-assembly-types.ts +7 -7
  56. package/src/lib/shader-assembly/shader-hooks.ts +55 -82
  57. package/src/lib/shader-assembly/shader-injections.ts +52 -8
  58. package/src/lib/shader-module/shader-module.ts +7 -14
  59. package/src/lib/shader-plugin.ts +31 -90
package/dist/dist.dev.js CHANGED
@@ -864,6 +864,129 @@ var __exports__ = (() => {
864
864
  return typeof value;
865
865
  }
866
866
 
867
+ // src/lib/shader-module/shader-module.ts
868
+ function initializeShaderModules(modules) {
869
+ modules.map((module) => initializeShaderModule(module));
870
+ }
871
+ function initializeShaderModule(module) {
872
+ if (module.instance) {
873
+ return;
874
+ }
875
+ initializeShaderModules(module.dependencies || []);
876
+ const { propTypes = {}, deprecations = [] } = module;
877
+ const instance = {
878
+ parsedDeprecations: parseDeprecationDefinitions(deprecations),
879
+ normalizedInjections: { vertex: {}, fragment: {} }
880
+ };
881
+ if (propTypes) {
882
+ instance.propValidators = makePropValidators(propTypes);
883
+ }
884
+ module.instance = instance;
885
+ let defaultProps = {};
886
+ if (propTypes) {
887
+ defaultProps = Object.entries(propTypes).reduce(
888
+ (obj, [key, propType]) => {
889
+ const value = propType?.value;
890
+ if (value) {
891
+ obj[key] = value;
892
+ }
893
+ return obj;
894
+ },
895
+ {}
896
+ );
897
+ }
898
+ module.defaultUniforms = {
899
+ ...module.defaultUniforms,
900
+ ...defaultProps
901
+ };
902
+ }
903
+ function getShaderModuleUniforms(module, props, oldUniforms) {
904
+ initializeShaderModule(module);
905
+ const uniforms2 = oldUniforms || { ...module.defaultUniforms };
906
+ if (props && module.getUniforms) {
907
+ return module.getUniforms(props, uniforms2);
908
+ }
909
+ return getValidatedProperties(props, module.instance?.propValidators, module.name);
910
+ }
911
+ function checkShaderModuleDeprecations(shaderModule, shaderSource, log) {
912
+ shaderModule.deprecations?.forEach((def) => {
913
+ if (def.regex?.test(shaderSource)) {
914
+ if (def.deprecated) {
915
+ log.deprecated(def.old, def.new)();
916
+ } else {
917
+ log.removed(def.old, def.new)();
918
+ }
919
+ }
920
+ });
921
+ }
922
+ function parseDeprecationDefinitions(deprecations) {
923
+ deprecations.forEach((def) => {
924
+ switch (def.type) {
925
+ case "function":
926
+ def.regex = new RegExp(`\\b${def.old}\\(`);
927
+ break;
928
+ default:
929
+ def.regex = new RegExp(`${def.type} ${def.old};`);
930
+ }
931
+ });
932
+ return deprecations;
933
+ }
934
+
935
+ // src/lib/shader-module/shader-module-dependencies.ts
936
+ function getShaderModuleDependencies(modules) {
937
+ const visited = /* @__PURE__ */ new Set();
938
+ const recursionStack = /* @__PURE__ */ new Set();
939
+ const result = [];
940
+ const nameToModule = /* @__PURE__ */ new Map();
941
+ function checkNameCollision(module) {
942
+ const existing = nameToModule.get(module.name);
943
+ if (existing && existing !== module) {
944
+ throw new Error(
945
+ `Shader module name collision: Multiple different module objects share the name "${module.name}". Each module object must have a unique name.`
946
+ );
947
+ }
948
+ nameToModule.set(module.name, module);
949
+ }
950
+ function visit(module, path) {
951
+ if (recursionStack.has(module)) {
952
+ const cyclePath = [...path, module.name].join(" -> ");
953
+ throw new Error(`Shader module dependency cycle detected: ${cyclePath}`);
954
+ }
955
+ if (visited.has(module)) {
956
+ return;
957
+ }
958
+ checkNameCollision(module);
959
+ recursionStack.add(module);
960
+ const newPath = [...path, module.name];
961
+ if (module.dependencies) {
962
+ for (const dep of module.dependencies) {
963
+ visit(dep, newPath);
964
+ }
965
+ }
966
+ recursionStack.delete(module);
967
+ visited.add(module);
968
+ result.push(module);
969
+ }
970
+ for (const module of modules) {
971
+ visit(module, []);
972
+ }
973
+ const interfaceProviders = /* @__PURE__ */ new Set();
974
+ for (const module of result) {
975
+ if (module.implements !== void 0) {
976
+ assert(!interfaceProviders.has(module.implements));
977
+ interfaceProviders.add(module.implements);
978
+ }
979
+ }
980
+ initializeShaderModules(result);
981
+ return result;
982
+ }
983
+ function getShaderDependencies(modules) {
984
+ return getShaderModuleDependencies(modules);
985
+ }
986
+ function resolveModules(modules) {
987
+ return getShaderDependencies(modules);
988
+ }
989
+
867
990
  // src/module-injectors.ts
868
991
  var MODULE_INJECTORS_VS = (
869
992
  /* glsl */
@@ -906,37 +1029,34 @@ var __exports__ = (() => {
906
1029
  var REGEX_END_OF_MAIN = /}\n?[^{}]*$/;
907
1030
  var fragments = [];
908
1031
  var DECLARATION_INJECT_MARKER = "__LUMA_INJECT_DECLARATIONS__";
909
- function normalizeInjections(injections) {
910
- const result = { vertex: {}, fragment: {} };
911
- for (const hook in injections) {
912
- let injection = injections[hook];
913
- const stage = getHookStage(hook);
914
- if (typeof injection === "string") {
915
- injection = {
916
- order: 0,
917
- injection
918
- };
1032
+ function resolveShaderInjections(declarations, language, pluginName) {
1033
+ const resolved = {};
1034
+ for (const declaration of declarations) {
1035
+ const source7 = declaration[language];
1036
+ if (source7 === void 0) continue;
1037
+ if (declaration.stage !== "vertex" && declaration.stage !== "fragment" || declaration.target.includes(":")) {
1038
+ throw new Error("Shader injection targets require an explicit stage and unqualified target");
919
1039
  }
920
- result[stage][hook] = injection;
921
- }
922
- return result;
923
- }
924
- function getHookStage(hook) {
925
- const type = hook.slice(0, 2);
926
- switch (type) {
927
- case "vs":
928
- return "vertex";
929
- case "fs":
930
- return "fragment";
931
- default:
932
- throw new Error(type);
1040
+ const target = `${declaration.stage === "vertex" ? "vs" : "fs"}:${declaration.target}`;
1041
+ if (!/^(vs|fs):(?:#(?:decl|main-start|main-end)|[A-Za-z_][A-Za-z0-9_]*)$/.test(target)) {
1042
+ throw new Error(`Shader injection target "${target}" must be a named shader anchor or hook`);
1043
+ }
1044
+ resolved[target] ||= [];
1045
+ resolved[target].push({
1046
+ injection: source7,
1047
+ order: declaration.order ?? 0,
1048
+ ...pluginName ? { pluginName } : {}
1049
+ });
933
1050
  }
1051
+ return resolved;
934
1052
  }
935
1053
  function injectShader(source7, stage, inject, injectStandardStubs = false, language = "glsl", entryPoints = {}) {
936
1054
  const isVertex = stage === "vertex";
937
1055
  for (const key in inject) {
938
1056
  const fragmentData = inject[key];
939
- fragmentData.sort((a, b) => a.order - b.order);
1057
+ fragmentData.sort(
1058
+ (a, b) => a.order - b.order
1059
+ );
940
1060
  fragments.length = fragmentData.length;
941
1061
  for (let i = 0, len = fragmentData.length; i < len; ++i) {
942
1062
  fragments[i] = fragmentData[i].injection;
@@ -1053,134 +1173,6 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1053
1173
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1054
1174
  }
1055
1175
 
1056
- // src/lib/shader-module/shader-module.ts
1057
- function initializeShaderModules(modules) {
1058
- modules.map((module) => initializeShaderModule(module));
1059
- }
1060
- function initializeShaderModule(module) {
1061
- if (module.instance) {
1062
- return;
1063
- }
1064
- initializeShaderModules(module.dependencies || []);
1065
- const {
1066
- propTypes = {},
1067
- deprecations = [],
1068
- // defines = {},
1069
- inject = {}
1070
- } = module;
1071
- const instance = {
1072
- normalizedInjections: normalizeInjections(inject),
1073
- parsedDeprecations: parseDeprecationDefinitions(deprecations)
1074
- };
1075
- if (propTypes) {
1076
- instance.propValidators = makePropValidators(propTypes);
1077
- }
1078
- module.instance = instance;
1079
- let defaultProps = {};
1080
- if (propTypes) {
1081
- defaultProps = Object.entries(propTypes).reduce(
1082
- (obj, [key, propType]) => {
1083
- const value = propType?.value;
1084
- if (value) {
1085
- obj[key] = value;
1086
- }
1087
- return obj;
1088
- },
1089
- {}
1090
- );
1091
- }
1092
- module.defaultUniforms = {
1093
- ...module.defaultUniforms,
1094
- ...defaultProps
1095
- };
1096
- }
1097
- function getShaderModuleUniforms(module, props, oldUniforms) {
1098
- initializeShaderModule(module);
1099
- const uniforms2 = oldUniforms || { ...module.defaultUniforms };
1100
- if (props && module.getUniforms) {
1101
- return module.getUniforms(props, uniforms2);
1102
- }
1103
- return getValidatedProperties(props, module.instance?.propValidators, module.name);
1104
- }
1105
- function checkShaderModuleDeprecations(shaderModule, shaderSource, log) {
1106
- shaderModule.deprecations?.forEach((def) => {
1107
- if (def.regex?.test(shaderSource)) {
1108
- if (def.deprecated) {
1109
- log.deprecated(def.old, def.new)();
1110
- } else {
1111
- log.removed(def.old, def.new)();
1112
- }
1113
- }
1114
- });
1115
- }
1116
- function parseDeprecationDefinitions(deprecations) {
1117
- deprecations.forEach((def) => {
1118
- switch (def.type) {
1119
- case "function":
1120
- def.regex = new RegExp(`\\b${def.old}\\(`);
1121
- break;
1122
- default:
1123
- def.regex = new RegExp(`${def.type} ${def.old};`);
1124
- }
1125
- });
1126
- return deprecations;
1127
- }
1128
-
1129
- // src/lib/shader-module/shader-module-dependencies.ts
1130
- function getShaderModuleDependencies(modules) {
1131
- const visited = /* @__PURE__ */ new Set();
1132
- const recursionStack = /* @__PURE__ */ new Set();
1133
- const result = [];
1134
- const nameToModule = /* @__PURE__ */ new Map();
1135
- function checkNameCollision(module) {
1136
- const existing = nameToModule.get(module.name);
1137
- if (existing && existing !== module) {
1138
- throw new Error(
1139
- `Shader module name collision: Multiple different module objects share the name "${module.name}". Each module object must have a unique name.`
1140
- );
1141
- }
1142
- nameToModule.set(module.name, module);
1143
- }
1144
- function visit(module, path) {
1145
- if (recursionStack.has(module)) {
1146
- const cyclePath = [...path, module.name].join(" -> ");
1147
- throw new Error(`Shader module dependency cycle detected: ${cyclePath}`);
1148
- }
1149
- if (visited.has(module)) {
1150
- return;
1151
- }
1152
- checkNameCollision(module);
1153
- recursionStack.add(module);
1154
- const newPath = [...path, module.name];
1155
- if (module.dependencies) {
1156
- for (const dep of module.dependencies) {
1157
- visit(dep, newPath);
1158
- }
1159
- }
1160
- recursionStack.delete(module);
1161
- visited.add(module);
1162
- result.push(module);
1163
- }
1164
- for (const module of modules) {
1165
- visit(module, []);
1166
- }
1167
- const interfaceProviders = /* @__PURE__ */ new Set();
1168
- for (const module of result) {
1169
- if (module.implements !== void 0) {
1170
- assert(!interfaceProviders.has(module.implements));
1171
- interfaceProviders.add(module.implements);
1172
- }
1173
- }
1174
- initializeShaderModules(result);
1175
- return result;
1176
- }
1177
- function getShaderDependencies(modules) {
1178
- return getShaderModuleDependencies(modules);
1179
- }
1180
- function resolveModules(modules) {
1181
- return getShaderDependencies(modules);
1182
- }
1183
-
1184
1176
  // src/types/shader-type-decoder.ts
1185
1177
  function getVariableShaderTypeInfo(format) {
1186
1178
  const resolvedFormat = resolveVariableShaderTypeAlias(format);
@@ -1382,7 +1374,6 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1382
1374
  };
1383
1375
 
1384
1376
  // src/lib/shader-plugin.ts
1385
- var SHADER_PLUGIN_INJECTION_TARGET_REGEX = /^(vs|fs):(?:#(?:decl|main-start|main-end)|[A-Za-z_][\w-]*)$/;
1386
1377
  function resolveShaderPlugins(plugins = [], shaderLanguage) {
1387
1378
  const resolved = {
1388
1379
  modules: [],
@@ -1452,20 +1443,21 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1452
1443
  resolved.interStageVariables[name] = normalizedVarying;
1453
1444
  }
1454
1445
  for (const hook of plugin.hooks || []) {
1455
- const declarationSource = typeof hook === "string" ? selectShaderLanguage(hook, shaderLanguage) : void 0;
1456
- if (typeof hook === "string" && declarationSource === void 0) continue;
1457
- const signature = typeof hook === "string" ? declarationSource.slice(declarationSource.indexOf("(")) : hook[shaderLanguage];
1458
- if (signature === void 0) continue;
1459
- const target = typeof hook === "string" ? declarationSource.split("(")[0] : getShaderPluginTarget(hook);
1460
- if (!/^(vs|fs):[A-Za-z_][A-Za-z0-9_]*$/.test(target) || !/^\([^]*\)$/.test(signature.trim())) {
1446
+ const implementation = hook[shaderLanguage];
1447
+ if (!implementation) continue;
1448
+ const target = `${hook.stage === "vertex" ? "vs" : "fs"}:${hook.name}`;
1449
+ if (!/^(vs|fs):[A-Za-z_][A-Za-z0-9_]*$/.test(target)) {
1461
1450
  throw new Error(`Invalid ShaderPlugin hook ${target}`);
1462
1451
  }
1463
- const declaration = { hook: `${target}${signature.trim()}` };
1464
- const existing = resolved.hooks.find((value) => value.hook.split("(")[0] === target);
1465
- if (existing && existing.hook !== declaration.hook) {
1452
+ const existing = resolved.hooks.find(
1453
+ (value) => value.stage === hook.stage && value.name === hook.name
1454
+ );
1455
+ const previous = existing?.[shaderLanguage];
1456
+ if (previous && (previous.parameters !== implementation.parameters || (previous.returnType || "void") !== (implementation.returnType || "void") || previous.header !== implementation.header || previous.footer !== implementation.footer)) {
1466
1457
  throw new Error(`Conflicting ShaderPlugin hook ${target}`);
1467
1458
  }
1468
- if (!existing) resolved.hooks.push(declaration);
1459
+ if (!existing)
1460
+ resolved.hooks.push({ name: hook.name, stage: hook.stage, [shaderLanguage]: implementation });
1469
1461
  }
1470
1462
  const pluginInjections = Array.isArray(plugin.injections) ? plugin.injections : Object.entries(plugin.injections || {}).flatMap(([key, source7]) => {
1471
1463
  const target = selectShaderLanguage(key, shaderLanguage);
@@ -1481,20 +1473,10 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1481
1473
  }
1482
1474
  ];
1483
1475
  });
1484
- for (const injection of pluginInjections) {
1485
- const source7 = injection[shaderLanguage];
1486
- if (source7 === void 0) continue;
1487
- const target = getShaderPluginTarget(injection);
1488
- assertNamedShaderPluginInjectionTarget(target);
1489
- if (!resolved.injections[target]) {
1490
- resolved.injections[target] = [];
1491
- }
1492
- const injections = resolved.injections[target];
1493
- injections.push({
1494
- pluginName: plugin.name,
1495
- injection: source7,
1496
- order: injection.order ?? 0
1497
- });
1476
+ const selectedInjections = resolveShaderInjections(pluginInjections, shaderLanguage, plugin.name);
1477
+ for (const [target, injections] of Object.entries(selectedInjections)) {
1478
+ resolved.injections[target] ||= [];
1479
+ resolved.injections[target].push(...injections);
1498
1480
  }
1499
1481
  }
1500
1482
  function assertShaderPluginInterfaceName(name, kind) {
@@ -1511,19 +1493,6 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1511
1493
  }
1512
1494
  return { type: varying.type, interpolation };
1513
1495
  }
1514
- function assertNamedShaderPluginInjectionTarget(target) {
1515
- if (!SHADER_PLUGIN_INJECTION_TARGET_REGEX.test(target)) {
1516
- throw new Error(
1517
- `ShaderPlugin injection target "${target}" must be a named shader anchor or hook`
1518
- );
1519
- }
1520
- }
1521
- function getShaderPluginTarget(value) {
1522
- if (value.stage !== "vertex" && value.stage !== "fragment" || value.target.includes(":")) {
1523
- throw new Error("ShaderPlugin object targets require an explicit stage and unqualified target");
1524
- }
1525
- return `${value.stage === "vertex" ? "vs" : "fs"}:${value.target}`;
1526
- }
1527
1496
  function selectShaderLanguage(value, language) {
1528
1497
  const match = /^(glsl|wgsl):([\s\S]+)$/.exec(value);
1529
1498
  return match ? match[1] === language ? match[2] : void 0 : value;
@@ -2733,8 +2702,10 @@ return ${outputName};
2733
2702
  let result = "";
2734
2703
  for (const hookName in hookFunctions) {
2735
2704
  const hookFunction = hookFunctions[hookName];
2736
- const functionPrefix = shaderLanguage === "wgsl" ? "fn" : "void";
2737
- result += `${functionPrefix} ${hookFunction.signature} {
2705
+ const returnType = hookFunction.returnType || "void";
2706
+ const functionPrefix = shaderLanguage === "wgsl" ? "fn" : returnType;
2707
+ const returnSuffix = shaderLanguage === "wgsl" && returnType !== "void" ? ` -> ${returnType}` : "";
2708
+ result += `${functionPrefix} ${hookFunction.signature}${returnSuffix} {
2738
2709
  `;
2739
2710
  if (hookFunction.header) {
2740
2711
  result += ` ${hookFunction.header}`;
@@ -2754,23 +2725,24 @@ return ${outputName};
2754
2725
  }
2755
2726
  return result;
2756
2727
  }
2757
- function normalizeShaderHooks(hookFunctions, pluginHooks = [], language) {
2728
+ function normalizeShaderHooks(hookFunctions, pluginHooks = [], language = "glsl") {
2758
2729
  const result = { vertex: {}, fragment: {} };
2759
- for (const hookFunction of hookFunctions) {
2760
- const text = typeof hookFunction === "string" ? hookFunction : hookFunction.hook;
2761
- const qualified = /^(glsl|wgsl):([\s\S]+)$/.exec(text);
2762
- if (qualified && qualified[1] !== language) continue;
2763
- addShaderHookToRegistry(
2764
- result,
2765
- qualified ? typeof hookFunction === "string" ? qualified[2] : { ...hookFunction, hook: qualified[2] } : hookFunction
2766
- );
2730
+ for (const hook of hookFunctions) {
2731
+ const implementation = hook[language];
2732
+ if (implementation) {
2733
+ const prefix = hook.stage === "vertex" ? "vs" : "fs";
2734
+ result[hook.stage][`${prefix}:${hook.name}`] = {
2735
+ ...implementation,
2736
+ signature: `${hook.name}(${implementation.parameters})`
2737
+ };
2738
+ }
2767
2739
  }
2768
2740
  for (const hook of pluginHooks) {
2769
- const normalized = normalizeShaderHooks([hook]);
2741
+ const selected = normalizeShaderHooks([hook], [], language);
2770
2742
  for (const stage of ["vertex", "fragment"]) {
2771
- for (const [name, declaration] of Object.entries(normalized[stage])) {
2743
+ for (const [name, declaration] of Object.entries(selected[stage])) {
2772
2744
  const existing = result[stage][name];
2773
- if (existing && existing.signature !== declaration.signature) {
2745
+ if (existing && (existing.signature !== declaration.signature || (existing.returnType || "void") !== (declaration.returnType || "void"))) {
2774
2746
  throw new Error(`Conflicting ShaderPlugin hook ${name}`);
2775
2747
  }
2776
2748
  result[stage][name] = existing || declaration;
@@ -2799,30 +2771,6 @@ return ${outputName};
2799
2771
  }
2800
2772
  }
2801
2773
  }
2802
- function addShaderHookToRegistry(registry, hookFunction) {
2803
- const options = typeof hookFunction === "string" ? {} : hookFunction;
2804
- const hook = (typeof hookFunction === "string" ? hookFunction : hookFunction.hook).trim();
2805
- const stageSeparatorIndex = hook.indexOf(":");
2806
- const shaderStage = hook.slice(0, stageSeparatorIndex);
2807
- const signature = hook.slice(stageSeparatorIndex + 1);
2808
- const name = hook.split("(")[0].trim();
2809
- const normalizedHook = {
2810
- hook: name,
2811
- signature,
2812
- header: options.header || "",
2813
- footer: options.footer || ""
2814
- };
2815
- switch (shaderStage) {
2816
- case "vs":
2817
- registry.vertex[name] = normalizedHook;
2818
- break;
2819
- case "fs":
2820
- registry.fragment[name] = normalizedHook;
2821
- break;
2822
- default:
2823
- throw new Error(shaderStage);
2824
- }
2825
- }
2826
2774
 
2827
2775
  // src/lib/shader-assembly/assemble-wgsl-shaders.ts
2828
2776
  var RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT = 100;
@@ -2877,8 +2825,11 @@ return ${outputName};
2877
2825
  const declInjections = {};
2878
2826
  const mainInjections = {};
2879
2827
  appendInjections(pluginInjections, hookInjections, declInjections, mainInjections);
2880
- for (const key in inject) {
2881
- const injection = typeof inject[key] === "string" ? { injection: inject[key], order: 0 } : inject[key];
2828
+ const applicationInjections = Array.isArray(inject) ? resolveShaderInjections(inject, "wgsl") : {};
2829
+ appendInjections(applicationInjections, hookInjections, declInjections, mainInjections);
2830
+ const injectionMap = Array.isArray(inject) ? {} : inject;
2831
+ for (const key in injectionMap) {
2832
+ const injection = typeof injectionMap[key] === "string" ? { injection: injectionMap[key], order: 0 } : injectionMap[key];
2882
2833
  const match = /^(v|f)s:(#)?([\w-]+)$/.exec(key);
2883
2834
  if (match) {
2884
2835
  const hash = match[2];
@@ -2932,19 +2883,6 @@ return ${outputName};
2932
2883
  checkShaderModuleDeprecations(module, coreSource, log);
2933
2884
  }
2934
2885
  assembledSource += moduleSource;
2935
- const injections = getWGSLModuleInjections(module);
2936
- for (const key in injections) {
2937
- const match = /^(v|f)s:#([\w-]+)$/.exec(key);
2938
- if (match) {
2939
- const name = match[2];
2940
- const injectionType = name === "decl" ? declInjections : mainInjections;
2941
- injectionType[key] = injectionType[key] || [];
2942
- injectionType[key].push(injections[key]);
2943
- } else if (isShaderHookRegistered(hookFunctionMap, key)) {
2944
- hookInjections[key] = hookInjections[key] || [];
2945
- hookInjections[key].push(injections[key]);
2946
- }
2947
- }
2948
2886
  }
2949
2887
  assembledSource += INJECT_SHADER_DECLARATIONS;
2950
2888
  assembledSource = injectShader(
@@ -3004,12 +2942,6 @@ return ${outputName};
3004
2942
  });
3005
2943
  }
3006
2944
  }
3007
- function getWGSLModuleInjections(module) {
3008
- return {
3009
- ...module.instance?.normalizedInjections.vertex || {},
3010
- ...module.instance?.normalizedInjections.fragment || {}
3011
- };
3012
- }
3013
2945
  function getWGSLDeclarationInjections(declarationInjections) {
3014
2946
  const unifiedDeclarations = [
3015
2947
  ...declarationInjections["vs:#decl"] || [],
@@ -3535,8 +3467,11 @@ ${getApplicationDefines(allDefines)}
3535
3467
  const declInjections = {};
3536
3468
  const mainInjections = {};
3537
3469
  appendInjections(pluginInjections, hookInjections, declInjections, mainInjections);
3538
- for (const key in inject) {
3539
- const injection = typeof inject[key] === "string" ? { injection: inject[key], order: 0 } : inject[key];
3470
+ const applicationInjections = Array.isArray(inject) ? resolveShaderInjections(inject, "glsl") : {};
3471
+ appendInjections(applicationInjections, hookInjections, declInjections, mainInjections);
3472
+ const injectionMap = Array.isArray(inject) ? {} : inject;
3473
+ for (const key in injectionMap) {
3474
+ const injection = typeof injectionMap[key] === "string" ? { injection: injectionMap[key], order: 0 } : injectionMap[key];
3540
3475
  const match = /^(v|f)s:(#)?([\w-]+)$/.exec(key);
3541
3476
  if (match) {
3542
3477
  const hash = match[2];
@@ -3581,19 +3516,6 @@ ${getApplicationDefines(allDefines)}
3581
3516
  }
3582
3517
  const moduleSource = getShaderModuleSource(module, stage, log);
3583
3518
  assembledSource += moduleSource;
3584
- const injections = module.instance?.normalizedInjections[stage] || {};
3585
- for (const key in injections) {
3586
- const match = /^(v|f)s:#([\w-]+)$/.exec(key);
3587
- if (match) {
3588
- const name = match[2];
3589
- const injectionType = name === "decl" ? declInjections : mainInjections;
3590
- injectionType[key] = injectionType[key] || [];
3591
- injectionType[key].push(injections[key]);
3592
- } else {
3593
- hookInjections[key] = hookInjections[key] || [];
3594
- hookInjections[key].push(injections[key]);
3595
- }
3596
- }
3597
3519
  }
3598
3520
  validateShaderHookInjections(hookFunctionMap, hookInjections, stage);
3599
3521
  assembledSource += "// ----- MAIN SHADER SOURCE -------------------------";
@@ -3698,11 +3620,10 @@ ${getApplicationDefines(allDefines)}
3698
3620
  }
3699
3621
  /**
3700
3622
  * Register a shader hook
3701
- * @param hook Stage-prefixed hook signature, such as `vs:OFFSET_POSITION(inout vec4 position)`.
3702
- * @param options Optional hook metadata such as always-on header and footer source.
3623
+ * @param hook Shared stage and name with language-specific parameters, return type, and source.
3703
3624
  */
3704
- addShaderHook(hook, options) {
3705
- this._hookFunctions.push(options ? { ...options, hook } : hook);
3625
+ addShaderHook(hook) {
3626
+ this._hookFunctions.push(hook);
3706
3627
  }
3707
3628
  /**
3708
3629
  * Remove all registered shader hooks, for example before re-registering hooks for a new