Skip to content

Running kernels

SyntaxDescription
Gpu.Buffer(64, 64)Creating a buffer
Gpu.Swap(ref a, ref b)Swapping the two
Gpu.Run(nameof(Step), next, current)Calling Run
Gpu.Run(nameof(Step), next, current, phase)Running with extra arguments
Gpu.Run("Step", next, current)Naming a kernel with a string
Gpu.Run(nameof(Fade), current, next)Multiple kernels
Gpu.Show(current, display)Displaying it on a surface
Gpu.Texture(current)Retrieving it as a texture
SyntaxDescriptionErrorReasonInstead
The kernel isn’t staticA method with [TsukimiKernel] that’s an instance methodTUKI0001by designMake it static
The return type isn’t Color4The kernel returns a different typeTUKI0001by designReturn Color4
The first argument isn’t KernelIdThe kernel’s argument order is wrongTUKI0001by designMake the first argument KernelId
The argument count for Gpu.Run doesn’t matchThe count differs from the kernel’s argumentsTUKI0001by designMatch the count
The argument types for Gpu.Run don’t matchThe types differ from the kernel’s argumentsTUKI0001by designMatch the types
Gpu.Run(k, next, next)The output buffer is also passed as an inputTUKI0001by designPass a different buffer
Gpu.Run("NotAKernel", ...)Passing the name of a method without [TsukimiKernel]TUKI0001by designFix the spelling with nameof
Calling Gpu.Pack16x2 outside a kernelThe packing is written outside the kernelTUKI0001, TUKI0099, TUKI0102by designPack it inside the kernel

All 8 forms above are rejected with the same message (TUKI0001, “This C# syntax can’t be lowered to Udon.” For the last one only, it also states that there’s no type). Each one breaks a kernel rule, but the message talks about Udon, so you can’t narrow down the cause from the wording. When you’re rejected, the fastest path is to check the rows of the table above, from the top.

Writing one kernel adds one public field for wiring the display surface. If you leave it unassigned and get as far as Gpu.Show, it silently stops on the actual hardware. The compiler only issues a warning for this; compilation still succeeds.

Each cell has 4 components at 8 bits each, and the contents start at zero.

using UnityEngine;
using Tsukimi;
public class ParHostBuffer : 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);
}
}

Use this when feeding the previous result back in as the next input. You can’t use ref on the arguments of a method you write yourself, but you can use it with Gpu.Swap.

using UnityEngine;
using Tsukimi;
public class ParHostSwap : 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);
}
}

The second argument is the output. The third argument onward corresponds, in order, to the kernel’s second argument onward.

using UnityEngine;
using Tsukimi;
public class ParHostRun : 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);
}
}

The count and types of the values you pass are checked against the kernel’s arguments at compile time.

using UnityEngine;
using Tsukimi;
public class ParHostRunExtraArgs : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
private float phase;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev, float t)
{
return new Color4(prev[id].R * t, 0f, 0f, 1f);
}
void Update()
{
phase = phase + Time.deltaTime;
Gpu.Run(nameof(Step), next, current, phase);
}
}

The kernel’s name can also be passed as a string. Using nameof lets C# catch a misspelling.

using UnityEngine;
using Tsukimi;
public class ParHostRunStringName : 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("Step", next, current);
}
}

You can write multiple kernels and run them in sequence.

using UnityEngine;
using Tsukimi;
public class ParHostTwoKernels : 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);
}
[TsukimiKernel]
static Color4 Fade(KernelId id, GpuBuffer2D prev)
{
return new Color4(prev[id].R * 0.5f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Run(nameof(Fade), current, next);
}
}

Calling Gpu.Show replaces the target material’s main texture with the buffer (the material becomes a per-target copy).

using UnityEngine;
using Tsukimi;
public class ParHostShow : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
public Renderer display;
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);
Gpu.Show(current, display);
}
}

Gpu.Texture is used when passing to a Unity or VRChat-side API. What’s returned is the buffer itself, not a copy.

using UnityEngine;
using Tsukimi;
public class ParHostTexture : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
private RenderTexture tex;
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);
tex = Gpu.Texture(current);
}
}