Add context adoption support for WGL
This is trickier than the GLX and EGL implementations due to the way pixel formats, windows and contexts work. Apart from some restrictions, it should be fully functional nonetheless. Add also some proper documentation. Change-Id: Ia6e3eb1ab2701e439b8621b9092c2b0934ff2151 Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
This commit is contained in:
parent
d8103d0e62
commit
e7056a8969
@ -1,3 +1,4 @@
|
||||
HEADERS += $$PWD/qglxnativecontext.h \
|
||||
$$PWD/qeglnativecontext.h \
|
||||
$$PWD/qcocoanativecontext.h
|
||||
$$PWD/qcocoanativecontext.h \
|
||||
$$PWD/qwglnativecontext.h
|
||||
|
76
src/platformheaders/nativecontexts/qwglnativecontext.h
Normal file
76
src/platformheaders/nativecontexts/qwglnativecontext.h
Normal file
@ -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
|
131
src/platformheaders/nativecontexts/qwglnativecontext.qdoc
Normal file
131
src/platformheaders/nativecontexts/qwglnativecontext.qdoc
Normal file
@ -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.
|
||||
*/
|
@ -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);
|
||||
|
||||
if (m_staticContext->wglGetSwapInternalExt)
|
||||
obtainedSwapInternal = m_staticContext->wglGetSwapInternalExt();
|
||||
|
||||
QOpenGLStaticContext::opengl32.wglMakeCurrent(0, 0);
|
||||
} while (false);
|
||||
|
||||
// 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)));
|
||||
|
||||
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();
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user