diff options
Diffstat (limited to 'src/plugins/platforms/windows/qwindowsglcontext.cpp')
-rw-r--r-- | src/plugins/platforms/windows/qwindowsglcontext.cpp | 971 |
1 files changed, 971 insertions, 0 deletions
diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp new file mode 100644 index 0000000000..e94b4ffdf2 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -0,0 +1,971 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsglcontext.h" +#include "qwindowscontext.h" +#include "qwindowswindow.h" + +#include <QtCore/QDebug> +#include <QtCore/QSysInfo> + +#include <WinGDI.h> +#if defined(Q_CC_MINGW) +# include <GL/Gl.h> +#else +# include <Gl.h> +#endif + +// #define DEBUG_GL + +// ARB extension API +#ifndef WGL_ARB_multisample +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#endif + +#ifndef WGL_ARB_pixel_format +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_DRAW_TO_BITMAP_ARB 0x2002 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NEED_PALETTE_ARB 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006 +#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_TRANSPARENT_ARB 0x200A +#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037 +#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038 +#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039 +#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A +#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B +#define WGL_SHARE_DEPTH_ARB 0x200C +#define WGL_SHARE_STENCIL_ARB 0x200D +#define WGL_SHARE_ACCUM_ARB 0x200E +#define WGL_SUPPORT_GDI_ARB 0x200F +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201A +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_ALPHA_SHIFT_ARB 0x201C +#define WGL_ACCUM_BITS_ARB 0x201D +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_GENERIC_ACCELERATION_ARB 0x2026 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_SWAP_EXCHANGE_ARB 0x2028 +#define WGL_SWAP_COPY_ARB 0x2029 +#define WGL_SWAP_UNDEFINED_ARB 0x202A +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_TYPE_COLORINDEX_ARB 0x202C +#endif + +#ifndef WGL_ARB_create_context +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x0001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x0002 +// Error codes returned by GetLastError(). +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#endif + +#ifndef GL_VERSION_3_2 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001 +#endif + +QT_BEGIN_NAMESPACE + +template <class MaskType, class FlagType> inline bool testFlag(MaskType mask, FlagType flag) +{ + return (mask & MaskType(flag)) != 0; +} + +static inline bool hasGLOverlay(const PIXELFORMATDESCRIPTOR &pd) +{ return (pd.bReserved & 0x0f) != 0; } + +static inline bool isDirectRendering(const PIXELFORMATDESCRIPTOR &pfd) +{ return (pfd.dwFlags & PFD_GENERIC_ACCELERATED) || !(pfd.dwFlags & PFD_GENERIC_FORMAT); } + +static inline void initPixelFormatDescriptor(PIXELFORMATDESCRIPTOR *d) +{ + memset(d, 0, sizeof(PIXELFORMATDESCRIPTOR)); + d->nSize = sizeof(PIXELFORMATDESCRIPTOR); + d->nVersion = 1; +} + +QDebug operator<<(QDebug d, const PIXELFORMATDESCRIPTOR &pd) +{ + QDebug nsp = d.nospace(); + nsp << "PIXELFORMATDESCRIPTOR " + << "dwFlags=" << hex << showbase << pd.dwFlags << dec << noshowbase; + if (pd.dwFlags & PFD_DRAW_TO_WINDOW) nsp << " PFD_DRAW_TO_WINDOW"; + if (pd.dwFlags & PFD_DRAW_TO_BITMAP) nsp << " PFD_DRAW_TO_BITMAP"; + if (pd.dwFlags & PFD_SUPPORT_GDI) nsp << " PFD_SUPPORT_GDI"; + if (pd.dwFlags & PFD_SUPPORT_OPENGL) nsp << " PFD_SUPPORT_OPENGL"; + if (pd.dwFlags & PFD_GENERIC_ACCELERATED) nsp << " PFD_GENERIC_ACCELERATED"; + if (pd.dwFlags & PFD_SUPPORT_DIRECTDRAW) nsp << " PFD_SUPPORT_DIRECTDRAW"; + if (pd.dwFlags & PFD_DIRECT3D_ACCELERATED) nsp << " PFD_DIRECT3D_ACCELERATED"; + if (pd.dwFlags & PFD_SUPPORT_COMPOSITION) nsp << " PFD_SUPPORT_COMPOSITION"; + if (pd.dwFlags & PFD_GENERIC_FORMAT) nsp << " PFD_GENERIC_FORMAT"; + if (pd.dwFlags & PFD_NEED_PALETTE) nsp << " PFD_NEED_PALETTE"; + if (pd.dwFlags & PFD_NEED_SYSTEM_PALETTE) nsp << " PFD_NEED_SYSTEM_PALETTE"; + if (pd.dwFlags & PFD_DOUBLEBUFFER) nsp << " PFD_DOUBLEBUFFER"; + if (pd.dwFlags & PFD_STEREO) nsp << " PFD_STEREO"; + if (pd.dwFlags & PFD_SWAP_LAYER_BUFFERS) nsp << " PFD_SWAP_LAYER_BUFFERS"; + if (hasGLOverlay(pd)) nsp << " overlay"; + nsp << " iPixelType=" << pd.iPixelType << " cColorBits=" << pd.cColorBits + << " cRedBits=" << pd.cRedBits << " cRedShift=" << pd.cRedShift + << " cGreenBits=" << pd.cGreenBits << " cGreenShift=" << pd.cGreenShift + << " cBlueBits=" << pd.cBlueBits << " cBlueShift=" << pd.cBlueShift; + nsp << " cDepthBits=" << pd.cDepthBits; + if (pd.cStencilBits) + nsp << " cStencilBits=" << pd.cStencilBits; + if (pd.cAuxBuffers) + nsp << " cAuxBuffers=" << pd.cAuxBuffers; + nsp << " iLayerType=" << pd.iLayerType; + if (pd.dwVisibleMask) + nsp << " dwVisibleMask=" << pd.dwVisibleMask; + if (pd.cAlphaBits) + nsp << " cAlphaBits=" << pd.cAlphaBits << " cAlphaShift=" << pd.cAlphaShift; + if (pd.cAccumBits) + nsp << " cAccumBits=" << pd.cAccumBits << " cAccumRedBits=" << pd.cAccumRedBits + << " cAccumGreenBits=" << pd.cAccumGreenBits << " cAccumBlueBits=" << pd.cAccumBlueBits + << " cAccumAlphaBits=" << pd.cAccumAlphaBits; + return d; +} + +// Check whether an obtained PIXELFORMATDESCRIPTOR matches the request. +static inline bool + isAcceptableFormat(const QWindowsOpenGLAdditionalFormat &additional, + const PIXELFORMATDESCRIPTOR &pfd, + bool ignoreGLSupport = false) // ARB format may not contain it. +{ + const bool pixmapRequested = testFlag(additional.formatFlags, QWindowsGLRenderToPixmap); + return (ignoreGLSupport || testFlag(pfd.dwFlags, PFD_SUPPORT_OPENGL)) + && testFlag(pfd.dwFlags, PFD_DRAW_TO_BITMAP) == pixmapRequested + && hasGLOverlay(pfd) == testFlag(additional.formatFlags, QWindowsGLOverlay) + && (!pixmapRequested || pfd.cColorBits == additional.pixmapDepth); +} + +static void describeFormats(HDC hdc) +{ + const int pfiMax = DescribePixelFormat(hdc, 0, 0, NULL); + for (int i = 0; i < pfiMax; i++) { + PIXELFORMATDESCRIPTOR pfd; + initPixelFormatDescriptor(&pfd); + DescribePixelFormat(hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd); + qDebug() << '#' << i << '/' << pfiMax << ':' << pfd; + } +} + +// Classic GDI API +namespace GDI { +static QSurfaceFormat + qSurfaceFormatFromPixelFormat(const PIXELFORMATDESCRIPTOR &pfd, + QWindowsOpenGLAdditionalFormat *additionalIn = 0) +{ + QSurfaceFormat format; + if (pfd.dwFlags & PFD_DOUBLEBUFFER) + format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + format.setDepthBufferSize(pfd.cDepthBits); + + if (pfd.iPixelType == PFD_TYPE_RGBA) + format.setAlphaBufferSize(pfd.cAlphaBits); + format.setRedBufferSize(pfd.cRedBits); + format.setGreenBufferSize(pfd.cGreenBits); + format.setBlueBufferSize(pfd.cBlueBits); + format.setStencilBufferSize(pfd.cStencilBits); + format.setStereo(pfd.dwFlags & PFD_STEREO); + if (additionalIn) { + QWindowsOpenGLAdditionalFormat additional; + if (isDirectRendering(pfd)) + additional.formatFlags |= QWindowsGLDirectRendering; + if (hasGLOverlay(pfd)) + additional.formatFlags |= QWindowsGLOverlay; + if (pfd.cAccumRedBits) + additional.formatFlags |= QWindowsGLAccumBuffer; + if (testFlag(pfd.dwFlags, PFD_DRAW_TO_BITMAP)) { + additional.formatFlags |= QWindowsGLRenderToPixmap; + additional.pixmapDepth = pfd.cColorBits; + } + *additionalIn = additional; + } + return format; +} + +static PIXELFORMATDESCRIPTOR + qPixelFormatFromSurfaceFormat(const QSurfaceFormat &format, + const QWindowsOpenGLAdditionalFormat &additional) +{ + PIXELFORMATDESCRIPTOR pfd; + initPixelFormatDescriptor(&pfd); + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.iLayerType = PFD_MAIN_PLANE; + pfd.dwFlags = PFD_SUPPORT_OPENGL; + if (QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA) + pfd.dwFlags = PFD_SUPPORT_COMPOSITION; + const bool isPixmap = (additional.formatFlags & QWindowsGLRenderToPixmap) != 0; + pfd.dwFlags |= isPixmap ? PFD_DRAW_TO_BITMAP : PFD_DRAW_TO_WINDOW; + if (!(additional.formatFlags & QWindowsGLDirectRendering)) + pfd.dwFlags |= PFD_GENERIC_FORMAT; + + if (format.stereo()) + pfd.dwFlags |= PFD_STEREO; + if (format.swapBehavior() == QSurfaceFormat::DoubleBuffer && !isPixmap) + pfd.dwFlags |= PFD_DOUBLEBUFFER; + pfd.cDepthBits = + format.depthBufferSize() >= 0 ? format.depthBufferSize() : 32; + pfd.cAlphaBits = format.alphaBufferSize() > 0 ? format.alphaBufferSize() : 8; + pfd.cStencilBits = format.stencilBufferSize() > 0 ? format.stencilBufferSize() : 8; + if (additional.formatFlags & QWindowsGLAccumBuffer) + pfd.cAccumRedBits = pfd.cAccumGreenBits = pfd.cAccumBlueBits = pfd.cAccumAlphaBits = 16; + return pfd; +} + +// Choose a suitable pixelformat using GDI WinAPI in case ARB +// functions cannot be found. First tries to find a suitable +// format using GDI function ChoosePixelFormat(). Since that +// does not handle overlay and direct-rendering requests, manually loop +// over the available formats to find the best one. +// Note: As of Windows 7, it seems direct-rendering is handled, so, +// the code might be obsolete? +static int choosePixelFormat(HDC hdc, const QSurfaceFormat &format, + const QWindowsOpenGLAdditionalFormat &additional, + PIXELFORMATDESCRIPTOR *obtainedPfd) +{ + // 1) Try ChoosePixelFormat(). + PIXELFORMATDESCRIPTOR requestedPfd = qPixelFormatFromSurfaceFormat(format, QWindowsGLDirectRendering); + initPixelFormatDescriptor(obtainedPfd); + int pixelFormat = ChoosePixelFormat(hdc, &requestedPfd); + if (pixelFormat >= 0) { + DescribePixelFormat(hdc, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), obtainedPfd); + if (isAcceptableFormat(additional, *obtainedPfd)) + return pixelFormat; + } + // 2) No matching format found, manual search loop. + const int pfiMax = DescribePixelFormat(hdc, 0, 0, NULL); + int bestScore = -1; + int bestPfi = -1; + const bool stereoRequested = format.stereo(); + const bool accumBufferRequested = testFlag(additional.formatFlags, QWindowsGLAccumBuffer); + const bool doubleBufferRequested = format.swapBehavior() == QSurfaceFormat::DoubleBuffer; + const bool directRenderingRequested = testFlag(additional.formatFlags, QWindowsGLDirectRendering); + for (int pfi = 1; pfi <= pfiMax; pfi++) { + PIXELFORMATDESCRIPTOR checkPfd; + initPixelFormatDescriptor(&checkPfd); + DescribePixelFormat(hdc, pfi, sizeof(PIXELFORMATDESCRIPTOR), &checkPfd); + if (isAcceptableFormat(additional, checkPfd)) { + int score = checkPfd.cColorBits + checkPfd.cAlphaBits + checkPfd.cStencilBits; + if (accumBufferRequested) + score += checkPfd.cAccumBits; + if (doubleBufferRequested == testFlag(checkPfd.dwFlags, PFD_DOUBLEBUFFER)) + score += 1000; + if (stereoRequested == testFlag(checkPfd.dwFlags, PFD_STEREO)) + score += 2000; + if (directRenderingRequested == isDirectRendering(checkPfd)) + score += 4000; + if (checkPfd.iPixelType == PFD_TYPE_RGBA) + score += 8000; + if (score > bestScore) { + bestScore = score; + bestPfi = pfi; + *obtainedPfd = checkPfd; + } + if (QWindowsContext::verboseGL) + qDebug() << __FUNCTION__ << " checking " << pfi << '/' << pfiMax + << " score=" << score << " (best " << bestPfi << '/' << bestScore + << ") " << checkPfd; + } + } // for + if (bestPfi > 0) + pixelFormat = bestPfi; + return pixelFormat; +} + +static inline HGLRC createContext(HDC hdc, HGLRC shared) +{ + HGLRC result = wglCreateContext(hdc); + if (!result) { + qErrnoWarning("%s: wglCreateContext failed.", __FUNCTION__); + return 0; + } + if (shared && !wglShareLists(shared, result)) + qErrnoWarning("%s: wglShareLists() failed.", __FUNCTION__); + return result; +} +} // namespace GDI + +// ARB OpenGL extension API +namespace ARB { +// Choose a suitable pixelformat using ARB extension functions. +static int choosePixelFormat(HDC hdc, + const QOpenGLStaticContext &staticContext, + const QSurfaceFormat &format, + const QWindowsOpenGLAdditionalFormat &additional, + PIXELFORMATDESCRIPTOR *obtainedPfd) +{ + enum { attribSize =40 }; + if ((additional.formatFlags & QWindowsGLRenderToPixmap) || !staticContext.hasExtensions()) + return 0; + + int iAttributes[attribSize]; + qFill(iAttributes, iAttributes + attribSize, int(0)); + int i = 0; + iAttributes[i++] = WGL_ACCELERATION_ARB; + iAttributes[i++] = testFlag(additional.formatFlags, QWindowsGLDirectRendering) ? + WGL_FULL_ACCELERATION_ARB : WGL_NO_ACCELERATION_ARB; + iAttributes[i++] = WGL_SUPPORT_OPENGL_ARB; + iAttributes[i++] = TRUE; + iAttributes[i++] = WGL_DRAW_TO_WINDOW_ARB; + iAttributes[i++] = TRUE; + iAttributes[i++] = WGL_COLOR_BITS_ARB; + iAttributes[i++] = 24; + switch (format.swapBehavior()) { + case QSurfaceFormat::DefaultSwapBehavior: + case QSurfaceFormat::TripleBuffer: + break; + case QSurfaceFormat::SingleBuffer: + iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; + iAttributes[i++] = FALSE; + break; + case QSurfaceFormat::DoubleBuffer: + iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; + iAttributes[i++] = TRUE; + break; + } + if (format.stereo()) { + iAttributes[i++] = WGL_STEREO_ARB; + iAttributes[i++] = TRUE; + } + if (format.depthBufferSize() >= 0) { + iAttributes[i++] = WGL_DEPTH_BITS_ARB; + iAttributes[i++] = format.depthBufferSize(); + } + iAttributes[i++] = WGL_PIXEL_TYPE_ARB; + iAttributes[i++] = WGL_TYPE_RGBA_ARB; + if (format.redBufferSize() >= 0) { + iAttributes[i++] = WGL_RED_BITS_ARB; + iAttributes[i++] = format.redBufferSize(); + } + if (format.greenBufferSize() >= 0) { + iAttributes[i++] = WGL_GREEN_BITS_ARB; + iAttributes[i++] = format.greenBufferSize(); + } + if (format.blueBufferSize() >= 0) { + iAttributes[i++] = WGL_BLUE_BITS_ARB; + iAttributes[i++] = format.blueBufferSize(); + } + iAttributes[i++] = WGL_ALPHA_BITS_ARB; + iAttributes[i++] = format.alphaBufferSize() >= 0 ? format.alphaBufferSize() : 8; + if (additional.formatFlags & QWindowsGLAccumBuffer) { + iAttributes[i++] = WGL_ACCUM_BITS_ARB; + iAttributes[i++] = 16; + } + iAttributes[i++] = WGL_STENCIL_BITS_ARB; + iAttributes[i++] = 8; + if (additional.formatFlags & QWindowsGLOverlay) { + iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB; + iAttributes[i++] = 1; + } + const bool sampleBuffersRequested = format.samples() > 1 + && testFlag(staticContext.extensions, QOpenGLStaticContext::SampleBuffers); + int samplesValuePosition = 0; + int samplesEnabledPosition = 0; + if (sampleBuffersRequested) { + iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; + samplesEnabledPosition = i; + iAttributes[i++] = TRUE; + iAttributes[i++] = WGL_SAMPLES_ARB; + samplesValuePosition = i; + iAttributes[i++] = format.samples(); + } + // If sample buffer request cannot be satisfied, reduce request. + int pixelFormat = 0; + uint numFormats = 0; + while (true) { + const bool valid = + staticContext.wglChoosePixelFormatARB(hdc, iAttributes, 0, 1, + &pixelFormat, &numFormats) + && numFormats >= 1; + if (valid || !sampleBuffersRequested) + break; + if (iAttributes[samplesValuePosition] > 1) { + iAttributes[samplesValuePosition] /= 2; + } else { + break; + } + } + // Verify if format is acceptable. Note that the returned + // formats have been observed to not contain PFD_SUPPORT_OPENGL, ignore. + initPixelFormatDescriptor(obtainedPfd); + DescribePixelFormat(hdc, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), obtainedPfd); + if (!isAcceptableFormat(additional, *obtainedPfd, true)) { + if (QWindowsContext::verboseGL) + qDebug() << __FUNCTION__ << " obtained px #" << pixelFormat + << " not acceptable=" << *obtainedPfd; + pixelFormat = 0; + } + + if (QWindowsContext::verboseGL) { + QDebug nsp = qDebug().nospace(); + nsp << __FUNCTION__; + if (sampleBuffersRequested) + nsp << " samples=" << iAttributes[samplesValuePosition]; + nsp << " Attributes: " << hex << showbase; + for (int ii = 0; ii < i; ++ii) + nsp << iAttributes[ii] << ','; + nsp << noshowbase << dec << "\n obtained px #" << pixelFormat + << " of " << numFormats << "\n " << *obtainedPfd; + } // Debug + + return pixelFormat; +} + +static QSurfaceFormat + qSurfaceFormatFromHDC(const QOpenGLStaticContext &staticContext, + HDC hdc, int pixelFormat, + QWindowsOpenGLAdditionalFormat *additionalIn = 0) +{ + enum { attribSize =40 }; + + QSurfaceFormat result; + if (!staticContext.hasExtensions()) + return result; + int iAttributes[attribSize]; + int iValues[attribSize]; + qFill(iAttributes, iAttributes + attribSize, int(0)); + qFill(iValues, iValues + attribSize, int(0)); + + int i = 0; + const bool hasSampleBuffers = testFlag(staticContext.extensions, QOpenGLStaticContext::SampleBuffers); + + iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; // 0 + iAttributes[i++] = WGL_DEPTH_BITS_ARB; // 1 + iAttributes[i++] = WGL_PIXEL_TYPE_ARB; // 2 + iAttributes[i++] = WGL_RED_BITS_ARB; // 3 + iAttributes[i++] = WGL_GREEN_BITS_ARB; // 4 + iAttributes[i++] = WGL_BLUE_BITS_ARB; // 5 + iAttributes[i++] = WGL_ALPHA_BITS_ARB; // 6 + iAttributes[i++] = WGL_ACCUM_BITS_ARB; // 7 + iAttributes[i++] = WGL_STENCIL_BITS_ARB; // 8 + iAttributes[i++] = WGL_STEREO_ARB; // 9 + iAttributes[i++] = WGL_ACCELERATION_ARB; // 10 + iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB; // 11 + if (hasSampleBuffers) { + iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; // 12 + iAttributes[i++] = WGL_SAMPLES_ARB; // 13 + } + if (!staticContext.wglGetPixelFormatAttribIVARB(hdc, pixelFormat, 0, i, + iAttributes, iValues)) + return result; + if (iValues[0]) + result.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + result.setDepthBufferSize(iValues[1]); + result.setRedBufferSize(iValues[3]); + result.setGreenBufferSize(iValues[4]); + result.setBlueBufferSize(iValues[5]); + result.setAlphaBufferSize(iValues[6]); + result.setStencilBufferSize(iValues[8]); + result.setStereo(iValues[9]); + if (hasSampleBuffers) + result.setSamples(iValues[13]); + if (additionalIn) { + if (iValues[7]) + additionalIn->formatFlags |= QWindowsGLAccumBuffer; + if (iValues[10] == WGL_FULL_ACCELERATION_ARB) + additionalIn->formatFlags |= QWindowsGLDirectRendering; + if (iValues[11]) + additionalIn->formatFlags |= QWindowsGLOverlay; + } + return result; +} + +static HGLRC createContext(const QOpenGLStaticContext &staticContext, + HDC hdc, + const QSurfaceFormat &format, + const QWindowsOpenGLAdditionalFormat &additional, + int majorVersion = 0, + int minorVersion = 0, + HGLRC shared = 0) +{ + enum { attribSize = 11 }; + + if (!staticContext.hasExtensions()) + return 0; + int attributes[attribSize]; + int attribIndex = 0; + qFill(attributes, attributes + attribSize, int(0)); + + if (majorVersion) { + attributes[attribIndex++] = WGL_CONTEXT_MAJOR_VERSION_ARB; + attributes[attribIndex++] = majorVersion; + attributes[attribIndex++] = WGL_CONTEXT_MINOR_VERSION_ARB; + attributes[attribIndex++] = minorVersion; + } + if (majorVersion >= 3 && additional.formatFlags & QWindowsGLDeprecatedFunctions) { + attributes[attribIndex++] = WGL_CONTEXT_FLAGS_ARB; + attributes[attribIndex++] = WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + } + if ((staticContext.majorVersion == 3 && staticContext.minorVersion >= 2) + || staticContext.majorVersion > 3) { + switch (format.profile()) { + case QSurfaceFormat::NoProfile: + break; + case QSurfaceFormat::CoreProfile: + attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB; + attributes[attribIndex++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + break; + case QSurfaceFormat::CompatibilityProfile: + attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB; + attributes[attribIndex++] = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + break; + } + } + const HGLRC result = + staticContext.wglCreateContextAttribsARB(hdc, shared, attributes); + if (!result) + qErrnoWarning("%s: wglCreateContextAttribsARB() failed.", __FUNCTION__); + return result; +} + +} // namespace ARB + +// Helpers for temporary contexts +static inline HWND createDummyGLWindow() +{ + return QWindowsContext::instance()-> + createDummyWindow(QStringLiteral("QtOpenGLDummyWindow"), + L"OpenGLDummyWindow", 0, WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); +} + +// Create a dummy GL context (see QOpenGLTemporaryContext). +static inline HGLRC createDummyGLContext(HDC dc) +{ + if (!dc) + return 0; + PIXELFORMATDESCRIPTOR pixelFormDescriptor; + initPixelFormatDescriptor(&pixelFormDescriptor); + pixelFormDescriptor.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_GENERIC_FORMAT; + pixelFormDescriptor.iPixelType = PFD_TYPE_RGBA; + const int pixelFormat = ChoosePixelFormat(dc, &pixelFormDescriptor); + if (!pixelFormat) { + qErrnoWarning("%s: ChoosePixelFormat failed.", __FUNCTION__); + return 0; + } + if (!SetPixelFormat(dc, pixelFormat, &pixelFormDescriptor)) { + qErrnoWarning("%s: SetPixelFormat failed.", __FUNCTION__); + return 0; + } + HGLRC rc = wglCreateContext(dc); + if (!rc) { + qErrnoWarning("%s: wglCreateContext failed.", __FUNCTION__); + return 0; + } + return rc; +} + +static inline QOpenGLContextData currentOpenGLContextData() +{ + QOpenGLContextData result; + result.hdc = wglGetCurrentDC(); + result.renderingContext = wglGetCurrentContext(); + return result; +} + +static inline QOpenGLContextData createDummyWindowOpenGLContextData() +{ + QOpenGLContextData result; + result.hwnd = createDummyGLWindow(); + result.hdc = GetDC(result.hwnd); + result.renderingContext = createDummyGLContext(result.hdc); + return result; +} + +/*! + \class QOpenGLTemporaryContext + \brief A temporary context that can be instantiated on the stack. + + Functions like wglGetProcAddress() or glGetString() only work if there + is a current GL context. + + \ingroup qt-lighthouse-win +*/ + +class QOpenGLTemporaryContext +{ + Q_DISABLE_COPY(QOpenGLTemporaryContext) +public: + QOpenGLTemporaryContext(); + ~QOpenGLTemporaryContext(); + +private: + const QOpenGLContextData m_previous; + const QOpenGLContextData m_current; +}; + +QOpenGLTemporaryContext::QOpenGLTemporaryContext() : + m_previous(currentOpenGLContextData()), + m_current(createDummyWindowOpenGLContextData()) +{ + wglMakeCurrent(m_current.hdc, m_current.renderingContext); +} + +QOpenGLTemporaryContext::~QOpenGLTemporaryContext() +{ + wglMakeCurrent(m_previous.hdc, m_previous.renderingContext); + ReleaseDC(m_current.hwnd, m_current.hdc); + DestroyWindow(m_current.hwnd); + wglDeleteContext(m_current.renderingContext); +} + +/*! + \class QWindowsOpenGLAdditionalFormat + \brief Additional format information that is not in QSurfaceFormat + \ingroup qt-lighthouse-win +*/ + +/*! + \class QOpenGLStaticContext + \brief Static Open GL context containing version information, extension function pointers, etc. + + Functions pending integration in the next version of OpenGL are post-fixed ARB. + + \note Initialization requires an active context (see create()). + + \sa QWindowsGLContext + \ingroup qt-lighthouse-win +*/ + +#define SAMPLE_BUFFER_EXTENSION "GL_ARB_multisample" + +QOpenGLStaticContext::QOpenGLStaticContext() : + vendor(QOpenGLStaticContext::getGlString(GL_VENDOR)), + renderer(QOpenGLStaticContext::getGlString(GL_RENDERER)), + extensionNames(QOpenGLStaticContext::getGlString(GL_EXTENSIONS)), + majorVersion(0), minorVersion(0), + extensions(0), + wglGetPixelFormatAttribIVARB((WglGetPixelFormatAttribIVARB)wglGetProcAddress("wglGetPixelFormatAttribivARB")), + wglChoosePixelFormatARB((WglChoosePixelFormatARB)wglGetProcAddress("wglChoosePixelFormatARB")), + wglCreateContextAttribsARB((WglCreateContextAttribsARB)wglGetProcAddress("wglCreateContextAttribsARB")) +{ + if (extensionNames.startsWith(SAMPLE_BUFFER_EXTENSION" ") + || extensionNames.indexOf(" "SAMPLE_BUFFER_EXTENSION" ") != -1) + extensions |= SampleBuffers; + // Get version + do { + const QByteArray version = QOpenGLStaticContext::getGlString(GL_VERSION); + if (version.isEmpty()) + break; + const int majorDot = version.indexOf('.'); + if (majorDot == -1) + break; + int minorDot = version.indexOf('.', majorDot + 1); + if (minorDot == -1) + minorDot = version.size(); + majorVersion = version.mid(0, majorDot).toInt(); + minorVersion = version.mid(majorDot + 1, minorDot - majorDot - 1).toInt(); + } while (false); +} + +QByteArray QOpenGLStaticContext::getGlString(unsigned int which) +{ + if (const GLubyte *s = glGetString(which)) + return QByteArray((const char*)s); + return QByteArray(); +} + +QOpenGLStaticContext *QOpenGLStaticContext::create() +{ + // We need a current context for wglGetProcAdress()/getGLString() to work. + QScopedPointer<QOpenGLTemporaryContext> temporaryContext; + if (!wglGetCurrentContext()) + temporaryContext.reset(new QOpenGLTemporaryContext); + QOpenGLStaticContext *result = new QOpenGLStaticContext; + if (QWindowsContext::verboseGL) + qDebug() << __FUNCTION__ << *result; + return result; +} + +QDebug operator<<(QDebug d, const QOpenGLStaticContext &s) +{ + QDebug nsp = d.nospace(); + nsp << "OpenGL: " << s.vendor << ',' << s.renderer << ",v" + << s.majorVersion << '.' << s.minorVersion; + if (s.extensions & QOpenGLStaticContext::SampleBuffers) + nsp << ",SampleBuffers"; + if (s.hasExtensions()) + nsp << ", Extension-API present"; + nsp << "\nExtensions: " << s.extensionNames; + return d; +} + +/*! + \class QWindowsGLContext + \brief Open GL context. + + An Open GL context for use with several windows. + As opposed to other implementations, activating a GL context for + a window requires a HDC allocated for it. The first time this + HDC is created for the window, the pixel format must be applied, + which will affect the window as well. The HDCs are stored in a list of + QOpenGLContextData and are released in doneCurrent(). + + \ingroup qt-lighthouse-win +*/ + +QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContext, + QGuiGLContext *context) : + m_staticContext(staticContext), + m_context(context), + m_pixelFormat(0), m_extensionsUsed(false) +{ + // workaround for matrox driver: + // make a cheap call to opengl to force loading of DLL + static bool opengl32dll = false; + if (!opengl32dll) { + GLint params; + glGetIntegerv(GL_DEPTH_BITS, ¶ms); + opengl32dll = true; + } + + // SetPixelFormat (as of Windows 7) requires a real window. + // Create a dummy one as we are not associated with a window yet. + // Try to find a suitable pixel format using preferably ARB extensions + // (default to GDI) and store that. + HWND dummyWindow = 0; + HDC hdc = 0; + do { + dummyWindow = createDummyGLWindow(); + if (!dummyWindow) + break; + hdc = GetDC(dummyWindow); + if (!hdc) + break; + + if (QWindowsContext::verboseGL > 1) + describeFormats(hdc); + // Preferably use direct rendering and ARB extensions (unless pixmap) + const QWindowsOpenGLAdditionalFormat + requestedAdditional(QWindowsGLDirectRendering|QWindowsGLDeprecatedFunctions); + const bool tryExtensions = m_staticContext->hasExtensions() + && !testFlag(requestedAdditional.formatFlags, QWindowsGLRenderToPixmap); + QWindowsOpenGLAdditionalFormat obtainedAdditional; + if (tryExtensions) { + m_pixelFormat = + ARB::choosePixelFormat(hdc, *m_staticContext, context->format(), + requestedAdditional, &m_obtainedPixelFormatDescriptor); + if (m_pixelFormat > 0) { + m_obtainedFormat = + ARB::qSurfaceFormatFromHDC(*m_staticContext, hdc, m_pixelFormat, + &obtainedAdditional); + m_extensionsUsed = true; + } + } // tryExtensions + if (!m_pixelFormat) { // Failed, try GDI + m_pixelFormat = GDI::choosePixelFormat(hdc, context->format(), requestedAdditional, + &m_obtainedPixelFormatDescriptor); + if (m_pixelFormat) + m_obtainedFormat = + GDI::qSurfaceFormatFromPixelFormat(m_obtainedPixelFormatDescriptor, + &obtainedAdditional); + } // try GDI + if (!m_pixelFormat) { + qWarning("%s: Unable find a suitable pixel format.", __FUNCTION__); + break; + } + if (!SetPixelFormat(hdc, m_pixelFormat, &m_obtainedPixelFormatDescriptor)) { + qErrnoWarning("SetPixelFormat failed."); + break; + } + // Create context with sharing, again preferably using ARB. + HGLRC sharingRenderingContext = 0; + if (const QPlatformGLContext *sc = context->shareHandle()) + sharingRenderingContext = static_cast<const QWindowsGLContext *>(sc)->renderingContext(); + + if (m_extensionsUsed) + m_renderingContext = + ARB::createContext(*m_staticContext, hdc, context->format(), + requestedAdditional, 0, 0, sharingRenderingContext); + if (!m_renderingContext) + m_renderingContext = GDI::createContext(hdc, sharingRenderingContext); + + if (!m_renderingContext) { + qWarning("Unable to create a GL Context."); + break; + } + } while (false); + if (hdc) + ReleaseDC(dummyWindow, hdc); + if (dummyWindow) + DestroyWindow(dummyWindow); + + if (QWindowsContext::verboseGL) + qDebug() + << __FUNCTION__ << this << " requested: " << context->format() + << "\n obtained #" << m_pixelFormat << (m_extensionsUsed ? "ARB" : "GDI") + << m_obtainedFormat << "\n " << m_obtainedPixelFormatDescriptor + << "\n HGLRC=" << m_renderingContext; +} + +QWindowsGLContext::~QWindowsGLContext() +{ + if (m_renderingContext) + wglDeleteContext(m_renderingContext); + releaseDCs(); +} + +void QWindowsGLContext::releaseDCs() +{ + const QOpenGLContextData *end = m_windowContexts.end(); + for (const QOpenGLContextData *p = m_windowContexts.begin(); p < end; ++p) + ReleaseDC(p->hwnd, p->hdc); + m_windowContexts.resize(0); +} + +static inline QWindowsWindow *glWindowOf(QPlatformSurface *s) +{ + return static_cast<QWindowsWindow *>(s); +} + +static inline HWND handleOf(QPlatformSurface *s) +{ + return glWindowOf(s)->handle(); +} + +// Find a window in a context list. +static inline const QOpenGLContextData * + findByHWND(const Array<QOpenGLContextData> &data, HWND hwnd) +{ + const QOpenGLContextData *end = data.end(); + for (const QOpenGLContextData *p = data.begin(); p < end; ++p) + if (p->hwnd == hwnd) + return p; + return 0; +} + +void QWindowsGLContext::swapBuffers(QPlatformSurface *surface) +{ + if (QWindowsContext::verboseGL > 1) + qDebug() << __FUNCTION__ << surface; + if (const QOpenGLContextData *contextData = findByHWND(m_windowContexts, handleOf(surface))) { + SwapBuffers(contextData->hdc); + } else { + qWarning("%s: Cannot find window %p", __FUNCTION__, handleOf(surface)); + } +} + +bool QWindowsGLContext::makeCurrent(QPlatformSurface *surface) +{ +#ifdef DEBUG_GL + if (QWindowsContext::verboseGL > 1) + qDebug("%s context=%p contexts=%d", __FUNCTION__, this, m_windowContexts.size()); +#endif // DEBUG_GL + // Do we already have a DC entry for that window? + QWindowsWindow *window = static_cast<QWindowsWindow *>(surface); + const HWND hwnd = window->handle(); + if (const QOpenGLContextData *contextData = findByHWND(m_windowContexts, hwnd)) + return wglMakeCurrent(contextData->hdc, contextData->renderingContext); + // Create a new entry. + const QOpenGLContextData newContext(m_renderingContext, hwnd, GetDC(hwnd)); + if (!newContext.hdc) + return false; + // Initialize pixel format first time. This will apply to + // the HWND as well and must be done only once. + if (!window->testFlag(QWindowsWindow::PixelFormatInitialized)) { + if (!SetPixelFormat(newContext.hdc, m_pixelFormat, &m_obtainedPixelFormatDescriptor)) { + qErrnoWarning("%s: SetPixelFormat() failed", __FUNCTION__); + ReleaseDC(newContext.hwnd, newContext.hdc); + return false; + } + window->setFlag(QWindowsWindow::PixelFormatInitialized); + } + m_windowContexts.append(newContext); + return wglMakeCurrent(newContext.hdc, newContext.renderingContext); +} + +void QWindowsGLContext::doneCurrent() +{ +#ifdef DEBUG_GL + if (QWindowsContext::verboseGL > 1) + qDebug("%s context=%p %d contexts", __FUNCTION__, this, m_windowContexts.size()); +#endif // DEBUG_GL + wglMakeCurrent(0, 0); + releaseDCs(); +} + +QWindowsGLContext::GL_Proc QWindowsGLContext::getProcAddress(const QByteArray &procName) +{ + // TODO: Will that work with the calling conventions? + GL_Proc procAddress = reinterpret_cast<GL_Proc>(wglGetProcAddress(procName.constData())); + if (QWindowsContext::verboseGL) + qDebug("%s('%s') with current_hglrc=%p returns %p", + __FUNCTION__, procName.constData(), + wglGetCurrentContext(), procAddress); + return procAddress; +} + +QT_END_NAMESPACE |