PyOpenCL:gl 共享上下文创建失败(溢出错误)

PyOpenCL: gl sharing context creation failed (overflow error)

我想创建一个应用程序,使用 OpenCL 计算纹理中每个像素的颜色值,并使用 OpenGL 显示该纹理。问题是,当我尝试创建具有 GL 共享属性的上下文时,程序会冻结。使用函数 get_gl_sharing_context_properties() 我得到一个列表 [(8200, 65538), (8203, 18446744072971422270)]。列表中的最后一个数字太大,无法转换为 64 位 int,我收到溢出错误。 我用来创建 CL 上下文的代码:

def cl_init():
    platform = cl.get_platforms()[1]
    device = platform.get_devices(cl.device_type.GPU)

    from pyopencl.tools import get_gl_sharing_context_properties

    print(cl.have_gl())
    print(get_gl_sharing_context_properties())
    print(sys.platform)
    context = cl.Context(properties=[
                (cl.context_properties.PLATFORM, platform)] +
                get_gl_sharing_context_properties())

    print("Context creation done")

    queue = cl.CommandQueue(context)

代码永远不会到达 print("Context creation done")。我使用 QtPy4 和 QGLWidget 创建 OpenGL 上下文并显示纹理。

好的 运行 我自己也遇到过类似的问题,这是我的解决方法。如果你根据tools模块下的PyOpenCL's source code on github具体看一下get_gl_sharing_context_properties()是干什么的,你会发现下面的平台相关代码:

def get_gl_sharing_context_properties():
    ctx_props = cl.context_properties

    from OpenGL import platform as gl_platform

    props = []

    import sys
    if sys.platform in ["linux", "linux2"]:
        from OpenGL import GLX
        props.append(
            (ctx_props.GL_CONTEXT_KHR, gl_platform.GetCurrentContext()))
        props.append(
                (ctx_props.GLX_DISPLAY_KHR,
                    GLX.glXGetCurrentDisplay()))
    elif sys.platform == "win32":
        from OpenGL import WGL
        props.append(
            (ctx_props.GL_CONTEXT_KHR, gl_platform.GetCurrentContext()))
        props.append(
                (ctx_props.WGL_HDC_KHR,
                    WGL.wglGetCurrentDC()))
    elif sys.platform == "darwin":
        props.append(
            (ctx_props.CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE,
                cl.get_apple_cgl_share_group()))
    else:
        raise NotImplementedError("platform '%s' not yet supported"
                % sys.platform)

    return props

现在,由于我在 Windows,我查看了 WGL 代码(来自 PyOpenGL)并发现 WGL.wglGetCurrentDC()(如 get_gl_sharing_context_properties()当然)有时会返回不正确的句柄值,这会导致整数溢出。事实证明,DC(设备上下文)确实 应该 是一个 32 位有符号整数 。但是,在 PyOpenGL 包装函数中,它被错误地转换为 64 位无符号整数 。因此,每当将负的 DC 句柄值传递到 PyOpenGL 时,它就会被转换为 2^64 + annoying_negative_dc_number

解决方法:转换回来!这是我现在为可靠地获得有效的 gl 互操作上下文所做的工作:

platform = cl.get_platforms()[0]
ctx_props = cl.context_properties
gl_props = get_gl_sharing_context_properties()
device_context = gl_props[-1][-1]
if device_context >= 2 ** 32:
    device_context -= (2 ** 64)
fixed_gl_props = [gl_props[0], (gl_props[-1][0], device_context)]
ctx = cl.Context(properties=[(ctx_props.PLATFORM, platform)] + fixed_gl_props)  

pyopencl 中存在导致上下文创建处理不正确的错误。本质上,pyopencl 获取 opencl 属性的元组列表并在内部解析它们并将它们传递给 CL 上下文创建处理程序。元组作为指针或整数传递。当为某些属性传递指针时,假设将指针转换为整数值以传递给 CL 上下文创建处理程序,但这样做是错误的。相反,它获取指针本身的地址。

有两种可能的修复方法。

从用户代码端,您可以执行类似以下的操作来手动传递损坏属性的整数值,例如 GLX_DISPLAY_KHR。或者您可以通过分离从 get_gl_sharing_context_properties() 返回的元组并更正它们来解决问题:

    glx_addr = ctypes.cast(OpenGL.GLX.glXGetCurrentDisplay(), ctypes.c_void_p).value #retrieve the glx ptr val as an int
    context = cl.Context(properties=
        [(cl.context_properties.GL_CONTEXT_KHR, OpenGL.platform.GetCurrentContext() )] +
        [(cl.context_properties.GLX_DISPLAY_KHR, glx_addr )] +
        [(cl.context_properties.PLATFORM, cl.get_platforms()[0])]
    )

或者在 cffi_cl.py pyopencl 的 src 代码中修复错误并重新编译:

def _parse_context_properties(properties):
    ...
    from ctypes import _Pointer, addressof
    from ctypes import cast # add this

    if isinstance(value, _Pointer):
        #val = addressof(value) # remove this
        val = cast(value, ctypes.c_void_p).value # add this
    else:
        val = int(value)
    ...

我不久前将此错误告知了开发人员,希望它能尽快修复。参见:https://lists.tiker.net/pipermail/pyopencl/2017-April/002285.html