-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Motivation
The goal of this proposal is to allow developers to write GPU kernels directly in TypeScript using a @lazyCompute
decorator. The decorated functions will be compiled at build-time into GPU shader code (WGSL or GLSL) and executed on the GPU via runtime APIs such as WebGPU. This enables parallel computation directly from TypeScript while keeping the syntax familiar.
Syntax
@lazyCompute(options?)
function funcName(param1: Type1, param2: Type2, ...): ReturnType {
// GPU-executable code
}
@lazyCompute
marks the function as a GPU kernel.options
is an optional object with:workgroupSize
: number — Threads per GPU workgroup (default 64)sharedMemory?
: number — Shared memory in bytes
Supported Parameter Types
TypeScript Type | Description | WGSL Type | GLSL Type |
---|---|---|---|
f32 | 32-bit float | f32 | float |
f64 | 64-bit float | f64 | double (optional) |
i32 | 32-bit signed integer | i32 | int |
u32 | 32-bit unsigned integer | u32 | uint |
Vec2 | 2D float vector | vec2 | vec2 |
Vec3 | 3D float vector | vec3 | vec3 |
Vec4 | 4D float vector | vec4 | vec4 |
Float32Array | GPU buffer array | array | float[] / buffer |
Int32Array | GPU buffer array | array | int[] / buffer |
Uint32Array | GPU buffer array | array | uint[] / buffer |
Note: GLSL arrays often require a fixed length at compile time; WGSL supports dynamic arrays and structs.
Function Body Limitations
- Control flow:
for
,while
,if
,else
- GPU built-ins:
global_id.x/y/z
,local_id.x/y/z
,workgroup_id.x/y/z
- Not allowed: async functions, dynamic typing, DOM or JS-specific APIs, variable-length array operations in GLSL.
Built-in Variables
Name | Description | WGSL | GLSL |
---|---|---|---|
global_id | Global thread index | @Builtin(global_invocation_id) | gl_GlobalInvocationID |
local_id | Thread index in workgroup | @Builtin(local_invocation_id) | gl_LocalInvocationID |
workgroup_id | Workgroup index | @Builtin(workgroup_id) | gl_WorkGroupID |
Compilation Behavior
- Compile-time validation
- Check parameter types
- Validate kernel body syntax
- Throw errors for unsupported types or operations
- Code generation
- AST of the TS compute function is transformed into corresponding computation graph, then compiled to WGSL or GLSL at execution time
- Shader files or inline strings are produced
- JS wrapper
- Original TS function is replaced by a GPU call wrapper
- Automatically maps buffers, dispatches the shader, and returns results
Example
TypeScript functions
@lazyCompute({ workgroupSize: 128 })
function vecAdd(a: Float32Array, b: Float32Array, out: Float32Array, n: i32) {
let i = global_id.x;
if (i < n) {
out[i] = a[i] + b[i];
}
}
WGSL output
@group(0) @binding(0) var<storage, read> a: array<f32>;
@group(0) @binding(1) var<storage, read> b: array<f32>;
@group(0) @binding(2) var<storage, read_write> out: array<f32>;
@compute @workgroup_size(128)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
let i = gid.x;
if (i < n) {
out[i] = a[i] + b[i];
}
}
GLSL output
#version 450
layout(local_size_x = 128) in;
layout(set = 0, binding = 0) buffer A { float a[]; };
layout(set = 0, binding = 1) buffer B { float b[]; };
layout(set = 0, binding = 2) buffer Out { float out[]; };
void main() {
uint i = gl_GlobalInvocationID.x;
if (i < n) {
out[i] = a[i] + b[i];
}
}
Future Extensions
- Multi-dimensional indices: global_id.xy, global_id.xyz
- Shared memory and atomic operations
- Graph composition and chaining
- Automatic optimization of workgroup sizes and memory layout
Summary
@lazyCompute
is a TypeScript compile-time DSL enabling GPU programming directly from TypeScript.
It allows developers to write GPU-executable code using familiar TS syntax, with SWC or custom transforms converting kernels to WGSL or GLSL shaders, and JS wrappers for runtime execution.