|
| 1 | +--- |
| 2 | +title: Functions |
| 3 | +description: A guide on how to create and use the TypeGPU typed functions. |
| 4 | +--- |
| 5 | + |
| 6 | +:::caution[Experimental] |
| 7 | +Functions are an *unstable* feature. The API may be subject to change in the near future. |
| 8 | +::: |
| 9 | + |
| 10 | +:::note[Recommended reading] |
| 11 | +We assume that you are familiar with the following concepts: |
| 12 | +- <a href="https://webgpufundamentals.org/webgpu/lessons/webgpu-fundamentals.html" target="_blank" rel="noopener noreferrer">WebGPU Fundamentals</a> |
| 13 | +- <a href="https://webgpufundamentals.org/webgpu/lessons/webgpu-wgsl.html" target="_blank" rel="noopener noreferrer">WebGPU Shading Language</a> |
| 14 | +::: |
| 15 | + |
| 16 | +TypeGPU allows writing shaders by composing typed functions, which are special wrappers around WGSL code. |
| 17 | +These functions can reference outside resources, like other user-defined or helper functions, buffers, bind group layouts etc. |
| 18 | + |
| 19 | +## Creating a function |
| 20 | + |
| 21 | +Functions are constructed by first defining their shells, which specify their inputs and outputs. |
| 22 | +Then the actual WGSL implementation is passed in through the `does` method. |
| 23 | + |
| 24 | +The following code defines a function that accepts one argument and returns one value. |
| 25 | + |
| 26 | +```ts |
| 27 | +const getGradientColor = tgpu['~unstable'] |
| 28 | + .fn([d.f32], d.vec4f) |
| 29 | + .does(`(ratio: f32) -> vec4f { |
| 30 | + let color = mix(vec4f(0.769, 0.392, 1.0, 1), d.vec4f(0.114, 0.447, 0.941, 1), ratio); |
| 31 | + return color; |
| 32 | + }`) |
| 33 | + .$name('getGradientColor'); |
| 34 | +``` |
| 35 | + |
| 36 | +If you're using Visual Studio Code, you can use an [extension](https://marketplace.visualstudio.com/items?itemName=ggsimm.wgsl-literal) that brings syntax highlighting to the code fragments marked with `/* wgsl */` comments. |
| 37 | + |
| 38 | +## External resources |
| 39 | + |
| 40 | +Functions can use external resources passed inside a record via the `$uses` method. |
| 41 | +Externals can be any value or TypeGPU resource that can be resolved to WGSL (functions, buffer usages, slots, accessors, constants, variables, declarations, vectors, matrices, textures, samplers etc.). |
| 42 | + |
| 43 | +```ts |
| 44 | +const getBlue = tgpu['~unstable'] |
| 45 | + .fn([], d.f32) |
| 46 | + .does('() -> f32 { return 0.941; }'); |
| 47 | + |
| 48 | +const purple = d.vec4f(0.769, 0.392, 1.0, 1); |
| 49 | + |
| 50 | +const getGradientColor = tgpu['~unstable'] |
| 51 | + .fn([d.f32], d.vec4f) |
| 52 | + .does(`(ratio: f32) -> vec4f { |
| 53 | + let color = mix(purple, getBlue(), ratio); |
| 54 | + return color; |
| 55 | + }`) |
| 56 | + .$uses({ purple, getBlue }) |
| 57 | + .$name('getGradientColor'); |
| 58 | +``` |
| 59 | + |
| 60 | +The `getGradientColor` function, when resolved to WGSL, includes the definitions of all used external resources: |
| 61 | + |
| 62 | +```wgsl |
| 63 | +fn getBlue_1() -> f32 { return 0.941; } |
| 64 | +
|
| 65 | +fn getGradientColor_0(ratio: f32) -> vec4f { |
| 66 | + let color = mix(vec4f(0.769, 0.392, 1, 1), getBlue_1(), ratio); |
| 67 | + return color; |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | +## Entry functions |
| 72 | + |
| 73 | +Defining entry functions is similar to regular ones, but is done through dedicated constructors: |
| 74 | +- `tgpu['~unstable'].vertexFn` |
| 75 | +- `tgpu['~unstable'].fragmentFn` |
| 76 | +- `tgpu['~unstable'].computeFn` |
| 77 | + |
| 78 | +They can be passed to root-defined pipelines and they accept special arguments like builtins (`d.builtin`) and decorated data (`d.location`). |
| 79 | + |
| 80 | +```ts |
| 81 | +const mainVertex = tgpu['~unstable'] |
| 82 | + .vertexFn( |
| 83 | + { vertexIndex: d.builtin.vertexIndex }, |
| 84 | + { outPos: d.builtin.position, uv: d.vec2f }, |
| 85 | + ) |
| 86 | + .does(/* wgsl */ `(input: VertexInput) -> VertexOutput { |
| 87 | + var pos = array<vec2f, 3>( |
| 88 | + vec2(0.0, 0.5), |
| 89 | + vec2(-0.5, -0.5), |
| 90 | + vec2(0.5, -0.5) |
| 91 | + ); |
| 92 | +
|
| 93 | + var uv = array<vec2f, 3>( |
| 94 | + vec2(0.5, 1.0), |
| 95 | + vec2(0.0, 0.0), |
| 96 | + vec2(1.0, 0.0), |
| 97 | + ); |
| 98 | +
|
| 99 | + return VertexOutput(vec4f(pos[input.vertexIndex], 0.0, 1.0), uv[input.vertexIndex]); |
| 100 | + }`); |
| 101 | + |
| 102 | +const mainFragment = tgpu['~unstable'] |
| 103 | + .fragmentFn({ uv: d.vec2f }, d.vec4f) |
| 104 | + .does(/* wgsl */ `(input: FragmentInput) -> @location(0) vec4f { |
| 105 | + return getGradientColor((input.uv[0] + input.uv[1]) / 2); |
| 106 | + }`) |
| 107 | + .$uses({ getGradientColor }); |
| 108 | +``` |
| 109 | + |
| 110 | +When entry function inputs or outputs are specified as objects containing builtins and inter-stage variables, the WGSL implementations need to access these arguments as passed in via structs (see *VertexInput*, *VertexOutput*, *FragmentInput*). |
| 111 | +TypeGPU schemas for these structs are created automatically by the library and their definitions are included when resolving the functions. The names referenced in the implementations do not matter. |
| 112 | + |
| 113 | +## Usage in pipelines |
| 114 | + |
| 115 | +Typed functions are crucial for simplified pipeline creation offered by TypeGPU. You can define and run pipelines as follows: |
| 116 | + |
| 117 | +```ts |
| 118 | +const pipeline = root['~unstable'] |
| 119 | + .withVertex(mainVertex, {}) |
| 120 | + .withFragment(mainFragment, { format: presentationFormat }) |
| 121 | + .createPipeline(); |
| 122 | + |
| 123 | +pipeline |
| 124 | + .withColorAttachment({ |
| 125 | + view: context.getCurrentTexture().createView(), |
| 126 | + clearValue: [0, 0, 0, 0], |
| 127 | + loadOp: 'clear', |
| 128 | + storeOp: 'store', |
| 129 | + }) |
| 130 | + .draw(3); |
| 131 | + |
| 132 | +root['~unstable'].flush(); |
| 133 | +``` |
| 134 | + |
| 135 | +The rendering result looks like this: |
| 136 | + |
| 137 | + |
| 138 | +You can check out the full example on [our examples page](/TypeGPU/examples#example=simple--triangle). |
| 139 | + |
| 140 | + |
0 commit comments