# OpenCL-OpenGL Interop

OpenCL is a cross-platform interface to compute devices.

OpenGL is a cross-platform interface to graphic devices.

Interop means sharing data directly on the device (GPU), without having to transfer via the host (CPU). This provides significant efficiency savings.

So far this page focuses on writing OpenGL textures from OpenCL kernels.

# 1 Device Support

To check if the OpenCL platform supports OpenGL sharing, check the platform extensions string:

// TODO: code example to follow

To check if the OpenCL device supports OpenGL sharing, check the device extensions string:

cl_int err
size_t size = 0;
err = clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, 0, 0, &size);
char *extensions = malloc(size + 1);
err = clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, size, extensions, 0);
extensions[size] = 0;
const char *extension = "cl_khr_gl_sharing";
int len = strlen(extension);
char *match = strstr(extensions, extension);
bool have_gl_sharing = !!match && (match[len] == 0 || match[len] == ' ');

I have support with rusticl but not Clover, using Mesa 25.0.7-2 on AMD RX 580.

# 2 Context Initialisation

OpenGL context etc must be provided at OpenCL context creation time, so you need to initialize OpenGL first.

# 2.1 SDL

Using OpenCL C API and SDL2, with the SDL GL context current:

#include <SDL_syswm.h>

#ifdef SDL_VIDEO_DRIVER_X11
#include <GL/glx.h>
#endif

#ifdef SDL_VIDEO_DRIVER_WINDOWS
#include <wingdi.h>
#endif

cl_context_properties properties[8];
memset(properties, 0, sizeof(properties));
properties[0] = CL_CONTEXT_PLATFORM;
properties[1] = platform;

SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(window, &info);
switch (info.subsystem)
{

#ifdef SDL_VIDEO_DRIVER_X11
  case SDL_SYSWM_X11:
    properties[2] = CL_GLX_DISPLAY_KHR;
    properties[3] = (intptr_t) info.info.x11.display;
    properties[4] = CL_GL_CONTEXT_KHR;
    properties[5] = (intptr_t) glXGetCurrentContext();
    break;
#endif

#ifdef SDL_VIDEO_DRIVER_WINDOWS
  case SDL_SYSWM_WINDOWS:
    properties[2] = CL_WGL_HDC_KHR;
    properties[3] = (intptr_t) info.info.win.hdc;
    properties[4] = CL_GL_CONTEXT_KHR;
    properties[5] = (intptr_t) wglGetCurrentContext();
    break;
#endif

// TODO: Wayland, Apple, ...
}

cl_context *context = clCreateContext(properties, num_devices, devices, 0, 0, &err);

# 2.2 GLFW

I found an OpenCL-OpenGL interop example using GLFW3.

Key things to modify from above SDL snippet:

#ifdef USE_X11
#define GLFW_EXPOSE_NATIVE_X11
#define GLFW_EXPOSE_NATIVE_GLX
#endif

#ifdef USE_WINDOWS
#define GLFW_EXPOSE_NATIVE_WIN32
#define GLFW_EXPOSE_NATIVE_WGL
#endif

#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>

#ifdef USE_X11
properties[3] = (intptr_t) glfwGetX11Display();
properties[5] = (intptr_t) glfwGetGLXContext(window);
#endif

#ifdef USE_WINDOWS
properties[3] = (intptr_t) GetDC(glfwGetWin32Window(window));
properties[5] = (intptr_t) glfwGetWGLContext(window);
#endif

// TODO: Wayland, Apple, ...

# 3 Textures / Images

For writing textures from OpenCL:

# 3.1 Kernel Image Functions

In OpenCL kernel source:

__kernel foo(write_only image2d_t out)
{
  write_imagef(out, (int2)(x, y), (float4)(...));
  write_imagei(out, (int2)(x, y), (int4)(...));
  write_imageui(out, (int2)(x, y), (uint4)(...));
}

The correct write function to use depends on the OpenGL texture internal format. Needs four channels even if texture has fewer.

# 3.2 Image Creation

Create OpenGL texture as normal. After each glTexImage2D() (which invalidates any corresponding OpenCL image), for writing from OpenCL:

glFinish();
int mipmap_level = 0;
cl_mem image = clCreateFromGLTexture(context, CL_MEM_WRITE_ONLY, GL_TEXTURE_2D, mipmap_level, texture, &err);

Connect to kernel with:

clSetKernelArg(kernel, 0, sizeof(image), &image);

Need to clReleaseMemObject() when done.

# 3.3 OpenCL Image Write

See Synchronisation below.

glFinish(); // see Synchronisation below
cl_event acquired_event, kernel_event;
clEnqueueAcquireGLObjects(command_queue, 1, &image, 0, 0, &acquired_event);
clEnqueueNDRangeKernel(command_queue, kernel, dim, offset, global_size, local_size, 1, &acquired_event, &kernel_event);
clEnqueueReleaseGLObjects(command_queue, 1, &image, 1, &kernel_event, 0);
clFinish(command_queue); // see Synchronisation below
clReleaseEvent(acquired_event);
clReleaseEvent(kernel_event);

# 3.4 OpenGL Texture Read

After the image has been written from OpenCL, the texture needs to be rebound in OpenGL:

glBindTexture(GL_TEXTURE_2D, texture);

# 4 Synchronisation

glFinish() and clFinish() are expensive. Synchronisation through GLsync and cl_event is preferable but not yet supported by Mesa 25.0.7-2.

# 4.1 cl_khr_gl_event

OpenGL 3.2+ has:

GLsync glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);

OpenCL extension: cl_khr_gl_event provides:

cl_event clCreateEventFromGLsyncKHR(cl_context context, GLsync sync, cl_int *err);

These events can only be used by clEnqueueAcquireGLObjects().

Need to glDeleteSync() and clReleaseEvent() after use.

Unfortunately does not seem to be widely supported (I don’t have it in rusticl or Clover from Mesa 25.0.7-2 on AMD RX 580).

# 4.2 GL_ARB_cl_event

OpenGL extension GL_ARB_cl_event provides:

GLsync glCreateSyncFromCLeventARB(cl_context context, cl_event event, 0);

Only events from clEnqueueReleaseGLObjects() can be used.

Then OpenGL 3.2+ has:

void glWaitSync(GLsync sync, 0, GL_TIMEOUT_IGNORED); // server (GPU) waits
void glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout_nanoseconds); // client (CPU) waits

There is a potential issue with client wait and flushing.

Need to glDeleteSync() and clReleaseEvent() after use.

Unfortunately does not seem to be widely supported (I don’t have it in Mesa 25.0.7-2 on AMD RX 580).