UNPKG

@luma.gl/shadertools

Version:

Shader module system for luma.gl

4 lines 226 kB
{ "version": 3, "sources": ["../src/index.ts", "../src/lib/utils/assert.ts", "../src/lib/filters/prop-types.ts", "../src/module-injectors.ts", "../src/lib/shader-assembly/shader-injections.ts", "../src/lib/shader-module/shader-module.ts", "../src/lib/shader-module/shader-module-dependencies.ts", "../src/lib/shader-assembly/platform-defines.ts", "../src/lib/shader-transpiler/transpile-glsl-shader.ts", "../src/lib/shader-assembly/shader-hooks.ts", "../src/lib/glsl-utils/get-shader-info.ts", "../src/lib/shader-assembly/assemble-shaders.ts", "../src/lib/preprocessor/preprocessor.ts", "../src/lib/shader-assembler.ts", "../src/lib/glsl-utils/shader-utils.ts", "../src/lib/shader-generator/utils/capitalize.ts", "../src/lib/shader-generator/glsl/generate-glsl.ts", "../src/lib/shader-generator/wgsl/generate-wgsl.ts", "../src/lib/shader-generator/generate-shader.ts", "../src/lib/wgsl/get-shader-layout-wgsl.ts", "../src/modules/math/fp16/fp16-utils.ts", "../src/modules/math/fp64/fp64-utils.ts", "../src/modules/math/random/random.ts", "../src/modules/math/fp32/fp32.ts", "../src/modules/math/fp64/fp64-arithmetic-glsl.ts", "../src/modules/math/fp64/fp64-functions-glsl.ts", "../src/modules/math/fp64/fp64.ts", "../src/modules/engine/picking/picking.ts", "../src/modules/lighting/lights/lighting.ts", "../src/modules/lighting/lights/lighting-uniforms-glsl.ts", "../src/modules/lighting/lights/lighting-uniforms-wgsl.ts", "../src/modules/lighting/no-material/dirlight.ts", "../src/modules/lighting/phong-material/phong-shaders-glsl.ts", "../src/modules/lighting/phong-material/phong-shaders-wgsl.ts", "../src/modules/lighting/gouraud-material/gouraud-material.ts", "../src/modules/lighting/phong-material/phong-material.ts", "../src/modules/lighting/pbr-material/pbr-vertex-glsl.ts", "../src/modules/lighting/pbr-material/pbr-fragment-glsl.ts", "../src/modules/lighting/pbr-material/pbr-projection.ts", "../src/modules/lighting/pbr-material/pbr-material.ts", "../src/modules-webgl1/geometry/geometry.ts", "../src/modules-webgl1/project/project.ts", "../src/modules-webgl1/lighting/lights/lights-glsl.ts", "../src/modules-webgl1/lighting/lights/lights.ts", "../src/modules-webgl1/lighting/dirlight/dirlight.ts", "../src/modules-webgl1/lighting/phong-lighting/phong-lighting-glsl.ts", "../src/modules-webgl1/lighting/phong-lighting/phong-lighting.ts", "../src/modules-webgl1/lighting/pbr/pbr-vertex-glsl.ts", "../src/modules-webgl1/lighting/pbr/pbr-fragment-glsl.ts", "../src/modules-webgl1/lighting/pbr/pbr.ts"], "sourcesContent": ["// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\n// shadertools exports\n\n/**\n * Marks GLSL shaders for syntax highlighting: glsl`...`\n * Install https://marketplace.visualstudio.com/items?itemName=boyswan.glsl-literal\n */\nexport type {PlatformInfo} from './lib/shader-assembly/platform-info';\n\n// ShaderModules\n\nexport type {ShaderModule} from './lib/shader-module/shader-module';\nexport type {ShaderPass} from './lib/shader-module/shader-pass';\nexport type {UniformTypes} from './lib/utils/uniform-types';\n\nexport {initializeShaderModule, initializeShaderModules} from './lib/shader-module/shader-module';\nexport {getShaderModuleUniforms} from './lib/shader-module/shader-module';\nexport {getShaderModuleDependencies} from './lib/shader-module/shader-module-dependencies';\nexport {checkShaderModuleDeprecations} from './lib/shader-module/shader-module';\n\nexport {getShaderModuleSource} from './lib/shader-assembly/assemble-shaders';\n\nexport {resolveModules as _resolveModules} from './lib/shader-module/shader-module-dependencies';\nexport {getDependencyGraph as _getDependencyGraph} from './lib/shader-module/shader-module-dependencies';\n\n// ShaderAssembler\nexport {ShaderAssembler} from './lib/shader-assembler';\nexport type {ShaderHook} from './lib/shader-assembly/shader-hooks';\nexport type {ShaderInjection} from './lib/shader-assembly/shader-injections';\n\n// SHADER HELPERS\n\n// Shader source introspection\nexport {getShaderInfo} from './lib/glsl-utils/get-shader-info';\nexport {\n getQualifierDetails,\n getPassthroughFS,\n typeToChannelSuffix,\n typeToChannelCount,\n convertToVec4\n} from './lib/glsl-utils/shader-utils';\n\n// EXPERIMENTAL - Do not use in production applications\nexport type {ShaderGenerationOptions} from './lib/shader-generator/generate-shader';\nexport {generateShaderForModule} from './lib/shader-generator/generate-shader';\nexport {capitalize} from './lib/shader-generator/utils/capitalize';\n\n// TEST EXPORTS - Do not use in production applications\nexport {preprocess} from './lib/preprocessor/preprocessor';\nexport {assembleGLSLShaderPair} from './lib/shader-assembly/assemble-shaders';\nexport {combineInjects} from './lib/shader-assembly/shader-injections';\n\n// EXPERIMENTAL WGSL\nexport {getShaderLayoutFromWGSL} from './lib/wgsl/get-shader-layout-wgsl';\n\n// data utils\nexport {toHalfFloat, fromHalfFloat} from './modules/math/fp16/fp16-utils';\nexport {fp64ify, fp64LowPart, fp64ifyMatrix4} from './modules/math/fp64/fp64-utils';\n\n// math libraries\nexport {random} from './modules/math/random/random';\n\nexport {fp32} from './modules/math/fp32/fp32';\nexport {fp64, fp64arithmetic} from './modules/math/fp64/fp64';\n\n// engine shader modules\n\n// // projection\n// export type {ProjectionUniforms} from './modules/engine/project/project';\n// export {projection} from './modules/engine/project/project';\nexport type {PickingProps, PickingUniforms} from './modules/engine/picking/picking';\nexport {picking} from './modules/engine/picking/picking';\n\n// // lighting\nexport type {LightingProps, LightingUniforms} from './modules/lighting/lights/lighting';\nexport {lighting} from './modules/lighting/lights/lighting';\nexport {dirlight} from './modules/lighting/no-material/dirlight';\nexport type {GouraudMaterialProps} from './modules/lighting/gouraud-material/gouraud-material';\nexport {gouraudMaterial} from './modules/lighting/gouraud-material/gouraud-material';\nexport type {PhongMaterialProps} from './modules/lighting/phong-material/phong-material';\nexport {phongMaterial} from './modules/lighting/phong-material/phong-material';\nexport type {\n PBRMaterialBindings,\n PBRMaterialProps,\n PBRMaterialUniforms\n} from './modules/lighting/pbr-material/pbr-material';\nexport type {PBRProjectionProps} from './modules/lighting/pbr-material/pbr-projection';\n\nexport {pbrMaterial} from './modules/lighting/pbr-material/pbr-material';\n\n// DEPRECATED - v8 legacy shader modules (non-uniform buffer)\n\n// math libraries\n// export {fp64, fp64arithmetic} from './modules-webgl1/math/fp64/fp64';\n\n// projection and lighting\nexport {geometry as geometry1} from './modules-webgl1/geometry/geometry';\nexport {project as project1} from './modules-webgl1/project/project';\n\nexport {lights as lights1} from './modules-webgl1/lighting/lights/lights';\nexport {dirlight as dirlight1} from './modules-webgl1/lighting/dirlight/dirlight';\nexport {\n gouraudLighting,\n phongLighting\n} from './modules-webgl1/lighting/phong-lighting/phong-lighting';\nexport {pbr} from './modules-webgl1/lighting/pbr/pbr';\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\n// Recommendation is to ignore message but current test suite checks agains the\n// message so keep it for now.\nexport function assert(condition: unknown, message?: string): void {\n if (!condition) {\n throw new Error(message || 'shadertools: assertion failed.');\n }\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {assert} from '../utils/assert';\n\n/**\n * For use by shader module and shader pass writers to describe the types of the\n * properties they expose (properties ultimately map to uniforms).\n */\nexport type PropType =\n | {\n type?: string;\n value?: unknown;\n max?: number;\n min?: number;\n softMax?: number;\n softMin?: number;\n hint?: string;\n /** @deprecated internal uniform */\n private?: boolean;\n }\n | number;\n\n/**\n * Internal property validators generated by processing the prop types ,\n * The `validate()` method can be used to validate the type of properties passed in to\n * shader module or shader pass\n */\nexport type PropValidator = {\n type: string;\n value?: unknown;\n max?: number;\n min?: number;\n private?: boolean;\n validate?(value: unknown, propDef: PropValidator): boolean;\n};\n\n/** Minimal validators for number and array types */\nconst DEFAULT_PROP_VALIDATORS: Record<string, PropValidator> = {\n number: {\n type: 'number',\n validate(value: unknown, propType: PropType) {\n return (\n Number.isFinite(value) &&\n typeof propType === 'object' &&\n (propType.max === undefined || (value as number) <= propType.max) &&\n (propType.min === undefined || (value as number) >= propType.min)\n );\n }\n },\n array: {\n type: 'array',\n validate(value: unknown, propType: PropType) {\n return Array.isArray(value) || ArrayBuffer.isView(value);\n }\n }\n};\n\n/**\n * Parse a list of property types into property definitions that can be used to validate\n * values passed in by applications.\n * @param propTypes\n * @returns\n */\nexport function makePropValidators(\n propTypes: Record<string, PropType>\n): Record<string, PropValidator> {\n const propValidators: Record<string, PropValidator> = {};\n for (const [name, propType] of Object.entries(propTypes)) {\n propValidators[name] = makePropValidator(propType);\n }\n return propValidators;\n}\n\n/**\n * Validate a map of user supplied properties against a map of validators\n * Inject default values when user doesn't supply a property\n * @param properties\n * @param propValidators\n * @returns\n */\nexport function getValidatedProperties(\n properties: Record<string, unknown>,\n propValidators: Record<string, PropValidator>,\n errorMessage: string\n): Record<string, unknown> {\n const validated: Record<string, unknown> = {};\n\n for (const [key, propsValidator] of Object.entries(propValidators)) {\n if (properties && key in properties && !propsValidator.private) {\n if (propsValidator.validate) {\n assert(\n propsValidator.validate(properties[key], propsValidator),\n `${errorMessage}: invalid ${key}`\n );\n }\n validated[key] = properties[key];\n } else {\n // property not supplied - use default value\n validated[key] = propsValidator.value;\n }\n }\n\n // TODO - warn for unused properties that don't match a validator?\n\n return validated;\n}\n\n/**\n * Creates a property validator for a prop type. Either contains:\n * - a valid prop type object ({type, ...})\n * - or just a default value, in which case type and name inference is used\n */\nfunction makePropValidator(propType: PropType): PropValidator {\n let type = getTypeOf(propType);\n\n if (type !== 'object') {\n return {value: propType, ...DEFAULT_PROP_VALIDATORS[type], type};\n }\n\n // Special handling for objects\n if (typeof propType === 'object') {\n if (!propType) {\n return {type: 'object', value: null};\n }\n if (propType.type !== undefined) {\n return {...propType, ...DEFAULT_PROP_VALIDATORS[propType.type], type: propType.type};\n }\n // If no type and value this object is likely the value\n if (propType.value === undefined) {\n return {type: 'object', value: propType};\n }\n\n type = getTypeOf(propType.value);\n return {...propType, ...DEFAULT_PROP_VALIDATORS[type], type};\n }\n\n throw new Error('props');\n}\n\n/**\n * \"improved\" version of javascript typeof that can distinguish arrays and null values\n */\nfunction getTypeOf(value: unknown): string {\n if (Array.isArray(value) || ArrayBuffer.isView(value)) {\n return 'array';\n }\n return typeof value;\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nexport const MODULE_INJECTORS_VS = /* glsl */ `\\\n#ifdef MODULE_LOGDEPTH\n logdepth_adjustPosition(gl_Position);\n#endif\n`;\n\nexport const MODULE_INJECTORS_FS = /* glsl */ `\\\n#ifdef MODULE_MATERIAL\n fragColor = material_filterColor(fragColor);\n#endif\n\n#ifdef MODULE_LIGHTING\n fragColor = lighting_filterColor(fragColor);\n#endif\n\n#ifdef MODULE_FOG\n fragColor = fog_filterColor(fragColor);\n#endif\n\n#ifdef MODULE_PICKING\n fragColor = picking_filterHighlightColor(fragColor);\n fragColor = picking_filterPickingColor(fragColor);\n#endif\n\n#ifdef MODULE_LOGDEPTH\n logdepth_setFragDepth();\n#endif\n`;\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {MODULE_INJECTORS_VS, MODULE_INJECTORS_FS} from '../../module-injectors';\nimport {assert} from '../utils/assert';\n\n// TODO - experimental\nconst MODULE_INJECTORS = {\n vertex: MODULE_INJECTORS_VS,\n fragment: MODULE_INJECTORS_FS\n};\n\nconst REGEX_START_OF_MAIN = /void\\s+main\\s*\\([^)]*\\)\\s*\\{\\n?/; // Beginning of main\nconst REGEX_END_OF_MAIN = /}\\n?[^{}]*$/; // End of main, assumes main is last function\nconst fragments: string[] = [];\n\nexport const DECLARATION_INJECT_MARKER = '__LUMA_INJECT_DECLARATIONS__';\n\n/**\n *\n */\nexport type ShaderInjection = {\n injection: string;\n order: number;\n};\n\n/**\n * ShaderInjections, parsed and split per shader\n */\nexport type ShaderInjections = {\n vertex: Record<string, ShaderInjection>;\n fragment: Record<string, ShaderInjection>;\n};\n\n/**\n *\n */\nexport function normalizeInjections(\n injections: Record<string, string | ShaderInjection>\n): ShaderInjections {\n const result: ShaderInjections = {vertex: {}, fragment: {}};\n\n for (const hook in injections) {\n let injection = injections[hook];\n const stage = getHookStage(hook);\n if (typeof injection === 'string') {\n injection = {\n order: 0,\n injection\n };\n }\n\n result[stage][hook] = injection;\n }\n\n return result;\n}\n\nfunction getHookStage(hook: string): 'vertex' | 'fragment' {\n const type = hook.slice(0, 2);\n switch (type) {\n case 'vs':\n return 'vertex';\n case 'fs':\n return 'fragment';\n default:\n throw new Error(type);\n }\n}\n\n/**\n// A minimal shader injection/templating system.\n// RFC: https://github.com/visgl/luma.gl/blob/7.0-release/dev-docs/RFCs/v6.0/shader-injection-rfc.md\n * @param source \n * @param type \n * @param inject \n * @param injectStandardStubs \n * @returns \n */\n// eslint-disable-next-line complexity\nexport function injectShader(\n source: string,\n stage: 'vertex' | 'fragment',\n inject: Record<string, ShaderInjection[]>,\n injectStandardStubs = false\n): string {\n const isVertex = stage === 'vertex';\n\n for (const key in inject) {\n const fragmentData = inject[key];\n fragmentData.sort((a: ShaderInjection, b: ShaderInjection): number => a.order - b.order);\n fragments.length = fragmentData.length;\n for (let i = 0, len = fragmentData.length; i < len; ++i) {\n fragments[i] = fragmentData[i].injection;\n }\n const fragmentString = `${fragments.join('\\n')}\\n`;\n switch (key) {\n // declarations are injected before the main function\n case 'vs:#decl':\n if (isVertex) {\n source = source.replace(DECLARATION_INJECT_MARKER, fragmentString);\n }\n break;\n // inject code at the beginning of the main function\n case 'vs:#main-start':\n if (isVertex) {\n source = source.replace(REGEX_START_OF_MAIN, (match: string) => match + fragmentString);\n }\n break;\n // inject code at the end of main function\n case 'vs:#main-end':\n if (isVertex) {\n source = source.replace(REGEX_END_OF_MAIN, (match: string) => fragmentString + match);\n }\n break;\n // declarations are injected before the main function\n case 'fs:#decl':\n if (!isVertex) {\n source = source.replace(DECLARATION_INJECT_MARKER, fragmentString);\n }\n break;\n // inject code at the beginning of the main function\n case 'fs:#main-start':\n if (!isVertex) {\n source = source.replace(REGEX_START_OF_MAIN, (match: string) => match + fragmentString);\n }\n break;\n // inject code at the end of main function\n case 'fs:#main-end':\n if (!isVertex) {\n source = source.replace(REGEX_END_OF_MAIN, (match: string) => fragmentString + match);\n }\n break;\n\n default:\n // TODO(Tarek): I think this usage should be deprecated.\n\n // inject code after key, leaving key in place\n source = source.replace(key, (match: string) => match + fragmentString);\n }\n }\n\n // Remove if it hasn't already been replaced\n source = source.replace(DECLARATION_INJECT_MARKER, '');\n\n // Finally, if requested, insert an automatic module injector chunk\n if (injectStandardStubs) {\n source = source.replace(/\\}\\s*$/, (match: string) => match + MODULE_INJECTORS[stage]);\n }\n\n return source;\n}\n\n// Takes an array of inject objects and combines them into one\nexport function combineInjects(injects: any[]): Record<string, string> {\n const result: Record<string, string> = {};\n assert(Array.isArray(injects) && injects.length > 1);\n injects.forEach(inject => {\n for (const key in inject) {\n result[key] = result[key] ? `${result[key]}\\n${inject[key]}` : inject[key];\n }\n });\n return result;\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {UniformFormat} from '../../types';\nimport {\n PropType,\n PropValidator,\n makePropValidators,\n getValidatedProperties\n} from '../filters/prop-types';\nimport type {UniformTypes, UniformValue} from '../utils/uniform-types';\nimport {ShaderInjection, normalizeInjections} from '../shader-assembly/shader-injections';\n\n// To avoid dependency on core module, do not import `Binding` type.\n// The ShaderModule is not concerned with the type of `Binding`,\n// it is the repsonsibility of `splitUniformsAndBindings` in\n// ShaderInputs to type the result of `getUniforms()`\ntype Binding = unknown; // import type {Binding} from '@luma.gl/core';\n\nexport type UniformInfo = {\n format?: UniformFormat;\n} & PropType;\n\n// Helper types\ntype BindingKeys<T> = {[K in keyof T]: T[K] extends UniformValue ? never : K}[keyof T];\ntype UniformKeys<T> = {[K in keyof T]: T[K] extends UniformValue ? K : never}[keyof T];\nexport type PickBindings<T> = {[K in BindingKeys<Required<T>>]: T[K]};\nexport type PickUniforms<T> = {[K in UniformKeys<Required<T>>]: T[K]};\n\n/**\n * A shader module definition object\n *\n * @note Needs to be initialized with `initializeShaderModules`\n * @note `UniformsT` & `BindingsT` are deduced from `PropsT` by default. If\n * a custom type for `UniformsT` is used, `BindingsT` should be also be provided.\n */\nexport type ShaderModule<\n PropsT extends Record<string, any> = Record<string, any>,\n UniformsT extends Record<string, UniformValue> = PickUniforms<PropsT>,\n BindingsT extends Record<string, Binding> = PickBindings<PropsT>\n> = {\n /** Used for type inference not for values */\n props?: PropsT;\n /** Used for type inference, not currently used for values */\n uniforms?: UniformsT;\n /** Used for type inference, not currently used for values */\n bindings?: BindingsT;\n\n name: string;\n\n /** WGSL code */\n source?: string;\n /** GLSL fragment shader code */\n fs?: string;\n /** GLSL vertex shader code */\n vs?: string;\n\n /** Uniform shader types @note: Both order and types MUST match uniform block declarations in shader */\n uniformTypes?: Required<UniformTypes<UniformsT>>; // Record<keyof UniformsT, UniformFormat>;\n /** Uniform JS prop types */\n propTypes?: Record<keyof UniformsT, UniformInfo>;\n /** Default uniform values */\n defaultUniforms?: Required<UniformsT>; // Record<keyof UniformsT, UniformValue>;\n\n /** Function that maps props to uniforms & bindings */\n getUniforms?: (\n props: Partial<PropsT>,\n prevUniforms?: UniformsT\n ) => Partial<UniformsT & BindingsT>;\n\n defines?: Record<string, string | number>;\n /** Injections */\n inject?: Record<string, string | {injection: string; order: number}>;\n dependencies?: ShaderModule<any, any>[];\n /** Information on deprecated properties */\n deprecations?: ShaderModuleDeprecation[];\n\n /** The instance field contains information that is generated at run-time */\n instance?: {\n propValidators?: Record<string, PropValidator>;\n parsedDeprecations: ShaderModuleDeprecation[];\n\n normalizedInjections: {\n vertex: Record<string, ShaderInjection>;\n fragment: Record<string, ShaderInjection>;\n };\n };\n};\n\n/** Use to generate deprecations when shader module is used */\nexport type ShaderModuleDeprecation = {\n type: string;\n regex?: RegExp;\n new: string;\n old: string;\n deprecated?: boolean;\n};\n\n// SHNDER MODULE API\n\nexport function initializeShaderModules(modules: ShaderModule[]): void {\n modules.map((module: ShaderModule) => initializeShaderModule(module));\n}\n\nexport function initializeShaderModule(module: ShaderModule): void {\n if (module.instance) {\n return;\n }\n\n initializeShaderModules(module.dependencies || []);\n\n const {\n propTypes = {},\n deprecations = [],\n // defines = {},\n inject = {}\n } = module;\n\n const instance: Required<ShaderModule>['instance'] = {\n normalizedInjections: normalizeInjections(inject),\n parsedDeprecations: parseDeprecationDefinitions(deprecations)\n };\n\n if (propTypes) {\n instance.propValidators = makePropValidators(propTypes);\n }\n\n module.instance = instance;\n\n // TODO(ib) - we need to apply the original prop types to the default uniforms\n let defaultProps: ShaderModule['props'] = {};\n if (propTypes) {\n defaultProps = Object.entries(propTypes).reduce(\n (obj: ShaderModule['props'], [key, propType]) => {\n // @ts-expect-error\n const value = propType?.value;\n if (value) {\n // @ts-expect-error\n obj[key] = value;\n }\n return obj;\n },\n {} as ShaderModule['props']\n );\n }\n\n module.defaultUniforms = {...module.defaultUniforms, ...defaultProps} as any;\n}\n\n/** Convert module props to uniforms */\nexport function getShaderModuleUniforms<\n ShaderModuleT extends ShaderModule<Record<string, unknown>, Record<string, UniformValue>>\n>(\n module: ShaderModuleT,\n props?: ShaderModuleT['props'],\n oldUniforms?: ShaderModuleT['uniforms']\n): Record<string, Binding | UniformValue> {\n initializeShaderModule(module);\n\n const uniforms = oldUniforms || {...module.defaultUniforms};\n // If module has a getUniforms function, use it\n if (props && module.getUniforms) {\n return module.getUniforms(props, uniforms);\n }\n\n // Build uniforms from the uniforms array\n // @ts-expect-error\n return getValidatedProperties(props, module.instance?.propValidators, module.name);\n}\n\n/* TODO this looks like it was unused code\n _defaultGetUniforms(opts: Record<string, any> = {}): Record<string, any> {\n const uniforms: Record<string, any> = {};\n const propTypes = this.uniforms;\n\n for (const key in propTypes) {\n const propDef = propTypes[key];\n if (key in opts && !propDef.private) {\n if (propDef.validate) {\n assert(propDef.validate(opts[key], propDef), `${this.name}: invalid ${key}`);\n }\n uniforms[key] = opts[key];\n } else {\n uniforms[key] = propDef.value;\n }\n }\n\n return uniforms;\n }\n}\n*/\n// Warn about deprecated uniforms or functions\nexport function checkShaderModuleDeprecations(\n shaderModule: ShaderModule,\n shaderSource: string,\n log: any\n): void {\n shaderModule.deprecations?.forEach(def => {\n if (def.regex?.test(shaderSource)) {\n if (def.deprecated) {\n log.deprecated(def.old, def.new)();\n } else {\n log.removed(def.old, def.new)();\n }\n }\n });\n}\n\n// HELPERS\n\nfunction parseDeprecationDefinitions(deprecations: ShaderModuleDeprecation[]) {\n deprecations.forEach(def => {\n switch (def.type) {\n case 'function':\n def.regex = new RegExp(`\\\\b${def.old}\\\\(`);\n break;\n default:\n def.regex = new RegExp(`${def.type} ${def.old};`);\n }\n });\n\n return deprecations;\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {ShaderModule, initializeShaderModules} from './shader-module';\n\n// import type {ShaderModule} from '../shader-module/shader-module';\n\ntype AbstractModule = {\n name: string;\n dependencies?: AbstractModule[];\n};\n\n/**\n * Takes a list of shader module names and returns a new list of\n * shader module names that includes all dependencies, sorted so\n * that modules that are dependencies of other modules come first.\n *\n * If the shader glsl code from the returned modules is concatenated\n * in the reverse order, it is guaranteed that all functions be resolved and\n * that all function and variable definitions come before use.\n *\n * @param modules - Array of modules (inline modules or module names)\n * @return - Array of modules\n */\nexport function getShaderModuleDependencies<T extends AbstractModule>(modules: T[]): T[] {\n initializeShaderModules(modules);\n const moduleMap: Record<string, T> = {};\n const moduleDepth: Record<string, number> = {};\n getDependencyGraph({modules, level: 0, moduleMap, moduleDepth});\n\n // Return a reverse sort so that dependencies come before the modules that use them\n const dependencies = Object.keys(moduleDepth)\n .sort((a, b) => moduleDepth[b] - moduleDepth[a])\n .map(name => moduleMap[name]);\n initializeShaderModules(dependencies);\n return dependencies;\n}\n\n/**\n * Recursively checks module dependencies to calculate dependency level of each module.\n *\n * @param options.modules - Array of modules\n * @param options.level - Current level\n * @param options.moduleMap -\n * @param options.moduleDepth - Current level\n * @return - Map of module name to its level\n */\n// Adds another level of dependencies to the result map\nexport function getDependencyGraph<T extends AbstractModule>(options: {\n modules: T[];\n level: number;\n moduleMap: Record<string, T>;\n moduleDepth: Record<string, number>;\n}) {\n const {modules, level, moduleMap, moduleDepth} = options;\n if (level >= 5) {\n throw new Error('Possible loop in shader dependency graph');\n }\n\n // Update level on all current modules\n for (const module of modules) {\n moduleMap[module.name] = module;\n if (moduleDepth[module.name] === undefined || moduleDepth[module.name] < level) {\n moduleDepth[module.name] = level;\n }\n }\n\n // Recurse\n for (const module of modules) {\n if (module.dependencies) {\n getDependencyGraph({modules: module.dependencies, level: level + 1, moduleMap, moduleDepth});\n }\n }\n}\n\n/**\n * Takes a list of shader module names and returns a new list of\n * shader module names that includes all dependencies, sorted so\n * that modules that are dependencies of other modules come first.\n *\n * If the shader glsl code from the returned modules is concatenated\n * in the reverse order, it is guaranteed that all functions be resolved and\n * that all function and variable definitions come before use.\n *\n * @param modules - Array of modules (inline modules or module names)\n * @return - Array of modules\n */\nexport function getShaderDependencies(modules: ShaderModule[]): ShaderModule[] {\n initializeShaderModules(modules);\n const moduleMap: Record<string, ShaderModule> = {};\n const moduleDepth: Record<string, number> = {};\n getDependencyGraph({modules, level: 0, moduleMap, moduleDepth});\n\n // Return a reverse sort so that dependencies come before the modules that use them\n modules = Object.keys(moduleDepth)\n .sort((a, b) => moduleDepth[b] - moduleDepth[a])\n .map(name => moduleMap[name]);\n initializeShaderModules(modules);\n return modules;\n}\n\n// DEPRECATED\n\n/**\n * Instantiate shader modules and resolve any dependencies\n * @deprecated Use getShaderDpendencies\n */\nexport function resolveModules(modules: ShaderModule[]): ShaderModule[] {\n return getShaderDependencies(modules);\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {PlatformInfo} from './platform-info';\n\n/** Adds defines to help identify GPU architecture / platform */\nexport function getPlatformShaderDefines(platformInfo: PlatformInfo): string {\n switch (platformInfo?.gpu.toLowerCase()) {\n case 'apple':\n return /* glsl */ `\\\n#define APPLE_GPU\n// Apple optimizes away the calculation necessary for emulated fp64\n#define LUMA_FP64_CODE_ELIMINATION_WORKAROUND 1\n#define LUMA_FP32_TAN_PRECISION_WORKAROUND 1\n// Intel GPU doesn't have full 32 bits precision in same cases, causes overflow\n#define LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND 1\n`;\n\n case 'nvidia':\n return /* glsl */ `\\\n#define NVIDIA_GPU\n// Nvidia optimizes away the calculation necessary for emulated fp64\n#define LUMA_FP64_CODE_ELIMINATION_WORKAROUND 1\n`;\n\n case 'intel':\n return /* glsl */ `\\\n#define INTEL_GPU\n// Intel optimizes away the calculation necessary for emulated fp64\n#define LUMA_FP64_CODE_ELIMINATION_WORKAROUND 1\n// Intel's built-in 'tan' function doesn't have acceptable precision\n#define LUMA_FP32_TAN_PRECISION_WORKAROUND 1\n// Intel GPU doesn't have full 32 bits precision in same cases, causes overflow\n#define LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND 1\n`;\n\n case 'amd':\n // AMD Does not eliminate fp64 code\n return /* glsl */ `\\\n#define AMD_GPU\n`;\n\n default:\n // We don't know what GPU it is, could be that the GPU driver or\n // browser is not implementing UNMASKED_RENDERER constant and not\n // reporting a correct name\n return /* glsl */ `\\\n#define DEFAULT_GPU\n// Prevent driver from optimizing away the calculation necessary for emulated fp64\n#define LUMA_FP64_CODE_ELIMINATION_WORKAROUND 1\n// Headless Chrome's software shader 'tan' function doesn't have acceptable precision\n#define LUMA_FP32_TAN_PRECISION_WORKAROUND 1\n// If the GPU doesn't have full 32 bits precision, will causes overflow\n#define LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND 1\n`;\n }\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\n// TRANSPILATION TABLES\n\n/**\n * Transpiles GLSL 3.00 shader source code to target GLSL version (3.00 or 1.00)\n *\n * @note We always run transpiler even if same version e.g. 3.00 => 3.00\n * @note For texture sampling transpilation, apps need to use non-standard texture* calls in GLSL 3.00 source\n * RFC: https://github.com/visgl/luma.gl/blob/7.0-release/dev-docs/RFCs/v6.0/portable-glsl-300-rfc.md\n */\nexport function transpileGLSLShader(source: string, stage: 'vertex' | 'fragment'): string {\n const sourceGLSLVersion = Number(source.match(/^#version[ \\t]+(\\d+)/m)?.[1] || 100);\n if (sourceGLSLVersion !== 300) {\n // TODO - we splurge on a longer error message to help deck.gl custom layer developers\n throw new Error('luma.gl v9 only supports GLSL 3.00 shader sources');\n }\n\n switch (stage) {\n case 'vertex':\n source = convertShader(source, ES300_VERTEX_REPLACEMENTS);\n return source;\n case 'fragment':\n source = convertShader(source, ES300_FRAGMENT_REPLACEMENTS);\n return source;\n default:\n // Unknown shader stage\n throw new Error(stage);\n }\n}\n\ntype GLSLReplacement = [RegExp, string];\n\n/** Simple regex replacements for GLSL ES 1.00 syntax that has changed in GLSL ES 3.00 */\nconst ES300_REPLACEMENTS: GLSLReplacement[] = [\n // Fix poorly formatted version directive\n [/^(#version[ \\t]+(100|300[ \\t]+es))?[ \\t]*\\n/, '#version 300 es\\n'],\n // The individual `texture...()` functions were replaced with `texture()` overloads\n [/\\btexture(2D|2DProj|Cube)Lod(EXT)?\\(/g, 'textureLod('],\n [/\\btexture(2D|2DProj|Cube)(EXT)?\\(/g, 'texture(']\n];\n\nconst ES300_VERTEX_REPLACEMENTS: GLSLReplacement[] = [\n ...ES300_REPLACEMENTS,\n // `attribute` keyword replaced with `in`\n [makeVariableTextRegExp('attribute'), 'in $1'],\n // `varying` keyword replaced with `out`\n [makeVariableTextRegExp('varying'), 'out $1']\n];\n\n/** Simple regex replacements for GLSL ES 1.00 syntax that has changed in GLSL ES 3.00 */\nconst ES300_FRAGMENT_REPLACEMENTS: GLSLReplacement[] = [\n ...ES300_REPLACEMENTS,\n // `varying` keyword replaced with `in`\n [makeVariableTextRegExp('varying'), 'in $1']\n];\n\nfunction convertShader(source: string, replacements: GLSLReplacement[]) {\n for (const [pattern, replacement] of replacements) {\n source = source.replace(pattern, replacement);\n }\n return source;\n}\n\n/**\n * Creates a regexp that tests for a specific variable type\n * @example\n * should match:\n * in float weight;\n * out vec4 positions[2];\n * should not match:\n * void f(out float a, in float b) {}\n */\nfunction makeVariableTextRegExp(qualifier: 'attribute' | 'varying' | 'in' | 'out'): RegExp {\n return new RegExp(`\\\\b${qualifier}[ \\\\t]+(\\\\w+[ \\\\t]+\\\\w+(\\\\[\\\\w+\\\\])?;)`, 'g');\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {ShaderInjection} from './shader-injections';\n\n// A normalized hook function\n/**\n * The shader hook mechanism allows the application to create shaders\n * that can be automatically extended by the shader modules the application\n * includes.\n *\n * A shader hook function that shader modules can inject code into.\n * Shaders can call these functions, which will be no-ops by default.\n *\n * If a shader module injects code it will be executed upon the hook\n * function call.\n */\nexport type ShaderHook = {\n /** `vs:` or `fs:` followed by the name and arguments of the function, e.g. `vs:MYHOOK_func(inout vec4 value)`. Hook name without arguments\n will also be used as the name of the shader hook */\n hook: string;\n /** Code always included at the beginning of a hook function */\n header: string;\n /** Code always included at the end of a hook function */\n footer: string;\n /** To Be Documented */\n signature?: string;\n};\n\n/** Normalized shader hooks per shader */\nexport type ShaderHooks = {\n /** Normalized shader hooks for vertex shader */\n vertex: Record<string, ShaderHook>;\n /** Normalized shader hooks for fragment shader */\n fragment: Record<string, ShaderHook>;\n};\n\n/** Generate hook source code */\nexport function getShaderHooks(\n hookFunctions: Record<string, ShaderHook>,\n hookInjections: Record<string, ShaderInjection[]>\n): string {\n let result = '';\n for (const hookName in hookFunctions) {\n const hookFunction = hookFunctions[hookName];\n result += `void ${hookFunction.signature} {\\n`;\n if (hookFunction.header) {\n result += ` ${hookFunction.header}`;\n }\n if (hookInjections[hookName]) {\n const injections = hookInjections[hookName];\n injections.sort((a: {order: number}, b: {order: number}): number => a.order - b.order);\n for (const injection of injections) {\n result += ` ${injection.injection}\\n`;\n }\n }\n if (hookFunction.footer) {\n result += ` ${hookFunction.footer}`;\n }\n result += '}\\n';\n }\n\n return result;\n}\n\n/**\n * Parse string based hook functions\n * And split per shader\n */\nexport function normalizeShaderHooks(hookFunctions: (string | ShaderHook)[]): ShaderHooks {\n const result: ShaderHooks = {vertex: {}, fragment: {}};\n\n for (const hookFunction of hookFunctions) {\n let opts: ShaderHook;\n let hook: string;\n if (typeof hookFunction !== 'string') {\n opts = hookFunction;\n hook = opts.hook;\n } else {\n opts = {} as ShaderHook;\n hook = hookFunction;\n }\n hook = hook.trim();\n const [shaderStage, signature] = hook.split(':');\n const name = hook.replace(/\\(.+/, '');\n const normalizedHook: ShaderHook = Object.assign(opts, {signature});\n switch (shaderStage) {\n case 'vs':\n result.vertex[name] = normalizedHook;\n break;\n case 'fs':\n result.fragment[name] = normalizedHook;\n break;\n default:\n throw new Error(shaderStage);\n }\n }\n\n return result;\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\n/** Information extracted from shader source code */\nexport type ShaderInfo = {\n name: string;\n language: 'glsl' | 'wgsl';\n version: number;\n};\n\n/** Extracts information from shader source code */\nexport function getShaderInfo(source: string, defaultName?: string): ShaderInfo {\n return {\n name: getShaderName(source, defaultName),\n language: 'glsl',\n version: getShaderVersion(source)\n };\n}\n\n/** Extracts GLSLIFY style naming of shaders: `#define SHADER_NAME ...` */\nfunction getShaderName(shader: string, defaultName: string = 'unnamed'): string {\n const SHADER_NAME_REGEXP = /#define[^\\S\\r\\n]*SHADER_NAME[^\\S\\r\\n]*([A-Za-z0-9_-]+)\\s*/;\n const match = SHADER_NAME_REGEXP.exec(shader);\n return match ? match[1] : defaultName;\n}\n\n/** returns GLSL shader version of given shader string */\nfunction getShaderVersion(source: string): 100 | 300 {\n let version = 100;\n const words = source.match(/[^\\s]+/g);\n if (words && words.length >= 2 && words[0] === '#version') {\n const parsedVersion = parseInt(words[1], 10);\n if (Number.isFinite(parsedVersion)) {\n version = parsedVersion;\n }\n }\n if (version !== 100 && version !== 300) {\n throw new Error(`Invalid GLSL version ${version}`);\n }\n return version;\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {getShaderModuleDependencies} from '../shader-module/shader-module-dependencies';\nimport {PlatformInfo} from './platform-info';\nimport {getPlatformShaderDefines} from './platform-defines';\nimport {injectShader, DECLARATION_INJECT_MARKER} from './shader-injections';\nimport {transpileGLSLShader} from '../shader-transpiler/transpile-glsl-shader';\nimport {checkShaderModuleDeprecations} from '../shader-module/shader-module';\nimport type {ShaderInjection} from './shader-injections';\nimport type {ShaderModule} from '../shader-module/shader-module';\nimport {ShaderHook, normalizeShaderHooks, getShaderHooks} from './shader-hooks';\nimport {assert} from '../utils/assert';\nimport {getShaderInfo} from '../glsl-utils/get-shader-info';\n\n/** Define map */\nexport type ShaderDefine = string | number | boolean;\n\nconst INJECT_SHADER_DECLARATIONS = `\\n\\n${DECLARATION_INJECT_MARKER}\\n`;\n\n/**\n * Precision prologue to inject before functions are injected in shader\n * TODO - extract any existing prologue in the fragment source and move it up...\n */\nconst FRAGMENT_SHADER_PROLOGUE = /* glsl */ `\\\nprecision highp float;\n`;\n\n/**\n * Options for `ShaderAssembler.assembleShaders()`\n */\nexport type AssembleShaderProps = AssembleShaderOptions & {\n platformInfo: PlatformInfo;\n /** WGSL: single shader source. */\n source?: string | null;\n /** GLSL vertex shader source. */\n vs?: string | null;\n /** GLSL fragment shader source. */\n fs?: string | null;\n};\n\nexport type AssembleShaderOptions = {\n /** information about the platform (which shader language & version, extensions etc.) */\n platformInfo: PlatformInfo;\n /** Inject shader id #defines */\n id?: string;\n /** Modules to be injected */\n modules?: ShaderModule[];\n /** Defines to be injected */\n defines?: Record<string, ShaderDefine>;\n /** Hook functions */\n hookFunctions?: (ShaderHook | string)[];\n /** Code injections */\n inject?: Record<string, string | ShaderInjection>;\n /** Whether to inject prologue */\n prologue?: boolean;\n /** logger object */\n log?: any;\n};\n\ntype AssembleStageOptions = {\n /** Inject shader id #defines */\n id?: string;\n /** Vertex shader */\n source: string;\n stage: 'vertex' | 'fragment';\n /** Modules to be injected */\n modules: any[];\n /** Defines to be injected */\n defines?: Record<string, ShaderDefine>;\n /** Hook functions */\n hookFunctions?: (ShaderHook | string)[];\n /** Code injections */\n inject?: Record<string, string | ShaderInjection>;\n /** Whether to inject prologue */\n prologue?: boolean;\n /** logger object */\n log?: any;\n};\n\nexport type HookFunction = {hook: string; header: string; footer: string; signature?: string};\n\n/**\n * getUniforms function returned from the shader module system\n */\nexport type GetUniformsFunc = (opts: Record<string, any>) => Record<string, any>;\n\n/**\n * Inject a list of shader modules into a single shader source for WGSL\n */\nexport function assembleWGSLShader(\n options: AssembleShaderOptions & {\n /** Single WGSL shader */\n source: string;\n }\n): {\n source: string;\n getUniforms: GetUniformsFunc;\n} {\n const modules = getShaderModuleDependencies(options.modules || []);\n\n return {\n source: assembleShaderWGSL(options.platformInfo, {\n ...options,\n source: options.source,\n stage: 'vertex',\n modules\n }),\n getUniforms: assembleGetUniforms(modules)\n };\n}\n\n/**\n * Injects dependent shader module sources into pair of main vertex/fragment shader sources for GLSL\n */\nexport function assembleGLSLShaderPair(\n options: AssembleShaderOptions & {\n /** Vertex shader */\n vs: string;\n /** Fragment shader */\n fs?: string;\n }\n): {\n vs: string;\n fs: string;\n getUniforms: GetUniformsFunc;\n} {\n const {vs, fs} = options;\n const modules = getShaderModuleDependencies(options.modules || []);\n\n return {\n vs: assembleShaderGLSL(options.platformInfo, {\n ...options,\n source: vs,\n stage: 'vertex',\n modules\n }),\n fs: assembleShaderGLSL(options.platformInfo, {\n ...options,\n // @ts-expect-error\n source: fs,\n stage: 'fragment',\n modules\n }),\n getUniforms: assembleGetUniforms(modules)\n };\n}\n\n/**\n * Pulls together complete source code for either a vertex or a fragment shader\n * adding prologues, requested module chunks, and any final injections.\n * @param gl\n * @param options\n * @returns\n */\nexport function assembleShaderWGSL(platformInfo: PlatformInfo, options: AssembleStageOptions) {\n const {\n // id,\n source,\n stage,\n modules,\n // defines = {},\n hookFunctions = [],\n inject = {},\n log\n } = options;\n\n assert(typeof source === 'string', 'shader source must be a string');\n\n // const isVertex = type === 'vs';\n // const sourceLines = source.split('\\n');\n\n const coreSource = source;\n\n // Combine Module and Application Defines\n // const allDefines = {};\n // modules.forEach(module => {\n // Object.assign(allDefines, module.getDefines());\n // });\n // Object.assign(allDefines, defines);\n\n // Add platform defines (use these to work around platform-specific bugs and limitations)\n // Add common defines (GLSL version compatibility, feature detection)\n // Add precision declaration for fragment shaders\n let assembledSource = '';\n // prologue\n // ? `\\\n // ${getShaderNameDefine({id, source, type})}\n // ${getShaderType(type)}\n // ${getPlatformShaderDefines(platformInfo)}\n // ${getApplicationDefines(allDefines)}\n // ${isVertex ? '' : FRAGMENT_SHADER_PROLOGUE}\n // `\n // `;\n\n const hookFunctionMap = normalizeShaderHooks(hookFunctions);\n\n // Add source of dependent modules in resolved order\n const hookInjections: Record<string, ShaderInjection[]> = {};\n const declInjections: Record<string, ShaderInjection[]> = {};\n const mainInjections: Record<string, ShaderInjection[]> = {};\n\n for (const key in inject) {\n const injection =\n typeof inject[key] === 'string' ? {injection: inject[key], order: 0} : inject[key];\n const match = /^(v|f)s:(#)?([\\w-]+)$/.exec(key);\n if (match) {\n const hash = match[2];\n const name = match[3];\n if (hash) {\n if (name === 'decl') {\n declInjections[key] = [injection as any];\n } else {\n mainInjections[key] = [injection as any];\n }\n } else {\n hookInjections[key] = [injection as any];\n }\n } else {\n // Regex injection\n mainInjections[key] = [injection as any];\n }\n }\n\n // TODO - hack until shadertool modules support WebGPU\n const modulesToInject = modules;\n\n for (const module of modulesToInject) {\n if (log) {\n checkShaderModuleDeprecations(module, coreSource, log);\n }\n const moduleSource = getShaderModuleSource(module, 'wgsl');\n // Add the module source, and a #define that declares it presence\n assembledSource += moduleSource;\n\n const injections = module.injections?.[stage] || {};\n for (const key in injections) {\n const match = /^(v|f)s:#([\\w-]+)$/.exec(key);\n if (match) {\n const name = match[2];\n const injectionType = name === 'decl' ? declInjections : mainInjections;\n injectionType[key] = injectionType[key] || [];\n injectionType[key].push(injections[key]);\n } else {\n hookInjections[key] = hookInjections[key] || [];\n hookInjections[key].push(injections[key]);\n }\n }\n }\n\n // For injectShader\n assembledSource += INJECT_SHADER_DECLARATIONS;\n\n assembledSource = injectShader(assembledSource, stage, declInjections);\n\n assembledSource += getShaderHooks(hookFunctionMap[stage], hookInjections);\n\n // Add the version directive and actual source of this shader\n assembledSource += coreSource;\n\n // Apply any requested shader injections\n assembledSource = injectShader(assembledSource, stage, mainInjections);\n\n return assembledSource;\n}\n\n/**\n * Pulls together complete source code for either a vertex or a fragment shader\n * adding prologues, requested module chunks, and any final injections.\n * @param gl\n * @param options\n * @returns\n */\nfunction assembleShaderGLSL(\n platformInfo: PlatformInfo,\n options: {\n id?: string;\n source: string;\n language?: 'glsl' | 'wgsl';\n stage: 'vertex' | 'fragment';\n modules: ShaderModule[];\n defines?: Record<string, ShaderDefine>;\n hookFunctions?: any[];\n inject?: Record<string, string | ShaderInjection>;\n prologue?: boolean;\n log?: any;\n }\n) {\n const {\n id,\n source,\n stage,\n language = 'glsl',\n modules,\n defines = {},\n hookFunctions = [],\n inject = {},\n prologue = true,\n log\n } = options;\n\n assert(typeof source === 'string', 'shader source must be a string');\n\n const sourceVersion = language === 'glsl' ? getShaderInfo(source).version : -1;\n const targetVersion = platformInfo.shaderLanguageVersion;\n\n const sourceVersionDirective = sourceVersion === 100 ? '#version 100' : '#version 300 es';\n\n const sourceLines = source.split('\\n');\n // TODO : keep all pre-processor statements at the beginning of the shader.\n const coreSource = sourceLines.slice(1).join('\\n');\n\n // Combine Module and Application Defines\n const allDefines = {};\n modules.forEach(module => {\n Object.assign(allDefines, module.defines);\n });\n Object.assign(allDefines, defines);\n\n // Add platform defines (use these to work around platform-specific bugs and limitations)\n // Add common defines (GLSL version compatibility, feature detection)\n // Add precision declaration for fragment shaders\n let assembledSource = '';\n switch (language) {\n case 'wgsl':\n break;\n case 'glsl':\n assembledSource = prologue\n ? `\\\n${sourceVersionDirective}\n\n// ----- PROLOGUE -------------------------\n${getShaderNameDefine({id, source, stage})}\n${`#define SHADER_TYPE_${stage.toUpperCase()}`}\n\n${getPlatformShaderDefines(platformInfo)}\n${stage === 'fragment' ? FRAGMENT_SHADER_PROLOGUE : ''}\n\n// ----- APPLICATION DEFINES -------------------------\n\n${getApplicationDefines(allDefines)}\n\n`\n : `${sourceVersionDirective}\n`;\n break;\n }\n\n const hookFunctionMap = normalizeShaderHooks(hookFunctions);\n\n // Add source of dependent modules in resolved order\n const hookInjections: Record<string, ShaderInjection[]> = {};\n const declInjections: Record<string, ShaderInjection[]> = {};\n const mainInjections: Record<string, ShaderInjection[]> = {};\n\n for (const key in inject) {\n const injection: ShaderInjection =\n typeof inject[key] === 'string' ? {injection: inject[key], order: 0} : inject[key];\n const match = /^(v|f)s:(#)?([\\w-]+)$/.exec(key);\n if (match) {\n const hash = match[2];\n const name = match[3];\n if (hash) {\n if (name === 'decl') {\n declInjections[key] = [injection];\n } else {\n mainInjections[key] = [injection];\n }\n } else {\n hookInjections[key] = [injection];\n }\n } else {\n // Regex injection\n mainInjections[key] = [injection];\n }\n }\n\n for (const module of modules) {\n if (log) {\n checkShaderModuleDeprecations(module, coreSource, log);\n }\n const moduleSource = getShaderModuleSource(module, stage);\n // Add the module source, and a #define that declares it presence\n assembledSource += moduleSource;\n\n const injections = module.instance?.normalizedInjections[stage] || {};\n for (const key in injections) {\n