Compiler Spec¶
The Noisemaker Rendering Pipeline compiler is responsible for transforming high-level Polymorphic DSL code into an executable GPU Render Graph. It bridges the gap between the user’s intent (DSL) and the machine’s execution model (Pipeline).
Compilation Pipeline¶
The compilation process occurs in four distinct stages:
Parsing: Source Code → Abstract Syntax Tree (AST)
Analysis: AST → Logical Graph (Effect Chain)
Expansion: Logical Graph → Render Graph (Passes)
Assembly: Render Graph → Execution Plan (Linear Pass Schedule)
graph TD
A[Source Code] -->|Lexer/Parser| B[AST]
B -->|Semantic Analyzer| C[Logical Graph]
C -->|Effect Expander| D[Render Graph]
D -->|Resource Allocator| E[Execution Plan]
Stage 1: Parsing¶
The parser converts the raw string input into a structured tree representation.
Input:
string(e.g.,osc(10).write(o0))Output:
ProgramNode(AST Root)
1.1 Lexical Analysis¶
The lexer tokenizes the input, handling:
Identifiers (
osc,o0)Literals (
10,#ff0000,"string")Operators (
.,(,))Comments (
//,/* ... */)Special Tokens:
OUTPUT_REF(o0),HEX(#ff0000).Keywords:
out,render,let,if, etc.
1.2 Syntax Analysis¶
The parser constructs the AST based on the grammar defined in Polymorphic DSL. Unlike traditional ESTree-like structures, the Polymorphic parser produces a specialized AST optimized for the pipeline’s needs.
Example AST for ``osc(10).write(o0)``:
{
"type": "Program",
"plans": [ // Corresponds to ChainStmt in grammar
{
"chain": [
{
"type": "Call",
"name": "osc",
"args": [{ "type": "Number", "value": 10 }]
}
],
"out": { "type": "OutputRef", "name": "o0" }
}
],
"vars": [],
"render": null
}
Key Structural Differences:
Flat Chains: Chains are represented as a flat array of
Callnodes, not nestedCallExpressionobjects.Explicit Output: The
.write()directive is parsed separately from the chain and stored in theoutproperty of the statement.Separated State: Variable assignments (
vars) and render instructions (plans) are segregated at the root level.plansis an array ofChainStmtnodes.
Stage 2: Analysis (AST → Logical Graph)¶
This stage resolves symbols, validates types, and constructs a high-level graph of Effect instances.
Input:
ProgramNodeOutput:
LogicalGraph(Nodes = Effects, Edges = Data Flow)
2.1 Symbol Resolution¶
Search Order Resolution: The
searchdirective (if present) defines the namespace search order for the program. If omitted, the default order['synth', 'filter']is used.Namespace Lookup: Resolves function names (e.g.,
osc) to Effect Definitions by walking the search order until a match is found.Variable Scope: Tracks
letassignments and resolves variable references.
2.2 Chain Analysis¶
Since the AST already represents chains as flat arrays, the analyzer iterates sequentially through the chain list.
Root Identification: The first element of the
chainarray is identified as the generator or source.Instance Creation: Instantiates the
Effectclass for eachCallnode.Parameter Binding:
Validates arguments against the Effect’s
globalsschema.Resolves named arguments (
freq: 10) vs positional (10).Coerces types (e.g.,
int→float).
2.3 Logical Graph Construction¶
Nodes are created for each effect instance. Edges are created to represent the flow of the outputColor from one effect to the inputColor of the next.
Logical Node Structure:
{
id: "node_1",
effect: "Oscillator", // Reference to Definition
params: { freq: 10 },
inputs: { inputColor: null }, // Generator has no input
outputs: { outputColor: "node_1_out" }
}
Stage 3: Expansion (Logical Graph → Render Graph)¶
This stage lowers the high-level Effects into their constituent GPU Passes.
Input:
LogicalGraphOutput:
RenderGraph(Nodes = Passes, Edges = Texture Dependencies)
3.1 Pass Expansion¶
For each Logical Node, the compiler looks up the passes array in the Effect Definition.
Texture Allocation:
Resolves internal textures (e.g.,
downsampled).Creates implicit
inputColorandoutputColortextures if not explicitly defined.Calculates dimensions based on screen size and relative specifiers (e.g.,
50%).
Pass Generation:
Creates a Render Pass Node for each entry in
passes.Maps logical texture names to unique resource IDs (e.g.,
tex_node1_downsampled).Injects
definesbased on static parameters.
3.2 Shader Program Compilation¶
This is where the “Single Effect → GPU Program” transformation happens.
Source Resolution: Locates the shader file (
.glslor.wgsl) based on theprogramkey.Define Injection: Prepend
#definestatements for static configuration.Backend Transpilation (WebGL only):
For Compute passes, wraps the logic in a full-screen quad vertex shader.
Generates the fragment shader boilerplate (uniform declarations).
Example: Compute Pass Expansion (WebGL)
Input: User GLSL snippet.
Output: Full Fragment Shader.
#version 300 es precision highp float; uniform float u_time; // ... injected uniforms ... out vec4 fragColor; void main() { // ... user code ... }
Stage 4: Assembly (Render Graph → Executable Pipeline)¶
The final stage prepares the graph for execution by the runtime.
Input:
RenderGraphOutput:
ExecutionPlan(Sorted List of Commands)
4.1 Topological Sort¶
Orders the passes so that all dependencies are satisfied before a pass is executed.
Algorithm: Kahn’s Algorithm.
Cycle Detection: Identifies feedback loops. If a loop is found, it must be broken by a
persistenttexture (reading from the previous frame).
4.2 Resource Optimization¶
Performs liveness analysis to minimize VRAM usage.
Liveness Interval: Calculates
[first_write, last_read]for each texture.Pooling: Assigns physical GPU textures from a shared pool to virtual texture IDs. Two virtual textures with non-overlapping intervals can share the same physical texture.
4.3 Command Generation¶
Generates the linear list of commands for the GPU driver.
SetGlobal(time)BindTexture(unit=0, tex=pool_A)BindProgram(prog_Bloom)Draw()
Error Codes & Stages¶
The following table maps error codes to the compilation stage where they are raised.
Code |
Stage |
Description |
|---|---|---|
|
Parser |
Malformed DSL syntax (unexpected token, missing brace) |
|
Analysis |
Identifier not found in scope |
|
Analysis |
Argument type mismatch (e.g. string passed to float) |
|
Validation |
Effect definition violates JSON schema |
|
Validation |
Duplicate pass name in Effect definition |
|
Validation |
Input/output references unknown texture/surface |
|
Validation |
Ping-pong texture undeclared |
|
Validation |
Iterative pass missing pingpong or self-read unsafe |
|
Assembly |
Cyclic dependency detected in Render Graph |
|
Validation |
Compute feature not emulatable on WebGL |
|
Assembly |
Viewport out of target bounds |
|
Assembly |
Workgroup size exceeds device limits |
|
Assembly |
Uniform value invalid/coercion failed |
|
Assembly |
Multiple writes to same surface without extension |
|
Validation |
Multi-render-target compute emulation unsupported |
|
Runtime |
Attempted GPU-to-CPU readback within frame |
|
Assembly |
Exceeded maximum texture units for backend |
|
Assembly |
Texture dimension spec invalid |
|
Assembly |
Texture format not supported by backend |
|
Backend |
Shader compilation failed (driver error) |
|
Backend |
Shader program linking failed (driver error) |
|
Expansion |
Non-generator effect missing input |
|
Validation |
Unknown enum string provided |
|
Validation |
Invalid pass condition entry |
|
Analysis |
Break/continue in invalid context |