Skip to main content

GLRE Reactive GPU Engine Design Specification

Overviewโ€‹

GLRE is a reactive engine that bridges CPU-side JavaScript code with GPU-side shader code. It supports both WebGL2 and WebGPU, allowing direct GLSL or WGSL shader authoring as well as shader generation through a TypeScript-like node system.

Architecture Compositionโ€‹

CPU-GPU Binding Automation Systemโ€‹

The core functionality is to automatically construct configurations for transmitting CPU-side data to GPU-side shader programs. This process is managed by the createGL function in src/index.ts, which abstracts the differences between WebGL2 and WebGPU.

The createGL function uses the reev library's event function to generate a reactive GL instance. This instance automatically queues uniform, attribute, and texture data settings and transfers them to the GPU at timing.

WebGL2 Backend Implementationโ€‹

webgl.ts implements an OpenGL ES 3.0-based rendering pipeline. This implementation adopts a functional approach rather than class-based, leveraging closures to manage private state.

After initializing the shader program from the WebGL context, uniform and attribute locations are cached using nested functions. The render, clean functions, and configuration functions (_uniform, _attribute, _texture) are returned as values, which are then merged into the original gl object via reev's event function.

WebGPU Backend Implementationโ€‹

webgpu.ts implements a rendering pipeline using the WebGPU API. Unlike the WebGL backend, pipeline initialization is executed during the render phase.

This is because buffer and bindGroup layouts need to be constructed after attribute and uniform settings are added to gl.queue and processed through flush operations. The cached function is used to cache uniforms, textures, and attribs data, and GPU resources are constructed using createVertexBuffers, createBindGroup, and createPipeline functions based on this information.

Node System Detailsโ€‹

Abstract Syntax Tree Construction Mechanismโ€‹

The node system is a DSL (Domain Specific Language) for generating GLSL/WGSL shader code from JavaScript code. The create function in create.ts constructs abstract syntax trees with argument formats identical to React.createElement.

NodeProxy objects construct dynamic abstract syntax trees through getter/setter methods using the Proxy pattern. They have type, props, and children properties, and these relationships are analyzed by code function and converted to shader code.

Abstract Syntax Tree Example:
operator(+)
/ \
uniform(a) uniform(b)

Code Generation Processโ€‹

code function provides core functionality for converting abstract syntax trees to string-based shader code. This function performs conditional branching based on type and generates strings compatible with both WebGL (GLSL) and WebGPU (WGSL).

The generation process is classified into three categories: variables, scopes, and headers. Variables handle basic operator one-liner generation, scopes manage multi-line processing, and headers contain definitions added to the beginning of the entire shader.

Through NodeContext, post-processing required after completing builds of all nodes is managed, such as struct integration of uniforms and topological sorting of dependencies.

Type Inference Systemโ€‹

infer.ts implements functionality for inferring shader types from node trees. This is not TypeScript type inference, but a system that determines types for GLSL/WGSL generation at JavaScript runtime.

Type inference determines return types based on operators, functions, and variables. For example, comparison operators always return bool, and operations between vec types return vec types of dimensions.

Scope Management Systemโ€‹

scope.ts provides functionality for generating multi-line code. It manages current scope context using let scope and let define variables.

Lines are added to specified scopes via the addToScope function, and the scoped function temporarily switches to specific scopes to execute processing. Code structures like If, Loop, and Switch use this mechanism to generate nested code.

GPU Resource Managementโ€‹

Pipeline Construction Automationโ€‹

pipeline.ts provides resource management functionality for WebGPU. The createBindings function automatically assigns group and binding numbers for uniform, texture, and attribute resources.

This number assignment must match between shader code generation and GPU resource creation, so it's referenced by parseUniformHead and parseAttribHead functions in parse.ts.

Buffer Management Systemโ€‹

The createUniformBuffer and createAttribBuffer functions generate GPU-ready Float32Array and GPUBuffer from JavaScript number arrays. Uniform buffers are aligned to 256-byte boundaries, and attribute buffers are created with sizes based on vertex data stride.

Rendering Pipeline Integrationโ€‹

Shader Compilation Flowโ€‹

webgl.ts and webgpu.ts call fragment and vertex functions to generate string-format shader code. These functions are defined in node/index.ts and manage conversion from NodeProxy to final GLSL/WGSL code.

Generated shader code is initialized as GPU programs via the createProgram function for WebGL and createPipeline function for WebGPU.

Reactive Update Mechanismโ€‹

Using reev library's durable function, uniform, attribute, and texture configuration functions are queued. This ensures that value changes from JavaScript code are automatically reflected on the GPU.

Update cycles are triggered by gl.queue.flush(), sending accumulated changes to the GPU in batches. This approach achieves efficient updates per rendering frame.

Implementation Guidelinesโ€‹

Node System Usage Considerationsโ€‹

When using the node system, generate NodeProxy objects from factory functions in node.ts (uniform, attribute, constant, etc.) and combine them to construct abstract syntax trees.

Use the toVar() method for variable declarations and the assign() method for value assignments. Use the Fn() function to define custom functions and explicitly specify argument and return types with the setLayout() method.

Direct Shader Code Usage Guidelinesโ€‹

When writing GLSL or WGSL directly without using the node system, pass them as strings to the vs and fs properties of createGL. Even in this case, uniform, attribute, and texture configurations are performed through methods of the GL instance.

Uniform names and attribute names used in shader code must match those on the JavaScript side. For WebGPU, group and binding numbers must also be configured.

Design Principles for Error Avoidanceโ€‹

Understanding the design patterns of NodeProxy children arrays is important. For example, in If nodes, conditions and scopes are arranged alternately, and when the last element is Else, it's placed without conditions.

When defining structs, topological sorting of dependencies is executed, so designs that avoid circular references are necessary. Since uniform group/binding numbers are automatically assigned, manual specification is discouraged.

TypeScript Coding Style Guide

Core Design Philosophyโ€‹

Functional Programming Implementationโ€‹

Use closure-based constructor patterns instead of class keywords. This provides private variable access control and state management.

Complete initialization at the beginning of constructor functions, define private variables and methods, then return only necessary elements as public APIs. This maintains encapsulation principles while utilizing JavaScript characteristics.

TypeScript Type Safety Applicationโ€‹

Use TypeScript as type hints, avoiding code complexity from elaborate type definitions. Maintain JavaScript as the primary language while gaining type inference benefits, balancing implementation flexibility with development efficiency.

When complex types are needed, temporarily use leverage the as keyword for type bridging. Prioritize implementation simplicity over type safety to reduce cognitive load and focus on problem-solving.

Code Structure and File Organizationโ€‹

File Division Principlesโ€‹

Target approximately 100 lines per file to clarify responsibility boundaries. File names should be determinable with single words; multiple words indicate the need for directory structure revision.

Adopt the pattern of types.ts, const.ts, config.ts, index.ts as the file structure. This configuration makes each file's role self-evident and unifies developer understanding.

Naming Conventions and Consistencyโ€‹

Variable and Function Naming Patternsโ€‹

Use camelCase for lowercase-starting identifiers, with boolean variables beginning with the is prefix. Avoid the has prefix to express state clearly. Function names start with verbs to indicate their functionality.

Use PascalCase for uppercase-starting identifiers, applying to type and interface definitions. Prioritize interface usage, emphasizing extends compatibility. Use the type keyword only when interface cannot define constructs like typeof operators or union types.

File Names and Directory Structureโ€‹

Avoid camelCase in file names, composing them with simple words. Determine package subdirectories considering convenience when used as import paths.

Formatting Specificationsโ€‹

Structure Enforcement Through Indentationโ€‹

Enhance code structure visualization with 8-character space indentation. This configuration makes 3+ level indentation difficult to read, naturally promoting function division and refactoring.

Ensure adequate length with 120-character line width limits and achieve code conciseness with semicolon-free settings. Unify string notation through single quote usage.

Conditional Statement Writingโ€‹

Define complex conditions as boolean variables beforehand to clarify conditional intent. Aggregate conditions like const isValid = condition1 && condition2 and utilize early returns in if statements as if (isValid) return.

Implementation Pattern Unificationโ€‹

Closure-Based Constructor Patternโ€‹

Adopt the form const createInstance = (args) => {} as constructor functions, ompleting initialization at the function's beginning. Define private variables with let or const and return public APIs as objects at the end.

good: const createRenderer = (config) => { const context = initContext(config); const render = () => context.draw(); return { render } }

Branching Pattern Unificationโ€‹

Implement branching logic using if-return patterns instead of switch keywords. This pattern prevents conditional complexity and maintains independence of each branch.

good: const getFormat = (count) => { if (count === 2) return 'vec2'; if (count === 3) return 'vec3'; return 'float' }

Emphasis on Symmetryโ€‹

Maintain structural symmetry between related functions and modules. Apply consistent patterns for paired operations like input-output, initialization-destruction, and setting-getting to improve system comprehensibility.

Type Definitions and Constraintsโ€‹

Type Safety Implementationโ€‹

Prioritize interface definitions to clarify object structure. Utilize union type enumeration expressions and avoid enum keyword usage.

Function Definition Unificationโ€‹

Use const arrow functions instead of function declarations. This unification clarifies function characteristics and prevents unintended behavior from hoisting.

Error Handling and Logging Restrictionsโ€‹

Simple Implementation Maintenanceโ€‹

Avoid try-catch constructs and console.log usage to preserve code simplicity. When error handling is necessary, utilize function return values or Promise rejection to encourage proper handling at call sites. To avoid implementation complexity, refrain from excessive type safety checks and emphasize runtime flexibility.

Import and Export Structureโ€‹

Module Management Optimizationโ€‹

Arrange import statements in the order of external libraries, project helpers, utilities, and type definitions.

Group type imports at the end, clearly separating them from value imports. Minimize default export usage and emphasize explicit API design through named exports.

Function Internal Structuringโ€‹

Line Spacing Managementโ€‹

Minimize line insertion within functions to increase processing density. Place const declarations closely together and open spacing when transitioning to let to visually represent variable role changes.

Establish clear spacing between functions to emphasize each function's independence. This management enables code placement within the 100-line constraint.

Node System Language Specification

Fundamental Conceptsโ€‹

The GLRE node system is a domain-specific language for generating GLSL/WGSL shader code from JavaScript. This system provides a mechanism for constructing abstract syntax trees and converting them to target shader languages.

JavaScript DSL        Abstract Syntax Tree    Shader Code
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚vec4(1,0,0,1)โ”‚ โ”€โ”€โ”€โ†’ โ”‚ NodeProxy โ”‚ โ”€โ”€โ”€โ†’ โ”‚vec4(1,0,0,1)โ”‚
โ”‚.mul(2.0) โ”‚ โ”‚ children[] โ”‚ โ”‚* 2.0 โ”‚
โ”‚.add(pos) โ”‚ โ”‚ type/props โ”‚ โ”‚+ position โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Node Construction Systemโ€‹

NodeProxy Architectureโ€‹

NodeProxy objects implement method chaining and dynamic property access using the Proxy pattern. Each NodeProxy has a structure of type, props, and children, which combine to form abstract syntax trees.

NodeProxy Structure:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ NodeProxy โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ type: string โ”‚ โ”€โ”€ Node type
โ”‚ props: object โ”‚ โ”€โ”€ Properties
โ”‚ children: [] โ”‚ โ”€โ”€ Child nodes array
โ”‚ listeners: Set โ”‚ โ”€โ”€ Event listeners
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The getter determines operator, function, conversion, and swizzling operations based on property names and returns NodeProxies. The setter manages value updates using the listener pattern.

Abstract Syntax Tree Structureโ€‹

Abstract syntax trees are binary tree structures but support variable-length child nodes using the children property. This enables expression of function arguments and conditional branches in If statements.

Function Call Example:
function_node
/ | \
func_name arg1 arg2

Type System and Conversionsโ€‹

Basic Type Definitionsโ€‹

The type system provides shader types: float, int, bool, vec2, vec3, vec4, mat2, mat3, and mat4. Each type is generated through factory functions and inferred through type inference.

TypeFactory FunctionDescriptionGLSL/WGSL Mapping
floatfloat(value)32-bit floating pointfloat / f32
intint(value)32-bit integerint / i32
boolbool(value)Boolean valuebool / bool
vec2vec2(x, y)2D vectorvec2 / vec2<f32>
vec3vec3(x, y, z)3D vectorvec3 / vec3<f32>
vec4vec4(x, y, z, w)4D vectorvec4 / vec4<f32>
mat2mat2(...)2x2 matrixmat2 / mat2x2<f32>
mat3mat3(...)3x3 matrixmat3 / mat3x3<f32>
mat4mat4(...)4x4 matrixmat4 / mat4x4<f32>

Type Conversion Methodsโ€‹

Method NameReturn TypeDescription
.toFloat()floatConvert to floating point
.toInt()intConvert to integer
.toBool()boolConvert to boolean
.toVec2()vec2Convert to 2D vector
.toVec3()vec3Convert to 3D vector
.toVec4()vec4Convert to 4D vector
.toColor()vec3Convert to color
.toMat2()mat2Convert to 2x2 matrix
.toMat3()mat3Convert to 3x3 matrix
.toMat4()mat4Convert to 4x4 matrix

Type conversions support explicit conversion functions (toFloat(), toVec3(), etc.) and automatic type promotion. In operations between float and vec3, float is broadcast to vec3.

Type Inference Mechanismโ€‹

The type inference engine analyzes node trees at runtime to determine return types for each node. For operator type inference, it compares type priorities of left and right operands and selects the higher priority type as the result type.

Comparison operators return bool type. Logical operators return bool type. In operations between vector types, higher-dimensional vector types take priority.

Operator Systemโ€‹

Arithmetic Operatorsโ€‹

Operator MethodSymbolDescriptionReturn Type
.add(x)+AdditionHigher priority type
.sub(x)-SubtractionHigher priority type
.mul(x)*MultiplicationHigher priority type
.div(x)/DivisionHigher priority type
.mod(x)%ModuloHigher priority type

Comparison Operatorsโ€‹

Operator MethodSymbolDescriptionReturn Type
.equal(x)==Equalitybool
.notEqual(x)!=Inequalitybool
.lessThan(x)<Less thanbool
.greaterThan(x)>Greater thanbool
.lessThanEqual(x)<=Less than or equalbool
.greaterThanEqual(x)>=Greater than or equalbool

Logical Operatorsโ€‹

Operator MethodSymbolDescriptionReturn Type
.and(x)&&Logical ANDbool
.or(x)||Logical ORbool
.not()!Logical NOTbool
.xor(x)^^Logical XORbool

Bitwise Operatorsโ€‹

Operator MethodSymbolDescriptionReturn Type
.bitAnd(x)&Bitwise ANDInput type
.bitOr(x)|Bitwise ORInput type
.bitXor(x)^Bitwise XORInput type
.bitNot()~Bitwise NOTInput type
.shiftLeft(x)<<Left shiftInput type
.shiftRight(x)>>Right shiftInput type

Assignment Operatorsโ€‹

| Operator Method | Symbol | Description | Return Type | | ---------------------- | ------ | ------------------------- | --------------------- | ---------- | | .addAssign(x) | += | Addition assignment | Input type | | .subAssign(x) | -= | Subtraction assignment | Input type | | .mulAssign(x) | *= | Multiplication assignment | Input type | | .divAssign(x) | /= | Division assignment | Input type | | .modAssign(x) | %= | Modulo assignment | Input type | | .bitAndAssign(x) | &= | Bitwise AND assignment | Input type | | .bitOrAssign(x) | | = | Bitwise OR assignment | Input type | | .bitXorAssign(x) | ^= | Bitwise XOR assignment | Input type | | .shiftLeftAssign(x) | <<= | Left shift assignment | Input type | | .shiftRightAssign(x) | >>= | Right shift assignment | Input type |

Mathematical Function Libraryโ€‹

Trigonometric Functionsโ€‹

FunctionDescriptionArgument TypeReturn Type
sin(x)SineScalar/VectorInput type
cos(x)CosineScalar/VectorInput type
tan(x)TangentScalar/VectorInput type
asin(x)ArcsineScalar/VectorInput type
acos(x)ArccosineScalar/VectorInput type
atan(x)ArctangentScalar/VectorInput type
atan2(y, x)Two-argument arctangentScalar/VectorHigher priority type

Exponential Functionsโ€‹

FunctionDescriptionArgument TypeReturn Type
pow(x, y)PowerScalar/VectorHigher priority type
pow2(x)SquareScalar/VectorInput type
pow3(x)CubeScalar/VectorInput type
pow4(x)Fourth powerScalar/VectorInput type
sqrt(x)Square rootScalar/VectorInput type
inverseSqrt(x)Inverse square rootScalar/VectorInput type
exp(x)Natural exponentialScalar/VectorInput type
exp2(x)Base-2 exponentialScalar/VectorInput type
log(x)Natural logarithmScalar/VectorInput type
log2(x)Base-2 logarithmScalar/VectorInput type

Common Mathematical Functionsโ€‹

FunctionDescriptionArgument TypeReturn Type
abs(x)Absolute valueScalar/VectorInput type
sign(x)SignScalar/VectorInput type
floor(x)FloorScalar/VectorInput type
ceil(x)CeilingScalar/VectorInput type
round(x)RoundScalar/VectorInput type
fract(x)Fractional partScalar/VectorInput type
trunc(x)TruncateScalar/VectorInput type
min(x, y)MinimumScalar/VectorHigher priority type
max(x, y)MaximumScalar/VectorHigher priority type
clamp(x, min, max)ClampScalar/VectorHigher priority type
saturate(x)Saturate (0-1)Scalar/VectorInput type
mix(x, y, a)Linear interpolationScalar/VectorHigher priority type
step(edge, x)Step functionScalar/VectorHigher priority type
smoothstep(edge0, edge1, x)Smooth stepScalar/VectorHigher priority type

Vector Functionsโ€‹

FunctionDescriptionArgument TypeReturn Type
length(x)Vector lengthVectorfloat
distance(x, y)DistanceVectorfloat
dot(x, y)Dot productVectorfloat
cross(x, y)Cross productvec3vec3
normalize(x)NormalizeVectorInput type
reflect(I, N)ReflectionVectorInput type
refract(I, N, eta)RefractionVectorInput type

Derivative Functionsโ€‹

FunctionDescriptionArgument TypeReturn Type
dFdx(x)X-direction derivativeScalar/VectorInput type
dFdy(x)Y-direction derivativeScalar/VectorInput type
fwidth(x)Derivative widthScalar/VectorInput type

Utility Functionsโ€‹

FunctionDescriptionArgument TypeReturn Type
oneMinus(x)One minus xScalar/VectorInput type
negate(x)NegateScalar/VectorInput type
reciprocal(x)ReciprocalScalar/VectorInput type

Swizzling Operationsโ€‹

Vector Component Accessโ€‹

Vector type NodeProxies recognize swizzling operations like .xyz, .rgb, .stpq. These operations are represented as member nodes with dimensional return types.

PatternDescriptionUsage Examples
xyzwPosition coordinatesvec.xyz, vec.xy
rgbaColor componentscolor.rgb, color.rg
stpqTexture coordinatesuv.st, uv.s
Swizzling Example:
vec4(1,2,3,4) โ”€โ”ฌโ”€ .xyz โ”€โ”€โ†’ vec3(1,2,3)
โ”œโ”€ .xy โ”€โ”€โ†’ vec2(1,2)
โ”œโ”€ .w โ”€โ”€โ†’ float(4)
โ””โ”€ .rgba โ”€โ”€โ†’ vec4(1,2,3,4)

Variable and Scope Managementโ€‹

Variable Declaration Systemโ€‹

The toVar() method converts expression nodes to variable declaration nodes. This process generates variable IDs and adds variable declaration statements to scopes through declare type nodes.

Factory Function Groupsโ€‹

FunctionPurposeGenerated NodeDescription
attribute(value, id)Vertex attributesattributeVertex data reception
uniform(value, id)UniformsuniformCPU-GPU data transfer
constant(value, id)ConstantsconstantCompile-time constants
variable(id)VariablesvariableLocal variables
builtin(id)BuiltinsbuiltinShader builtin variables
varying(node, id)VaryingsvaryingVertex-fragment data

Scope Operationsโ€‹

MethodDescriptionReturn Value
.toVar(name)VariablizeVariable node
.assign(value)AssignmentAssignment node

Scope Hierarchy Structureโ€‹

The scope system manages context using scope and define variables. The scoped() function switches scopes to execute processing within scopes.

Scope Hierarchy:
Global Scope
โ”œโ”€โ”€ Function Scope
โ”‚ โ”œโ”€โ”€ If Scope
โ”‚ โ”œโ”€โ”€ Loop Scope
โ”‚ โ””โ”€โ”€ Switch Scope
โ””โ”€โ”€ Struct Scope

The addToScope() function adds nodes to the current scope. For return statements, they are added to the define variable's inferFrom property for function return type inference.

Control Structuresโ€‹

Conditional Branchingโ€‹

The If() function receives conditional expressions and callback functions to construct if statement node structures. Conditional branches can be expressed by chaining ElseIf() and Else() methods.

Control StructureSyntaxDescription
If(condition, callback)if statementStart conditional
.ElseIf(condition, callback)else if statementAdditional condition
.Else(callback)else statementDefault case

Loop Structuresโ€‹

Loop TypeSyntaxDescription
Loop(count, callback)for loopCount-based loop
Loop(condition, callback)while loopCondition-based loop
Break()break statementExit loop
Continue()continue statementContinue loop

The Loop() function defines iteration counts and iteration processing. Within iteration processing, access to generated loop variable i is available for array access and computation.

Switch Statementsโ€‹

Control StructureSyntaxDescription
Switch(value)switch statementMulti-branch start
.Case(value, callback)case statementValue-based branch
.Default(callback)default statementDefault case

Conditional Operatorsโ€‹

The select() function provides ternary operator functionality. It receives conditional expressions, true values, and false values to generate conditional branch nodes.

In the children array, conditional expressions and scopes are arranged alternately, with scopes placed without conditional expressions for the Else clause. This arrangement enables determination of conditional branch structures during code generation.

select(value if condition is false, value if condition is true, condition boolean)
// or
(value if condition is false).select(value if condition is true, condition boolean)

Function Definition Systemโ€‹

Fn() Function Detailsโ€‹

The Fn() function provides mechanisms for defining shader functions. Processing defined within callback functions is constructed as function nodes with scopes.

Function Definition Structure:
define_node
/ \
scope layout_info
/ | \
name inputs return_type

Layout Specificationโ€‹

PropertyTypeDescription
namestringFunction name
typeConstantsReturn type
inputsArrayInput specification array

Parameter Processingโ€‹

Function parameters follow layout specifications when provided, or are type-inferred from arguments when not specified. Each parameter is generated as variable nodes and becomes accessible within function scopes.

The setLayout() method enables specification of function names, return types, and argument lists. This improves type safety and readability.

Struct Systemโ€‹

Struct Definitionโ€‹

The struct() function provides two-stage factories for field definition and instance generation. Field definition specifies names and types for each field, while instance generation allows setting initial values.

Struct Definition Flow:
struct(fields) โ”€โ”€โ†’ Factory โ”€โ”€โ†’ (initialValues) โ”€โ”€โ†’ Instance
โ†“ โ†“
Field Specification Initialized Struct

Struct definitions are topologically sorted through the dependencies system, generating header code in dependency order.

Member Accessโ€‹

Struct member access is represented through member nodes. When field names exist in the struct definition's fields property, that field's type is inferred.

Uniforms and Attributesโ€‹

Uniform Variablesโ€‹

The uniform() function defines uniform variables for transferring data from CPU to GPU. Generated NodeProxies set listeners to monitor value changes, enabling reactive updates.

Uniform TypeJavaScript TypeGLSL TypeWGSL Type
Scalarnumberfloatf32
Vectornumber[]vec2/3/4vec2/3/4<f32>
Matrixnumber[]mat2/3/4mat2x2/3x3/4x4<f32>
Texturestringsampler2Dtexture_2d<f32>

Uniform variable types are inferred from initial values or specified through type conversion nodes. Processing passes are executed for texture types.

Attribute Variablesโ€‹

The attribute() function defines attribute variables for receiving vertex data. Stride is calculated from array length and vertex count, with type inference performed.

Attribute TypeStrideGLSL TypeWGSL Type
1 component1floatf32
2 components2vec2vec2<f32>
3 components3vec3vec3<f32>
4 components4vec4vec4<f32>

Built-in Variablesโ€‹

The builtin() function generates nodes for accessing built-in variables like position and normal. These variables are values provided at each stage of the rendering pipeline.

Built-in VariableDescriptionTypeAvailable Stage
positionVertex positionvec4Vertex/Fragment
normalNormal vectorvec3Fragment
uvTexture coordinatesvec2Fragment
colorVertex colorvec4Fragment

Texture Samplingโ€‹

Texture Functionsโ€‹

The texture() function generates function nodes representing texture sampling operations. Sampling or mipmap level sampling is determined based on argument count.

Function SyntaxDescriptionReturn Type
texture(sampler, uv)Basic samplingvec4
texture(sampler, uv, level)Mipmap level samplingvec4

Texture names are assigned binding numbers through coordination with the uniform system, generating sampler references during shader code generation.

Code Generation Rulesโ€‹

Header Managementโ€‹

Header information like uniformHead, structHead, defineHead is managed through NodeContext. These are sorted by dependency order after node processing and placed at the beginning of shader code.

Header Generation Order:
Dependencies โ”€โ”€โ†’ TopologicalSort โ”€โ”€โ†’ Header Order
โ†“ โ†“ โ†“
Struct Relations Topological Sort Dependency Order
Function Relations Algorithm Headers

Varying Processingโ€‹

WebGPU varying (data transfer from vertex shader to fragment shader) is managed through the varying() function. This function assigns location numbers and generates varying declarations for both shaders.

Dependency Resolutionโ€‹

For struct definitions and function definitions, topological sort algorithms are used to handle cross-references and circular references. Circular references are detected using visiting sets, generating header code in order.

Dependency Resolution:
Node Graph โ”€โ”€โ†’ Dependency Analysis โ”€โ”€โ†’ Sorted Headers
โ†“ โ†“ โ†“
Node Relations Dependency Analysis Sorted Headers
Cycle Detection Visiting Sets Code Generation Order