OpenCL Kernels can be either executed on the GPU or the CPU. This allows for fallback solutions, where the customer may have a very outdated system. The programmer can also choose to limit their functionality to either the CPU or GPU.
To get started using OpenCL, you'll need a 'Context' and a 'Device'. Both are structures defined by the OpenCL API (also known as cl::Context or clContext & ~Device) and define the used target processor.
To get your device and context, you need to query a list of available platforms, which each can host multiple devices. A Platform represents your physical GPU and CPU, while a device can further distinguish the contained computing units. For GPUs, most platforms will only have one device. But a CPU may offer an additional integrated GPU beside its CPU capabilities.
The context manages memory, command queues, the different kernels and programs. A context can either be limited to a single device but also reference multiple devices.
A quick API note before we start coding: Almost every call to OpenCL gives you an error value, either as return value or via a ref-value (pointer in C). Now lets get started.
ErrorCode err;
var platforms = Cl.GetPlatformIDs(out err);
if(!CheckError(err, "Cl.GetPlatformIDs")) return;
foreach (var platform in platforms) {
foreach (var device in Cl.GetDeviceIDs(platform, DeviceType.Gpu, out err)) {
if(!CheckError(err, "Cl.GetDeviceIDs")) continue;
[...]
}
}
This code snippet queries all available GPU devices on the system. You can now add them to a list or start your context directly with the first match. The 'CheckError(...)' function is a simple utility, that checks whether the error code has the success-value or a different one and can offer you some logging. It is recommended to use a seperate function or macro, because you will call that a lot.
ErrorCode is just an enum on the datatype cl_int for C#, C/C++ can compare the int value with predefined error constants as listed here: https://www.khronos.org/registry/OpenCL/sdk/1.0/docs/man/xhtml/errors.html
You also might want to check whether the device supports all needed features, otherwise your kernels might crash at runtime. You can query a device capability with
Cl.GetDeviceInfo(_device, DeviceInfo.ImageSupport, out err)
This example asks the device whether it can execute image functions. For the next and final step we need to construct our context out of the collected devices.
_context = Cl.CreateContext(null, 1, new[] { _device }, ContextNotify, IntPtr.Zero, out err);
Some stuff is going on here. For C/C++ folks, IntPtr is a pointer address in C#. I will concentrate on the important parts here.
For futher usage, you'll need to preserve your used devices and the context somewhere.
When you have finished all your OpenCL interaction you'll need to release the context again with
Cl.ReleaseContext(_context);