GPU kernel
[Kernel] was previously spelled [TsukimiKernel]. The old spelling still works.
Forms that compile (18)
Section titled “Forms that compile (18)”| Syntax | Description | Note |
|---|---|---|
return new Color4(...) | Method with a return value | This is the only way to write. |
static Color4 Step(...) => prev[id] * 0.5f | Expression-bodied kernel | |
int bands | Integer parameter | |
bool invert | Boolean parameter | |
Vector2 shift | 2-component parameter | |
Vector3 tint | 3-component parameter | |
Vector4 weights | 4-component parameter | |
prev[id] | Reading your own cell | prev[id] reads that cell’s value. |
c.R c.G c.B c.A | Reading a component | |
new Color4(r, g, b, a) | Constructing a cell value | |
Color4.White | Constant color | |
Color4.Black | Returning a constant color | |
id.X id.Y | Coordinates of the assigned cell | |
prev[id.Offset(1, -1)] | Reading a cell at a relative position | An index pointing outside the buffer is clamped to the edge value (see “Properties of numbers” below). |
new KernelId(1, 2) | Constructing coordinates | |
prev[id.Offset(-1000, -1000)] | Reading outside the buffer | |
prev.Wrap(id.Offset(1, 0)) | Reading wrapped to the far side | Unlike an index clamped to the edge value, it lets you tile a small board. On a buffer 64 wide, asking for -1 gives you cell 63. |
prev.Smooth(position) | Reading blended with the neighbours | No seams show when you scale it up. It reads four cells, so it costs more than reading one. |
Forms that don’t compile (18)
Section titled “Forms that don’t compile (18)”| Syntax | Description | Error | Reason | Outside the kernel |
|---|---|---|---|---|
foreach | Iterating over a sequence | TUKI0001 | by design | Forms that compile |
float v = (i++) + 1f | Increment or decrement where a value is expected | TUKI0001 | by design | Forms that compile |
float w = (v = 1f) + 1f | Assignment where a value is expected | TUKI0001 | by design | Forms that compile |
double d | Double-precision float | TUKI0001 | undecided | Forms that compile |
string s | Strings | TUKI0001 | by design | Forms that compile |
float[] xs | Arrays | TUKI0001 | by design | Forms that compile |
struct Pair { } | A custom value type | TUKI0001 | undecided | Forms that compile |
Debug.Log(x) | Calling a Unity API | TUKI0001 | by design | Forms that compile |
Random.value | Random number | TUKI0001 | by design | Forms that compile |
Time.time | Time | TUKI0001 | by design | Forms that compile |
0f / 0f | A constant that becomes NaN | TUKI0001 | by design | Forms that compile |
1f / 0f | A constant that becomes infinity | TUKI0001 | by design | Forms that compile |
out argument | Returning a value through an argument | TUKI0001 | by design | Forms that compile |
goto | Jump to an arbitrary location | TUKI0001 | by design | Forms that don’t compile |
try / catch | Catching exceptions | TUKI0001 | runtime | Forms that don’t compile |
| A function that calls itself | Recursion | TUKI0001 | by design | Forms that don’t compile |
Peek(GpuBuffer2D b, ...) | Passing a buffer to a helper function | TUKI0001 | by design | — |
prev[id.Offset(1, 0)] = c | Writing to another cell | CS0200, TUKI0001 | by design | — |
| Types | Contents | Example |
|---|---|---|
GpuBuffer2D | An array of cells. Received as a kernel parameter and read by index | Reading your own cell |
KernelId | The coordinates of the assigned cell. X and Y are int | Coordinates of the assigned cell |
Color4 | The value of one cell. R G B A are float values from 0 to 1 | Reading a component |
float int bool | Scalars. Usable as parameters, local variables, and helper function parameters and return values | Integer parameter |
Vector2 Vector3 Vector4 | Values with 2 to 4 components. The operators are listed under Operators by type | 2-component parameter |
Vector4[] | A table. Received only as a parameter marked [Capacity(N)] | Running with a table |
Pages for statements, expressions, and functions
Section titled “Pages for statements, expressions, and functions”| Page | Contents |
|---|---|
| Kernel statements and expressions | Control flow, operators, and the per-type operation tables |
| Functions callable from a kernel | The built-in table and helper functions |
Differences between inside and outside the kernel
Section titled “Differences between inside and outside the kernel”| What you can write | A narrow part of this language. The “Outside the kernel” column of the table above is the difference: the 13 forms can be written in an ordinary method but not inside a kernel |
| The error message | The same for either reason (TUKI0001, “C# syntax that cannot be lowered to Udon”). Those 13 forms do work in Udon, so reading the message literally leads you to the wrong cause |
| How to tell | If the form that errored is supported in an ordinary method, the cause is not the form: it is that you wrote it inside a kernel |
Restriction on where you can write
Section titled “Restriction on where you can write”The only way to write is the kernel’s return value, which is written to your own cell. Assigning to another cell
you get CS0200 (assignment to a read-only element) rather than a TUKI number. Because the buffer’s indexer
is read-only, it never reaches this language’s checks and errors first under C#‘s own rules.
Properties of numbers
Section titled “Properties of numbers”- One cell is 4 components, and a component is 8 bits (256 steps) by default. For finer values inside 0..1, pack two with
Gpu.Pack16x2(65536 steps each, two values per cell). For values outside 0..1, make the buffer withGpuFormat.Half - An index outside the buffer is clamped to the edge value, so you don’t need to write your own range check
- Keeping the side length a multiple of 16 widens the range of supported environments
Gpu.Pack16x2can only be used inside a kernel (you cannot pack a value outside and pass it in)
In a default buffer, colour rounds to 8 bits. Packing several values into one colour loses precision by that many steps.
Shape of a kernel
Section titled “Shape of a kernel”Method with a return value
Section titled “Method with a return value”The kernel’s return value is written to that cell.
using UnityEngine;using Tsukimi;
public class ParReturn : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { return new Color4(0.5f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Expression-bodied kernel
Section titled “Expression-bodied kernel”If the body is a single expression, it can be written with =>.
using UnityEngine;using Tsukimi;
public class ParExpressionBody : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
// If the body is a single expression, it can be written with => [Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) => prev[id] * 0.5f;
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Integer parameter
Section titled “Integer parameter”Pass a value of the same type at the same position in Gpu.Run.
using UnityEngine;using Tsukimi;
public class ParArgInt : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next; public int bands;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev, int bands) { // For an integer parameter, pass an int at the same position in Gpu.Run float v = prev[id].R * bands; return new Color4(v, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current, bands); Gpu.Swap(ref current, ref next); }}Boolean parameter
Section titled “Boolean parameter”using UnityEngine;using Tsukimi;
public class ParArgBool : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next; public bool invert;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev, bool invert) { float v = prev[id].R; // A boolean parameter can be used directly as a condition return new Color4(invert ? 1f - v : v, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current, invert); Gpu.Swap(ref current, ref next); }}2-component parameter
Section titled “2-component parameter”using UnityEngine;using Tsukimi;
public class ParArgVector2 : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next; public Vector2 shift;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev, Vector2 shift) { // A 2-component parameter. Here it shifts which cell is read return prev[id.Offset((int)shift.x, (int)shift.y)]; }
void Update() { Gpu.Run(nameof(Step), next, current, shift); Gpu.Swap(ref current, ref next); }}3-component parameter
Section titled “3-component parameter”using UnityEngine;using Tsukimi;
public class ParArgVector3 : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next; public Vector3 tint;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev, Vector3 tint) { Color4 c = prev[id]; // A 3-component parameter. Here it scales the color return new Color4(c.R * tint.x, c.G * tint.y, c.B * tint.z, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current, tint); Gpu.Swap(ref current, ref next); }}4-component parameter
Section titled “4-component parameter”using UnityEngine;using Tsukimi;
public class ParArgVector4 : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next; public Vector4 weights;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev, Vector4 weights) { Color4 c = prev[id]; // A 4-component parameter. Here it weights each component float v = c.R * weights.x + c.G * weights.y + c.B * weights.z + c.A * weights.w; return new Color4(v, v, v, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current, weights); Gpu.Swap(ref current, ref next); }}Cells and coordinates
Section titled “Cells and coordinates”Reading your own cell
Section titled “Reading your own cell”id is the coordinate of the cell that kernel is responsible for.
using UnityEngine;using Tsukimi;
public class ParCellRead : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { Color4 c = prev[id]; return new Color4(c.R, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Reading a component
Section titled “Reading a component”Each component is a value between 0 and 1.
using UnityEngine;using Tsukimi;
public class ParCellComponents : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { Color4 c = prev[id]; return new Color4(c.R + c.G + c.B + c.A, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Constructing a cell value
Section titled “Constructing a cell value”Builds the value to write to that cell from 4 components.
using UnityEngine;using Tsukimi;
public class ParCellConstruct : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { Color4 c = new Color4(0.5f, 0f, 0f, 1f); return new Color4(c.R, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Constant color
Section titled “Constant color”You can use a built-in constant directly as a value.
using UnityEngine;using Tsukimi;
public class ParCellWhite : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { return new Color4(Color4.White.R, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Returning a constant color
Section titled “Returning a constant color”You can return a constant color directly.
using UnityEngine;using Tsukimi;
public class ParCellBlack : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { return Color4.Black; }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Coordinates of the assigned cell
Section titled “Coordinates of the assigned cell”You can get the position of the assigned cell as integers.
using UnityEngine;using Tsukimi;
public class ParIdXy : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { return new Color4((id.X + id.Y) * 0.001f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Reading a cell at a relative position
Section titled “Reading a cell at a relative position”Offset reads a cell at a relative position.
using UnityEngine;using Tsukimi;
public class ParIdOffset : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { return new Color4(prev[id.Offset(1, -1)].R, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Constructing coordinates
Section titled “Constructing coordinates”You can build a coordinate yourself to point at any cell.
using UnityEngine;using Tsukimi;
public class ParIdConstruct : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { KernelId k = new KernelId(1, 2); return new Color4(prev[k].R, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Reading outside the buffer
Section titled “Reading outside the buffer”An index outside the buffer is clamped to the edge value, so you don’t need to write your own range check.
using UnityEngine;using Tsukimi;
public class ParEdgeRead : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { // An index outside the buffer is clamped to the edge value Color4 outside = prev[id.Offset(-1000, -1000)]; return new Color4(outside.R, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Reading wrapped to the far side
Section titled “Reading wrapped to the far side”Wrap sends an index outside the buffer round to the far side.
using UnityEngine;using Tsukimi;
public class ParCellWrap : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { // An index outside the buffer wraps round to the far side Color4 far = prev.Wrap(id.Offset(-1, 0)); return new Color4(far.R, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Reading blended with the neighbours
Section titled “Reading blended with the neighbours”Smooth takes a position (0 to 1) and returns the value blended with the surrounding cells.
using UnityEngine;using Tsukimi;
public class ParCellSmooth : TsukimiBehaviour{ private GpuBuffer2D small; private GpuBuffer2D big;
void Start() { small = Gpu.Buffer(16, 16); big = Gpu.Buffer(256, 256); }
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D board) { // Take a position (0 to 1) and read it blended with the surrounding cells Vector2 position = new Vector2(id.X * 0.00390625f, id.Y * 0.00390625f); return board.Smooth(position); }
void Update() { Gpu.Run(nameof(Step), big, small); }}