Kernel
Forms that compile (44)
Section titled “Forms that compile (44)”Forms that compile with different values (2)
Section titled “Forms that compile with different values (2)”No error occurs. It compiles and runs. But there are conditions where the value differs from regular C#.
| Syntax | What happens |
|---|---|
Mathf.Log(f, g) | When the base is 0 or 1 at runtime, the result is ±0 or ±infinity (C# gives NaN for both). |
Mathf.Pow(f, g) | When the base is negative, the result is NaN (C# returns a value if the exponent is an integer) |
Forms that don’t compile (24)
Section titled “Forms that don’t compile (24)”| Syntax | Description | Error | Reason | Outside the kernel |
|---|---|---|---|---|
do { } while (c) | Loop with the check after the body | TUKI0001 | not yet | Compiles |
switch | Branching on a value | TUKI0001 | not yet | Compiles |
break | Loop exit | TUKI0001 | not yet | Compiles |
continue | Continue to the next iteration | TUKI0001 | not yet | Compiles |
foreach | Iterating over a sequence | TUKI0001 | by design | Compiles |
a & b | Bitwise AND | TUKI0001 | not yet | Compiles |
a << 1 | Bit shift | TUKI0001 | not yet | Compiles |
double d | Double-precision float | TUKI0001 | undecided | Compiles |
Vector3 v | 3-component value | TUKI0001 | not yet | Compiles |
string s | Strings | TUKI0001 | by design | Compiles |
float[] xs | Arrays | TUKI0001 | by design | Compiles |
struct Pair { } | A custom value type | TUKI0001 | undecided | Compiles |
Debug.Log(x) | Calling a Unity API | TUKI0001 | by design | Compiles |
Random.value | Random number | TUKI0001 | by design | Compiles |
Time.time | Time | TUKI0001 | by design | Compiles |
v == w | Equality between 2-component values | TUKI0001 | by design | Compiles |
0f / 0f | A constant that becomes NaN | TUKI0001 | by design | Compiles |
1f / 0f | A constant that becomes infinity | TUKI0001 | by design | Compiles |
goto | Jump to an arbitrary location | TUKI0001 | by design | Rejected |
try / catch | Catching exceptions | TUKI0001 | runtime | Rejected |
out 引数 | Returning a value through an argument | TUKI0001 | not yet | Rejected |
自分を呼ぶ関数 | Recursion | TUKI0001 | by design | Rejected |
Peek(GpuBuffer2D b, ...) | Passing a buffer to a helper function | TUKI0001 | by design | — |
prev[id.Offset(1, 0)] = c | Writing to another cell | CS0200 | by design | — |
Built-ins (45)
Section titled “Built-ins (45)”A list of what you can call from inside a kernel. Anything not in this table is rejected.
The “Difference from C#” column carries these marks:
- Adjusted / The compiler rewrites the expression so it produces the same value.
- Differs / A difference remains even after rewriting. Listed above under “Forms that compile with different values”.
Mathf (30)
Section titled “Mathf (30)”| Syntax | Description | Difference from C# |
|---|---|---|
Mathf.Abs(f) | Absolute value | |
Mathf.Acos(f) | Arccosine | |
Mathf.Asin(f) | Arcsine | |
Mathf.Atan(f) | Arctangent | |
Mathf.Atan2(f, g) | Angle from y, x | |
Mathf.Ceil(f) | Ceiling | |
Mathf.Clamp(f, g, h) | Clamp between a lower and upper bound | Adjusted |
Mathf.Clamp01(f) | Clamp to 0..1 | |
Mathf.Cos(f) | Cosine | |
Mathf.Deg2Rad | Degrees-to-radians factor | |
Mathf.Exp(f) | Power of e | |
Mathf.Floor(f) | Floor | |
Mathf.InverseLerp(f, g, h) | Where the value falls between a and b (0..1) | Adjusted |
Mathf.Lerp(f, g, h) | Linear interpolation (t is clamped to 0..1) | Adjusted |
Mathf.LerpUnclamped(f, g, h) | Linear interpolation (t is not clamped) | |
Mathf.Log(f) | Natural logarithm | |
Mathf.Log(f, g) | Logarithm with a specified base | Differs |
Mathf.Log10(f) | Base-10 logarithm | |
Mathf.Max(f, g) | The larger one | |
Mathf.Min(f, g) | The smaller one | |
Mathf.PI | Pi | |
Mathf.Pow(f, g) | Power | Differs |
Mathf.Rad2Deg | Radians-to-degrees factor | |
Mathf.Repeat(f, g) | Wrap within 0..length (remainder) | Adjusted |
Mathf.Round(f) | Round to nearest (0.5 rounds to even) | |
Mathf.Sign(f) | Sign (0 counts as positive, returns 1) | Adjusted |
Mathf.Sin(f) | Sine | |
Mathf.SmoothStep(f, g, h) | Smooth interpolation (zero velocity at both ends) | Adjusted |
Mathf.Sqrt(f) | Square root | |
Mathf.Tan(f) | Tangent |
Vector2 (10)
Section titled “Vector2 (10)”| Syntax | Description | Difference from C# |
|---|---|---|
Vector2.Distance(v, w) | Distance between 2 points | |
Vector2.Dot(v, w) | Dot product | |
Vector2.Lerp(v, w, f) | Linear interpolation (t is clamped to 0..1) | Adjusted |
Vector2.LerpUnclamped(v, w, f) | Linear interpolation (t is not clamped) | |
Vector2.Max(v, w) | The larger value per component | |
Vector2.Min(v, w) | The smaller value per component | |
Vector2.one | (1, 1) | |
Vector2.right | (1, 0) | |
Vector2.up | (0, 1) | |
Vector2.zero | (0, 0) |
Members of a 2-component value (3)
Section titled “Members of a 2-component value (3)”| Syntax | Description | Difference from C# |
|---|---|---|
v.magnitude | Length | |
v.normalized | Direction normalized to length 1 (returns (0,0) if length is 0) | Adjusted |
v.sqrMagnitude | Squared length (no square root) |
Gpu (2)
Section titled “Gpu (2)”| Syntax | Description | Difference from C# |
|---|---|---|
Gpu.Pack16x2(v) | Pack 2 values into 1 cell (65536 steps each) | |
Gpu.Unpack16x2(c) | Unpack a value packed by Pack16x2 |
Differences between inside and outside the kernel
Section titled “Differences between inside and outside the kernel”What you can write inside a kernel is a narrow subset of the language. The “Outside the kernel” column above shows the difference, 18 forms compile in an ordinary method but not inside a kernel.
The error text is the same regardless of which reason caused the rejection.
(TUKI0001, “This C# syntax cannot be compiled to Udon.”). The 18 forms above do compile in Udon,
so reading the text literally leads to the wrong cause. If the rejected form compiles in an ordinary method,
the cause is not the form itself but writing it inside the 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
stops with CS0200 (assignment to a read-only value) instead of a TUKI number. Because the buffer’s index
is read-only, this never reaches this language’s check — the C# rule stops it first.
Properties of numbers
Section titled “Properties of numbers”- A cell has 4 components, each 8 bits (256 steps). If you need finer values, pack 2 with
Gpu.Pack16x2(65536 steps each) - 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)
Color rounds to 8 bits. Packing multiple values into one color loses precision by that many steps.
Statements
Section titled “Statements”Local variable
Section titled “Local variable”using UnityEngine;using Tsukimi;
public class ParLocalVariable : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { float v = prev[id].R; return new Color4(v, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Assignment
Section titled “Assignment”using UnityEngine;using Tsukimi;
public class ParAssignment : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { float v = 0f; v = prev[id].R; return new Color4(v, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Compound assignment
Section titled “Compound assignment”using UnityEngine;using Tsukimi;
public class ParCompoundAssignment : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { float v = prev[id].R; v += 0.25f; return new Color4(v, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Increment and decrement
Section titled “Increment and decrement”using UnityEngine;using Tsukimi;
public class ParIncrement : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { int n = id.X; n++; return new Color4(n * 0.01f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Method with a return value
Section titled “Method with a return value”The kernel’s return value is written to that cell. This is the only way to write.
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); }
[TsukimiKernel] 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); }}Branch on a condition
Section titled “Branch on a condition”using UnityEngine;using Tsukimi;
public class ParIf : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { float v = prev[id].R; if (v > 0.5f) { v = 1f; } return new Color4(v, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Two-way branch
Section titled “Two-way branch”using UnityEngine;using Tsukimi;
public class ParIfElse : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { float v = prev[id].R; if (v > 0.5f) { v = 1f; } else { v = 0f; } return new Color4(v, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Return partway through
Section titled “Return partway through”using UnityEngine;using Tsukimi;
public class ParEarlyReturn : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { if (id.X == 0) { return Color4.Black; } return new Color4(prev[id].R, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Loop a fixed number of times
Section titled “Loop a fixed number of times”using UnityEngine;using Tsukimi;
public class ParFor : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { float v = 0f; for (int i = 0; i < 4; i++) { v = v + prev[id.Offset(i, 0)].R; } return new Color4(v * 0.25f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Conditional loop
Section titled “Conditional loop”using UnityEngine;using Tsukimi;
public class ParWhile : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { float v = 0f; int i = 0; while (i < 4) { v = v + prev[id.Offset(i, 0)].R; i++; } return new Color4(v * 0.25f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}using UnityEngine;using Tsukimi;
public class ParBlock : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { float v = prev[id].R; { v = v * 2f; } return new Color4(Mathf.Clamp01(v), 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Nested condition
Section titled “Nested condition”using UnityEngine;using Tsukimi;
public class ParNestedIf : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { float v = prev[id].R; if (v > 0.25f) { if (v > 0.75f) { v = 1f; } } return new Color4(v, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); 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 this kernel is responsible for. prev[id] reads that cell’s value.
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); }
[TsukimiKernel] 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); }
[TsukimiKernel] 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); }
[TsukimiKernel] 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); }
[TsukimiKernel] 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); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { return Color4.Black; }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Adding cells
Section titled “Adding cells”using UnityEngine;using Tsukimi;
public class ParCellAdd : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { Color4 c = prev[id] + Color4.Black; return new Color4(c.R, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Subtracting cells
Section titled “Subtracting cells”using UnityEngine;using Tsukimi;
public class ParCellSub : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { Color4 c = prev[id] - Color4.Black; return new Color4(c.R, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Scaling a cell by a constant
Section titled “Scaling a cell by a constant”using UnityEngine;using Tsukimi;
public class ParCellScale : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { Color4 c = prev[id] * 0.5f; return new Color4(c.R, 0f, 0f, 1f); }
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); }
[TsukimiKernel] 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. An index outside the buffer is clamped to the edge value (see “Properties of numbers” below).
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); }
[TsukimiKernel] 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); }
[TsukimiKernel] 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); }}Values and operators
Section titled “Values and operators”Float value
Section titled “Float value”Write a float constant by appending f.
using UnityEngine;using Tsukimi;
public class ParFloatLiteral : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] 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); }}Integer arithmetic
Section titled “Integer arithmetic”using UnityEngine;using Tsukimi;
public class ParIntArith : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { int n = id.X * 2 + 1; return new Color4(n * 0.001f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Combining booleans
Section titled “Combining booleans”using UnityEngine;using Tsukimi;
public class ParBoolLogic : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { bool b = id.X > 0 && id.Y > 0; return new Color4(b ? 1f : 0f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Negation
Section titled “Negation”using UnityEngine;using Tsukimi;
public class ParNot : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { bool b = !(id.X > 0); return new Color4(b ? 1f : 0f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Range comparison
Section titled “Range comparison”using UnityEngine;using Tsukimi;
public class ParComparisonChain : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { bool b = prev[id].R >= 0.25f && prev[id].R <= 0.75f; return new Color4(b ? 1f : 0f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Choosing a value by condition
Section titled “Choosing a value by condition”using UnityEngine;using Tsukimi;
public class ParTernary : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { return new Color4(id.X > 32 ? 1f : 0f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Sign negation
Section titled “Sign negation”using UnityEngine;using Tsukimi;
public class ParNegate : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { return new Color4(-(-prev[id].R), 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Converting int to float
Section titled “Converting int to float”using UnityEngine;using Tsukimi;
public class ParCastIntToFloat : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { int n = id.X; return new Color4((float)n * 0.001f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Float-to-int conversion
Section titled “Float-to-int conversion”using UnityEngine;using Tsukimi;
public class ParCastFloatToInt : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { int n = (int)(prev[id].R * 4f); return new Color4(n * 0.25f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Integer remainder
Section titled “Integer remainder”% computes the integer remainder. The target shader compiler may warn that it’s slow.
using UnityEngine;using Tsukimi;
public class ParModuloInt : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { int n = id.X % 4; return new Color4(n * 0.25f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Float wraparound
Section titled “Float wraparound”Use Mathf.Repeat to wrap a float value.
using UnityEngine;using Tsukimi;
public class ParModuloFloat : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { return new Color4(Mathf.Repeat(prev[id].R, 0.5f), 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Constructing a 2-component value
Section titled “Constructing a 2-component value”using UnityEngine;using Tsukimi;
public class ParVector2Construct : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { Vector2 v = new Vector2(0.25f, 0.5f); return new Color4(v.x + v.y, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Operating on a 2-component value
Section titled “Operating on a 2-component value”Besides adding and subtracting Vector2 values, you can use members like magnitude.
using UnityEngine;using Tsukimi;
public class ParVector2Ops : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { Vector2 v = new Vector2(1f, 0f) + new Vector2(0f, 1f); return new Color4(v.magnitude * 0.5f, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Constants and names
Section titled “Constants and names”Type constant
Section titled “Type constant”A constant is replaced with its value at compile time.
using UnityEngine;using Tsukimi;
public class ParConstField : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
private const float Gain = 0.5f;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { return new Color4(prev[id].R * Gain, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Local constant
Section titled “Local constant”using UnityEngine;using Tsukimi;
public class ParConstLocal : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { const float gain = 0.5f; return new Color4(prev[id].R * gain, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Same name as a reserved word in the compile target
Section titled “Same name as a reserved word in the compile target”A name that collides with a reserved word on the shader side is renamed at compile time.
using UnityEngine;using Tsukimi;
public class ParReservedWordName : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { float sample = prev[id].R; float matrix = 0.5f; return new Color4(sample * matrix, 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Splitting into functions
Section titled “Splitting into functions”Calling a helper function
Section titled “Calling a helper function”A static method called from a kernel is emitted alongside it as a shader function. That helper function does not need [TsukimiKernel].
using UnityEngine;using Tsukimi;
public class ParCallHelper : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
static float Falloff(float d) { return Mathf.Clamp01(1f - d * d); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { float d = (id.X - 32) * 0.05f; return new Color4(Falloff(d), 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Helper function taking 2 arguments
Section titled “Helper function taking 2 arguments”using UnityEngine;using Tsukimi;
public class ParHelperTwoArgs : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
static float Mix(float a, float b) { return a * 0.5f + b * 0.5f; }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { return new Color4(Mix(prev[id].R, prev[id].G), 0f, 0f, 1f); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Packing and reading the edges
Section titled “Packing and reading the edges”Packing 2 values
Section titled “Packing 2 values”A cell has 4 components, each 8 bits (256 steps). Packing preserves up to 65536 steps each.
using UnityEngine;using Tsukimi;
public class ParPackTwoValues : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { Vector2 fine = new Vector2(prev[id].R, prev[id].G); return Gpu.Pack16x2(fine); }
void Update() { Gpu.Run(nameof(Step), next, current); Gpu.Swap(ref current, ref next); }}Unpacking a packed value
Section titled “Unpacking a packed value”A packed value can be split back into the original 2 with Gpu.Unpack16x2.
using UnityEngine;using Tsukimi;
public class ParPackRoundtrip : TsukimiBehaviour{ private GpuBuffer2D current; private GpuBuffer2D next;
void Start() { current = Gpu.Buffer(64, 64); next = Gpu.Buffer(64, 64); }
[TsukimiKernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { Vector2 fine = new Vector2(0.5f, 0.25f); Color4 packed = Gpu.Pack16x2(fine); Vector2 back = Gpu.Unpack16x2(packed); return new Color4(back.x, back.y, 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); }
[TsukimiKernel] 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); }}