22 Replies Latest reply on Jul 6, 2013 9:58 PM by perfunction

    Qt5.1 + multi-device OpenCL-GL interop

    Meteorhead

      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.

        • Re: Qt5.1 + multi-device OpenCL-GL interop
          Meteorhead

          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)
            • Re: Qt5.1 + multi-device OpenCL-GL interop
              nou

              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.

                • Re: Qt5.1 + multi-device OpenCL-GL interop
                  Meteorhead

                  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.

                    • Re: Qt5.1 + multi-device OpenCL-GL interop
                      tzachi.cohen

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

                        • Re: Qt5.1 + multi-device OpenCL-GL interop
                          Meteorhead

                          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.

                            • Re: Qt5.1 + multi-device OpenCL-GL interop
                              himanshu.gautam

                              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

                                • Re: Qt5.1 + multi-device OpenCL-GL interop
                                  Meteorhead

                                  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.

                                  • Re: Qt5.1 + multi-device OpenCL-GL interop
                                    Meteorhead

                                    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.

                                      • Re: Qt5.1 + multi-device OpenCL-GL interop
                                        Meteorhead

                                        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.

                                          • Re: Qt5.1 + multi-device OpenCL-GL interop
                                            Meteorhead

                                            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?

                                              • Re: Qt5.1 + multi-device OpenCL-GL interop
                                                wojtek

                                                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.

                                                  • Re: Qt5.1 + multi-device OpenCL-GL interop
                                                    Meteorhead

                                                    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.

                                • Re: Qt5.1 + multi-device OpenCL-GL interop
                                  moozoo

                                  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,

                                    • Re: Qt5.1 + multi-device OpenCL-GL interop
                                      Meteorhead

                                      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.

                                        • Re: Qt5.1 + multi-device OpenCL-GL interop
                                          moozoo

                                          >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.

                                      • Re: Qt5.1 + multi-device OpenCL-GL interop
                                        Meteorhead

                                        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.

                                          • Re: Qt5.1 + multi-device OpenCL-GL interop
                                            moozoo

                                            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.

                                              • Re: Re: Qt5.1 + multi-device OpenCL-GL interop
                                                himanshu.gautam

                                                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.

                                            • Re: Qt5.1 + multi-device OpenCL-GL interop
                                              perfunction

                                              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.