cancel
Showing results for 
Search instead for 
Did you mean: 

Archives Discussions

Meteorhead
Challenger

Qt5.1 + multi-device OpenCL-GL interop

Hi everyone!

I took on a nice project that uses Qt5.1 alpha and there are problems with OpenCL-GL interop. Let me not repeat myself, but simply link the Qt forum post I made there. All remakrs are welcome on both sides.

0 Likes
1 Solution
moozoo
Adept III

From http://developer.amd.com/download/AMD_APP_SDK_Release_Notes_Developer.pdf

Section 5.3

"

For OpenGL interoperability with OpenCL, there currently is a requirement on when the

OpenCL context is created and when texture/buffer shared allocations can be made. To use

shared resources, the OpenGL application must create an OpenGL context and then an

OpenCL context. All resources (GL buffers and textures) created after creation of the OpenCL

context can be shared between OpenGL and OpenCL. If resources are allocated before the

OpenCL context creation, they cannot be shared between OpenGL and OpenCL."

ie.for your minimalistic program, you need to move the code related to making your vbo (QOpenGLBuffer m_vbo; etc) until after you have made your cl context

i.e. after your for(auto& plat : my_cl_platforms)  code block

The SimpleGL sample in the amd sdk creates the CL command queue before the vbo, so maybe that is required as well.

Will this help... It didn't for me. I also don't have access to an AMD graphics card.

May try GLIntercept with the ARB_debug_output, it might log something that tells you what is going on.

Also a pnf_notify on your CL context.

Note to AMD The info in Section 5.3 should also be in the AMD_Accelerated_Parallel_Processing_OpenCL_Programming_Guide under appendix G

I would have found this information a lot faster,

View solution in original post

0 Likes
22 Replies
Meteorhead
Challenger

Hi!

I would really like to get on with this project, but I got no leads on why it doesn't work. Since my QPainter does not show on my canvas at all (trying to get all the same things working I had when using SFML and SFGUI), so I disabled it just for now. I got 2 complete set of OpenCL inits, both do the same thing (crash), and I have no idea what could cause the problem.

The things I would like to ask here:

  • Is it possible that underlying Qt threading crashes my app?
  • What are the thread restrictions (the context must be used in the same thread as it was created...) to using AMD's OpenCL?
  • Is there any way to debug into "Unhandled exception at 0x000007F92850BD80 (amdocl64.dll) in QInterop_test.exe: 0xC0000005: Access violation reading location 0x0000000000000118."
  • Does really noone have working interop application that is not using QGLWidget? (Something possibly building on top of QWindow and QOpenGLContext)
0 Likes

IMHO it is possible that Qt is doing some threading under. OpenCL should be thread safe with exeption of clSetKernelArg() but OpenGL is not. I have Qt 4.8 app which use OpenCL and QGLWidget interoperability.

0 Likes

I have looked at it a little more. It seems that the app is really one threaded (at least I have not been able to identify any thread being launched by Qt itself, though it's not impossible). I definately know that I have no chance of getting a debug version of amdocl64.dll, but is there any chance that someone who has such a version can tell me what is at location 0x0000000000000118? This address is in the error is very much static, so I guess indeed it is something internal.

I am aware that the problem might be inside the code I wrote myself, but I have been debugging it for 5 days straight, and I can't find what's causing the problem. I would gladly upload a working test case with the base class using QWindow only which is most likely the future of Qt and many would appreciate it.

If someone who has access to debug versions of AMD dlls could check what resource is missing that causes a crash, I would very much appreciate that. I am using 5.1 beta which has installers here, plus VS2012 and Qt VS-addon 1.2.1.

0 Likes

If you place your binaries on some public share, I will have a look at it.

0 Likes

I have my 64-bit built binaries in the x64 dir on the root of this directory. They are meant to be launched from QInterop_test, as they are looking for shaders and kernels as ./Resources/my_shader.glsl etc. I wouldn't like to upload Qt binaries (pls refer to the prebuilt packages link in my previous post) they do link against icu 4.99 dlls, which I compressed in the root directory as icu.rar for convenience. Someone with VS2012, an installed 5.1 beta release of Qt, plus the Qt VS-addin found here can freely rebuild the app and debug inside VS.

I very much appreciate the help.

0 Likes

Hi Meteorhead,

cant access your link to the binary. It may be something from your side, or maybe my organisation blocking the content. Can you attach a zip file here. Also If you can elaborate on the steps to reproduce the issue, please do so

0 Likes

I have attached the entire Qt5_test solution, the same dir that I shared via the read-only Skydrive link. The precompiled exe of QInterop_test already holds the bug which selects the first interop capable GPU it finds (if not, then it falls back to the first interop capable CPU) and after displaying the first frame and trying to update, it segfaults inside clEnqueueAcquireGLObjects. If one wishes to debug, they need the aformentioned libs and the Qt Addin, all install fairly fast from the provided links.

To understand a little bit of code without reading through the link of the Qt forum post in my first message, the code is based upon a pure OpenGL example application (link in my Qt post). It basically shows a base class similar to QGLWidget, which specializes QWidget in a manner that it hides OpenGL context creation from the user and provides a few unimplemented functions that are called automatically by the event process loop and ensure that the unimplemneted initGL (or whatever the exact name is) function is run before the first paintGL function is called (both being triggered by the first expose or resize event the Widget gets).

The example application shows how to rewrite a class similar to QGLWidget, but not inheriting from QWidget (big overkill for most cases), but from QWindow which is a lot more lightweight and pretty much the future of writing GUI in terms of Qt. My InteropWindow base class is very similar to OpenGLWindow in the aforementioned link, but it provides both initializeGL and initializeCL functions which are run in the correct order (first OpenGL context is created, then OpenGL extension functions are initialized, then OpenGL resources are created (initializeGL), then OpenCL context is created on an interop device, then OpenCL resources are created (initializeCL) and all of this is ensured to run prior to the first attempt to draw the contents of the window. My inheriting class CubeRotator implements the mandatory functions and segfaults utterly when trying to update the scene.

To put it short: to reproduce the issue, run one of the prebuilt executable inside Qt5_test/x64 or to look at source code, look inside Qt5_test/QInterop_test directory, and to do step-by-step debug, install everything I mentioned, and do everthing in VS2012.

0 Likes

Hi!

I was hoping to get some response, but I guess nobody's got the time to debug user SW (which is understandable), however... I have spent roughly 1 week of my time (with a few other sideprojects) debugging into the issue, and I lost a small piece of my mind when I figured that it all works under linux.

I have attached the solution with the missing parts filled in for linux, and it all works (naturally, on the GPU). Effectively 3 smaller parts of the code differ:

// Resource handles

    QPlatformNativeInterface* plat_int;     // Platform native interface to obtain OS-spcific handles

#ifdef _WIN32

    QPair<HDC,HGLRC> m_gl_device;           // Windows-specific OpenGL device handles

#endif

#ifdef __linux__

    QPair<Display*,GLXContext> m_gl_device; // Linux-specific OpenGL device handles

#endif

inside InteropWindow.hpp

#ifdef _WIN32

    // Qt5 way

    m_gl_device.first = GetDC(static_cast<HWND>(plat_int->nativeResourceForWindow(QByteArrayLiteral("handle"), this)));

    m_gl_device.second = static_cast<HGLRC>(plat_int->nativeResourceForContext(QByteArrayLiteral("renderingContext"), m_gl_context));

    // Native way

    QPair<HDC,HGLRC> m_gl_device_native;

    m_gl_device_native.first = wglGetCurrentDC();

    m_gl_device_native.second = wglGetCurrentContext();

    qDebug() << "InteropWindow: Window on screen " << this->screen()->name() << " has managed HDC = " << m_gl_device.first << " and HGLRC = " << m_gl_device.second;

    qDebug() << "InteropWindow: Window on screen " << this->screen()->name() << " has native HDC = " << m_gl_device_native.first << " and HGLRC = " << m_gl_device_native.second;

#endif

#ifdef __linux__

    /*

    // Qt5 way

    m_gl_device.first = *static_cast<Screen*>(plat_int->nativeResourceForWindow(QByteArrayLiteral("handle"), this));

    m_gl_device.second = static_cast<GLXContext>(plat_int->nativeResourceForContext(QByteArrayLiteral("renderingContext"), m_gl_context));

    */

    // Native way

    QPair<Display*,GLXContext> m_gl_device_native;

    m_gl_device_native.first = glXGetCurrentDisplay();

    m_gl_device_native.second = glXGetCurrentContext();

    m_gl_device = m_gl_device_native;

//    qDebug() << "InteropWindow: Window on screen " << this->screen()->name() << " has managed Screen = " << m_gl_device.first.root << " and GLXContext = " << (void*)m_gl_device.second;

    qDebug() << "InteropWindow: Window on screen " << this->screen()->name() << " has native Screen = " << m_gl_device_native.first << " and GLXContext = " << m_gl_device_native.second;

inside void InteropWindow::createCLcontext_helper()

cl_context_properties properties[] = {

     CL_CONTEXT_PLATFORM, reinterpret_cast<cl_context_properties>(platform()),

#ifdef _WIN32

            CL_GL_CONTEXT_KHR, reinterpret_cast<cl_context_properties>(m_gl_device.second),

            CL_WGL_HDC_KHR, reinterpret_cast<cl_context_properties>(m_gl_device.first),

#endif

#ifdef __linux__

            CL_GLX_DISPLAY_KHR, reinterpret_cast<intptr_t>(m_gl_device.first),

            CL_GL_CONTEXT_KHR, reinterpret_cast<intptr_t>(m_gl_device.second),

#endif

     0 };

inside bool InteropWindow::lookForDeviceType(cl_bitfield devtype).

I do not know the QPA literals to obtain the handles, so in under linux I only implemented the native way of obtaining the handles. On Windows, both QPA and the native way resolve to the same pointers.

I am on the verge of giving up, as I see really no reason why it doesn't work, moreover I have tried numerous things the could potentially solve a non-existing problem, but nothing works.

Sidenote: I used the official Qt5.1 beta prebuilt packages (linked against desktop OpenGL) found here.

0 Likes

In case someone would find the code too intimidating, I have created a minimalistic program that shows the problem. One main.cpp, Windows crashes, linux survives. Compile the program with whatever IDE or compiler you like.

#include <QGuiApplication>

#include <QtGui>

#include <CL/cl.hpp>

#ifdef __linux__

#include <X11/Xlib.h>

#include <X11/Xutil.h>

#include <X11/Xresource.h>

#include <GL/glx.h>

#undef Bool

#endif

void myError(cl_int& err)

{

    if(err != CL_SUCCESS)

    {

        qDebug() << "ERROR: " << err;

    }

}

int main(int argc, char *argv[])

{

    QGuiApplication app(argc, argv);

    // Setup desired format

    QSurfaceFormat my_surfaceformat;

    my_surfaceformat.setRenderableType(QSurfaceFormat::RenderableType::OpenGL);

    my_surfaceformat.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);

    my_surfaceformat.setSwapBehavior(QSurfaceFormat::SwapBehavior::DoubleBuffer);

    my_surfaceformat.setMajorVersion(2);

    my_surfaceformat.setMinorVersion(0);

    my_surfaceformat.setRedBufferSize(8);

    my_surfaceformat.setGreenBufferSize(8);

    my_surfaceformat.setBlueBufferSize(8);

    my_surfaceformat.setAlphaBufferSize(8);

    my_surfaceformat.setDepthBufferSize(24);

    my_surfaceformat.setStencilBufferSize(8);

    my_surfaceformat.setStereo(false);

    QOffscreenSurface my_offscreen_target;

    my_offscreen_target.setFormat(my_surfaceformat);

    my_offscreen_target.create();

    QOpenGLContext my_gl_context(&app);

    my_gl_context.setFormat(my_offscreen_target.format());

    my_gl_context.create();

    if(!my_gl_context.makeCurrent(&my_offscreen_target)) qWarning("Could not create OpenGL context");

    QOpenGLBuffer m_vbo;

    if(!m_vbo.create()) qWarning("Could not create VBO");

    m_vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);

    if(!m_vbo.bind()) qWarning("Could not bind VBO");

    m_vbo.allocate(8*sizeof(QVector4D));

    m_vbo.release();

    cl_int CL_err = CL_SUCCESS;

    std::vector<cl::Platform> my_cl_platforms;

    cl::Platform::get(&my_cl_platforms);

    cl::Context my_cl_context;

    cl::Device my_cl_device;

    cl::CommandQueue my_cl_commandqueue;

    for(auto& plat : my_cl_platforms)

    {

        std::vector<cl::Device> devices;

        plat.getDevices(CL_DEVICE_TYPE_GPU, &devices);

        if(!devices.empty())

        {

            cl_context_properties properties[] = {

            CL_CONTEXT_PLATFORM, reinterpret_cast<cl_context_properties>(plat()),

#ifdef _WIN32

            CL_WGL_HDC_KHR, reinterpret_cast<cl_context_properties>(wglGetCurrentDC()),

            CL_GL_CONTEXT_KHR, reinterpret_cast<cl_context_properties>(wglGetCurrentContext()),

#endif

#ifdef __linux__

            CL_GLX_DISPLAY_KHR, reinterpret_cast<cl_context_properties>(glXGetCurrentDisplay()),

            CL_GL_CONTEXT_KHR, reinterpret_cast<cl_context_properties>(glXGetCurrentContext()),

#endif

            0 };

            my_cl_device = devices.at(0);

            my_cl_context = cl::Context(my_cl_device, properties, NULL, NULL, &CL_err);

            myError(CL_err);

            my_cl_commandqueue = cl::CommandQueue(my_cl_context, my_cl_device, 0, &CL_err);

            myError(CL_err);

        }

    }

    std::vector<cl::Memory> my_interop_buffs;

    my_interop_buffs.push_back(cl::BufferGL(my_cl_context, CL_MEM_READ_WRITE, m_vbo.bufferId(), &CL_err));

    myError(CL_err);

    my_cl_commandqueue.enqueueAcquireGLObjects(&my_interop_buffs, NULL, NULL);

    my_cl_commandqueue.enqueueReleaseGLObjects(&my_interop_buffs, NULL, NULL);

    return app.exec();

}

Ultimately nothing should display, not even error messages.

I stepped through in debug mode inside my_gl_context.create() and makeCurrent() both, and as far as I could see nothing fishy is done inside. Apart from the many-many error checks and validity checks and misuse checks inside, when making the context current, it checks every time whether the context has already been registered in a container, for some misterious reason. (Of course it is not the misterous, I had to implement such a thing myself when I was creating a C++ wrapper for OpenGL so that getCurrent() can return the corresponding wrapper object, and not the low-level handles. I guess this does the same thing.) Qt also keeps track of the thread owning a context, so it cannot be made valid in another thread at the same time. So apart from MANY MANY checks, it looks OK to me.

I thought the problem could be that Qt uses EGL instead of WGL or whatever underneath, something that OpenCL is not friends with, but there's only ordinary wgl calls underneath. No black magic.

Could someone lend me a hand in this?

0 Likes

If you use default windows compilation it utilises ANGLE as OpenGL ES 2.0 wrapper on top of DirectX. That could be the reason. If you want to use native OpenGL calls you should compile Qt from sources using "-opengl desktop" configuration flag.

0 Likes

Hello wojtek,

I am aware of the ANGLE project being default, that is why use binaries that link against desktop OpenGL. 3 posts earlier I finished my post with:

"Sidenote: I used the official Qt5.1 beta prebuilt packages (linked against desktop OpenGL) found here."

Just to make sure, I built Qt myself using the configuration options as follow:

configure.bat -platform win32-msvc2012 -opensource -debug-and-release -c++11 -mp -opengl desktop -nomake webkit -nomake examples -nomake demos -nomake tests -prefix C:\Kellekek\Qt-5.1.0-beta

I do not see any ANGLE dlls loaded when I run my app, only amd OpenGL dlls.

0 Likes

I am pretty sure you should also share the context using setShareContext() function: http://doc-snapshot.qt-project.org/qt5-dev/qtgui/qopenglcontext.html#setShareContext

0 Likes

Hi wojtek!

I appreciate your comment, however this function is totally unapplicable to this situation. setShareContext creates an OpenGL context share group and must be used when one wishes to share resources between OpenGL contexts. However I cannot call this function here, since I have only a single OpenGL context. The input parameter of the function is QOpenGLContext*, which should point to the other OpenGL context. I tried calling it on the context itself, but to no surprise, it segfaults inside setShareContext. I cannot set this share behavior onto the OpenCL context, because that has no GL handles.

Interop must work without OpenGL being explicitly told, that it's resources are going to be used by OpenCL. Interop must be completely transparent from OpenGL's POV.

0 Likes
moozoo
Adept III

From http://developer.amd.com/download/AMD_APP_SDK_Release_Notes_Developer.pdf

Section 5.3

"

For OpenGL interoperability with OpenCL, there currently is a requirement on when the

OpenCL context is created and when texture/buffer shared allocations can be made. To use

shared resources, the OpenGL application must create an OpenGL context and then an

OpenCL context. All resources (GL buffers and textures) created after creation of the OpenCL

context can be shared between OpenGL and OpenCL. If resources are allocated before the

OpenCL context creation, they cannot be shared between OpenGL and OpenCL."

ie.for your minimalistic program, you need to move the code related to making your vbo (QOpenGLBuffer m_vbo; etc) until after you have made your cl context

i.e. after your for(auto& plat : my_cl_platforms)  code block

The SimpleGL sample in the amd sdk creates the CL command queue before the vbo, so maybe that is required as well.

Will this help... It didn't for me. I also don't have access to an AMD graphics card.

May try GLIntercept with the ARB_debug_output, it might log something that tells you what is going on.

Also a pnf_notify on your CL context.

Note to AMD The info in Section 5.3 should also be in the AMD_Accelerated_Parallel_Processing_OpenCL_Programming_Guide under appendix G

I would have found this information a lot faster,

0 Likes

Hi moozoo,

Thank you for your suggestion. Indeed this was the problem. I created the GL resources before the OpenCL context was created. The only question remains: how does this misuse work on linux then? I appreciate that things work even when I misuse them, but going easy on the specs makes life harder when one goes for portability. It might lead the programmer to think that his code is otherwise OK, when infact one platform automagically corrects a bug. This issue arises a lot of times between GCC and MSVC (explicit template instantiation for one, where MSVC figures out what I wanted to do even my syntax is completely broken).

Anyhow, thanks for the help, my InteropWindow base class works now. I will post a working, cleaned up version sometime soon.

0 Likes

>how does this misuse work on linux then

Beware that this is a limitation of AMD's implementation of OpenGL/Opencl.

With Intel and Nvidia you can make your vbo before your cl context.

Whats more the AMD openGL combined with the AMD openCL CPU driver also works (i'm told).

I don't think the openCL standard covers this.

Most of the example code I have seen, bar the AMD simpleGL one create the vbo before the cl context.

However it seems logical to me that the vbo's to be shared should be made after the cl context so that the openGL is aware of this when its created.

NOTE to AMD.

it is a bug that clCreateFromGLBuffer does not return CL_INVALID_GL_OBJECT when the GL object was created prior to the CL Context.

It should not  quietly fail.

0 Likes

Thanks for reporting it. It will be helpful if you could give a small repro-case too.

0 Likes
Meteorhead
Challenger

I think moozoo referred to the last sample code I posted which holds just this minimal case. Issue is solved if the QOpenGLBuffer creation (the .create() function call) is moved after the OpenCL context creation part.

0 Likes

What is needed is for someone to take the SimpleGL example in the AMD SDK and move the vbo creation to a point just before the CL context is created. And then see what happens.when it runs against the GPU CL device.

i.e. in SimpleGLSample::setupCL()

Move lines 676 to 686 (starts with //Create Vertex buffer object) to the start of the function.

What should happen is

"

    posBuf = clCreateFromGLBuffer(context, CL_MEM_WRITE_ONLY, vertexObj, &status);

    CHECK_OPENCL_ERROR(status, "clCreateFromGLBuffer failed. (posBuf)");"

should report an error.

From my code, and Meteorhead's code we predict it won't. It will either crash or just keep going without error.

0 Likes

Hi moozoo,

sorry, but i am not that familiar with OpenGL

I tried the following changes in SimpleGL.cpp, and disabled as much code as possible to make the sample still run. But I am getting a segmentation fault in glGenbuffers function. Probably you can help here.

EDIT: Just search for the code you mentioned at line 678 to know the changes. Its location has been changed, and most of the code after creating it has been disabled.

0 Likes
perfunction
Journeyman III

I've just spent the better part of a day trying to debug a very similar problem. I was coding directly to the Windows API and running the app on a Windows 8 desktop. No matter what I tried I was getting an access violation at the address 0xFC when I tried to use clEnqueueAcquireGLObjects on a shared vertex buffer. If I passed in invalid data to clEnqueueAcquireGLObjects, it handle that correctly. But whenever I tried to actually acquire my vertex buffer it would throw an exception.

I finally "solved" my problem by throwing out my own window creation code and using FreeGLUT 2.8.1. It would seem that there is something very particular about the way an OpenGL ready window gets created under Windows for the AMD SDK to integrate with it. I've attached a sample of the code I was previously using to setup the window.

0 Likes