fn()
Function in Authoring
Author a function. One call covers a plain helper and a @vertex, @fragment or @compute entry point.
import { fn } from 'typeshade'
The signature, the description and the examples come from the compiler's own source at commit 26de7be8.
Syntax
function fn<P extends FnParamSpec, R extends string>( params: P, body: FnBodyValue<P, R>, opts?: FnOpts,): FnHandle<P, R>;function fn<P extends FnParamSpec, R extends string>( name: string, params: P, body: FnBodyValue<P, R>, opts?: FnOpts,): FnHandle<P, R>;function fn<P extends FnParamSpec>( params: P, body: FnBodyVoid<P>, opts?: FnOpts,): FnHandle<P, 'void'>;function fn<P extends FnParamSpec>( name: string, params: P, body: FnBodyVoid<P>, opts?: FnOpts,): FnHandle<P, 'void'>;function fn<P extends FnParamSpec, T extends ShaderType>( params: P, ret: T, body: FnBody<P, KeyOf<T>>, opts?: FnOpts,): FnHandle<P, KeyOf<T>>;function fn<P extends FnParamSpec, T extends ShaderType>( name: string, params: P, ret: T, body: FnBody<P, KeyOf<T>>, opts?: FnOpts,): FnHandle<P, KeyOf<T>>;Parameters
paramsP extends FnParamSpecthe parameter record, in declaration order, as described above.
bodyFnBodyValue<P, R>the function body, receiving the typed params and the builder.
optsoptionalFnOptsthe stage, the workgroup size, the return attribute, and the lint deviations listed above.
namethe emitted function name. Omit it to let a
funcskey record name the function at module assembly.retthe return type to pin. Omit it when the body’s own
returncarries the type, or when the body returns nothing at all.
Return value
FnHandle<P, R>
a callable handle that is also the function declaration.
Exceptions
Description
The returned FnHandle is both the callable and the
declaration: call it directly, list it in module({ funcs }), or read its .decl.
The leading name is optional. An anonymous handle carries a placeholder name (_fn0,
_fn1, and so on) that a funcs key record renames when module assembles, so no
generated name reaches the emitted source. Keep an explicit name when something refers to
the function by its string name and no record renames it, such as an externFn
declaration whose body this function provides.
params is a record whose keys are the parameter names, in declaration order. A value is
one of three things. A plain ShaderType declares an ordinary parameter. A
builtin or location spec declares a stage-attributed entry-point
parameter, emitting @builtin(...) or @location(...) on it; these are the same two
helpers an ioStruct field uses, so an entry parameter and a struct field are
authored alike. A struct handle (structDecl or ioStruct) hands the body
that struct’s typed field proxy, so the body reads p.vo.uv with no VsOut.of(p.vo)
step. Params are read-only, so p.uv.assign(...) is a tsc error; declare a Var
where the body has to mutate.
A parameter may be named in, or any other word GLSL reserves. The IR carries the name
and the GLSL backend renames it at emit. JavaScript cannot bind that name in a
destructuring pattern, so give it a local name there: ({ in: inp }) => inp.uv.
The return-type token ret is optional when the body returns a value TypeScript can see,
(p) => expr or a struct field proxy, since the handle takes that value’s key. It is
optional again for a function that returns nothing, the shape of a compute entry: such a
body may drop voidT and the handle lands on 'void'.
Pass the token when the body sends its value out through an ambient Return inside a
nested closure. TypeScript reads such a body as returning nothing — the same shape as the
genuinely void one — so the two are separated once the body has run: a body that took the
void form and returned a value is rejected with SD0113 naming the token to write, rather
than handed a 'void' key its call sites would believe.
The body is (p, b) => …: the typed param nodes first, the Builder second. Most
bodies need only p and the ambient statement surface (Let, Var,
If, Loop, Return) plus a final native return, which is
type-checked against the return type. Reach for b when a statement is authored outside
an active scope, and for b.raw(...).
opts carries six fields:
stage: 'vertex' | 'fragment' | 'compute'makes the function a pipeline entry point. Omit it for an ordinary helper.workgroupSizesizes a compute entry, emitting@compute @workgroup_size(N). It defaults to 64.[8, 8]or[4, 4, 4]gives a two- or three-dimensional workgroup, emitting@workgroup_size(8, 8).retAttrattaches an attribute to a bare, non-struct stage return, giving-> @location(0) vec4<f32>. A typedlocation(0, T)spec is accepted and its.attris used. A bare non-struct fragment return defaults to@location(0). A struct return carries its attributes in the struct instead.allowEarlyReturnrecords a deliberate deviation from thesingle-exitlint rule, for a body whose earlyReturnskips work, such as a guard in front of a bounded loop.portabledeclares a compute entry a portable kernel; see below.lintDisablelists rule ids whose diagnostics are dropped for this function, the general form ofallowEarlyReturn. Use either with a comment saying why.
A portable kernel emits on both backends: natively as @compute on WGSL, and on GLSL ES
3.00 through a compute-to-fragment translation, which WebGL2 dispatches as a fullscreen
draw into an R32UI target. In exchange the kernel stays inside the gather-only tier: a
one-dimensional workgroup, a global_invocation_id read only as .x, exactly one read_write storage binding of
array<u32> written exactly once at the invocation index, a first uniform binding of
vec4<u32> whose .x is the invocation count and .y the output-grid width, and no raw
statements anywhere the entry can reach. Anything outside that shape fails validation on
both writers with SD0111 and a per-violation remedy.
Examples
Example
import { fn, location, vec4, length, vec2fT } from 'typeshade'
const dist = fn('dist', { p: vec2fT, q: vec2fT }, ({ p, q }) => length(p.sub(q)))
const fs = fn('fs_main', { uv: location(0, vec2fT) }, ({ uv }) => vec4(uv, 0, 1), { stage: 'fragment',})In the guide
See also
Source
src/core/ir/builder.ts, line 848, at commit 26de7be8