OpenCL C++ Utilities

I recently created a small utility library for OpenCL with C++. It consists of a set of function based on the OpenCL C++ bindings to help set up an OpenCL context, compiling OpenCL code and viewing error functions. I hope these functions can be useful for others and I’m planning on adding more utility functions in the future. Note that I haven’t tested it on all platforms yet. Feedback and comments are most welcome.

The source code can be downloaded from my GitHub page.

Context creation

Creating a context with OpenCL can sometimes be a hassle if you want to choose the platform of a specific vendor such as AMD, Intel or NVIDIA. But also if you want to use OpenCL-OpenGL interoperability. I have created two function to ease the pain of OpenCL contextes. To simply create a context with no specifications towards processor type or vendor:

#include "openCLUtilities.hpp"
 
cl::Context context = createCLContext();

If you want to specify processor type you can pass the cl_device_type as the first argument:

#include "openCLUtilities.hpp"
 
cl::Context context = createCLContext(CL_DEVICE_TYPE_GPU);

The second argument to this function can be used to specify which vendor platform you want to use. Currently the following vendors are specified: VENDOR_AMD, VENDOR_INTEL, VENDOR_NVIDIA.

#include "openCLUtilities.hpp"
 
cl::Context context = createCLContext(CL_DEVICE_TYPE_CPU, VENDOR_AMD);

Choosing device type and vendor by program arguments

I’ve also created a function that allows you to specify which type of device or vendor should be used through program arguments. This function is called createCLContextFromArguments and takes argc and argv from your main method as input:

#include "openCLUtilities.hpp"
 
int main(int argc, char ** argv) {
    // Using this function you can add --device gpu/cpu 
    // or --vendor nvidia/amd/intel to your program arguments
    cl::Context context = createCLContextFromArguments(argc, argv);
}

If you now want to specify that you want to use a GPU you can add the “–device gpu” to your executable. And if you wish to specify vendor platform you can do so by adding “–vendor nvidia”.

OpenCL-OpenGL Interoperability

To set up an OpenCL context with OpenGL interopability some extra OS specific properties has to be specified and some libraries included. To make this easier I created an extra function called createCLGLContext(). This function takes the same arguments as the createCLContext() function above.

#include "openCLGLUtilities.hpp"
 
cl::Context context = createCLGLContext();

Note that the file “openCLGLUtilities.hpp” is included instead of “openCLUtilities.hpp”.
This is how the actual function is implemented:

cl::Context createCLGLContext(cl_device_type type, cl_vendor vendor) {
    cl::Platform platform = getPlatform(type, vendor);
 
#if defined(__APPLE__) || defined(__MACOSX)
    // Apple (untested)
    cl_context_properties cps[] = {
        CL_CGL_SHAREGROUP_KHR,
        (cl_context_properties)CGLGetShareGroup(CGLGetCurrentContext()),
        CL_CONTEXT_PLATFORM,
        (cl_context_properties)(platform)(),
        0
    };
#else
#ifdef _WIN32
    // Windows
    cl_context_properties cps[] = {
        CL_GL_CONTEXT_KHR,
        (cl_context_properties)wglGetCurrentContext(),
        CL_WGL_HDC_KHR,
        (cl_context_properties)wglGetCurrentDC(),
        CL_CONTEXT_PLATFORM,
        (cl_context_properties)(platform)(),
        0
    };
#else
    // Linux
    cl_context_properties cps[] = {
        CL_GL_CONTEXT_KHR,
        (cl_context_properties)glXGetCurrentContext(),
        CL_GLX_DISPLAY_KHR,
        (cl_context_properties)glXGetCurrentDisplay(),
        CL_CONTEXT_PLATFORM,
        (cl_context_properties)(platform)(),
        0
    };
#endif
#endif
 
    try {
        cl::Context context = cl::Context(type, cps);
 
        return context;
    } catch(cl::Error error) {
        throw cl::Error(1, "Failed to create an OpenCL context!");
    }
}

Compiling source code and print compile errors

The function buildProgramFromSource() will compile the source file specified for all devices in the given OpenCL context and throw an exception and print out any OpenCL compilation errors. Quite usueful when developing OpenCL kernels.

#include "openCLUtilities.hpp"
 
cl::Context context = createCLContext();
cl::Program program = buildProgramFromSource(context, "kernels.cl");

The compilation errors are retrieved in the following way:

try{
    program.build(devices);
} catch(cl::Error error) {
    if(error.err() == CL_BUILD_PROGRAM_FAILURE) {
        std::cout << "Build log:" << std::endl << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(devices[0]) << std::endl;
    }   
    throw error;
}

The source code can be downloaded from my GitHub page.

You may also like...

16 Responses

  1. Eugene says:

    Hi, thanks for post. I wonder if it’s possible to adapt this for iOS platform. I’m trying to make it just there is bug: clBuildProgram/buildProgramFromSource always to receive error = -11 without build_log.
    is it possible to integrate that for iPhone/iPad? Thanks.

    • Erik Smistad says:

      I have never tried to use OpenCL on the iOS platform. The fact that it doesn’t fail until the build command indicates that the device at least has an OpenCL capable platform and processor. It is difficult to debug since you have no build log, but you can try to comment out some code of the OpenCL code until it builds to identify what lines are causing the problem.

  2. BJ says:

    Are lines 53/70 in OpenCLGLUtilities.cpp and line 24 in the header correct?

    As far as I can tell they should all be the same, but as it is now 53/70 can never both be true at the same time.

    • Erik Smistad says:

      Line 70 is correct, but line 53 in the .cpp file and line 24 in the .hpp file are not correct. They should be the same as line 70 (which means if not Mac OS X system):

      #if !(defined(__APPLE__) || defined(__MACOSX))

      Note that I haven’t really tested this code on Mac OS X, there might be other issues as well.

  3. Mahesh says:

    This really helped me while adding OpenGL interoperability in my OCL application.

  4. Lennart says:

    Hi!

    I am tying to use your utility to load a very simple kernel.

    It fails during build with CL_INVALID_OPERATION in the clBuildProgram call, and this is in turn not caught by your catch-phrase, since that looks for CL_BUILD_PROGRAM_FAILURE:

    catch(cl::Error error) {
    if(error.err() == CL_BUILD_PROGRAM_FAILURE) {
    std::cerr << "Build log:" << std::endl << program.getBuildInfo(devices[0]) << std::endl;
    }
    throw error;
    }

    The error I get is puzzling:

    "/tmp/OCL5RLveT.cl", line 11: warning: null (zero) character in input line
    ignored

    ^

    Suggesting that I have an actual 0-byte in my kernel source, however I have just opened a plain text file in gedit and pasted in the 4 lines of kernel code. When I add empty lines to the end of that file, the error line (11 above) just increases to point at the end of the file.

    I opened my kernel source in an hex editor to confirm that there was no 0-byte, suggesting that there is an error in the loading code in cl::Program buildProgramFromSource(cl::Context context, std::string filename) ;

    I will try to narrow it down further, just wanted to let you know 🙂

  5. leo says:

    Hello Erik,

    thx for this nice piece of code. however, I have some difficulty to make use of CL/GL interop when handling a multi-GPU machine. I actually have both AMD (Radeon HD5870) and NVIDIA cards (GeForce GTX460) in my system (each of these hooked to a different screen). When I try to create the CL context from GL, it succeeds only when the GPU device I chose is the one set as the main screen (for Windows desktop). If I chose the other GPU, clCreatContextFromType will return -1000 as error code. Do you know how to handle this situation? I know that GPU Caps Viewer can handle this kind of situation, but its not open source (and they don’t reply my mail). If you have any suggestion, I’d be glad to hear from you.

    Cheers, leo

    • Erik Smistad says:

      I haven’t tried anything like that myself, but I’m guessing the GL context and display IDs have to be set to some specific value instead of just some default which I guess is the main screen.

      CL_GL_CONTEXT_KHR,
      (cl_context_properties)wglGetCurrentContext(),
      CL_WGL_HDC_KHR,
      (cl_context_properties)wglGetCurrentDC(),

      Let me know if you find out how to do it!

  6. rajko says:

    Hi Erik,

    have you actually used the GL-CL-interop via an Image2DGL? I’m trying …and failing. I’m providing captured frames (uint8 RGB) as input to my test kernel, which is then writing to an Image2DGL. The output is scattered and I’m having a hard time finding the corresponding image formats and channel-orders (Using the ones that are stated in the specs).

    I was able to bind the captured frames to the texture directly just fine, though. So have you managed to get that form of interop working?

    • Erik Smistad says:

      Hi

      Sorry, I haven’t tried to share a texture/image with OpenGL and CL yet. I’ve only tried sharing buffers, which works fine. Should think it would work quite similar. Are you remembering to use glFinish() and enqueueAcquireGLObjects / enqueueReleaseGLObjects to synchronize between OpenGL/CL? If you don’t do this properly you can get very strange results.

      • rajko says:

        Hi,

        thanks for the quick reply, yea I made sure to acquire and release the gl objects. Issued a gl finish before that as well. And a cl finish after the release.

        I’ve posted the problem at stackoverflow if you (or someone else) want to have a further look.

        Thanks!

        • Erik Smistad says:

          If the Image2DGL does not create a valid object, you can check the error it returns, perhaps by using exceptions. Then see which of the following errors you get. This might lead you to the actual problem.

          CL_INVALID_CONTEXT if context is not a valid context.
          CL_INVALID_VALUE if values specified in flags are not valid.
          CL_INVALID_GL_OBJECT if bufobj is not a GL buffer object or is a GL buffer object but does not have an existing data store.
          CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required by the runtime.

          • rajko says:

            No errors on creation of the Image2DGL object, I checked that.

          • rajko says:

            Argh, I found out the reason: Grabbing an RGB frame (3 channels obviously) and then reading float4s in the kernel… That might mess up the picture…

            Sorry that I wasted your time and thanks for your ideas.

            If you ever need a texture interop sample, let me know 😉

Leave a Reply

Your email address will not be published.