Skip to content

Commit aea3f00

Browse files
Add LevelZero.NET source files (core types)
1 parent bb2409c commit aea3f00

10 files changed

Lines changed: 1057 additions & 0 deletions

src/ComputeDevice.cs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
using System.Runtime.CompilerServices;
2+
using System.Runtime.InteropServices;
3+
using System.Text;
4+
using LevelZero.Native;
5+
6+
namespace LevelZero;
7+
8+
/// <summary>
9+
/// Represents a Level Zero compute device with its context, command queue, and command list.
10+
/// This is the primary object for GPU interaction — load modules, allocate memory, and launch kernels.
11+
/// </summary>
12+
public sealed class ComputeDevice : IDisposable
13+
{
14+
private IntPtr _driver;
15+
private IntPtr _device;
16+
private IntPtr _context;
17+
private IntPtr _queue;
18+
private IntPtr _commandList;
19+
private bool _disposed;
20+
21+
/// <summary>Human-readable device name (e.g. "Intel(R) UHD Graphics 770").</summary>
22+
public string Name { get; }
23+
24+
/// <summary>Driver index this device belongs to.</summary>
25+
public uint DriverIndex { get; }
26+
27+
/// <summary>Device index within the driver.</summary>
28+
public uint DeviceIndex { get; }
29+
30+
internal IntPtr ContextHandle => !_disposed ? _context : throw new ObjectDisposedException(nameof(ComputeDevice));
31+
internal IntPtr DeviceHandle => !_disposed ? _device : throw new ObjectDisposedException(nameof(ComputeDevice));
32+
33+
private ComputeDevice(IntPtr driver, IntPtr device, IntPtr context, IntPtr queue, IntPtr commandList,
34+
string name, uint driverIndex, uint deviceIndex)
35+
{
36+
_driver = driver;
37+
_device = device;
38+
_context = context;
39+
_queue = queue;
40+
_commandList = commandList;
41+
Name = name;
42+
DriverIndex = driverIndex;
43+
DeviceIndex = deviceIndex;
44+
}
45+
46+
internal static ComputeDevice Create(uint driverIndex, uint deviceIndex)
47+
{
48+
LevelZeroNative.EnsureSuccess(LevelZeroNative.lz_init(0));
49+
LevelZeroNative.EnsureSuccess(LevelZeroNative.lz_get_driver_handle(driverIndex, out var driver));
50+
LevelZeroNative.EnsureSuccess(LevelZeroNative.lz_get_device_handle(driverIndex, deviceIndex, out var device));
51+
LevelZeroNative.EnsureSuccess(LevelZeroNative.lz_context_create(driver, out var context));
52+
LevelZeroNative.EnsureSuccess(LevelZeroNative.lz_command_queue_create(context, device, out var queue));
53+
LevelZeroNative.EnsureSuccess(LevelZeroNative.lz_command_list_create(context, device, out var commandList));
54+
55+
var nameBuf = new StringBuilder(256);
56+
LevelZeroNative.lz_get_device_name(driverIndex, deviceIndex, nameBuf, (uint)nameBuf.Capacity);
57+
var name = nameBuf.ToString();
58+
59+
return new ComputeDevice(driver, device, context, queue, commandList, name, driverIndex, deviceIndex);
60+
}
61+
62+
/// <summary>
63+
/// Loads a SPIR-V module from a file path.
64+
/// </summary>
65+
public ComputeModule LoadModule(string spirvPath)
66+
{
67+
ObjectDisposedException.ThrowIf(_disposed, this);
68+
var spirv = File.ReadAllBytes(spirvPath);
69+
return LoadModule(spirv);
70+
}
71+
72+
/// <summary>
73+
/// Loads a SPIR-V module from a byte array.
74+
/// </summary>
75+
public ComputeModule LoadModule(byte[] spirv)
76+
{
77+
ObjectDisposedException.ThrowIf(_disposed, this);
78+
var logBuf = new StringBuilder(4096);
79+
var result = LevelZeroNative.lz_module_create(_context, _device, spirv, (uint)spirv.Length,
80+
out var module, logBuf, (uint)logBuf.Capacity);
81+
var buildLog = logBuf.ToString();
82+
if (result != 0)
83+
throw new LevelZeroException(result,
84+
$"Module compilation failed. Build log: {buildLog}");
85+
return new ComputeModule(module, _context, buildLog);
86+
}
87+
88+
/// <summary>
89+
/// Tries to load a SPIR-V module. Returns null on failure instead of throwing.
90+
/// </summary>
91+
public ComputeModule? TryLoadModule(string spirvPath, out string buildLog)
92+
{
93+
buildLog = string.Empty;
94+
if (string.IsNullOrWhiteSpace(spirvPath) || !File.Exists(spirvPath))
95+
return null;
96+
97+
var spirv = File.ReadAllBytes(spirvPath);
98+
return TryLoadModule(spirv, out buildLog);
99+
}
100+
101+
/// <summary>
102+
/// Tries to load a SPIR-V module from bytes. Returns null on failure.
103+
/// </summary>
104+
public ComputeModule? TryLoadModule(byte[] spirv, out string buildLog)
105+
{
106+
ObjectDisposedException.ThrowIf(_disposed, this);
107+
var logBuf = new StringBuilder(4096);
108+
var result = LevelZeroNative.lz_module_create(_context, _device, spirv, (uint)spirv.Length,
109+
out var module, logBuf, (uint)logBuf.Capacity);
110+
buildLog = logBuf.ToString();
111+
return result == 0 ? new ComputeModule(module, _context, buildLog) : null;
112+
}
113+
114+
/// <summary>
115+
/// Allocates a typed USM shared-memory buffer accessible from both host and device.
116+
/// </summary>
117+
public SharedBuffer<T> AllocShared<T>(int count) where T : unmanaged
118+
{
119+
ObjectDisposedException.ThrowIf(_disposed, this);
120+
var byteSize = (UIntPtr)(count * Unsafe.SizeOf<T>());
121+
LevelZeroNative.EnsureSuccess(
122+
LevelZeroNative.lz_usm_alloc_shared(_context, _device, byteSize, UIntPtr.Zero, out var ptr));
123+
return new SharedBuffer<T>(_context, ptr, count);
124+
}
125+
126+
/// <summary>
127+
/// Allocates a shared buffer and immediately fills it from a source array.
128+
/// </summary>
129+
public SharedBuffer<T> AllocShared<T>(T[] data) where T : unmanaged
130+
{
131+
var buffer = AllocShared<T>(data.Length);
132+
buffer.Write(data);
133+
return buffer;
134+
}
135+
136+
/// <summary>
137+
/// Launches a kernel with the given work-group counts across up to 3 dimensions,
138+
/// then synchronizes (blocks until complete).
139+
/// </summary>
140+
public void Launch(ComputeKernel kernel, uint groupCountX, uint groupCountY = 1, uint groupCountZ = 1)
141+
{
142+
ObjectDisposedException.ThrowIf(_disposed, this);
143+
LevelZeroNative.EnsureSuccess(LevelZeroNative.lz_command_list_reset(_commandList));
144+
LevelZeroNative.EnsureSuccess(
145+
LevelZeroNative.lz_command_list_append_launch_kernel(_commandList, kernel.Handle,
146+
groupCountX, groupCountY, groupCountZ));
147+
LevelZeroNative.EnsureSuccess(LevelZeroNative.lz_command_list_close(_commandList));
148+
LevelZeroNative.EnsureSuccess(LevelZeroNative.lz_command_queue_execute(_queue, _commandList));
149+
LevelZeroNative.EnsureSuccess(LevelZeroNative.lz_command_queue_synchronize(_queue, ulong.MaxValue));
150+
}
151+
152+
/// <summary>Computes the number of work-groups needed to cover totalItems with the given local size.</summary>
153+
public static uint GroupCount(int totalItems, uint localSize) =>
154+
(uint)Math.Max(1, (totalItems + (int)localSize - 1) / (int)localSize);
155+
156+
public void Dispose()
157+
{
158+
if (_disposed) return;
159+
_disposed = true;
160+
161+
if (_commandList != IntPtr.Zero)
162+
LevelZeroNative.lz_command_list_destroy(_commandList);
163+
if (_queue != IntPtr.Zero)
164+
LevelZeroNative.lz_command_queue_destroy(_queue);
165+
if (_context != IntPtr.Zero)
166+
LevelZeroNative.lz_context_destroy(_context);
167+
168+
_commandList = _queue = _context = _device = _driver = IntPtr.Zero;
169+
}
170+
}

src/ComputeKernel.cs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using System.Runtime.InteropServices;
2+
using LevelZero.Native;
3+
4+
namespace LevelZero;
5+
6+
/// <summary>
7+
/// Wraps a Level Zero kernel handle. Provides typed argument setters and group-size configuration.
8+
/// </summary>
9+
public sealed class ComputeKernel : IDisposable
10+
{
11+
private IntPtr _handle;
12+
private bool _disposed;
13+
14+
internal IntPtr Handle => !_disposed ? _handle : throw new ObjectDisposedException(nameof(ComputeKernel));
15+
16+
internal ComputeKernel(IntPtr handle)
17+
{
18+
_handle = handle;
19+
}
20+
21+
/// <summary>Sets the work-group size for this kernel (local_size in OpenCL terms).</summary>
22+
public void SetGroupSize(uint x, uint y = 1, uint z = 1)
23+
{
24+
ObjectDisposedException.ThrowIf(_disposed, this);
25+
LevelZeroNative.EnsureSuccess(
26+
LevelZeroNative.lz_kernel_set_group_size(_handle, x, y, z));
27+
}
28+
29+
/// <summary>Binds a USM memory pointer to a kernel argument slot.</summary>
30+
public void SetArgBuffer(uint index, IntPtr ptr)
31+
{
32+
ObjectDisposedException.ThrowIf(_disposed, this);
33+
LevelZeroNative.EnsureSuccess(
34+
LevelZeroNative.lz_kernel_set_arg_mem(_handle, index, ptr));
35+
}
36+
37+
/// <summary>Binds a SharedBuffer to a kernel argument slot.</summary>
38+
public void SetArgBuffer<T>(uint index, SharedBuffer<T> buffer) where T : unmanaged
39+
{
40+
SetArgBuffer(index, buffer.Pointer);
41+
}
42+
43+
/// <summary>Sets a scalar int kernel argument.</summary>
44+
public void SetArgInt(uint index, int value)
45+
{
46+
ObjectDisposedException.ThrowIf(_disposed, this);
47+
var ptr = Marshal.AllocHGlobal(sizeof(int));
48+
try
49+
{
50+
Marshal.WriteInt32(ptr, value);
51+
LevelZeroNative.EnsureSuccess(
52+
LevelZeroNative.lz_kernel_set_arg_value(_handle, index, (UIntPtr)sizeof(int), ptr));
53+
}
54+
finally { Marshal.FreeHGlobal(ptr); }
55+
}
56+
57+
/// <summary>Sets a scalar float kernel argument.</summary>
58+
public void SetArgFloat(uint index, float value)
59+
{
60+
ObjectDisposedException.ThrowIf(_disposed, this);
61+
var ptr = Marshal.AllocHGlobal(sizeof(float));
62+
try
63+
{
64+
Marshal.Copy(BitConverter.GetBytes(value), 0, ptr, sizeof(float));
65+
LevelZeroNative.EnsureSuccess(
66+
LevelZeroNative.lz_kernel_set_arg_value(_handle, index, (UIntPtr)sizeof(float), ptr));
67+
}
68+
finally { Marshal.FreeHGlobal(ptr); }
69+
}
70+
71+
/// <summary>Sets a scalar uint kernel argument.</summary>
72+
public void SetArgUInt(uint index, uint value)
73+
{
74+
ObjectDisposedException.ThrowIf(_disposed, this);
75+
var ptr = Marshal.AllocHGlobal(sizeof(uint));
76+
try
77+
{
78+
Marshal.WriteInt32(ptr, unchecked((int)value));
79+
LevelZeroNative.EnsureSuccess(
80+
LevelZeroNative.lz_kernel_set_arg_value(_handle, index, (UIntPtr)sizeof(uint), ptr));
81+
}
82+
finally { Marshal.FreeHGlobal(ptr); }
83+
}
84+
85+
public void Dispose()
86+
{
87+
if (_disposed) return;
88+
_disposed = true;
89+
if (_handle != IntPtr.Zero)
90+
{
91+
LevelZeroNative.lz_kernel_destroy(_handle);
92+
_handle = IntPtr.Zero;
93+
}
94+
}
95+
}

src/ComputeModule.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using LevelZero.Native;
2+
3+
namespace LevelZero;
4+
5+
/// <summary>
6+
/// Wraps a SPIR-V module loaded onto a device. Create kernels from this module.
7+
/// </summary>
8+
public sealed class ComputeModule : IDisposable
9+
{
10+
private IntPtr _handle;
11+
private readonly IntPtr _contextHandle;
12+
private bool _disposed;
13+
14+
/// <summary>Build log from SPIR-V module compilation (may be empty on success).</summary>
15+
public string BuildLog { get; }
16+
17+
internal IntPtr Handle => !_disposed ? _handle : throw new ObjectDisposedException(nameof(ComputeModule));
18+
19+
internal ComputeModule(IntPtr handle, IntPtr contextHandle, string buildLog)
20+
{
21+
_handle = handle;
22+
_contextHandle = contextHandle;
23+
BuildLog = buildLog;
24+
}
25+
26+
/// <summary>
27+
/// Creates a kernel from this module by name.
28+
/// </summary>
29+
public ComputeKernel GetKernel(string name)
30+
{
31+
ObjectDisposedException.ThrowIf(_disposed, this);
32+
LevelZeroNative.EnsureSuccess(
33+
LevelZeroNative.lz_kernel_create(_handle, name, out var kernel));
34+
return new ComputeKernel(kernel);
35+
}
36+
37+
/// <summary>
38+
/// Tries to create a kernel by name. Returns null if the kernel is not found in the module.
39+
/// </summary>
40+
public ComputeKernel? TryGetKernel(string name)
41+
{
42+
ObjectDisposedException.ThrowIf(_disposed, this);
43+
var result = LevelZeroNative.lz_kernel_create(_handle, name, out var kernel);
44+
return result == 0 ? new ComputeKernel(kernel) : null;
45+
}
46+
47+
public void Dispose()
48+
{
49+
if (_disposed) return;
50+
_disposed = true;
51+
if (_handle != IntPtr.Zero)
52+
{
53+
LevelZeroNative.lz_module_destroy(_handle);
54+
_handle = IntPtr.Zero;
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)