Functions
Function declaration, shader entry points, generics, and scoping in BWSL.
Pressure-test the syntax
Take the concept from this page into the playground and deliberately break a pass, binding, or type signature to see how the compiler responds.
Try a Live EditBWSL functions use the :: declaration form and support overloading, pass-local helpers, module-qualified calls, stage-returning helpers, and pass-block-returning helpers.
Function Declaration
Functions are declared with typed parameters and an explicit return type after ->:
add :: (int a, int b) -> int {
return a + b;
}
calculateNormal :: (float3 p0, float3 p1, float3 p2) -> float3 {
float3 edge1 = p1 - p0;
float3 edge2 = p2 - p0;
return normalize(cross(edge1, edge2));
}
The general form is:
functionName :: (Type arg1, Type arg2, ...) -> ReturnType {
// body
}
Function Calls
Call functions with positional arguments:
int result = add(5, 3);
float3 normal = calculateNormal(v0, v1, v2);
The current parser surface uses positional calls only.
Overloading
Multiple functions can share a name as long as their parameter lists differ:
square :: (float x) -> float {
return x * x;
}
square :: (float3 x) -> float3 {
return x * x;
}
Shader Entry Points
BWSL can package shader stages into helper functions that return vertex_function, fragment_function, or compute_function.
Vertex and Fragment Helpers
vertexMain :: () -> vertex_function {
vertex {
output.position = float4(attributes.position, 1.0);
output.uv = attributes.texcoord;
}
}
fragmentMain :: () -> fragment_function {
fragment {
output.color = float4(input.uv, 0.0, 1.0);
}
}
Use them in a pass with stage assignment:
pass "Main" {
use attributes { position, texcoord }
vertex = vertexMain()
fragment = fragmentMain()
}
Compute Helpers
clearPass :: () -> compute_function {
compute "Main" [64, 1, 1] {
uint idx = input.global_id.x;
if (idx >= 1024u) {
return;
}
// resources.output[idx] = 0.0;
}
}
Pass Block Helpers
Functions can also return pass_block to package a whole pass body:
makePass :: () -> pass_block {
pass {
vertex {
output.position = float4(0.0, 0.0, 0.0, 1.0);
}
fragment {
output.color = float4(1.0);
}
}
}
pass "Generated" = makePass();
Module pass blocks can map their local attributes, resources, and variants to the caller pipeline. See Pass Blocks for the complete rules.
Function Scope
Functions can live at pipeline scope, pass scope, or module scope.
Pipeline Scope
Pipeline-scoped helpers are visible to every pass in the pipeline:
pipeline MyPipeline {
luma :: (float3 color) -> float {
return dot(color, float3(0.299, 0.587, 0.114));
}
pass "Forward" {
fragment {
float v = luma(float3(1.0, 0.8, 0.2));
output.color = float4(v, v, v, 1.0);
}
}
}
Pass Scope
Helpers declared inside a pass stay inside that pass:
pipeline MyPipeline {
pass "SpecialEffect" {
wobble :: (float2 uv, float t) -> float2 {
return uv + sin(uv.y * 10.0 + t) * 0.01;
}
fragment {
float2 distorted = wobble(input.uv, 1.0);
output.color = float4(distorted, 0.0, 1.0);
}
}
}
Module Scope
Functions defined in modules are accessed using the :: namespace operator:
module Math {
square :: (float x) -> float {
return x * x;
}
}
pipeline MyPipeline {
import Math
pass "Main" {
fragment {
float v = Math::square(0.5);
output.color = float4(v, v, v, 1.0);
}
}
}
Imported modules can be aliased, and using ModuleName can opt an imported module into unqualified lookup:
pipeline MyPipeline {
import Math as M
using M
pass "Main" {
fragment {
float v = square(0.5) + M::PI;
output.color = float4(v, v, v, 1.0);
}
}
}
Constraint-Based Functions
BWSL's active generic system is constraint-based:
constraint FloatVectors = float2 | float3 | float4;
scale :: (FloatVectors v, float s) -> FloatVectors {
return v * s;
}
Constrained functions can dispatch on the resolved concrete type:
componentSum :: (FloatVectors v) -> float {
float2: v.x + v.y
float3: v.x + v.y + v.z
float4: v.x + v.y + v.z + v.w
}
Angle-bracket syntax such as foo<T> and where clauses are not part of the current supported surface. See Generics for the constraint-based form.
Function Summary
| Feature | Syntax |
|---|---|
| Declaration | name :: (params) -> ReturnType { } |
| Positional call | func(a, b, c) |
| Overload | name :: (float x) -> ... and name :: (float3 x) -> ... |
| Vertex helper | name :: () -> vertex_function { vertex { } } |
| Fragment helper | name :: () -> fragment_function { fragment { } } |
| Compute helper | name :: () -> compute_function { compute "Main" [X, Y, Z] { } } |
| Pass-block helper | name :: () -> pass_block { pass { } } |
| Type constraint | constraint Name = Type1 | Type2; |
| Constrained parameter | (ConstraintName param) |
| Module function | moduleName::functionName() |
| Imported module alias | import Module as Alias |
| Unqualified module lookup | using Alias |
| Type alias | using Name = ExistingType |
See Also
- Generics - Type constraints and generic functions
- Modules - Organizing code into reusable modules
- Shader I/O - Input and output in shader stages
- Language Overview - BWSL syntax and concepts