diff options
-rw-r--r-- | src/platformheaders/nativecontexts/nativecontexts.pri | 3 | ||||
-rw-r--r-- | src/platformheaders/nativecontexts/qwglnativecontext.h | 76 | ||||
-rw-r--r-- | src/platformheaders/nativecontexts/qwglnativecontext.qdoc | 131 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowsglcontext.cpp | 82 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowsglcontext.h | 2 | ||||
-rw-r--r-- | tests/auto/gui/qopengl/tst_qopengl.cpp | 63 |
6 files changed, 344 insertions, 13 deletions
diff --git a/src/platformheaders/nativecontexts/nativecontexts.pri b/src/platformheaders/nativecontexts/nativecontexts.pri index 09ad14dd71..cfb190e4fa 100644 --- a/src/platformheaders/nativecontexts/nativecontexts.pri +++ b/src/platformheaders/nativecontexts/nativecontexts.pri @@ -1,3 +1,4 @@ HEADERS += $$PWD/qglxnativecontext.h \ $$PWD/qeglnativecontext.h \ - $$PWD/qcocoanativecontext.h + $$PWD/qcocoanativecontext.h \ + $$PWD/qwglnativecontext.h diff --git a/src/platformheaders/nativecontexts/qwglnativecontext.h b/src/platformheaders/nativecontexts/qwglnativecontext.h new file mode 100644 index 0000000000..4a68633703 --- /dev/null +++ b/src/platformheaders/nativecontexts/qwglnativecontext.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** 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 QWGLNATIVECONTEXT_H +#define QWGLNATIVECONTEXT_H + +#include <QtCore/QMetaType> +#include <wingdi.h> +#include <GL/gl.h> + +QT_BEGIN_NAMESPACE + +class QWGLNativeContext +{ +public: + QWGLNativeContext() + : m_context(0), + m_window(0) + { } + + QWGLNativeContext(HGLRC ctx, HWND wnd) + : m_context(ctx), + m_window(wnd) + { } + + HGLRC context() const { return m_context; } + HWND window() const { return m_window; } + +private: + HGLRC m_context; + HWND m_window; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QWGLNativeContext) + +#endif // QWGLNATIVECONTEXT_H diff --git a/src/platformheaders/nativecontexts/qwglnativecontext.qdoc b/src/platformheaders/nativecontexts/qwglnativecontext.qdoc new file mode 100644 index 0000000000..fd25318217 --- /dev/null +++ b/src/platformheaders/nativecontexts/qwglnativecontext.qdoc @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** 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 QWGLNativeContext + \inmodule QtPlatformHeaders + \since 5.4 + + \brief A class encapsulating a WGL context on Windows with desktop OpenGL (opengl32.dll) + + \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. + + QWGLNativeContext is a value class that can be passed to + QOpenGLContext::setNativeHandle(). When creating a QOpenGLContext with the + native handle set, no new context will get created. Instead, the provided + handles are used, without taking ownership. This allows wrapping a context + created by an external framework or rendering engine. The typical usage will + be similar to the following snippet: + + \code + #include <QtPlatformSupport/QWGLNativeContext> + ...create and retrieve the WGL context and the corresponding window... + QOpenGLContext *context = new QOpenGLContext; + QWGLNativeContext nativeContext(hglrc, hwnd); + context->setNativeHandle(QVariant::fromValue(nativeContext)); + context->create(); + ... + \endcode + + The window is needed because the its pixel format will be queried. When the + adoption is successful, QOpenGLContext::format() will return a QSurfaceFormat + describing this pixel format. + + It is recommended to restrict the usage of QOpenGLContexts created this way. + Various platform-specific behavior and issues may prevent such contexts to be + made current with windows (surfaces) created by Qt due to non-matching pixel + formats for example. A potentially safer solution is to use the wrapped + context only to set up sharing and perform Qt-based rendering offscreen, + using a separate, dedicated QOpenGLContext. The resulting textures are then + accessible in the foreign context too. + + \code + ...like above... + QOpenGLContext *qtcontext = new QOpenGLContext; + qtcontext->setShareContext(context); + qtcontext->setFormat(context->format()); + qtcontext->create(); + ...use qtcontext for rendering with Qt... + \endcode + + In addition to being used with QOpenGLContext::setNativeHandle(), this class + is used also to retrieve the native context handle, that is, a HGLRC value, + from a QOpenGLContext. Calling QOpenGLContext::nativeHandle() returns a + QVariant which, on Windows with opengl32.dll at least, will contain a + QWGLNativeContext: + + \code + QVariant nativeHandle = context->nativeHandle(); + if (!nativeHandle.isNull() && nativeHandle.canConvert<QWGLNativeContext>()) { + QWGLNativeContext nativeContext = nativeHandle.value<QWGLNativeContext>(); + HGLRC hglrc = nativeContext.context(); + ... + } + \endcode + + \sa QOpenGLContext::setNativeHandle(), QOpenGLContext::nativeHandle() + */ + +/*! + \fn HGLRC QWGLNativeContext::context() const + + \return the WGL context. + */ + +/*! + \fn HWND QWGLNativeContext::window() const + + \note The window handle is not available when the QWGLNativeContext is + queried from a regular, non-adopted QOpenGLContext using + QOpenGLContext::nativeHandle(). This is because the \e windows platform + plugin creates WGL contexts using a dummy window that is not available + afterwards. Instead, the native window handle (HWND) is queriable from a + QWindow via QPlatformNativeInterface::nativeResourceForWindow() using the \e + "handle" resource key. Note however that the window will not have its pixel + format set until it is first associated with a context via + QOpenGLContext::makeCurrent(). + + \return handle for the window for which the context was created. + */ + +/*! + \fn QWGLNativeContext::QWGLNativeContext() + + Construct a new instance with no handles. + */ + +/*! + \fn QWGLNativeContext::QWGLNativeContext(HGLRC ctx, HWND wnd) + + Constructs a new instance with the provided \a ctx context handle and \a wnd window handle. + + \note The window specified by \a wnd must have its pixel format set to a + format compatible with the context's. If no SetPixelFormat() call was made on + any device context belonging to the window, adopting the context will fail. + */ diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp index b1152de854..e336c6414e 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -48,6 +48,7 @@ #include <QtCore/QSysInfo> #include <QtGui/QGuiApplication> #include <qpa/qplatformnativeinterface.h> +#include <QtPlatformHeaders/QWGLNativeContext> #include <algorithm> @@ -1038,11 +1039,54 @@ QWindowsGLContext::QWindowsGLContext(QOpenGLStaticContext *staticContext, m_renderingContext(0), m_pixelFormat(0), m_extensionsUsed(false), - m_swapInterval(-1) + m_swapInterval(-1), + m_ownsContext(true) { if (!m_staticContext) // Something went very wrong. Stop here, isValid() will return false. return; + QVariant nativeHandle = context->nativeHandle(); + if (!nativeHandle.isNull()) { + // Adopt and existing context. + if (!nativeHandle.canConvert<QWGLNativeContext>()) { + qWarning("QWindowsGLContext: Requires a QWGLNativeContext"); + return; + } + QWGLNativeContext handle = nativeHandle.value<QWGLNativeContext>(); + HGLRC wglcontext = handle.context(); + HWND wnd = handle.window(); + if (!wglcontext || !wnd) { + qWarning("QWindowsGLContext: No context and window given"); + return; + } + + HDC dc = GetDC(wnd); + // A window with an associated pixel format is mandatory. + // When no SetPixelFormat() call has been made, the following will fail. + m_pixelFormat = GetPixelFormat(dc); + bool ok = m_pixelFormat != 0; + if (!ok) + qWarning("QWindowsGLContext: Failed to get pixel format"); + ok = DescribePixelFormat(dc, m_pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &m_obtainedPixelFormatDescriptor); + if (!ok) { + qWarning("QWindowsGLContext: Failed to describe pixel format"); + } else { + QWindowsOpenGLAdditionalFormat obtainedAdditional; + m_obtainedFormat = GDI::qSurfaceFormatFromPixelFormat(m_obtainedPixelFormatDescriptor, &obtainedAdditional); + m_renderingContext = wglcontext; + ok = updateObtainedParams(dc); + } + + ReleaseDC(wnd, dc); + + if (ok) + m_ownsContext = false; + else + m_renderingContext = 0; + + return; + } + QSurfaceFormat format = context->format(); if (format.renderableType() == QSurfaceFormat::DefaultRenderableType) format.setRenderableType(QSurfaceFormat::OpenGL); @@ -1065,7 +1109,7 @@ QWindowsGLContext::QWindowsGLContext(QOpenGLStaticContext *staticContext, HWND dummyWindow = 0; HDC hdc = 0; bool tryExtensions = false; - int obtainedSwapInternal = -1; + int obtainedSwapInterval = -1; do { dummyWindow = createDummyGLWindow(); if (!dummyWindow) @@ -1131,18 +1175,16 @@ QWindowsGLContext::QWindowsGLContext(QOpenGLStaticContext *staticContext, } // Query obtained parameters and apply swap interval. - if (!QOpenGLStaticContext::opengl32.wglMakeCurrent(hdc, m_renderingContext)) { - qWarning("Failed to make context current."); + if (!updateObtainedParams(hdc, &obtainedSwapInterval)) break; - } - QWindowsOpenGLContextFormat::current().apply(&m_obtainedFormat); + } while (false); - if (m_staticContext->wglGetSwapInternalExt) - obtainedSwapInternal = m_staticContext->wglGetSwapInternalExt(); + // Make the HGLRC retrievable via QOpenGLContext::nativeHandle(). + // Do not provide the window since it is the dummy one and it is about to disappear. + if (m_renderingContext) + context->setNativeHandle(QVariant::fromValue<QWGLNativeContext>(QWGLNativeContext(m_renderingContext, 0))); - QOpenGLStaticContext::opengl32.wglMakeCurrent(0, 0); - } while (false); if (hdc) ReleaseDC(dummyWindow, hdc); if (dummyWindow) @@ -1151,18 +1193,34 @@ QWindowsGLContext::QWindowsGLContext(QOpenGLStaticContext *staticContext, qCDebug(lcQpaGl) << __FUNCTION__ << this << (tryExtensions ? "ARB" : "GDI") << " requested: " << context->format() << "\n obtained #" << m_pixelFormat << (m_extensionsUsed ? "ARB" : "GDI") << m_obtainedFormat - << "\n " << m_obtainedPixelFormatDescriptor << " swap interval: " << obtainedSwapInternal + << "\n " << m_obtainedPixelFormatDescriptor << " swap interval: " << obtainedSwapInterval << "\n default: " << m_staticContext->defaultFormat << "\n HGLRC=" << m_renderingContext; } QWindowsGLContext::~QWindowsGLContext() { - if (m_renderingContext) + if (m_renderingContext && m_ownsContext) QOpenGLStaticContext::opengl32.wglDeleteContext(m_renderingContext); releaseDCs(); } +bool QWindowsGLContext::updateObtainedParams(HDC hdc, int *obtainedSwapInterval) +{ + if (!QOpenGLStaticContext::opengl32.wglMakeCurrent(hdc, m_renderingContext)) { + qWarning("Failed to make context current."); + return false; + } + + QWindowsOpenGLContextFormat::current().apply(&m_obtainedFormat); + + if (m_staticContext->wglGetSwapInternalExt && obtainedSwapInterval) + *obtainedSwapInterval = m_staticContext->wglGetSwapInternalExt(); + + QOpenGLStaticContext::opengl32.wglMakeCurrent(0, 0); + return true; +} + void QWindowsGLContext::releaseDCs() { const QOpenGLContextData *end = m_windowContexts.end(); diff --git a/src/plugins/platforms/windows/qwindowsglcontext.h b/src/plugins/platforms/windows/qwindowsglcontext.h index dcc31c6197..d92d94aa1a 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.h +++ b/src/plugins/platforms/windows/qwindowsglcontext.h @@ -258,6 +258,7 @@ public: private: inline void releaseDCs(); + bool updateObtainedParams(HDC hdc, int *obtainedSwapInterval = 0); QOpenGLStaticContext *m_staticContext; QOpenGLContext *m_context; @@ -268,6 +269,7 @@ private: int m_pixelFormat; bool m_extensionsUsed; int m_swapInterval; + bool m_ownsContext; }; QT_END_NAMESPACE diff --git a/tests/auto/gui/qopengl/tst_qopengl.cpp b/tests/auto/gui/qopengl/tst_qopengl.cpp index 1e8727ca04..972c2a7ea4 100644 --- a/tests/auto/gui/qopengl/tst_qopengl.cpp +++ b/tests/auto/gui/qopengl/tst_qopengl.cpp @@ -65,6 +65,10 @@ #include <QtPlatformHeaders/QGLXNativeContext> #endif +#if defined(Q_OS_WIN32) && !defined(QT_OPENGL_ES_2) +#include <QtPlatformHeaders/QWGLNativeContext> +#endif + Q_DECLARE_METATYPE(QImage::Format) class tst_QOpenGL : public QObject @@ -100,6 +104,10 @@ private slots: void glxContextWrap(); #endif +#if defined(Q_OS_WIN32) && !defined(QT_OPENGL_ES_2) + void wglContextWrap(); +#endif + void vaoCreate(); }; @@ -1033,6 +1041,61 @@ void tst_QOpenGL::glxContextWrap() } #endif // USE_GLX +#if defined(Q_OS_WIN32) && !defined(QT_OPENGL_ES_2) +void tst_QOpenGL::wglContextWrap() +{ + QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext); + QVERIFY(ctx->create()); + if (ctx->isOpenGLES()) + QSKIP("Not applicable to EGL"); + + QScopedPointer<QWindow> window(new QWindow); + window->setSurfaceType(QWindow::OpenGLSurface); + window->setGeometry(0, 0, 256, 256); + window->show(); + QTest::qWaitForWindowExposed(window.data()); + + QVariant v = ctx->nativeHandle(); + QVERIFY(!v.isNull()); + QVERIFY(v.canConvert<QWGLNativeContext>()); + QWGLNativeContext nativeContext = v.value<QWGLNativeContext>(); + QVERIFY(nativeContext.context()); + + // Now do a makeCurrent() do make sure the pixel format on the native + // window (the HWND we are going to retrieve below) is set. + QVERIFY(ctx->makeCurrent(window.data())); + ctx->doneCurrent(); + + HWND wnd = (HWND) qGuiApp->platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("handle"), window.data()); + QVERIFY(wnd); + + QScopedPointer<QOpenGLContext> adopted(new QOpenGLContext); + adopted->setNativeHandle(QVariant::fromValue<QWGLNativeContext>(QWGLNativeContext(nativeContext.context(), wnd))); + QVERIFY(adopted->create()); + + // This tests two things: that a regular, non-adopted QOpenGLContext is + // able to return a QSurfaceFormat containing the real values after + // create(), and that the adopted context got the correct pixel format from + // window and was able to update its format accordingly. + QCOMPARE(adopted->format().version(), ctx->format().version()); + QCOMPARE(adopted->format().profile(), ctx->format().profile()); + QVERIFY(ctx->format().redBufferSize() > 0); + QCOMPARE(adopted->format().redBufferSize(), ctx->format().redBufferSize()); + QVERIFY(ctx->format().greenBufferSize() > 0); + QCOMPARE(adopted->format().greenBufferSize(), ctx->format().greenBufferSize()); + QVERIFY(ctx->format().blueBufferSize() > 0); + QCOMPARE(adopted->format().blueBufferSize(), ctx->format().blueBufferSize()); + QVERIFY(ctx->format().depthBufferSize() > 0); + QCOMPARE(adopted->format().depthBufferSize(), ctx->format().depthBufferSize()); + QVERIFY(ctx->format().stencilBufferSize() > 0); + QCOMPARE(adopted->format().stencilBufferSize(), ctx->format().stencilBufferSize()); + + // This must work since we are using the exact same window. + QVERIFY(adopted->makeCurrent(window.data())); + adopted->doneCurrent(); +} +#endif // Q_OS_WIN32 && !QT_OPENGL_ES_2 + void tst_QOpenGL::vaoCreate() { QScopedPointer<QSurface> surface(createSurface(QSurface::Window)); |