OpenGL 3.x and Qt framework

Hi folks,

This is my very first post in this forum, actually I’m quite a noob in OpenGL, and I should say in visualization too.

I’m trying to create and use an OpenGL 3.x context with the Qt framework, using a subclass of QWidget (and not QGLWidget).

Firstly, I looked on Google, and it seems that nobody did this, or at least nodody shared. There is a similar post in the OpenGL on Windows forum, but I don’t think the poster made it eventually.

Actually I almost made it : I succeeded in creating a 3.0 context, but without alpha blending, and this is my issue … When I ask within a visual attributes array a GLX_ALPHA_SIZE of 8 for my context, context is created, but then I get a BadMatch X error when calling glXMakeCurrent. When I remove the GLX_ALPHA_SIZE, it do works, I can use my context, but without any transparency …

Here is the piece of code initializing OpenGL inside my derived QWidget class :


void GL3Widget::init_gl3() {

  if(initialized_ == true)
    return;

  display_=XOpenDisplay(0);
  QX11Info current_x11_info;
  XVisualInfo *current_vi=(XVisualInfo*)current_x11_info.visual();
  printf("Current visual ID 0x%x
", (unsigned int)current_vi -> visualid);

  // Get a matching FB config
  
  static int visual_attribs[] =
    {
      GLX_X_RENDERABLE    , true,
      GLX_DRAWABLE_TYPE   , GLX_WINDOW_BIT,
      GLX_RENDER_TYPE     , GLX_RGBA_BIT,
      GLX_X_VISUAL_TYPE   , GLX_TRUE_COLOR,
      GLX_RED_SIZE        , 8,
      GLX_GREEN_SIZE      , 8,
      GLX_BLUE_SIZE       , 8,
      GLX_ALPHA_SIZE      , 8,
      GLX_DEPTH_SIZE      , 24,
      GLX_DOUBLEBUFFER    , true,
      None
    };
  

  printf( "Getting matching framebuffer configs
" );
  int fbcount;
  GLXFBConfig *fbc = glXChooseFBConfig( display_, DefaultScreen( display_ ), 
                                        visual_attribs, &fbcount );

  if ( !fbc )
  {
    printf( "Failed to retrieve a framebuffer config
" );
    exit(1);
  }
  printf( "Found %d matching FB configs.
", fbcount );

  // Pick the FB config/visual with the most samples per pixel
  printf( "Getting XVisualInfos
" );
  int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;

  int i;
  for ( i = 0; i < fbcount; i++ )
  {
    XVisualInfo *vi = glXGetVisualFromFBConfig( display_, fbc[i] );
    if ( vi )
    {
      int samp_buf, samples, visual_id, red_size, blue_size, green_size, alpha_size;
      glXGetFBConfigAttrib( display_, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf );
      glXGetFBConfigAttrib( display_, fbc[i], GLX_SAMPLES       , &samples  );
      glXGetFBConfigAttrib( display_, fbc[i], GLX_VISUAL_ID       , &visual_id  );
      glXGetFBConfigAttrib( display_, fbc[i], GLX_RED_SIZE       , &red_size  );
      glXGetFBConfigAttrib( display_, fbc[i], GLX_BLUE_SIZE       , &blue_size  );
      glXGetFBConfigAttrib( display_, fbc[i], GLX_GREEN_SIZE       , &green_size  );
      glXGetFBConfigAttrib( display_, fbc[i], GLX_ALPHA_SIZE       , &alpha_size  );
      printf( "  Matching fbconfig %d, visual ID 0x%x , SAMPLE_BUFFERS = %d,"
              " SAMPLES = %d R:%d G:%d B:%d A%d
", 
              i, visual_id, samp_buf, samples, red_size, blue_size, green_size, alpha_size);

      if ( (best_fbc < 0 || samp_buf) && (samples > best_num_samp) )
        best_fbc = i, best_num_samp = samples;
      if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp )
        worst_fbc = i, worst_num_samp = samples;
    }
    XFree( vi );
  }

  // Get a visual
  int fbc_id = best_fbc;

  XVisualInfo *vi = glXGetVisualFromFBConfig( display_, fbc[ fbc_id ]  );
  printf( "Chosen visual ID = 0x%x
", (unsigned int)vi->visualid );

  // See if GL driver supports glXCreateContextAttribsARB()
  //   Create an old-style GLX context first, to get the correct function ptr.
  glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;

  GLXContext ctx_old=glXCreateContext( display_, vi, 0, true );
  glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
           glXGetProcAddress( (const GLubyte *) "glXCreateContextAttribsARB" );

  ctxt_ = 0;

  // If it doesn't, just use the old-style 2.x GLX context
  if ( !glXCreateContextAttribsARB )
  {
    printf( "glXCreateContextAttribsARB() not found"
            " ... using old-style GLX context
" );
    ctxt_ = ctx_old;
  }

  // If it "does", try to get a GL 3.0 context!
  else
  {
    glXMakeCurrent( display_, 0, 0 );
    glXDestroyContext( display_, ctx_old );

    static int context_attribs[] =
      {
        GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
        GLX_CONTEXT_MINOR_VERSION_ARB, 0,
        None
      };

    printf( "Creating context
" );
    ctxt_ = glXCreateContextAttribsARB( display_, fbc[ fbc_id ], 0, 
                                      true, context_attribs );
    if ( ctxt_ )
      printf( "Created GL 3.0 context
" );
    else
    {
      // Couldn't create GL 3.0 context.  Fall back to old-style 2.x context.
      printf( "Failed to create GL 3.0 context"
              " ... using old-style GLX context
" );
      ctxt_ = glXCreateContext( display_, vi, 0, true );
    }
  }

  XFree( fbc );

  // Verifying that context is a direct context
  printf( "Verifying that context is direct
" );
  if ( ! glXIsDirect ( display_, ctxt_ ) )
  {
    printf( "Indirect GLX rendering context obtained" );
    exit(1);
  }

  printf( "Making context current on drawable 0x%x ...
", (unsigned int)winId() );
  if(!glXMakeCurrent( display_, winId(), ctxt_ )) {
    printf( "glxMakeCurrent failed :-/
" );
    exit(1);
  }

  glClearColor ( 0, 0.0, 1, 1 );
  glClear ( GL_COLOR_BUFFER_BIT );

  initialized_=true;
}

Most of this code comes from the OpenGL 3.0 and GLX tutorial on the wiki, I just tried to integrate it within Qt.
Hardware info : Nvidia GTX260 with latest 190.something OpenGL 3.2 drivers, running on Ubuntu 9.04 64 bits.

So to sum-up, if i comment out the GLX_ALPHA_SIZE line, i get a working context, but without transparency. If i keep the line, i get a XError : BadMatch on the glxMakeCurrent call.

Any idea or suggestion about what could be wrong ?

Thanks !

PS : if anyone is interested in testing the code, I can try to post the whole code with the qmake .pro file (very small, ~200 lines), is it possible to attach a file in this forum ?

I was having the same problems as you are. I found that creating my own window and forcing Qt to use that window was the solution. I couldn’t find any other way to force Qt to create a window with a visual I wanted…

It is not complete (i.e. it is still experimental) but here it is the code:


        Widget::Widget()
            : QWidget(NULL, Qt::MSWindowsOwnDC)
        {
            setAttribute(Qt::WA_PaintOnScreen);
            setAttribute(Qt::WA_NoSystemBackground);
            setAutoFillBackground(true);
#ifdef Q_WS_X11
            int visualAttribs[] =
            {
                GLX_X_RENDERABLE    , True,
                GLX_DRAWABLE_TYPE   , GLX_WINDOW_BIT,
                GLX_RENDER_TYPE     , GLX_RGBA_BIT,
                GLX_X_VISUAL_TYPE   , GLX_TRUE_COLOR,
                GLX_DOUBLEBUFFER    , True,
                GLX_RED_SIZE        , 8,
                GLX_GREEN_SIZE      , 8,
                GLX_BLUE_SIZE       , 8,
                GLX_ALPHA_SIZE      , 8,
                GLX_DEPTH_SIZE      , 24,
                GLX_SAMPLE_BUFFERS  , True,
                GLX_SAMPLES         , 2,
                None
            };
            const QX11Info &xinfo = x11Info();
            Display *display = xinfo.display();
            XVisualInfo *vi = NULL;
            {
                Window window = 0;
                int fbcount = 0;
                GLXFBConfig *fbc = ::glXChooseFBConfig(display, DefaultScreen(display), visualAttribs, &fbcount);
                if(fbc == NULL)
                {
                    qFatal("No valid framebuffer config.");
                }
                mFBConfig = fbc[0]; // Store the GLXFBConfig so that it can be used to create GLXContext later.
                XFree(fbc);
                vi = ::glXGetVisualFromFBConfig(display, mFBConfig);
                {
                    XSetWindowAttributes swa;
                    swa.colormap          = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone);
                    swa.background_pixmap = None;
                    swa.border_pixel      = 0;

                    window = XCreateWindow(display, RootWindow(display, vi->screen), 0, 0, 100, 100, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap, &swa);
                    if(window == 0)
                    {
                        qFatal("Unable to create window.");
                    }
                }

                // tell Qt we want to use this window instead...
                create(window, true, true); // http://doc.trolltech.com/4.5/qwidget.html#create
            }
#endif
        }


And then somewhere else in my code I create the context:


#ifdef Q_WS_X11
            Display *display;
            {
                const QX11Info &xinfo = glwidget.x11Info();
                display = xinfo.display();
            }
            XVisualInfo *vi = ::glXGetVisualFromFBConfig(display, glwidget.getFBConfig());

            GLXContext tempContext = ::glXCreateContext(display, vi, 0, True);
            Q_ASSERT(tempContext != NULL);
            mGLXFuncs.mCreateContextAttribsFn = (PFNGLXCREATECONTEXTATTRIBSARBPROC)::glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB");
            Q_ASSERT(mGLXFuncs.mCreateContextAttribsFn != NULL);
            ::glXMakeCurrent(display, 0, 0);
            ::glXDestroyContext(display, tempContext);
            int attribs[] =
            {
                GLX_CONTEXT_MAJOR_VERSION_ARB, requestedGLVersion.Major,
                GLX_CONTEXT_MINOR_VERSION_ARB, requestedGLVersion.Minor,
                //WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
                //WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
                None
            };
            GLXContext gl3Context = mGLXFuncs.mCreateContextAttribsFn(display, glwidget.getFBConfig(), 0, True, attribs);
            if(gl3Context == NULL)
            {
                qFatal("Unable to create gl context.");
            }
            if(::glXIsDirect(display, gl3Context) != True)
            {
                qFatal("The gl context is not direct.");
            }
#endif


Oh my god, thank you so much Orhun !!

You are right, the key is to force Qt to use a new compatible Window, it’s working now !

It is incredible that such basic information is so hard to find on the internet, I mean, Qt is a very famous and used framework, OpenGL 3.0 has been released for a year, but very few people seems to know how to do, so thanks again.

I am a Qt paying customer, so i even asked to the Qt support (without giving them the above code I must admit), and they only told me “Qt does not currently support OpenGL 3.0. But we are looking into adding support for this in the future.”

My next step is to make this code working on Microsoft Windows too, but I am not a WGL/win32 guru at all.

If I succeed, I will consider to write a tutorial on the wiki page because I’m sure lot of people are trying to achieve the same goal.

thank’s
how i can do this in windows?

Hi Palmic,

Do you succeed ? Did you write any tutorial on this subject or do you have any code sample to share ?

Thanks

Although this post is old, I thought a Windows version of this might be interesting to others as well. The core part of the code is this (sorry for the const char* throws :stuck_out_tongue: ):


if(0 == (qtDC = wglGetCurrentDC()))
	throw "Unable to accquire OpenGL device context";
if(0 == (qtRC = wglGetCurrentContext()))
	throw "Unable to accquire OpenGL rendering context";

typedef const char* (APIENTRYP PFNWGLGETEXTENSIONSTRINGARB) (HDC dc);
typedef HGLRC (APIENTRYP PFNWGLCREATECONTEXTATTRIBSARB) (HDC dc, HGLRC shareContext, const int *attribList);
PFNWGLGETEXTENSIONSTRINGARB wglGetExtensionsStringARB = 0;
PFNWGLCREATECONTEXTATTRIBSARB wglCreateContextAttribsARB = 0;

QString glExtensions = (const char*)glGetString(GL_EXTENSIONS);

if(!glExtensions.contains("WGL_ARB_extensions_string",Qt::CaseInsensitive))
	qDebug() << "WGL_ARB_extensions_string not found in GL extensions string. Retrieving proc address anyway...";

if(0 == (wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSTRINGARB)wglGetProcAddress("wglGetExtensionsStringARB")))
	throw "Unable to retrieve proc address for wglGetExtensionsStringARB";

QString wglExtensions = wglGetExtensionsStringARB(qtDC);

if(!wglExtensions.contains("WGL_ARB_create_context",Qt::CaseInsensitive))
	qDebug() << "WGL_ARB_create_context not found in WGL extensions string. Retrieving proc address anyway...";

if(0 == (wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARB)wglGetProcAddress("wglCreateContextAttribsARB")))
	throw "Unable to retrieve proc address for wglCreateContextAttribsARB";

// from http://www.opengl.org/registry/specs/ARB/wgl_create_context.txt
#define WGL_CONTEXT_MAJOR_VERSION_ARB          0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB          0x2092
#define WGL_CONTEXT_FLAGS_ARB                  0x2094
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002

int attribs[] =
{
	WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
	WGL_CONTEXT_MINOR_VERSION_ARB, 0,
	WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
	0
};

if(0 == (myRC = wglCreateContextAttribsARB(qtDC,0,attribs)))
	throw "Unable to create OpenGL 3.0 context";

After context creation, you can activate your new context (myRC), do OpenGL3 things, then activate the qt context (qtRC) again. This also happens to shield you from unwanted state changes, so it’s also useful if you want a GL 2 context (easier, as you can simply use wglCreateContext)

EDIT: Forgot to mention: this is expected to be called from within an active context created by the QGLWidget you want to use.

Qt 4.7 will support wglCreateContext, and thus 3.x

I had to patch in support for the debug output and robustness extension.

Nice, didn’t see that in any of the 4.7 change sets or anything though. Where did you find that? Also, I think I might still create my own context, because of the state separation.

I we t to qt.gitorious.org and downloaded the .tar for the 4.7 project by qt-nokia, or however the user is named.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.