Recursive AI

Choose a Vulkan physical device in C#

OneGpu is a free, open-source GPU compute and rendering library for .NET from Recursive AI, on Vulkan, Metal and WebGPU. MIT or Apache-2.0, no bundled native binaries.

Select a GPU by kind or name before creating its Vulkan device. OneGpu calls a Vulkan physical device an “adapter”. These examples need Windows or Linux and a Vulkan 1.3 driver.

On Apple silicon Macs, GpuBackends selects Metal instead; see backend selection.

Terminal
dotnet new console
dotnet add package RecursiveAI.OneGpu.Backends

Requires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.

C#
using RecursiveAI.OneGpu;
 
using var device = GpuBackends.CreateDevice(
    new GpuDeviceOptions { Adapter = "discrete" });
Console.WriteLine(device.Adapter.Name);

On the example machine, prints:

Text
NVIDIA GeForce RTX 4070

Unzip the example, then dotnet run.

The full program

C#
using RecursiveAI.OneGpu;
 
foreach (var adapter in GpuBackends.EnumerateAdapters())
    Console.WriteLine($"{adapter.Kind}: {adapter.Name}");
using var device = GpuBackends.CreateDevice(
    new GpuDeviceOptions { Adapter = "discrete" });
Console.WriteLine($"Selected: {device.Adapter.Name}");

Output on Linux with Vulkan and an NVIDIA GeForce RTX 4070:

Text
Discrete: NVIDIA GeForce RTX 4070
Integrated: AMD Ryzen 9 7950X3D 16-Core Processor (RADV RAPHAEL_MENDOCINO)
Software: llvmpipe (LLVM 21.1.8, 256 bits)
Selected: NVIDIA GeForce RTX 4070

Adapter names vary by machine. If no adapter matches, device creation throws GpuUnsupportedException; ONEGPU_ADAPTER, when set, wins over the option in code.

Variations

To choose the integrated GPU, replace the device creation:

C#
using var device = GpuBackends.CreateDevice(
    new GpuDeviceOptions { Adapter = "integrated" });
Console.WriteLine(device.Adapter.Name);
Text
AMD Ryzen 9 7950X3D 16-Core Processor (RADV RAPHAEL_MENDOCINO)

To match a model name, use Adapter = "RTX" instead; this selects the NVIDIA GeForce RTX 4070 on this machine.

The short answer

Enumerate adapters with GpuBackends.EnumerateAdapters(), then pass a kind or a name substring to GpuDeviceOptions.Adapter when creating the device. For example, discrete selects a matching discrete GPU and integrated selects an integrated one. Read device.Adapter afterward to see which adapter was actually chosen.

These examples use the Vulkan backend. On Windows and Linux it is the platform default; ONEGPU_BACKEND=vulkan can select it explicitly. A working Vulkan loader and driver are still required. An adapter that is not visible to Vulkan cannot be selected by its display name.

How do you choose a Vulkan physical device in C#?

Use the enumeration output to choose between a class of GPU and a particular model. Kind selection is useful when deploying to different machines: discrete does not depend on one manufacturer’s model name. A substring such as RTX is useful when you want a particular adapter from the list on a known machine. Name matching is case-insensitive.

The selector chooses the first matching adapter in the backend’s order. According to the adapter-order documentation, Vulkan sorts by kind first: discrete, integrated, virtual, software, then other. Ties use PCI vendor ID, device ID, name, API version with newest first, and the driver’s pipeline-cache UUID. The default is the first adapter in that order, not whichever driver happens to enumerate first.

A kind is therefore not an exact identity when several GPUs of that kind exist. A short name fragment can also match several adapters. Inspect the list and use a sufficiently specific name if the distinction matters. Create the device only after choosing: the option is applied during device creation rather than moving an already-created device to another GPU.

ONEGPU_ADAPTER overrides the C# option. This is useful for changing deployment configuration without rebuilding, but it also explains a program selecting an integrated GPU when its source says Adapter = "discrete". Check that environment variable before treating the mismatch as a device-selection bug. An unset override leaves the code’s option in control.

How do you get Vulkan GPU info?

The full program prints every visible adapter’s name and kind, then the selected device’s name. On the example machine the list includes a discrete NVIDIA GPU, an integrated RADV adapter and a software adapter. Your output depends on installed drivers and hardware; those three entries are observations from this machine, not a list every application should expect.

GpuAdapterInfo describes adapter identity, while device.Capabilities.Compute describes the limits and features exposed by the created device. Use the latter when deciding whether a shader can use a scalar type, subgroup operation or workgroup size. A familiar vendor or model name is not a substitute for querying the feature you actually need.

The environment reference specifies that a selector with no match throws GpuUnsupportedException. It does not silently fall back to a different GPU. If your application permits a fallback, make that an explicit policy: report the failed choice and deliberately create another device. Otherwise fail clearly so the caller knows its requested adapter was unavailable. The try/catch snippet below catches that exception without pretending a usable device was created.

This catches a deliberately unmatched selector:

C#
try
{
    using var device = GpuBackends.CreateDevice(
        new GpuDeviceOptions { Adapter = "no-such-adapter" });
}
catch (GpuUnsupportedException)
{
    Console.WriteLine("No matching adapter");
}
Text
No matching adapter
All onegpu recipes