Can You Use Recursion in GLSL Shaders?

The OpenGL Shading Language (GLSL) strictly forbids recursion in all shader functions, whether direct or indirect. This article explores the official GLSL specifications prohibiting recursive function calls, the hardware and architectural limitations of graphics processing units (GPUs) that necessitate this rule, how shader compilers enforce the restriction, and practical iterative techniques developers use to implement recursive-style algorithms.

The GLSL Specification on Recursion

The core GLSL specification across virtually all versions explicitly defines recursion as illegal. Under the function calling rules of the specification, functions may call other functions, but a function cannot call itself directly, nor can it call a sequence of other functions that ultimately leads back to itself.

// Illegal: Direct recursion
int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1); // Compilation error
}

// Illegal: Indirect recursion
void functionB();

void functionA() {
    functionB(); // Compilation error: cycle detected
}

void functionB() {
    functionA();
}

If a GLSL compiler detects any recursive invocation path, it is required to reject the shader and emit a compile-time or link-time error.

Architectural Reasons for Disallowing Recursion

The prohibition of recursion is rooted in the physical design and execution model of graphics hardware:

How Compilers Detect Recursion

GLSL compilers analyze shader source code by constructing a static call graph where each node represents a function and each directed edge represents a function call.

Before generating intermediate representation (such as SPIR-V) or native GPU machine code, the compiler performs cycle detection algorithms (such as Tarjan's or depth-first search) on the call graph. If a cycle exists within the directed graph, the compiler flags the cycle as recursive and halts compilation.

Common Workarounds for Recursive Algorithms

Certain algorithms—such as ray marching, bounding volume hierarchy (BVH) traversal, tree parsing, and fractal rendering—are naturally recursive. Developers translate these algorithms into non-recursive GLSL constructs through several established patterns.

1. Iterative Loops

Algorithms that follow tail recursion can be converted directly into iterative loops using standard for or while statements.

int factorialIterative(int n) {
    int result = 1;
    for (int i = 2; i <= n; ++i) {
        result *= i;
    }
    return result;
}

2. Explicit Fixed-Size Emulated Stacks

For non-linear traversals, such as navigating a binary tree or BVH during ray tracing, developers allocate a fixed-size local array to serve as an explicit stack.

#define STACK_CAPACITY 32

void traverseHierarchy(int rootNodeIndex) {
    int stack[STACK_CAPACITY];
    int stackPointer = 0;

    // Push root
    stack[stackPointer++] = rootNodeIndex;

    while (stackPointer > 0) {
        // Pop current node
        int currentNode = stack[--stackPointer];

        // Process node and push child nodes if capacity allows
        int leftChild = getLeftChild(currentNode);
        int rightChild = getRightChild(currentNode);

        if (rightChild != -1 && stackPointer < STACK_CAPACITY) {
            stack[stackPointer++] = rightChild;
        }
        if (leftChild != -1 && stackPointer < STACK_CAPACITY) {
            stack[stackPointer++] = leftChild;
        }
    }
}

3. State-Based State Machines

Complex recursive logic can also be structured as an explicit finite-state machine contained inside a single while loop, where an index or enum variable manages transitions between conceptual call frames.