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:
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:
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:
dotnet runThe output, on a Linux machine with an RTX 4070 and a Radeon integrated GPU:
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.CreateDeviceopens a device on the backend for this platform.GpuDeviceOptions.Adapterpicks the adapter whose name contains the text, or whose kind issoftware,discreteorintegrated; without it the backend takes the first adapter in its order, which on Vulkan lists discrete GPUs first, then integrated, virtual and software ones. TheONEGPU_ADAPTERenvironment variable does the same and wins over the option.device.Adapterreports the adapter the device opened.CreateBuffer<float>(a)makes a typedGpuBuffer<float>and queues the upload ofa, which runs before the next submission.CreateBuffer<float>(M * N)makes an empty one of that many elements.Gemmis a kernel family: create it once and it keeps its pipelines.Encoderecords the compute passes for one product into the encoder, andSubmitsends them to the GPU. PassGpuKernelMode.Deterministicas its third argument for bit-repeatable sums on one GPU and driver; the default isFast.ReadAsynccopies 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
GpuKernelShapeExceptionbefore anything reaches the driver; a missing capability throwsGpuUnsupportedException.
Next
- Compute surface: capabilities, buffers and readback, timestamps, passes and ordering
- Kernels: every operation, with its tolerances
- Matrix multiplication (GEMM)
- Names and environment variables:
ONEGPU_BACKEND,ONEGPU_ADAPTERand validation