From af1e32426c50694c0e4c1c292aa5eeee3b38c7bc Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 17 Mar 2014 10:35:35 +0100 Subject: Add public and QPA APIs for adapting existing OpenGL contexts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/gui/kernel/qopenglcontext.cpp | 62 +++++++- src/gui/kernel/qopenglcontext.h | 5 +- src/gui/kernel/qopenglcontext_p.h | 2 + src/gui/kernel/qplatformintegration.cpp | 21 ++- src/platformheaders/doc/qtplatformheaders.qdocconf | 51 +++++++ src/platformheaders/doc/src/qtplatformheaders.qdoc | 68 +++++++++ .../nativecontexts/nativecontexts.pri | 1 + .../nativecontexts/qglxnativecontext.h | 82 +++++++++++ .../nativecontexts/qglxnativecontext.qdoc | 83 +++++++++++ src/platformheaders/platformheaders.pro | 11 ++ .../glxconvenience/qglxconvenience.cpp | 39 ++++- .../glxconvenience/qglxconvenience_p.h | 3 +- .../offscreen/qoffscreenintegration_x11.cpp | 2 +- src/plugins/platforms/xcb/qglxintegration.cpp | 157 ++++++++++++++++++--- src/plugins/platforms/xcb/qglxintegration.h | 9 +- src/plugins/platforms/xcb/qxcbintegration.cpp | 5 +- src/src.pro | 8 +- 17 files changed, 581 insertions(+), 28 deletions(-) create mode 100644 src/platformheaders/doc/qtplatformheaders.qdocconf create mode 100644 src/platformheaders/doc/src/qtplatformheaders.qdoc create mode 100644 src/platformheaders/nativecontexts/nativecontexts.pri create mode 100644 src/platformheaders/nativecontexts/qglxnativecontext.h create mode 100644 src/platformheaders/nativecontexts/qglxnativecontext.qdoc create mode 100644 src/platformheaders/platformheaders.pro (limited to 'src') diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp index 7382d63a06..7fa0452fe5 100644 --- a/src/gui/kernel/qopenglcontext.cpp +++ b/src/gui/kernel/qopenglcontext.cpp @@ -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(); } /*! diff --git a/src/gui/kernel/qopenglcontext.h b/src/gui/kernel/qopenglcontext.h index fce983f975..736ae0c0b4 100644 --- a/src/gui/kernel/qopenglcontext.h +++ b/src/gui/kernel/qopenglcontext.h @@ -64,6 +64,7 @@ #include #include +#include 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 diff --git a/src/gui/kernel/qopenglcontext_p.h b/src/gui/kernel/qopenglcontext_p.h index b21ff67068..660fdd1298 100644 --- a/src/gui/kernel/qopenglcontext_p.h +++ b/src/gui/kernel/qopenglcontext_p.h @@ -242,6 +242,8 @@ public: QPaintEngineEx *active_engine; + QVariant nativeHandle; + static QOpenGLContext *setCurrentContext(QOpenGLContext *context); static void setGlobalShareContext(QOpenGLContext *context); diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp index a6e0d4705b..432199f142 100644 --- a/src/gui/kernel/qplatformintegration.cpp +++ b/src/gui/kernel/qplatformintegration.cpp @@ -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 diff --git a/src/platformheaders/doc/qtplatformheaders.qdocconf b/src/platformheaders/doc/qtplatformheaders.qdocconf new file mode 100644 index 0000000000..dfef69f892 --- /dev/null +++ b/src/platformheaders/doc/qtplatformheaders.qdocconf @@ -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 /dic/images and examples in +# /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..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" diff --git a/src/platformheaders/doc/src/qtplatformheaders.qdoc b/src/platformheaders/doc/src/qtplatformheaders.qdoc new file mode 100644 index 0000000000..4ed740d296 --- /dev/null +++ b/src/platformheaders/doc/src/qtplatformheaders.qdoc @@ -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. + */ diff --git a/src/platformheaders/nativecontexts/nativecontexts.pri b/src/platformheaders/nativecontexts/nativecontexts.pri new file mode 100644 index 0000000000..dcbeae2b0f --- /dev/null +++ b/src/platformheaders/nativecontexts/nativecontexts.pri @@ -0,0 +1 @@ +HEADERS += $$PWD/qglxnativecontext.h diff --git a/src/platformheaders/nativecontexts/qglxnativecontext.h b/src/platformheaders/nativecontexts/qglxnativecontext.h new file mode 100644 index 0000000000..e3dd547a8f --- /dev/null +++ b/src/platformheaders/nativecontexts/qglxnativecontext.h @@ -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 +#include + +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 diff --git a/src/platformheaders/nativecontexts/qglxnativecontext.qdoc b/src/platformheaders/nativecontexts/qglxnativecontext.qdoc new file mode 100644 index 0000000000..14c6d37c0e --- /dev/null +++ b/src/platformheaders/nativecontexts/qglxnativecontext.qdoc @@ -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. + */ diff --git a/src/platformheaders/platformheaders.pro b/src/platformheaders/platformheaders.pro new file mode 100644 index 0000000000..b243bfa4b5 --- /dev/null +++ b/src/platformheaders/platformheaders.pro @@ -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) diff --git a/src/platformsupport/glxconvenience/qglxconvenience.cpp b/src/platformsupport/glxconvenience/qglxconvenience.cpp index 4630b12a57..851ad37a6a 100644 --- a/src/platformsupport/glxconvenience/qglxconvenience.cpp +++ b/src/platformsupport/glxconvenience/qglxconvenience.cpp @@ -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; diff --git a/src/platformsupport/glxconvenience/qglxconvenience_p.h b/src/platformsupport/glxconvenience/qglxconvenience_p.h index 66548a3479..04fa8595ef 100644 --- a/src/platformsupport/glxconvenience/qglxconvenience_p.h +++ b/src/platformsupport/glxconvenience/qglxconvenience_p.h @@ -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 qglx_buildSpec(const QSurfaceFormat &format, int drawableBit = GLX_WINDOW_BIT); QSurfaceFormat qglx_reduceSurfaceFormat(const QSurfaceFormat &format, bool *reduced); diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp index 6c6c516a4e..3f959b859c 100644 --- a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp +++ b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp @@ -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); diff --git a/src/plugins/platforms/xcb/qglxintegration.cpp b/src/plugins/platforms/xcb/qglxintegration.cpp index c183deb3b8..de907f87df 100644 --- a/src/plugins/platforms/xcb/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/qglxintegration.cpp @@ -54,6 +54,7 @@ #include "qglxintegration.h" #include +#include #if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) #include @@ -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 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()) { + qWarning("QGLXContext: Requires a QGLXNativeContext"); + return; + } + QGLXNativeContext handle = nativeHandle.value(); + 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(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(m_context)); } bool QGLXContext::makeCurrent(QPlatformSurface *surface) diff --git a/src/plugins/platforms/xcb/qglxintegration.h b/src/plugins/platforms/xcb/qglxintegration.h index 00bba94ab3..a7787f5f86 100644 --- a/src/plugins/platforms/xcb/qglxintegration.h +++ b/src/plugins/platforms/xcb/qglxintegration.h @@ -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; }; diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index 0bab341914..2d4c523a4b 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -233,7 +233,10 @@ QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLCont { QXcbScreen *screen = static_cast(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()); diff --git a/src/src.pro b/src/src.pro index 6a805a6a06..b43c856070 100644 --- a/src/src.pro +++ b/src/src.pro @@ -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 -- cgit v1.2.3