/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@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 #include #include #include #include #include // #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 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 int samples = format.samples(); const bool sampleBuffersRequested = samples > 1 && testFlag(staticContext.extensions, QOpenGLStaticContext::SampleBuffers); int samplesValuePosition = 0; if (sampleBuffersRequested) { iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; iAttributes[i++] = TRUE; iAttributes[i++] = WGL_SAMPLES_ARB; samplesValuePosition = i; iAttributes[i++] = format.samples(); } else if (samples == 0 || samples == 1 ) { iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; iAttributes[i++] = FALSE; } // 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)) { qErrnoWarning("%s: wglGetPixelFormatAttribIVARB() failed for basic parameters.", __FUNCTION__); 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]); if (iValues[9]) result.setOption(QSurfaceFormat::StereoBuffers); 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 &, HGLRC shared = 0) { enum { attribSize = 11 }; if (!staticContext.hasExtensions()) return 0; int attributes[attribSize]; int attribIndex = 0; qFill(attributes, attributes + attribSize, int(0)); const int requestedVersion = (format.majorVersion() << 8) + format.minorVersion(); if (requestedVersion > 0x0101) { attributes[attribIndex++] = WGL_CONTEXT_MAJOR_VERSION_ARB; attributes[attribIndex++] = format.majorVersion(); attributes[attribIndex++] = WGL_CONTEXT_MINOR_VERSION_ARB; attributes[attribIndex++] = format.minorVersion(); } if (requestedVersion >= 0x0300) { attributes[attribIndex++] = WGL_CONTEXT_FLAGS_ARB; attributes[attribIndex] = 0; if (format.testOption(QSurfaceFormat::DeprecatedFunctions)) attributes[attribIndex] |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; if (format.testOption(QSurfaceFormat::DebugContext)) attributes[attribIndex++] |= WGL_CONTEXT_DEBUG_BIT_ARB; attribIndex++; } if (requestedVersion >= 0x0302) { 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; } } if (QWindowsContext::verboseGL) qDebug("%s: Creating context version %d.%d with %d attributes", __FUNCTION__, format.majorVersion(), format.minorVersion(), attribIndex / 2); 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 QOpenGLContextFormat \brief Format options that are related to the context (not pixelformats) Provides utility function to retrieve from currently active context and to apply to a QSurfaceFormat. \ingroup qt-lighthouse-win */ QWindowsOpenGLContextFormat::QWindowsOpenGLContextFormat() : profile(QSurfaceFormat::NoProfile), version(0), options(0) { } QWindowsOpenGLContextFormat QWindowsOpenGLContextFormat::current() { QWindowsOpenGLContextFormat result; const QByteArray version = QOpenGLStaticContext::getGlString(GL_VERSION); const int majorDot = version.indexOf('.'); if (majorDot != -1) { int minorDot = version.indexOf('.', majorDot + 1); if (minorDot == -1) minorDot = version.size(); result.version = (version.mid(0, majorDot).toInt() << 8) + version.mid(majorDot + 1, minorDot - majorDot - 1).toInt(); } if (result.version < 0x0300) { result.profile = QSurfaceFormat::NoProfile; result.options |= QSurfaceFormat::DeprecatedFunctions; return result; } // v3 onwards GLint value = 0; glGetIntegerv(GL_CONTEXT_FLAGS, &value); if (value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) result.options |= QSurfaceFormat::DeprecatedFunctions; if (value & WGL_CONTEXT_DEBUG_BIT_ARB) result.options |= QSurfaceFormat::DebugContext; if (result.version < 0x0302) return result; // v3.2 onwards: Profiles value = 0; glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value); switch (value) { case WGL_CONTEXT_CORE_PROFILE_BIT_ARB: result.profile = QSurfaceFormat::CoreProfile; break; case WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB: result.profile = QSurfaceFormat::CompatibilityProfile; break; default: result.profile = QSurfaceFormat::NoProfile; break; } return result; } void QWindowsOpenGLContextFormat::apply(QSurfaceFormat *format) const { format->setMajorVersion(version >> 8); format->setMinorVersion(version & 0xFF); format->setProfile(profile); if (options & QSurfaceFormat::DebugContext) format->setOption(QSurfaceFormat::DebugContext); if (options & QSurfaceFormat::DeprecatedFunctions) format->setOption(QSurfaceFormat::DeprecatedFunctions); } QDebug operator<<(QDebug d, const QWindowsOpenGLContextFormat &f) { d.nospace() << "ContextFormat: v" << (f.version >> 8) << '.' << (f.version & 0xFF) << " profile: " << f.profile << " options: " << f.options; return d; } /*! \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)), extensions(0), defaultFormat(QWindowsOpenGLContextFormat::current()), wglGetPixelFormatAttribIVARB((WglGetPixelFormatAttribIVARB)wglGetProcAddress("wglGetPixelFormatAttribivARB")), wglChoosePixelFormatARB((WglChoosePixelFormatARB)wglGetProcAddress("wglChoosePixelFormatARB")), wglCreateContextAttribsARB((WglCreateContextAttribsARB)wglGetProcAddress("wglCreateContextAttribsARB")), wglSwapInternalExt((WglSwapInternalExt)wglGetProcAddress("wglSwapIntervalEXT")), wglGetSwapInternalExt((WglGetSwapInternalExt)wglGetProcAddress("wglGetSwapIntervalEXT")) { if (extensionNames.startsWith(SAMPLE_BUFFER_EXTENSION" ") || extensionNames.indexOf(" "SAMPLE_BUFFER_EXTENSION" ") != -1) extensions |= SampleBuffers; } 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 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 << " default " << s.defaultFormat; if (s.extensions & QOpenGLStaticContext::SampleBuffers) nsp << ",SampleBuffers"; if (s.hasExtensions()) nsp << ", Extension-API present"; nsp << "\nExtensions: " << (s.extensionNames.count(' ') + 1); if (QWindowsContext::verboseGL > 1) nsp << s.extensionNames; return d; } // Use ARB unless explicitly turned off on command line. static inline bool useARB() { const QVariant glExtension = qApp->platformNativeInterface()->property("gl"); if (glExtension.type() == QVariant::String && !glExtension.toString().compare(QStringLiteral("gdi"), Qt::CaseInsensitive)) return false; return true; } /*! \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, QOpenGLContext *context) : m_staticContext(staticContext), m_context(context), m_renderingContext(0), 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; bool tryExtensions = false; int obtainedSwapInternal = -1; 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); tryExtensions = m_staticContext->hasExtensions() && !testFlag(requestedAdditional.formatFlags, QWindowsGLRenderToPixmap) && useARB(); 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 QPlatformOpenGLContext *sc = context->shareHandle()) sharingRenderingContext = static_cast(sc)->renderingContext(); if (m_extensionsUsed) m_renderingContext = ARB::createContext(*m_staticContext, hdc, context->format(), requestedAdditional, sharingRenderingContext); if (!m_renderingContext) m_renderingContext = GDI::createContext(hdc, sharingRenderingContext); if (!m_renderingContext) { qWarning("Unable to create a GL Context."); break; } // Query obtained parameters and apply swap interval. if (!wglMakeCurrent(hdc, m_renderingContext)) { qWarning("Failed to make context current."); break; } QWindowsOpenGLContextFormat::current().apply(&m_obtainedFormat); if (requestedAdditional.swapInterval != -1 && m_staticContext->wglSwapInternalExt) { m_staticContext->wglSwapInternalExt(requestedAdditional.swapInterval); if (m_staticContext->wglGetSwapInternalExt) obtainedSwapInternal = m_staticContext->wglGetSwapInternalExt(); } wglMakeCurrent(0, 0); } while (false); if (hdc) ReleaseDC(dummyWindow, hdc); if (dummyWindow) DestroyWindow(dummyWindow); if (QWindowsContext::verboseGL) qDebug() << __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 default: " << m_staticContext->defaultFormat << "\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(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 &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(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::OpenGlPixelFormatInitialized)) { if (!SetPixelFormat(newContext.hdc, m_pixelFormat, &m_obtainedPixelFormatDescriptor)) { qErrnoWarning("%s: SetPixelFormat() failed", __FUNCTION__); ReleaseDC(newContext.hwnd, newContext.hdc); return false; } window->setFlag(QWindowsWindow::OpenGlPixelFormatInitialized); if (m_obtainedFormat.swapBehavior() == QSurfaceFormat::DoubleBuffer) window->setFlag(QWindowsWindow::OpenGLDoubleBuffered); } 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(wglGetProcAddress(procName.constData())); if (QWindowsContext::verboseGL > 1) qDebug("%s('%s') with current_hglrc=%p returns %p", __FUNCTION__, procName.constData(), wglGetCurrentContext(), procAddress); if (!procAddress) qWarning("%s: Unable to resolve '%s'", __FUNCTION__, procName.constData()); return procAddress; } QT_END_NAMESPACE