Recursive AI

Getting started with OneGpu

All onegpu docs

Create a GPU device from C#, upload two matrices into typed buffers, multiply them with a compute kernel from RecursiveAI.OneGpu.Kernels, and read the result back. This guide was run on the 1.0.0 packages from NuGet, on Linux with Vulkan, on an NVIDIA GeForce RTX 4070.

Install

You need the .NET 10 SDK and, on Linux or Windows, a Vulkan 1.3 driver with the loader library (libvulkan.so.1, vulkan-1.dll); on macOS, Apple silicon. Make a console project and add the two packages:

Terminal
dotnet new console -n GpuHello
cd GpuHello
dotnet add package RecursiveAI.OneGpu.Backends --version 1.0.0
dotnet add package RecursiveAI.OneGpu.Kernels --version 1.0.0

RecursiveAI.OneGpu.Backends brings the Vulkan, Metal and WebGPU backends and picks one for the platform: Metal on Apple platforms, WebGPU in a browser, Vulkan elsewhere.

Multiply two matrices on the GPU

Replace Program.cs with:

C#
using RecursiveAI.OneGpu;
using RecursiveAI.OneGpu.Kernels;
 
// Every adapter the selected backend can see.
Console.WriteLine($"Backend: {GpuBackends.Selected}");
foreach (var info in GpuBackends.EnumerateAdapters())
    Console.WriteLine($"  {info.Kind,-10} {info.Name}");
 
// Ask for the discrete GPU by kind; a name fragment such as "RTX" works too.
using var device = GpuBackends.CreateDevice(
    new GpuDeviceOptions { Adapter = "discrete" });
var adapter = device.Adapter;
Console.WriteLine(
    $"Running on: {adapter.Name} ({adapter.Kind}, API {adapter.ApiVersion})");
 
// C = A x B: A is 4 x 2, B is 2 x 3, both row-major.
const int M = 4, N = 3, K = 2;
float[] a = [1, 2, 3, 4, 5, 6, 7, 8];
float[] b = [1, 0, 2, 0, 1, 3];
 
// Typed buffers; A and B are uploaded before the first submission.
using var bufferA = device.CreateBuffer<float>(a);
using var bufferB = device.CreateBuffer<float>(b);
using var bufferC = device.CreateBuffer<float>(M * N);
 
using var gemm = new Gemm(device, GemmElementType.F32);
var encoder = device.BeginCommands();
gemm.Encode(encoder, new GemmProblem(M, N, K), bufferA, bufferB, bufferC);
device.Submit(encoder);
 
// Completes when the GPU is done, without blocking a thread.
var c = new float[M * N];
await bufferC.ReadAsync(c);
 
for (int row = 0; row < M; row++)
    Console.WriteLine(string.Join(" ",
        c.Skip(row * N).Take(N).Select(v => v.ToString().PadLeft(3))));

Run it:

Terminal
dotnet run

The output, on a Linux machine with an RTX 4070 and a Radeon integrated GPU:

Text
Backend: Vulkan
  Discrete   NVIDIA GeForce RTX 4070
  Integrated AMD Ryzen 9 7950X3D 16-Core Processor (RADV RAPHAEL_MENDOCINO)
  Software   llvmpipe (LLVM 21.1.8, 256 bits)
Running on: NVIDIA GeForce RTX 4070 (Discrete, API 1.4.312)
  1   2   8
  3   4  18
  5   6  28
  7   8  38

The first row is [1 2] times B: 1·1 + 2·0 = 1, 1·0 + 2·1 = 2, 1·2 + 2·3 = 8.

What each part does

  • GpuBackends.CreateDevice opens a device on the backend for this platform. GpuDeviceOptions.Adapter picks the adapter whose name contains the text, or whose kind is software, discrete or integrated; without it the backend takes the first adapter in its order, which on Vulkan lists discrete GPUs first, then integrated, virtual and software ones. The ONEGPU_ADAPTER environment variable does the same and wins over the option. device.Adapter reports the adapter the device opened.
  • CreateBuffer<float>(a) makes a typed GpuBuffer<float> and queues the upload of a, which runs before the next submission. CreateBuffer<float>(M * N) makes an empty one of that many elements.
  • Gemm is a kernel family: create it once and it keeps its pipelines. Encode records the compute passes for one product into the encoder, and Submit sends them to the GPU. Pass GpuKernelMode.Deterministic as its third argument for bit-repeatable sums on one GPU and driver; the default is Fast.
  • ReadAsync copies the result back without blocking a thread; it completes when the GPU has finished the work submitted before it.
  • A wrong shape, stride, size or offset throws GpuKernelShapeException before anything reaches the driver; a missing capability throws GpuUnsupportedException.

Next