BabylonJS/Babylon.js · error

Helper function ${funcName} in blockType ${block.blockType}

Error message

Helper function ${funcName} in blockType ${block.blockType} accesses uniform(s) ${uniformsAccessed.join(", ")} which is not supported. Pass them in instead.

What it means

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.

Source

Thrown at packages/dev/smartFilters/src/optimization/smartFilterOptimizer.ts:360

            for (const remappedSymbol of this._remappedSymbols) {
                if (
                    remappedSymbol.type === "uniform" &&
                    remappedSymbol.owners[0] &&
                    remappedSymbol.owners[0].blockType === block.blockType &&
                    func.code.includes(remappedSymbol.remappedName)
                ) {
                    uniformsAccessed.push(remappedSymbol.remappedName);
                }
            }

            // Strip out any matches which are actually function params
            const functionParams = func.params ? func.params.split(",").map((p) => p.trim().split(" ")[1]) : [];
            uniformsAccessed = uniformsAccessed.filter((u) => !functionParams.includes(u));

            // If it accessed any uniforms, throw an error
            if (uniformsAccessed.length > 0) {
                uniformsAccessed = uniformsAccessed.map((u) => (u[0] === DecorateChar ? UndecorateSymbol(u) : u));
                throw new Error(
                    `Helper function ${funcName} in blockType ${block.blockType} accesses uniform(s) ${uniformsAccessed.join(", ")} which is not supported. Pass them in instead.`
                );
            }

            // Look to see if we have an exact match including parameters of this function in the list of remapped symbols
            const existingFunctionExactOverload = this._remappedSymbols.find(
                (s) => s.type === "function" && s.name === funcName && s.params === func.params && s.owners[0] && s.owners[0].blockType === block.blockType
            );

            // Look to see if we already have this function in the list of remapped symbols, regardless of parameters
            const existingFunction = this._remappedSymbols.find((s) => s.type === "function" && s.name === funcName && s.owners[0] && s.owners[0].blockType === block.blockType);

            // Get or create the remapped name, ignoring the parameter list
            let remappedName = existingFunction?.remappedName;
            let createdNewName = false;
            if (!remappedName) {
                remappedName = DecorateSymbol(this._makeSymbolUnique(funcName));
                createdNewName = true;

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Rewrite the helper function to take the uniform value as a parameter and pass it at each call site.
  2. Move the uniform read outside the helper into the main function body.
  3. Verify the helper's parameter names match the uniform names exactly (params are parsed from the signature) so they are correctly excluded.
  4. Undecorate/decorate handling: ensure uniform names after DecorateChar processing match the declared parameters.

Example fix

// before (GLSL)
float brighten(vec2 uv) { return texture2D(uTexture, uv).r * uIntensity; }
// after
float brighten(vec2 uv, float intensity) { return texture2D(uTexture, uv).r * intensity; }
// call: brighten(uv, uIntensity);
Defensive patterns

Strategy: validation

Validate before calling

// Static check on block GLSL: helper functions must not reference uniform names not in their params
const uniformNames = new Set(collectUniforms(shaderCode));
for (const fn of collectFunctions(shaderCode)) {
  const used = collectIdentifiers(fn.body).filter(id => uniformNames.has(id));
  const params = new Set(fn.params);
  const offending = used.filter(u => !params.has(u));
  if (offending.length) throw new Error(`Helpers must receive uniforms as params: ${offending}`);
}

Type guard

null

Try / catch

try {
  await SmartFilterOptimizer.OptimizeAsync(engine, filter, texture);
} catch (e) {
  if (e.message.includes("accesses uniform")) {
    // parse uniform names from the message and fix the block's GLSL
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/b1a9f6851681387a. Report an issue: GitHub.