{"record":{"id":"b1a9f6851681387a","repo":"BabylonJS/Babylon.js","slug":"helper-function-funcname-in-blocktype-block-b","errorCode":null,"errorMessage":"Helper function ${funcName} in blockType ${block.blockType} accesses uniform(s) ${uniformsAccessed.join(\", \")} which is not supported. Pass them in instead.","messagePattern":"Helper function (.+?) in blockType (.+?) accesses uniform\\(s\\) (.+?) which is not supported\\. Pass them in instead\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/smartFilters/src/optimization/smartFilterOptimizer.ts","lineNumber":360,"sourceCode":"            for (const remappedSymbol of this._remappedSymbols) {\r\n                if (\r\n                    remappedSymbol.type === \"uniform\" &&\r\n                    remappedSymbol.owners[0] &&\r\n                    remappedSymbol.owners[0].blockType === block.blockType &&\r\n                    func.code.includes(remappedSymbol.remappedName)\r\n                ) {\r\n                    uniformsAccessed.push(remappedSymbol.remappedName);\r\n                }\r\n            }\r\n\r\n            // Strip out any matches which are actually function params\r\n            const functionParams = func.params ? func.params.split(\",\").map((p) => p.trim().split(\" \")[1]) : [];\r\n            uniformsAccessed = uniformsAccessed.filter((u) => !functionParams.includes(u));\r\n\r\n            // If it accessed any uniforms, throw an error\r\n            if (uniformsAccessed.length > 0) {\r\n                uniformsAccessed = uniformsAccessed.map((u) => (u[0] === DecorateChar ? UndecorateSymbol(u) : u));\r\n                throw new Error(\r\n                    `Helper function ${funcName} in blockType ${block.blockType} accesses uniform(s) ${uniformsAccessed.join(\", \")} which is not supported. Pass them in instead.`\r\n                );\r\n            }\r\n\r\n            // Look to see if we have an exact match including parameters of this function in the list of remapped symbols\r\n            const existingFunctionExactOverload = this._remappedSymbols.find(\r\n                (s) => s.type === \"function\" && s.name === funcName && s.params === func.params && s.owners[0] && s.owners[0].blockType === block.blockType\r\n            );\r\n\r\n            // Look to see if we already have this function in the list of remapped symbols, regardless of parameters\r\n            const existingFunction = this._remappedSymbols.find((s) => s.type === \"function\" && s.name === funcName && s.owners[0] && s.owners[0].blockType === block.blockType);\r\n\r\n            // Get or create the remapped name, ignoring the parameter list\r\n            let remappedName = existingFunction?.remappedName;\r\n            let createdNewName = false;\r\n            if (!remappedName) {\r\n                remappedName = DecorateSymbol(this._makeSymbolUnique(funcName));\r\n                createdNewName = true;\r","sourceCodeStart":342,"sourceCodeEnd":378,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/smartFilters/src/optimization/smartFilterOptimizer.ts#L342-L378","documentation":"During optimization, SmartFilterOptimizer._processHelperFunctions inlines GLSL helper functions. A helper may only access uniforms that are passed in as parameters; if, after subtracting the function's parameters, it still references uniform symbols, the optimizer cannot safely inline it and throws this error naming the offending uniforms.","triggerScenarios":"A block's GLSL (from getCustomShaderCode or a custom block) declares a helper function whose body reads a uniform variable directly instead of receiving it as a function argument, and the optimizer processes that function.","commonSituations":"Writing a custom SmartFilter block with hand-authored GLSL helpers that close over uniforms; porting shader code that assumes global uniform access; renaming uniforms so parameter matching fails.","solutions":["Rewrite the helper function to take the uniform value as a parameter and pass it at each call site.","Move the uniform read outside the helper into the main function body.","Verify the helper's parameter names match the uniform names exactly (params are parsed from the signature) so they are correctly excluded.","Undecorate/decorate handling: ensure uniform names after DecorateChar processing match the declared parameters."],"exampleFix":"// before (GLSL)\nfloat brighten(vec2 uv) { return texture2D(uTexture, uv).r * uIntensity; }\n// after\nfloat brighten(vec2 uv, float intensity) { return texture2D(uTexture, uv).r * intensity; }\n// call: brighten(uv, uIntensity);","handlingStrategy":"validation","validationCode":"// Static check on block GLSL: helper functions must not reference uniform names not in their params\nconst uniformNames = new Set(collectUniforms(shaderCode));\nfor (const fn of collectFunctions(shaderCode)) {\n  const used = collectIdentifiers(fn.body).filter(id => uniformNames.has(id));\n  const params = new Set(fn.params);\n  const offending = used.filter(u => !params.has(u));\n  if (offending.length) throw new Error(`Helpers must receive uniforms as params: ${offending}`);\n}","typeGuard":"null","tryCatchPattern":"try {\n  await SmartFilterOptimizer.OptimizeAsync(engine, filter, texture);\n} catch (e) {\n  if (e.message.includes(\"accesses uniform\")) {\n    // parse uniform names from the message and fix the block's GLSL\n  } else throw e;\n}","preventionTips":["Write block helper functions to take every uniform they need as an explicit parameter.","Match helper parameter names exactly to the uniform names so the optimizer can exclude them.","Lint custom GLSL for direct uniform references inside function bodies before shipping blocks."],"tags":["optimization","glsl","shader-code","uniforms"],"backgroundTag":"uniform-accessed-in-helper-function","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}