Add public and QPA APIs for adapting existing OpenGL contexts

For now only xcb on GLX is supported. Other platforms will follow later.

Add also some missing documentation for the platform OpenGL context factory
functions.

[ChangeLog] QOpenGLContext is now able to adopt existing native contexts.

Task-number: QTBUG-37552
Change-Id: I5dd959f102df178f646b2df5989203b5dc6de376
Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
This commit is contained in:
Laszlo Agocs 2014-03-17 10:35:35 +01:00 committed by The Qt Project
parent 787692a09e
commit af1e32426c
20 changed files with 635 additions and 29 deletions

View File

@ -479,6 +479,64 @@ void QOpenGLContext::setScreen(QScreen *screen)
d->screen = QGuiApplication::primaryScreen();
}
/*!
Set the native handles for this context. When create() is called and a
native handle is set, configuration settings, like format(), are ignored
since this QOpenGLContext will wrap an already created native context
instead of creating a new one from scratch.
On some platforms the native context handle is not sufficient and other
related handles (for example, for a window or display) have to be provided
in addition. Therefore \a handle is variant containing a platform-specific
value type. These classes can be found in the QtPlatformHeaders module.
When create() is called with native handles set, the handles' ownership are
not taken, meaning that destroy() will not destroy the native context.
\note Some frameworks track the current context and surfaces internally.
Making the adopted QOpenGLContext current via Qt will have no effect on such
other frameworks' internal state. Therefore a subsequent makeCurrent done
via the other framework may have no effect. It is therefore advisable to
make explicit calls to make no context and surface current to reset the
other frameworks' internal state after performing OpenGL operations via Qt.
\note Using foreign contexts with Qt windows and Qt contexts with windows
and surfaces created by other frameworks may give unexpected results,
depending on the platform, due to potential mismatches in context and window
pixel formats. To make sure this does not happen, avoid making contexts and
surfaces from different frameworks current together. Instead, prefer
approaches based on context sharing where OpenGL resources like textures are
accessible both from Qt's and the foreign framework's contexts.
\since 5.4
\sa nativeHandle()
*/
void QOpenGLContext::setNativeHandle(const QVariant &handle)
{
Q_D(QOpenGLContext);
d->nativeHandle = handle;
}
/*!
Returns the native handle for the context.
This function provides access to the QOpenGLContext's underlying native
context. The returned variant contains a platform-specific value type. These
classes can be found in the module QtPlatformHeaders.
On platforms where retrieving the native handle is not supported, or if
neither create() nor setNativeHandle() was called, a null variant is
returned.
\since 5.4
\sa setNativeHandle()
*/
QVariant QOpenGLContext::nativeHandle() const
{
Q_D(const QOpenGLContext);
return d->nativeHandle;
}
/*!
Attempts to create the OpenGL context with the current configuration.
@ -500,7 +558,8 @@ void QOpenGLContext::setScreen(QScreen *screen)
*/
bool QOpenGLContext::create()
{
destroy();
if (isValid())
destroy();
Q_D(QOpenGLContext);
d->platformGLContext = QGuiApplicationPrivate::platformIntegration()->createPlatformOpenGLContext(this);
@ -550,6 +609,7 @@ void QOpenGLContext::destroy()
d->versionFunctionsBackend.clear();
delete d->textureFunctions;
d->textureFunctions = 0;
d->nativeHandle = QVariant();
}
/*!

View File

@ -64,6 +64,7 @@
#include <QtCore/qhash.h>
#include <QtCore/qpair.h>
#include <QtCore/qvariant.h>
QT_BEGIN_NAMESPACE
@ -153,6 +154,7 @@ public:
void setFormat(const QSurfaceFormat &format);
void setShareContext(QOpenGLContext *shareContext);
void setScreen(QScreen *screen);
void setNativeHandle(const QVariant &handle);
bool create();
bool isValid() const;
@ -161,6 +163,7 @@ public:
QOpenGLContext *shareContext() const;
QOpenGLContextGroup *shareGroup() const;
QScreen *screen() const;
QVariant nativeHandle() const;
GLuint defaultFramebufferObject() const;
@ -242,4 +245,4 @@ QT_END_NAMESPACE
#endif // QT_NO_OPENGL
#endif // QGUIGLCONTEXT_H
#endif // QOPENGLCONTEXT_H

View File

@ -242,6 +242,8 @@ public:
QPaintEngineEx *active_engine;
QVariant nativeHandle;
static QOpenGLContext *setCurrentContext(QOpenGLContext *context);
static void setGlobalShareContext(QOpenGLContext *context);

View File

@ -271,13 +271,32 @@ QPlatformPixmap *QPlatformIntegration::createPlatformPixmap(QPlatformPixmap::Pix
}
#ifndef QT_NO_OPENGL
/*!
Factory function for QPlatformOpenGLContext. The \a context parameter is a pointer to
the context for which a platform-specific context backend needs to be
created. Configuration settings like the format, share context and screen have to be
taken from this QOpenGLContext and the resulting platform context is expected to be
backed by a native context that fulfills these criteria.
If the context has native handles set, no new native context is expected to be created.
Instead, the provided handles have to be used. In this case the ownership of the handle
must not be taken and the platform implementation is not allowed to destroy the native
context. Configuration parameters like the format are also to be ignored. Instead, the
platform implementation is responsible for querying the configuriation from the provided
native context.
Returns a pointer to a QPlatformOpenGLContext instance or \c NULL if the context could
not be created.
\sa QOpenGLContext
*/
QPlatformOpenGLContext *QPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
{
Q_UNUSED(context);
qWarning("This plugin does not support createPlatformOpenGLContext!");
return 0;
}
#endif
#endif // QT_NO_OPENGL
/*!
Factory function for QPlatformSharedGraphicsCache. This function will return 0 if the platform

View File

@ -0,0 +1,51 @@
include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
# Name of the project which must match the outputdir. Determines the .index file
project = QtPlatformHeaders
# Directories in which to search for files to document and images.
# By default set to the root directory of the project for sources
# and headers and qdoc will therefore generate output for each file.
# Images should be placed in <rootdir>/dic/images and examples in
# <rootdir>/examples.
# Paths are relative to the location of this file.
headerdirs += ..
sourcedirs += ..
exampledirs += ..
imagedirs += images
depends += qtdoc qtcore qtgui qtwidgets
examplesinstallpath = platformheaders
# The following parameters are for creating a qhp file, the qhelpgenerator
# program can convert the qhp file into a qch file which can be opened in
# Qt Assistant and/or Qt Creator.
# Defines the name of the project. You cannot use operators (+, =, -) in
# the name. Properties for this project are set using a qhp.<projectname>.property
# format.
qhp.projects = QtPlatformHeaders
# Sets the name of the output qhp file.
qhp.QtPlatformHeaders.file = qtplatformheaders.qhp
# Namespace for the output file. This namespace is used to distinguish between
# different documentation files in Creator/Assistant.
qhp.QtPlatformHeaders.namespace = org.qt-project.qtplatformheaders.$QT_VERSION_TAG
# Title for the package, will be the main title for the package in
# Assistant/Creator.
qhp.QtPlatformHeaders.indexTitle = Qt Platform Headers
# Only update the name of the project for the next variables.
qhp.QtPlatformHeaders.virtualFolder = qtplatformheaders
qhp.QtPlatformHeaders.subprojects = classes
qhp.QtPlatformHeaders.subprojects.classes.title = C++ Classes
qhp.QtPlatformHeaders.subprojects.classes.indexTitle = Qt Platform Headers C++ Classes
qhp.QtPlatformHeaders.subprojects.classes.selectors = class fake:headerfile
qhp.QtPlatformHeaders.subprojects.classes.sortPages = true
navigation.landingpage = "Qt Platform Headers"
navigation.cppclassespage = "Qt Platform Headers C++ Classes"

View File

@ -0,0 +1,68 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: http://www.gnu.org/copyleft/fdl.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\module QtPlatformHeaders
\title Qt Platform Headers C++ Classes
\ingroup modules
\brief The Qt Platform Headers module offers header-only inline classes that
encapsulate platform-specific information that is tied to a given runtime
configuration of a platform plugin.
*/
/*!
\page qtplatformheaders-index.html
\title Qt Platform Headers
\brief The Qt Platform Headers module offers header-only inline classes that
encapsulate platform-specific information that is tied to a given runtime
configuration of a platform plugin.
Some applications may need to interface Qt with other frameworks. This often
means using graphics contexts or other types of native handles created by
one framework with another. For example, on some platforms, QOpenGLContext
offers the ability to wrap an existing native OpenGL context, instead of
creating a new one. This existing native context can be created by some
other third-party code.
The type of such native handles is highly platform specific and in some
cases the platform plugin will need more information to adopt a handle, just
the handle in itself will not be sufficient. Therefore the public API
consists of functions taking or returning a QVariant that contains a
platform-specific value type. See for example
QOpenGLContext::setNativeHandle() and QOpenGLContext::nativeHandle(). When
running on Linux/X11, using the xcb platform plugin and the GLX windowing
system interface, the variant contains a QGLXNativeContext. On other
platforms a different class will be used. These classes are all placed in
the Qt Platform Headers module.
\note Similar to the other QPA APIs, there are no binary compatibility
guarantees for these classes, meaning that an application using these
classes is only guaranteed to work with the Qt version it was developed
against. Unlike QPA however, source compatibility is guaranteed.
*/

View File

@ -0,0 +1 @@
HEADERS += $$PWD/qglxnativecontext.h

View File

@ -0,0 +1,82 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QGLXNATIVECONTEXT_H
#define QGLXNATIVECONTEXT_H
#include <X11/Xlib.h>
#include <GL/glx.h>
QT_BEGIN_NAMESPACE
struct QGLXNativeContext
{
QGLXNativeContext()
: m_context(0),
m_display(0),
m_window(0),
m_visualId(0)
{ }
QGLXNativeContext(GLXContext ctx, Display *dpy = 0, Window wnd = 0, VisualID vid = 0)
: m_context(ctx),
m_display(dpy),
m_window(wnd),
m_visualId(vid)
{ }
GLXContext context() const { return m_context; }
Display *display() const { return m_display; }
Window window() const { return m_window; }
VisualID visualId() const { return m_visualId; }
private:
GLXContext m_context;
Display *m_display;
Window m_window;
VisualID m_visualId;
};
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QGLXNativeContext)
#endif // QGLXNATIVECONTEXT_H

View File

@ -0,0 +1,83 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: http://www.gnu.org/copyleft/fdl.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\class QGLXNativeContext
\inmodule QtPlatformHeaders
\since 5.4
\brief A class encapsulating a GLXContext and related native handles.
\note Only context() is guaranteed to be valid. The other handles may be all \c 0. They are
useful however when QOpenGLContext::setNativeHandle() is used to adopt a legacy context
created by glXCreateContext. To adopt such a context, either the Window or VisualID
that had been used to create the context needs to be known, otherwise the adoption will
fail. For modern contexts created with an FBConfig, these are not necessary, the
GLXContext itself is sufficient. The Display is optional.
\note As of Qt 5.4 there is no binary compatibility guarantee for this class, meaning
that an application using it is only guaranteed to work with the Qt version it was
developed against.
\sa QOpenGLContext::setNativeHandle(), QOpenGLContext::nativeHandle()
*/
/*!
\fn GLXContext QGLXNativeContext::context() const
\return the GLXContext.
*/
/*!
\fn Display *QGLXNativeContext::display() const
\return a pointer to the X11 display or \c NULL if not available.
*/
/*!
\fn Window QGLXNativeContext::window() const
\return the X11 Window or \c 0 if not available.
*/
/*!
\fn VisualID QGLXNativeContext::visualId() const
\return the X11 visual ID or \c 0 if not available.
*/
/*!
\fn QGLXNativeContext::QGLXNativeContext()
Construct a new instance with no handles.
*/
/*!
\fn QGLXNativeContext::QGLXNativeContext(GLXContext ctx, Display *dpy = 0, Window wnd = 0, VisualID vid = 0)
Constructs a new instance with the provided \a ctx, \a dpy, \a wnd, \a vid handles.
*/

View File

@ -0,0 +1,11 @@
# Only headers here, no library is wanted.
TEMPLATE = subdirs
VERSION = $$MODULE_VERSION
MODULE_INCNAME = QtPlatformHeaders
include(nativecontexts/nativecontexts.pri)
QMAKE_DOCS = $$PWD/doc/qtplatformheaders.qdocconf
load(qt_module_headers)
load(qt_docs)

View File

@ -250,7 +250,7 @@ XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *f
return visualInfo;
}
void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config, GLXContext)
void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config)
{
int redSize = 0;
int greenSize = 0;
@ -262,8 +262,6 @@ void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display,
int sampleCount = 0;
int stereo = 0;
XVisualInfo *vi = glXGetVisualFromFBConfig(display,config);
XFree(vi);
glXGetFBConfigAttrib(display, config, GLX_RED_SIZE, &redSize);
glXGetFBConfigAttrib(display, config, GLX_GREEN_SIZE, &greenSize);
glXGetFBConfigAttrib(display, config, GLX_BLUE_SIZE, &blueSize);
@ -287,6 +285,41 @@ void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display,
format->setStereo(stereo);
}
void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display, XVisualInfo *visualInfo)
{
int redSize = 0;
int greenSize = 0;
int blueSize = 0;
int alphaSize = 0;
int depthSize = 0;
int stencilSize = 0;
int sampleBuffers = 0;
int sampleCount = 0;
int stereo = 0;
glXGetConfig(display, visualInfo, GLX_RED_SIZE, &redSize);
glXGetConfig(display, visualInfo, GLX_GREEN_SIZE, &greenSize);
glXGetConfig(display, visualInfo, GLX_BLUE_SIZE, &blueSize);
glXGetConfig(display, visualInfo, GLX_ALPHA_SIZE, &alphaSize);
glXGetConfig(display, visualInfo, GLX_DEPTH_SIZE, &depthSize);
glXGetConfig(display, visualInfo, GLX_STENCIL_SIZE, &stencilSize);
glXGetConfig(display, visualInfo, GLX_SAMPLES_ARB, &sampleBuffers);
glXGetConfig(display, visualInfo, GLX_STEREO, &stereo);
format->setRedBufferSize(redSize);
format->setGreenBufferSize(greenSize);
format->setBlueBufferSize(blueSize);
format->setAlphaBufferSize(alphaSize);
format->setDepthBufferSize(depthSize);
format->setStencilBufferSize(stencilSize);
if (sampleBuffers) {
glXGetConfig(display, visualInfo, GLX_SAMPLES_ARB, &sampleCount);
format->setSamples(sampleCount);
}
format->setStereo(stereo);
}
QSurfaceFormat qglx_reduceSurfaceFormat(const QSurfaceFormat &format, bool *reduced)
{
QSurfaceFormat retFormat = format;

View File

@ -50,7 +50,8 @@
XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *format);
GLXFBConfig qglx_findConfig(Display *display, int screen, const QSurfaceFormat &format, int drawableBit = GLX_WINDOW_BIT);
void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config, GLXContext context = 0);
void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config);
void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display, XVisualInfo *visualInfo);
QVector<int> qglx_buildSpec(const QSurfaceFormat &format, int drawableBit = GLX_WINDOW_BIT);
QSurfaceFormat qglx_reduceSurfaceFormat(const QSurfaceFormat &format, bool *reduced);

View File

@ -179,7 +179,7 @@ QOffscreenX11GLXContext::QOffscreenX11GLXContext(QOffscreenX11Info *x11, QOpenGL
// Get the basic surface format details
if (d->context)
qglx_surfaceFormatFromGLXFBConfig(&d->format, x11->display(), config, d->context);
qglx_surfaceFormatFromGLXFBConfig(&d->format, x11->display(), config);
// Create a temporary window so that we can make the new context current
d->window = createDummyWindow(x11, config);

View File

@ -54,6 +54,7 @@
#include "qglxintegration.h"
#include <QtPlatformSupport/private/qglxconvenience_p.h>
#include <QtPlatformHeaders/QGLXNativeContext>
#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
#include <dlfcn.h>
@ -83,31 +84,31 @@ typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXC
#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
#endif
static Window createDummyWindow(QXcbScreen *screen, XVisualInfo *visualInfo)
static Window createDummyWindow(Display *dpy, XVisualInfo *visualInfo, int screenNumber, Window rootWin)
{
Colormap cmap = XCreateColormap(DISPLAY_FROM_XCB(screen), screen->root(), visualInfo->visual, AllocNone);
Colormap cmap = XCreateColormap(dpy, rootWin, visualInfo->visual, AllocNone);
XSetWindowAttributes a;
a.background_pixel = WhitePixel(DISPLAY_FROM_XCB(screen), screen->screenNumber());
a.border_pixel = BlackPixel(DISPLAY_FROM_XCB(screen), screen->screenNumber());
a.background_pixel = WhitePixel(dpy, screenNumber);
a.border_pixel = BlackPixel(dpy, screenNumber);
a.colormap = cmap;
Window window = XCreateWindow(DISPLAY_FROM_XCB(screen), screen->root(),
Window window = XCreateWindow(dpy, rootWin,
0, 0, 100, 100,
0, visualInfo->depth, InputOutput, visualInfo->visual,
CWBackPixel|CWBorderPixel|CWColormap, &a);
#ifndef QT_NO_DEBUG
XStoreName(DISPLAY_FROM_XCB(screen), window, "Qt GLX dummy window");
XStoreName(dpy, window, "Qt GLX dummy window");
#endif
XFreeColormap(DISPLAY_FROM_XCB(screen), cmap);
XFreeColormap(dpy, cmap);
return window;
}
static Window createDummyWindow(QXcbScreen *screen, GLXFBConfig config)
static Window createDummyWindow(Display *dpy, GLXFBConfig config, int screenNumber, Window rootWin)
{
XVisualInfo *visualInfo = glXGetVisualFromFBConfig(DISPLAY_FROM_XCB(screen), config);
XVisualInfo *visualInfo = glXGetVisualFromFBConfig(dpy, config);
if (!visualInfo)
qFatal("Could not initialize GLX");
Window window = createDummyWindow(screen, visualInfo);
Window window = createDummyWindow(dpy, visualInfo, screenNumber, rootWin);
XFree(visualInfo);
return window;
}
@ -160,7 +161,8 @@ static void updateFormatFromContext(QSurfaceFormat &format)
}
}
QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share)
QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share,
const QVariant &nativeHandle)
: QPlatformOpenGLContext()
, m_screen(screen)
, m_context(0)
@ -168,6 +170,15 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
, m_format(format)
, m_isPBufferCurrent(false)
, m_swapInterval(-1)
, m_ownsContext(nativeHandle.isNull())
{
if (nativeHandle.isNull())
init(screen, share);
else
init(screen, share, nativeHandle);
}
void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share)
{
if (m_format.renderableType() == QSurfaceFormat::DefaultRenderableType)
m_format.setRenderableType(QSurfaceFormat::OpenGL);
@ -195,7 +206,7 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
&& (m_format.renderableType() != QSurfaceFormat::OpenGLES || (supportsProfiles && glxExt.contains("GLX_EXT_create_context_es2_profile")))) {
// Try to create an OpenGL context for each known OpenGL version in descending
// order from the requested version.
const int requestedVersion = format.majorVersion() * 10 + qMin(format.minorVersion(), 9);
const int requestedVersion = m_format.majorVersion() * 10 + qMin(m_format.minorVersion(), 9);
QVector<int> glVersions;
if (m_format.renderableType() == QSurfaceFormat::OpenGL) {
@ -282,10 +293,10 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
// Get the basic surface format details
if (m_context)
qglx_surfaceFormatFromGLXFBConfig(&m_format, DISPLAY_FROM_XCB(screen), config, m_context);
qglx_surfaceFormatFromGLXFBConfig(&m_format, DISPLAY_FROM_XCB(screen), config);
// Create a temporary window so that we can make the new context current
window = createDummyWindow(screen, config);
window = createDummyWindow(DISPLAY_FROM_XCB(screen), config, screen->screenNumber(), screen->root());
} else {
// requesting an OpenGL ES context requires glXCreateContextAttribsARB, so bail out
if (m_format.renderableType() == QSurfaceFormat::OpenGLES)
@ -303,7 +314,7 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
}
// Create a temporary window so that we can make the new context current
window = createDummyWindow(screen, visualInfo);
window = createDummyWindow(DISPLAY_FROM_XCB(screen), visualInfo, screen->screenNumber(), screen->root());
XFree(visualInfo);
}
@ -320,9 +331,123 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
XDestroyWindow(DISPLAY_FROM_XCB(screen), window);
}
void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share, const QVariant &nativeHandle)
{
if (!nativeHandle.canConvert<QGLXNativeContext>()) {
qWarning("QGLXContext: Requires a QGLXNativeContext");
return;
}
QGLXNativeContext handle = nativeHandle.value<QGLXNativeContext>();
GLXContext context = handle.context();
if (!context) {
qWarning("QGLXContext: No GLXContext given");
return;
}
// Use the provided Display, if available. If not, use our own. It may still work.
Display *dpy = handle.display();
if (!dpy)
dpy = DISPLAY_FROM_XCB(screen);
// Legacy contexts created using glXCreateContext are created using a visual
// and the FBConfig cannot be queried. The only way to adapt these contexts
// is to figure out the visual id.
XVisualInfo *vinfo = 0;
// If the VisualID is provided use it.
VisualID vid = handle.visualId();
if (!vid) {
// In the absence of the VisualID figure it out from the window.
Window wnd = handle.window();
if (wnd) {
XWindowAttributes attrs;
XGetWindowAttributes(dpy, wnd, &attrs);
vid = XVisualIDFromVisual(attrs.visual);
}
}
if (vid) {
XVisualInfo v;
v.screen = screen->screenNumber();
v.visualid = vid;
int n = 0;
vinfo = XGetVisualInfo(dpy, VisualScreenMask | VisualIDMask, &v, &n);
if (n < 1) {
XFree(vinfo);
vinfo = 0;
}
}
// For contexts created with an FBConfig using the modern functions providing the
// visual or window is not mandatory. Just query the config from the context.
GLXFBConfig config = 0;
if (!vinfo) {
int configId = 0;
if (glXQueryContext(dpy, context, GLX_FBCONFIG_ID, &configId) != Success) {
qWarning("QGLXContext: Failed to query config from the provided context");
return;
}
GLXFBConfig *configs;
int numConfigs = 0;
static const int attribs[] = { GLX_FBCONFIG_ID, configId, None };
configs = glXChooseFBConfig(dpy, screen->screenNumber(), attribs, &numConfigs);
if (!configs || numConfigs < 1) {
qWarning("QGLXContext: Failed to find config");
return;
}
if (configs && numConfigs > 1) // this is suspicious so warn but let it continue
qWarning("QGLXContext: Multiple configs for FBConfig ID %d", configId);
config = configs[0];
}
Q_ASSERT(vinfo || config);
int screenNumber = DefaultScreen(dpy);
Window window;
if (vinfo)
window = createDummyWindow(dpy, vinfo, screenNumber, RootWindow(dpy, screenNumber));
else
window = createDummyWindow(dpy, config, screenNumber, RootWindow(dpy, screenNumber));
if (!window) {
qWarning("QGLXContext: Failed to create dummy window");
return;
}
// Update OpenGL version and buffer sizes in our format.
if (!glXMakeCurrent(dpy, window, context)) {
qWarning("QGLXContext: Failed to make provided context current");
return;
}
m_format = QSurfaceFormat();
m_format.setRenderableType(QOpenGLContext::openGLModuleType() == QOpenGLContext::DesktopGL
? QSurfaceFormat::OpenGL : QSurfaceFormat::OpenGLES);
updateFormatFromContext(m_format);
if (vinfo)
qglx_surfaceFormatFromVisualInfo(&m_format, dpy, vinfo);
else
qglx_surfaceFormatFromGLXFBConfig(&m_format, dpy, config);
glXMakeCurrent(dpy, 0, 0);
XDestroyWindow(dpy, window);
if (vinfo)
XFree(vinfo);
// Success. Store the context. From this point on isValid() is true.
m_context = context;
if (share)
m_shareContext = static_cast<const QGLXContext*>(share)->glxContext();
}
QGLXContext::~QGLXContext()
{
glXDestroyContext(DISPLAY_FROM_XCB(m_screen), m_context);
if (m_ownsContext)
glXDestroyContext(DISPLAY_FROM_XCB(m_screen), m_context);
}
QVariant QGLXContext::nativeHandle() const
{
return QVariant::fromValue<QGLXNativeContext>(QGLXNativeContext(m_context));
}
bool QGLXContext::makeCurrent(QPlatformSurface *surface)

View File

@ -58,7 +58,8 @@ QT_BEGIN_NAMESPACE
class QGLXContext : public QPlatformOpenGLContext
{
public:
QGLXContext(QXcbScreen *xd, const QSurfaceFormat &format, QPlatformOpenGLContext *share);
QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share,
const QVariant &nativeHandle);
~QGLXContext();
bool makeCurrent(QPlatformSurface *surface);
@ -72,16 +73,22 @@ public:
GLXContext glxContext() const { return m_context; }
QVariant nativeHandle() const;
static bool supportsThreading();
static void queryDummyContext();
private:
void init(QXcbScreen *screen, QPlatformOpenGLContext *share);
void init(QXcbScreen *screen, QPlatformOpenGLContext *share, const QVariant &nativeHandle);
QXcbScreen *m_screen;
GLXContext m_context;
GLXContext m_shareContext;
QSurfaceFormat m_format;
bool m_isPBufferCurrent;
int m_swapInterval;
bool m_ownsContext;
static bool m_queriedDummyContext;
static bool m_supportsThreading;
};

View File

@ -233,7 +233,10 @@ QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLCont
{
QXcbScreen *screen = static_cast<QXcbScreen *>(context->screen()->handle());
#if defined(XCB_USE_GLX)
return new QGLXContext(screen, context->format(), context->shareHandle());
QGLXContext *platformContext = new QGLXContext(screen, context->format(),
context->shareHandle(), context->nativeHandle());
context->setNativeHandle(platformContext->nativeHandle());
return platformContext;
#elif defined(XCB_USE_EGL)
return new QEGLXcbPlatformContext(context->format(), context->shareHandle(),
screen->connection()->egl_display(), screen->connection());

View File

@ -95,6 +95,10 @@ src_platformsupport.subdir = $$PWD/platformsupport
src_platformsupport.target = sub-platformsupport
src_platformsupport.depends = src_corelib src_gui src_network
src_platformheaders.subdir = $$PWD/platformheaders
src_platformheaders.target = sub-platformheaders
src_platformheaders.depends = src_corelib src_gui
src_widgets.subdir = $$PWD/widgets
src_widgets.target = sub-widgets
src_widgets.depends = src_corelib src_gui src_tools_uic
@ -141,9 +145,9 @@ contains(QT_CONFIG, concurrent):SUBDIRS += src_concurrent
SUBDIRS += src_angle
src_gui.depends += src_angle
}
SUBDIRS += src_gui src_platformsupport
SUBDIRS += src_gui src_platformsupport src_platformheaders
contains(QT_CONFIG, opengl(es2)?):SUBDIRS += src_openglextensions
src_plugins.depends += src_gui src_platformsupport
src_plugins.depends += src_gui src_platformsupport src_platformheaders
!contains(QT_CONFIG, no-widgets) {
SUBDIRS += src_tools_uic src_widgets
TOOLS += src_tools_uic

View File

@ -11,6 +11,7 @@
"QtDBus" => "$basedir/src/dbus",
"QtConcurrent" => "$basedir/src/concurrent",
"QtPlatformSupport" => "$basedir/src/platformsupport",
"QtPlatformHeaders" => "$basedir/src/platformheaders",
"QtANGLE/KHR" => "!$basedir/src/3rdparty/angle/include/KHR",
"QtANGLE/GLES2" => "!$basedir/src/3rdparty/angle/include/GLES2",
"QtANGLE/EGL" => "!$basedir/src/3rdparty/angle/include/EGL",

View File

@ -10,3 +10,5 @@ QT += gui-private core-private testlib
SOURCES += tst_qopengl.cpp
win32-msvc2010:contains(QT_CONFIG, angle):CONFIG += insignificant_test # QTBUG-31611
linux:contains(QT_CONFIG, xcb-glx):contains(QT_CONFIG, xcb-xlib): DEFINES += USE_GLX

View File

@ -51,12 +51,19 @@
#include <QtGui/QGenericMatrix>
#include <QtGui/QMatrix4x4>
#include <QtGui/private/qopengltextureblitter_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformnativeinterface.h>
#include <QtTest/QtTest>
#include <QSignalSpy>
#ifdef USE_GLX
// Must be included last due to the X11 types
#include <QtPlatformHeaders/QGLXNativeContext>
#endif
class tst_QOpenGL : public QObject
{
Q_OBJECT
@ -85,6 +92,10 @@ private slots:
void textureblitterPartOriginTopLeftSourceRectTransform();
void textureblitterFullTargetRectTransform();
void textureblitterPartTargetRectTransform();
#ifdef USE_GLX
void glxContextWrap();
#endif
};
struct SharedResourceTracker
@ -970,6 +981,45 @@ void tst_QOpenGL::textureblitterPartTargetRectTransform()
QCOMPARE(targetBottomRight, expectedBottomRight);
}
#ifdef USE_GLX
void tst_QOpenGL::glxContextWrap()
{
QWindow *window = new QWindow;
window->setSurfaceType(QWindow::OpenGLSurface);
window->setGeometry(0, 0, 10, 10);
window->show();
QTest::qWaitForWindowExposed(window);
QPlatformNativeInterface *nativeIf = QGuiApplicationPrivate::instance()->platformIntegration()->nativeInterface();
QVERIFY(nativeIf);
// Fetch a GLXContext.
QOpenGLContext *ctx0 = new QOpenGLContext;
ctx0->setFormat(window->format());
QVERIFY(ctx0->create());
QVariant v = ctx0->nativeHandle();
QVERIFY(!v.isNull());
QVERIFY(v.canConvert<QGLXNativeContext>());
GLXContext context = v.value<QGLXNativeContext>().context();
QVERIFY(context);
// Then create another QOpenGLContext wrapping it.
QOpenGLContext *ctx = new QOpenGLContext;
ctx->setNativeHandle(QVariant::fromValue<QGLXNativeContext>(QGLXNativeContext(context)));
QVERIFY(ctx->create());
QVERIFY(ctx->nativeHandle().value<QGLXNativeContext>().context() == context);
QVERIFY(nativeIf->nativeResourceForContext(QByteArrayLiteral("glxcontext"), ctx) == (void *) context);
QVERIFY(ctx->makeCurrent(window));
ctx->doneCurrent();
delete ctx;
delete ctx0;
delete window;
}
#endif // USE_GLX
QTEST_MAIN(tst_QOpenGL)
#include "tst_qopengl.moc"