---
title: "Getting started with OneGpu"
url: https://recursiveai.org/onegpu/docs/getting-started/
description: "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…"
version: "1.0.0"
---

# Getting started with OneGpu

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:

```shell
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:

```csharp
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:

```shell
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

- [Compute surface](https://recursiveai.org/onegpu/docs/compute/): capabilities, buffers and readback, timestamps, passes and ordering
- [Kernels](https://recursiveai.org/onegpu/docs/kernels/): every operation, with its tolerances
- [Matrix multiplication (GEMM)](https://recursiveai.org/onegpu/docs/gemm/)
- [Names and environment variables](https://recursiveai.org/onegpu/docs/environment/): `ONEGPU_BACKEND`, `ONEGPU_ADAPTER` and validation
