Context
From Qt 4.8 it is possible to create an OpenGL (>=3.0) core context [1], but one can't yet create a debug context. Even if it seems that next major version of Qt will permit that [2], it is interesting to know how to use QGLWidget without a Qt OpenGL context. Actually custom context will always allow more freedom.Requirements
I'll use c++11 in code samples.Given a GLContext class and a function to create an instance of this class on Linux or Windows OS :
namespace virtrev { namespace glc {
class GLContext
{
struct Config;
struct WMInformation;
struct ContextPreconfigurationOut;
struct WindowInformation;
virtual ~GLContext();
virtual void configure( Config const & config, WMInformation const & wm_information, ContextPreconfigurationOut & context_preconfiguration_out ) = 0;
virtual void create( WindowInformation const & window_information, GLContext * shared ) = 0;
virtual void bind() = 0;
virtual void unbind() = 0;
virtual void swap() = 0;
};
GLContext * create_gl_context();
Config : OpenGL portable characteristic.
WMInformation : platform specific window manager information.
ContextPreconfigurationOut : platform specific information (configure output) to adapt QGLWidget before creating the context.
WindowInformation : platform specific windows (QGLWidget internal windows) information.
Let's start
We have to create a QGLContext child, so we can replace the QGLWidget OpenGL context.QGLContextAdapter.hpp
#ifndef QGLCONTEXTADAPTER_HPP
#define QGLCONTEXTADAPTER_HPP
#include
#include
namespace vglc = virtrev::glc;
/** Must be allocated on the heap! Because widget will take ownershipo of this. */
class QGLContextAdapter : public QGLContext
{
public:
explicit QGLContextAdapter( vglc::GLContext::Config const & config );
~QGLContextAdapter();
bool chooseContext( QGLContext const * shareContext = 0 ) override;
void makeCurrent() override;
void doneCurrent() override;
void swapBuffers() const override;
void setDevice(QPaintDevice *device) { QGLContext::setDevice(device); }
private:
void SetupQGLFormatFromConfig( vglc::GLContext::Config const & config, QGLFormat & format );
std::unique_ptr gl_context;
};
#endif // QGLCONTEXTADAPTER_HPP
QGLContextAdapter.cpp
#include "QGLContextAdapter.hpp"
#if defined(Q_WS_X11)
#include
#include
#include
void SetupQGLFormatFromVisual( Display * display, XVisualInfo * visual, QGLFormat & format );
#endif
QGLContextAdapter::QGLContextAdapter( vglc::GLContext::Config const & config )
: QGLContext( QGLFormat() )
{
gl_context.reset( vglc::create_gl_context() );
vglc::GLContext::WMInformation wm_info;
vglc::GLContext::ContextPreconfigurationOut context_preconf;
#if defined(Q_WS_X11)
XVisualInfo * visual;
wm_info.display = QX11Info::display();
wm_info.screen_idx = 0;
context_preconf.visual = &visual; // we'll get the visual needed by the context
#endif
gl_context->configure( config, wm_info, context_preconf );
QGLFormat new_format;
#if defined(Q_WS_X11)
//Under linux the QGLFormat can be incompatible. So set it from the visual used to configure the context.
SetupQGLFormatFromVisual( QX11Info::display(), visual, new_format );
#else
//Perhaps under windows we should do the same, but it seems useless. So set the QGLFormat directly from config.
SetupQGLFormatFromConfig( config, new_format );
#endif
setFormat( new_format );
}
QGLContextAdapter::~QGLContextAdapter()
{
setValid( false );
}
void QGLContextAdapter::SetupQGLFormatFromConfig( vglc::GLContext::Config const & config, QGLFormat & format )
{
format.setAccum( false );
format.setAccumBufferSize( 0 );
format.setAlpha( config.alpha_size > 0 );
format.setAlphaBufferSize( config.alpha_size);
format.setBlueBufferSize( config.blue_size );
format.setGreenBufferSize( config.green_size );
format.setRedBufferSize( config.red_size );
format.setRgba( true );
format.setDepth( config.depth_size > 0 );
format.setDepthBufferSize( config.depth_size );
format.setDirectRendering( true );
format.setDoubleBuffer( config.double_buffer );
format.setSampleBuffers( config.sample_count > 1 );
format.setSamples( config.sample_count );
format.setStencil( config.stencil_size > 0 );
format.setStencilBufferSize( config.stencil_size );
format.setStereo( config.stereo );
format.setOverlay( false );
format.setPlane( 0 );
format.setProfile( QGLFormat::CoreProfile );
format.setSwapInterval( 0 );
format.setVersion( 1, 0 );
}
bool QGLContextAdapter::chooseContext( QGLContext const * shared )
{
QPaintDevice * dev = device();
QGLWidget * widget = dynamic_cast(dev);
vglc::GLContext::WindowInformation win_info;
#if defined(Q_WS_X11)
Window window = widget->winId();
win_info.window_handle = &( window );
#elif defined(Q_WS_WIN)
HWND window_handle = widget->winId();
win_info.window_handle = &window_handle;
#endif
vglc::GLContext * shared_ctx = NULL;
if( shared != NULL )
{
QGLContextAdapter const * context_adapter = dynamic_cast(shared);
if( context_adapter != NULL )
{
shared_ctx = context_adapter->gl_context.get();
}
}
gl_context->create( win_info, shared_ctx );
setWindowCreated( true );
return true;
}
void QGLContextAdapter::makeCurrent()
{
gl_context->bind();
}
void QGLContextAdapter::doneCurrent()
{
gl_context->unbind();
}
void QGLContextAdapter::swapBuffers() const
{
gl_context->swap();
}
#if defined(Q_WS_X11)
struct GetVisualAttrib
{
GetVisualAttrib( Display * display, XVisualInfo * visual )
: display(display), visual(visual)
{
}
int operator () ( int attrib )
{
int value;
glXGetConfig( display, visual, attrib, &value );
return value;
}
Display * display;
XVisualInfo * visual;
};
void SetupQGLFormatFromVisual( Display * display, XVisualInfo * visual, QGLFormat & format )
{
GetVisualAttrib getter( display, visual );
int const accum_red_size = getter( GLX_ACCUM_RED_SIZE );
int const accum_green_size = getter( GLX_ACCUM_GREEN_SIZE );
int const accum_blue_size = getter( GLX_ACCUM_BLUE_SIZE );
int const accum_alpha_size = getter( GLX_ACCUM_ALPHA_SIZE );
int const accum_size = accum_red_size + accum_green_size + accum_blue_size + accum_alpha_size;
format.setAccum( accum_size > 0 );
format.setAccumBufferSize( accum_size / 4 );
int const alpha_size = getter(GLX_ALPHA_SIZE);
format.setAlpha( alpha_size > 0 );
format.setAlphaBufferSize( alpha_size );
format.setBlueBufferSize( getter(GLX_BLUE_SIZE) );
format.setGreenBufferSize( getter(GLX_GREEN_SIZE) );
format.setRedBufferSize( getter(GLX_RED_SIZE) );
format.setRgba( getter(GLX_RGBA) );
int const depth_size = getter( GLX_DEPTH_SIZE );
format.setDepth( depth_size > 0 );
format.setDepthBufferSize( depth_size );
format.setDirectRendering( true );
format.setDoubleBuffer( getter(GLX_DOUBLEBUFFER) );
format.setSampleBuffers( false );
format.setSamples(0);
int const stencil_size = getter(GLX_STENCIL_SIZE);
format.setStencil( stencil_size > 0 );
format.setStencilBufferSize( stencil_size );
format.setStereo( getter(GLX_STEREO) );
format.setOverlay( false );
format.setPlane( 0 );
format.setProfile( QGLFormat::CoreProfile );
format.setSwapInterval( 0 );
format.setVersion( 1, 0 );
}
#endif // defined(Q_WS_X11)
And now the only thing to do is to use this custom context in the WGLWidget :
vglc::GLContext::Config config; //setup elsewhere
QGLContextAdapter * create_context()
{
return new QGLContextAdapter( config );
}
GLWidget::GLWidget( QWidget * parent )
: QGLWidget(
#if defined(Q_WS_WIN)
// under windows it's not possible to set context later
create_context(),
#endif
parent
)
{
#if defined(Q_WS_X11)
// under linux if we pass the context in initialization list, QGLWidget will not use the good format
QGLContextAdapter * context = create_context();
setFormat( context->format() );
context->setDevice( this );
setContext( context );
#endif
}
References
1. qt-project.org2. qt-project.org