1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qwindowsglcontext.h"
41 #include "qwindowscontext.h"
42 #include "qwindowswindow.h"
43 #include "qwindowsintegration.h"
44 
45 #include <QtCore/qdebug.h>
46 #include <QtCore/qsysinfo.h>
47 #include <QtGui/qguiapplication.h>
48 #include <qpa/qplatformnativeinterface.h>
49 #include <QtPlatformHeaders/qwglnativecontext.h>
50 
51 #include <algorithm>
52 
53 #include <wingdi.h>
54 #include <GL/gl.h>
55 
56 // #define DEBUG_GL
57 
58 // ARB extension API
59 #ifndef WGL_ARB_multisample
60 #define WGL_SAMPLE_BUFFERS_ARB               0x2041
61 #define WGL_SAMPLES_ARB                      0x2042
62 #endif
63 
64 #ifndef WGL_ARB_pixel_format
65 #define WGL_NUMBER_PIXEL_FORMATS_ARB   0x2000
66 #define WGL_DRAW_TO_WINDOW_ARB         0x2001
67 #define WGL_DRAW_TO_BITMAP_ARB         0x2002
68 #define WGL_ACCELERATION_ARB           0x2003
69 #define WGL_NEED_PALETTE_ARB           0x2004
70 #define WGL_NEED_SYSTEM_PALETTE_ARB    0x2005
71 #define WGL_SWAP_LAYER_BUFFERS_ARB     0x2006
72 #define WGL_SWAP_METHOD_ARB            0x2007
73 #define WGL_NUMBER_OVERLAYS_ARB        0x2008
74 #define WGL_NUMBER_UNDERLAYS_ARB       0x2009
75 #define WGL_TRANSPARENT_ARB            0x200A
76 #define WGL_TRANSPARENT_RED_VALUE_ARB  0x2037
77 #define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
78 #define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
79 #define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
80 #define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
81 #define WGL_SHARE_DEPTH_ARB            0x200C
82 #define WGL_SHARE_STENCIL_ARB          0x200D
83 #define WGL_SHARE_ACCUM_ARB            0x200E
84 #define WGL_SUPPORT_GDI_ARB            0x200F
85 #define WGL_SUPPORT_OPENGL_ARB         0x2010
86 #define WGL_DOUBLE_BUFFER_ARB          0x2011
87 #define WGL_STEREO_ARB                 0x2012
88 #define WGL_PIXEL_TYPE_ARB             0x2013
89 #define WGL_COLOR_BITS_ARB             0x2014
90 #define WGL_RED_BITS_ARB               0x2015
91 #define WGL_RED_SHIFT_ARB              0x2016
92 #define WGL_GREEN_BITS_ARB             0x2017
93 #define WGL_GREEN_SHIFT_ARB            0x2018
94 #define WGL_BLUE_BITS_ARB              0x2019
95 #define WGL_BLUE_SHIFT_ARB             0x201A
96 #define WGL_ALPHA_BITS_ARB             0x201B
97 #define WGL_ALPHA_SHIFT_ARB            0x201C
98 #define WGL_ACCUM_BITS_ARB             0x201D
99 #define WGL_ACCUM_RED_BITS_ARB         0x201E
100 #define WGL_ACCUM_GREEN_BITS_ARB       0x201F
101 #define WGL_ACCUM_BLUE_BITS_ARB        0x2020
102 #define WGL_ACCUM_ALPHA_BITS_ARB       0x2021
103 #define WGL_DEPTH_BITS_ARB             0x2022
104 #define WGL_STENCIL_BITS_ARB           0x2023
105 #define WGL_AUX_BUFFERS_ARB            0x2024
106 #define WGL_NO_ACCELERATION_ARB        0x2025
107 #define WGL_GENERIC_ACCELERATION_ARB   0x2026
108 #define WGL_FULL_ACCELERATION_ARB      0x2027
109 #define WGL_SWAP_EXCHANGE_ARB          0x2028
110 #define WGL_SWAP_COPY_ARB              0x2029
111 #define WGL_SWAP_UNDEFINED_ARB         0x202A
112 #define WGL_TYPE_RGBA_ARB              0x202B
113 #define WGL_TYPE_COLORINDEX_ARB        0x202C
114 #endif
115 
116 #ifndef WGL_ARB_create_context
117 #define WGL_CONTEXT_MAJOR_VERSION_ARB               0x2091
118 #define WGL_CONTEXT_MINOR_VERSION_ARB               0x2092
119 #define WGL_CONTEXT_LAYER_PLANE_ARB                 0x2093
120 #define WGL_CONTEXT_FLAGS_ARB                       0x2094
121 #define WGL_CONTEXT_PROFILE_MASK_ARB                0x9126
122 #define WGL_CONTEXT_DEBUG_BIT_ARB                   0x0001
123 #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB      0x0002
124 #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB            0x0001
125 #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB   0x0002
126 // Error codes returned by GetLastError().
127 #define ERROR_INVALID_VERSION_ARB                   0x2095
128 #define ERROR_INVALID_PROFILE_ARB                   0x2096
129 #endif
130 
131 #ifndef GL_VERSION_3_2
132 #define GL_CONTEXT_PROFILE_MASK                     0x9126
133 #define GL_MAJOR_VERSION                            0x821B
134 #define GL_MINOR_VERSION                            0x821C
135 #define GL_NUM_EXTENSIONS                           0x821D
136 #define GL_CONTEXT_FLAGS                            0x821E
137 #define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT      0x0001
138 #endif
139 
140 #ifndef GL_CONTEXT_FLAG_DEBUG_BIT
141 #define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
142 #endif
143 
144 // Common GL and WGL constants
145 #define RESET_NOTIFICATION_STRATEGY_ARB 0x8256
146 #define LOSE_CONTEXT_ON_RESET_ARB 0x8252
147 
148 #ifndef WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT
149 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20A9
150 #endif
151 
152 QT_BEGIN_NAMESPACE
153 
154 QWindowsOpengl32DLL QOpenGLStaticContext::opengl32;
155 
resolve(const char * name)156 QFunctionPointer QWindowsOpengl32DLL::resolve(const char *name)
157 {
158     return m_lib
159         ? reinterpret_cast<QFunctionPointer>(::GetProcAddress(m_lib, name))
160         : nullptr;
161 }
162 
init(bool softwareRendering)163 bool QWindowsOpengl32DLL::init(bool softwareRendering)
164 {
165     const QByteArray opengl32 = QByteArrayLiteral("opengl32.dll");
166     const QByteArray swopengl = QByteArrayLiteral("opengl32sw.dll");
167 
168     QByteArray openglDll = qgetenv("QT_OPENGL_DLL");
169     if (openglDll.isEmpty())
170         openglDll = softwareRendering ? swopengl : opengl32;
171 
172     openglDll = openglDll.toLower();
173     m_nonOpengl32 = openglDll != opengl32;
174 
175     qCDebug(lcQpaGl) << "Qt: Using WGL and OpenGL from" << openglDll;
176 
177     m_lib = ::LoadLibraryA(openglDll.constData());
178     if (!m_lib) {
179         qErrnoWarning(::GetLastError(), "Failed to load %s", openglDll.constData());
180         return false;
181     }
182 
183     if (moduleIsNotOpengl32()) {
184         // Load opengl32.dll always. GDI functions like ChoosePixelFormat do
185         // GetModuleHandle for opengl32.dll and behave differently (and call back into
186         // opengl32) when the module is present. This is fine for dummy contexts and windows.
187         ::LoadLibraryA("opengl32.dll");
188     }
189 
190     wglCreateContext = reinterpret_cast<HGLRC (WINAPI *)(HDC)>(resolve("wglCreateContext"));
191     wglDeleteContext = reinterpret_cast<BOOL (WINAPI *)(HGLRC)>(resolve("wglDeleteContext"));
192     wglGetCurrentContext = reinterpret_cast<HGLRC (WINAPI *)()>(resolve("wglGetCurrentContext"));
193     wglGetCurrentDC = reinterpret_cast<HDC (WINAPI *)()>(resolve("wglGetCurrentDC"));
194     wglGetProcAddress = reinterpret_cast<PROC (WINAPI *)(LPCSTR)>(resolve("wglGetProcAddress"));
195     wglMakeCurrent = reinterpret_cast<BOOL (WINAPI *)(HDC, HGLRC)>(resolve("wglMakeCurrent"));
196     wglShareLists = reinterpret_cast<BOOL (WINAPI *)(HGLRC, HGLRC)>(resolve("wglShareLists"));
197     wglSwapBuffers = reinterpret_cast<BOOL (WINAPI *)(HDC)>(resolve("wglSwapBuffers"));
198     wglSetPixelFormat = reinterpret_cast<BOOL (WINAPI *)(HDC, int, const PIXELFORMATDESCRIPTOR *)>(resolve("wglSetPixelFormat"));
199     wglDescribePixelFormat = reinterpret_cast<int (WINAPI *)(HDC, int, UINT, PIXELFORMATDESCRIPTOR *)>(resolve("wglDescribePixelFormat"));
200 
201     glGetError = reinterpret_cast<GLenum (APIENTRY *)()>(resolve("glGetError"));
202     glGetIntegerv = reinterpret_cast<void (APIENTRY *)(GLenum , GLint *)>(resolve("glGetIntegerv"));
203     glGetString = reinterpret_cast<const GLubyte * (APIENTRY *)(GLenum )>(resolve("glGetString"));
204 
205     return wglCreateContext && glGetError && glGetString;
206 }
207 
swapBuffers(HDC dc)208 BOOL QWindowsOpengl32DLL::swapBuffers(HDC dc)
209 {
210     return moduleIsNotOpengl32() ? wglSwapBuffers(dc) : SwapBuffers(dc);
211 }
212 
setPixelFormat(HDC dc,int pf,const PIXELFORMATDESCRIPTOR * pfd)213 BOOL QWindowsOpengl32DLL::setPixelFormat(HDC dc, int pf, const PIXELFORMATDESCRIPTOR *pfd)
214 {
215     return moduleIsNotOpengl32() ? wglSetPixelFormat(dc, pf, pfd) : SetPixelFormat(dc, pf, pfd);
216 }
217 
describePixelFormat(HDC dc,int pf,UINT size,PIXELFORMATDESCRIPTOR * pfd)218 int QWindowsOpengl32DLL::describePixelFormat(HDC dc, int pf, UINT size, PIXELFORMATDESCRIPTOR *pfd)
219 {
220     return moduleIsNotOpengl32() ? wglDescribePixelFormat(dc, pf, size, pfd) : DescribePixelFormat(dc, pf, size, pfd);
221 }
222 
createContext(QOpenGLContext * context)223 QWindowsOpenGLContext *QOpenGLStaticContext::createContext(QOpenGLContext *context)
224 {
225     return new QWindowsGLContext(this, context);
226 }
227 
testFlag(MaskType mask,FlagType flag)228 template <class MaskType, class FlagType> inline bool testFlag(MaskType mask, FlagType flag)
229 {
230     return (mask & MaskType(flag)) != 0;
231 }
232 
hasGLOverlay(const PIXELFORMATDESCRIPTOR & pd)233 static inline bool hasGLOverlay(const PIXELFORMATDESCRIPTOR &pd)
234 { return (pd.bReserved & 0x0f) != 0; }
235 
isDirectRendering(const PIXELFORMATDESCRIPTOR & pfd)236 static inline bool isDirectRendering(const PIXELFORMATDESCRIPTOR &pfd)
237 { return (pfd.dwFlags & PFD_GENERIC_ACCELERATED) || !(pfd.dwFlags & PFD_GENERIC_FORMAT); }
238 
initPixelFormatDescriptor(PIXELFORMATDESCRIPTOR * d)239 static inline void initPixelFormatDescriptor(PIXELFORMATDESCRIPTOR *d)
240 {
241     memset(d, 0, sizeof(PIXELFORMATDESCRIPTOR));
242     d->nSize = sizeof(PIXELFORMATDESCRIPTOR);
243     d->nVersion = 1;
244 }
245 
246 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug d,const PIXELFORMATDESCRIPTOR & pd)247 QDebug operator<<(QDebug d, const PIXELFORMATDESCRIPTOR &pd)
248 {
249     QDebugStateSaver saver(d);
250     d.nospace();
251     d << "PIXELFORMATDESCRIPTOR "
252         << "dwFlags=" << Qt::hex << Qt::showbase << pd.dwFlags << Qt::dec << Qt::noshowbase;
253     if (pd.dwFlags & PFD_DRAW_TO_WINDOW) d << " PFD_DRAW_TO_WINDOW";
254     if (pd.dwFlags & PFD_DRAW_TO_BITMAP) d << " PFD_DRAW_TO_BITMAP";
255     if (pd.dwFlags & PFD_SUPPORT_GDI) d << " PFD_SUPPORT_GDI";
256     if (pd.dwFlags & PFD_SUPPORT_OPENGL) d << " PFD_SUPPORT_OPENGL";
257     if (pd.dwFlags & PFD_GENERIC_ACCELERATED) d << " PFD_GENERIC_ACCELERATED";
258     if (pd.dwFlags & PFD_SUPPORT_DIRECTDRAW) d << " PFD_SUPPORT_DIRECTDRAW";
259     if (pd.dwFlags & PFD_DIRECT3D_ACCELERATED) d << " PFD_DIRECT3D_ACCELERATED";
260     if (pd.dwFlags & PFD_SUPPORT_COMPOSITION) d << " PFD_SUPPORT_COMPOSITION";
261     if (pd.dwFlags & PFD_GENERIC_FORMAT) d << " PFD_GENERIC_FORMAT";
262     if (pd.dwFlags & PFD_NEED_PALETTE) d << " PFD_NEED_PALETTE";
263     if (pd.dwFlags & PFD_NEED_SYSTEM_PALETTE) d << " PFD_NEED_SYSTEM_PALETTE";
264     if (pd.dwFlags & PFD_DOUBLEBUFFER) d << " PFD_DOUBLEBUFFER";
265     if (pd.dwFlags & PFD_STEREO) d << " PFD_STEREO";
266     if (pd.dwFlags & PFD_SWAP_LAYER_BUFFERS) d << " PFD_SWAP_LAYER_BUFFERS";
267     if (hasGLOverlay(pd)) d << " overlay";
268     d << " iPixelType=" << pd.iPixelType << " cColorBits=" << pd.cColorBits
269         << " cRedBits=" << pd.cRedBits << " cRedShift=" << pd.cRedShift
270         << " cGreenBits=" << pd.cGreenBits << " cGreenShift=" << pd.cGreenShift
271         << " cBlueBits=" << pd.cBlueBits << " cBlueShift=" << pd.cBlueShift;
272     d  << " cDepthBits=" << pd.cDepthBits;
273     if (pd.cStencilBits)
274         d << " cStencilBits=" << pd.cStencilBits;
275     if (pd.cAuxBuffers)
276         d << " cAuxBuffers=" << pd.cAuxBuffers;
277     d << " iLayerType=" << pd.iLayerType;
278     if (pd.dwVisibleMask)
279         d << " dwVisibleMask=" << pd.dwVisibleMask;
280     if (pd.cAlphaBits)
281         d << " cAlphaBits=" << pd.cAlphaBits << " cAlphaShift=" << pd.cAlphaShift;
282     if (pd.cAccumBits)
283         d << " cAccumBits=" << pd.cAccumBits << " cAccumRedBits=" << pd.cAccumRedBits
284         << " cAccumGreenBits=" << pd.cAccumGreenBits << " cAccumBlueBits=" << pd.cAccumBlueBits
285         << " cAccumAlphaBits=" << pd.cAccumAlphaBits;
286     return d;
287 }
288 
operator <<(QDebug d,const QOpenGLStaticContext & s)289 QDebug operator<<(QDebug d, const QOpenGLStaticContext &s)
290 {
291     QDebugStateSaver saver(d);
292     d.nospace();
293     d << "OpenGL: " << s.vendor << ',' << s.renderer << " default "
294         <<  s.defaultFormat;
295     if (s.extensions &  QOpenGLStaticContext::SampleBuffers)
296         d << ",SampleBuffers";
297     if (s.hasExtensions())
298         d << ", Extension-API present";
299     d << "\nExtensions: " << (s.extensionNames.count(' ') + 1);
300     if (QWindowsContext::verbose > 1)
301         d << s.extensionNames;
302     return d;
303 }
304 
operator <<(QDebug d,const QWindowsOpenGLContextFormat & f)305 QDebug operator<<(QDebug d, const QWindowsOpenGLContextFormat &f)
306 {
307     QDebugStateSaver saver(d);
308     d.nospace();
309     d << "ContextFormat: v" << (f.version >> 8) << '.' << (f.version & 0xFF)
310         << " profile: " << f.profile << " options: " << f.options;
311     return d;
312 }
313 #endif // !QT_NO_DEBUG_STREAM
314 
315 // Check whether an obtained PIXELFORMATDESCRIPTOR matches the request.
316 static inline bool
isAcceptableFormat(const QWindowsOpenGLAdditionalFormat & additional,const PIXELFORMATDESCRIPTOR & pfd,bool ignoreGLSupport=false)317     isAcceptableFormat(const QWindowsOpenGLAdditionalFormat &additional,
318                        const PIXELFORMATDESCRIPTOR &pfd,
319                        bool ignoreGLSupport = false) // ARB format may not contain it.
320 {
321     const bool pixmapRequested = testFlag(additional.formatFlags, QWindowsGLRenderToPixmap);
322     const bool pixmapOk = !pixmapRequested || testFlag(pfd.dwFlags, PFD_DRAW_TO_BITMAP);
323     const bool colorOk =  !pixmapRequested || pfd.cColorBits == additional.pixmapDepth;
324     const bool glOk = ignoreGLSupport || testFlag(pfd.dwFlags, PFD_SUPPORT_OPENGL);
325     const bool overlayOk = hasGLOverlay(pfd) == testFlag(additional.formatFlags, QWindowsGLOverlay);
326     return pixmapOk && glOk && overlayOk && colorOk;
327 }
328 
describeFormats(HDC hdc)329 static void describeFormats(HDC hdc)
330 {
331     const int pfiMax = QOpenGLStaticContext::opengl32.describePixelFormat(hdc, 0, 0, nullptr);
332     for (int i = 0; i < pfiMax; i++) {
333         PIXELFORMATDESCRIPTOR pfd;
334         initPixelFormatDescriptor(&pfd);
335         QOpenGLStaticContext::opengl32.describePixelFormat(hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
336         qCDebug(lcQpaGl) << '#' << i << '/' << pfiMax << ':' << pfd;
337     }
338 }
339 
340 // Classic GDI API
341 namespace GDI {
342 static QSurfaceFormat
qSurfaceFormatFromPixelFormat(const PIXELFORMATDESCRIPTOR & pfd,QWindowsOpenGLAdditionalFormat * additionalIn=nullptr)343     qSurfaceFormatFromPixelFormat(const PIXELFORMATDESCRIPTOR &pfd,
344                                          QWindowsOpenGLAdditionalFormat *additionalIn = nullptr)
345 {
346     QSurfaceFormat format;
347     format.setRenderableType(QSurfaceFormat::OpenGL);
348     if (pfd.dwFlags & PFD_DOUBLEBUFFER)
349         format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
350     format.setDepthBufferSize(pfd.cDepthBits);
351 
352     if (pfd.iPixelType == PFD_TYPE_RGBA)
353         format.setAlphaBufferSize(pfd.cAlphaBits);
354     format.setRedBufferSize(pfd.cRedBits);
355     format.setGreenBufferSize(pfd.cGreenBits);
356     format.setBlueBufferSize(pfd.cBlueBits);
357     format.setStencilBufferSize(pfd.cStencilBits);
358     format.setStereo(pfd.dwFlags & PFD_STEREO);
359     if (additionalIn) {
360         QWindowsOpenGLAdditionalFormat additional;
361         if (isDirectRendering(pfd))
362             additional.formatFlags |= QWindowsGLDirectRendering;
363         if (hasGLOverlay(pfd))
364             additional.formatFlags |= QWindowsGLOverlay;
365         if (pfd.cAccumRedBits)
366             additional.formatFlags |= QWindowsGLAccumBuffer;
367         if (testFlag(pfd.dwFlags, PFD_DRAW_TO_BITMAP)) {
368             additional.formatFlags |= QWindowsGLRenderToPixmap;
369             additional.pixmapDepth = pfd.cColorBits;
370         }
371         *additionalIn = additional;
372     }
373     return format;
374 }
375 
376 static PIXELFORMATDESCRIPTOR
qPixelFormatFromSurfaceFormat(const QSurfaceFormat & format,const QWindowsOpenGLAdditionalFormat & additional)377     qPixelFormatFromSurfaceFormat(const QSurfaceFormat &format,
378                                   const QWindowsOpenGLAdditionalFormat &additional)
379 {
380     PIXELFORMATDESCRIPTOR pfd;
381     initPixelFormatDescriptor(&pfd);
382     pfd.iPixelType = PFD_TYPE_RGBA;
383     pfd.iLayerType  = PFD_MAIN_PLANE;
384     pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_SUPPORT_COMPOSITION;
385     const bool isPixmap = (additional.formatFlags & QWindowsGLRenderToPixmap) != 0;
386     pfd.dwFlags |= isPixmap ? PFD_DRAW_TO_BITMAP : PFD_DRAW_TO_WINDOW;
387     if (!(additional.formatFlags & QWindowsGLDirectRendering))
388         pfd.dwFlags |= PFD_GENERIC_FORMAT;
389 
390     if (format.stereo())
391         pfd.dwFlags |= PFD_STEREO;
392     if (format.swapBehavior() != QSurfaceFormat::SingleBuffer && !isPixmap)
393         pfd.dwFlags |= PFD_DOUBLEBUFFER;
394     pfd.cDepthBits =
395         format.depthBufferSize() >= 0 ? format.depthBufferSize() : 32;
396     const int redBufferSize = format.redBufferSize();
397     if (redBufferSize != -1)
398         pfd.cRedBits = BYTE(redBufferSize);
399     const int greenBufferSize = format.greenBufferSize();
400     if (greenBufferSize != -1)
401         pfd.cGreenBits = BYTE(greenBufferSize);
402     const int blueBufferSize = format.blueBufferSize();
403     if (blueBufferSize != -1)
404         pfd.cBlueBits = BYTE(blueBufferSize);
405     pfd.cAlphaBits = format.alphaBufferSize() > 0 ? format.alphaBufferSize() : 8;
406     pfd.cStencilBits = format.stencilBufferSize() > 0 ? format.stencilBufferSize() : 8;
407     if (additional.formatFlags & QWindowsGLAccumBuffer)
408         pfd.cAccumRedBits = pfd.cAccumGreenBits = pfd.cAccumBlueBits = pfd.cAccumAlphaBits = 16;
409     return pfd;
410 }
411 
412 // Choose a suitable pixelformat using GDI WinAPI in case ARB
413 // functions cannot be found. First tries to find a suitable
414 // format using GDI function ChoosePixelFormat(). Since that
415 // does not handle overlay and direct-rendering requests, manually loop
416 // over the available formats to find the best one.
417 // Note: As of Windows 7, it seems direct-rendering is handled, so,
418 // the code might be obsolete?
419 //
420 // NB! When using an implementation with a name different than opengl32.dll
421 // this code path should not be used since it will result in a mess due to GDI
422 // relying on and possibly calling back into functions in opengl32.dll (and not
423 // the one we are using). This is not a problem usually since for Mesa, which
424 // we are most likely to ship with a name other than opengl32.dll, the ARB code
425 // path should work. Hence the early bail out below.
426 //
choosePixelFormat(HDC hdc,const QSurfaceFormat & format,const QWindowsOpenGLAdditionalFormat & additional,PIXELFORMATDESCRIPTOR * obtainedPfd)427 static int choosePixelFormat(HDC hdc, const QSurfaceFormat &format,
428                             const QWindowsOpenGLAdditionalFormat &additional,
429                             PIXELFORMATDESCRIPTOR *obtainedPfd)
430 {
431     if (QOpenGLStaticContext::opengl32.moduleIsNotOpengl32()) {
432         qWarning("Attempted to use GDI functions with a non-opengl32.dll library");
433         return 0;
434     }
435 
436     // 1) Try ChoosePixelFormat().
437     PIXELFORMATDESCRIPTOR requestedPfd = qPixelFormatFromSurfaceFormat(format, QWindowsGLDirectRendering);
438     initPixelFormatDescriptor(obtainedPfd);
439     int pixelFormat = ChoosePixelFormat(hdc, &requestedPfd);
440     if (pixelFormat >= 0) {
441         DescribePixelFormat(hdc, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), obtainedPfd);
442         if (isAcceptableFormat(additional, *obtainedPfd))
443             return pixelFormat;
444     }
445     // 2) No matching format found, manual search loop.
446     const int pfiMax = DescribePixelFormat(hdc, 0, 0, nullptr);
447     int bestScore = -1;
448     int bestPfi = -1;
449     const bool stereoRequested = format.stereo();
450     const bool accumBufferRequested = testFlag(additional.formatFlags, QWindowsGLAccumBuffer);
451     const bool doubleBufferRequested = format.swapBehavior() == QSurfaceFormat::DoubleBuffer;
452     const bool directRenderingRequested = testFlag(additional.formatFlags, QWindowsGLDirectRendering);
453     for (int pfi = 1; pfi <= pfiMax; pfi++) {
454         PIXELFORMATDESCRIPTOR checkPfd;
455         initPixelFormatDescriptor(&checkPfd);
456         DescribePixelFormat(hdc, pfi, sizeof(PIXELFORMATDESCRIPTOR), &checkPfd);
457         if (isAcceptableFormat(additional, checkPfd)) {
458             int score = checkPfd.cColorBits + checkPfd.cAlphaBits + checkPfd.cStencilBits;
459             if (accumBufferRequested)
460                 score += checkPfd.cAccumBits;
461             if (doubleBufferRequested == testFlag(checkPfd.dwFlags, PFD_DOUBLEBUFFER))
462                 score += 1000;
463             if (stereoRequested == testFlag(checkPfd.dwFlags, PFD_STEREO))
464                 score += 2000;
465             if (directRenderingRequested == isDirectRendering(checkPfd))
466                 score += 4000;
467             if (checkPfd.iPixelType == PFD_TYPE_RGBA)
468                 score += 8000;
469             if (score > bestScore) {
470                 bestScore = score;
471                 bestPfi = pfi;
472                 *obtainedPfd = checkPfd;
473             }
474             qCDebug(lcQpaGl) << __FUNCTION__ << "    checking  " << pfi << '/' << pfiMax
475                 << " score=" << score << " (best " << bestPfi << '/' << bestScore << ") " << checkPfd;
476         }
477     } // for
478     if (bestPfi > 0)
479         pixelFormat = bestPfi;
480     return pixelFormat;
481 }
482 
createContext(HDC hdc,HGLRC shared)483 static inline HGLRC createContext(HDC hdc, HGLRC shared)
484 {
485     HGLRC result = QOpenGLStaticContext::opengl32.wglCreateContext(hdc);
486     if (!result) {
487         qErrnoWarning("%s: wglCreateContext failed.", __FUNCTION__);
488         return nullptr;
489     }
490     if (shared && !QOpenGLStaticContext::opengl32.wglShareLists(shared, result))
491         qErrnoWarning("%s: wglShareLists() failed.", __FUNCTION__);
492     return result;
493 }
494 } // namespace GDI
495 
496 // ARB OpenGL extension API
497 namespace ARB {
498 // Choose a suitable pixelformat using ARB extension functions.
choosePixelFormat(HDC hdc,const QOpenGLStaticContext & staticContext,const QSurfaceFormat & format,const QWindowsOpenGLAdditionalFormat & additional,PIXELFORMATDESCRIPTOR * obtainedPfd)499 static int choosePixelFormat(HDC hdc,
500                              const QOpenGLStaticContext &staticContext,
501                              const QSurfaceFormat &format,
502                              const QWindowsOpenGLAdditionalFormat &additional,
503                              PIXELFORMATDESCRIPTOR *obtainedPfd)
504 {
505     enum { attribSize = 42 };
506     if ((additional.formatFlags & QWindowsGLRenderToPixmap) || !staticContext.hasExtensions())
507         return 0;
508 
509     int iAttributes[attribSize];
510     std::fill(iAttributes, iAttributes + attribSize, int(0));
511     int i = 0;
512     iAttributes[i++] = WGL_ACCELERATION_ARB;
513     iAttributes[i++] = testFlag(additional.formatFlags, QWindowsGLDirectRendering) ?
514                        WGL_FULL_ACCELERATION_ARB : WGL_NO_ACCELERATION_ARB;
515     iAttributes[i++] = WGL_SUPPORT_OPENGL_ARB;
516     iAttributes[i++] = TRUE;
517     iAttributes[i++] = WGL_DRAW_TO_WINDOW_ARB;
518     iAttributes[i++] = TRUE;
519     iAttributes[i++] = WGL_COLOR_BITS_ARB;
520 
521     iAttributes[i++] = (format.redBufferSize() > 0)
522                        && (format.greenBufferSize() > 0)
523                        && (format.blueBufferSize() > 0) ?
524                        format.redBufferSize() + format.greenBufferSize() + format.blueBufferSize() :
525                        24;
526     switch (format.swapBehavior()) {
527     case QSurfaceFormat::SingleBuffer:
528         iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB;
529         iAttributes[i++] = FALSE;
530         break;
531     case QSurfaceFormat::DefaultSwapBehavior:
532     case QSurfaceFormat::DoubleBuffer:
533     case QSurfaceFormat::TripleBuffer:
534         iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB;
535         iAttributes[i++] = TRUE;
536         break;
537     }
538     if (format.stereo()) {
539         iAttributes[i++] = WGL_STEREO_ARB;
540         iAttributes[i++] = TRUE;
541     }
542     if (format.depthBufferSize() >= 0) {
543         iAttributes[i++] = WGL_DEPTH_BITS_ARB;
544         iAttributes[i++] = format.depthBufferSize();
545     }
546     iAttributes[i++] = WGL_PIXEL_TYPE_ARB;
547     iAttributes[i++] = WGL_TYPE_RGBA_ARB;
548     if (format.redBufferSize() >= 0) {
549         iAttributes[i++] = WGL_RED_BITS_ARB;
550         iAttributes[i++] = format.redBufferSize();
551     }
552     if (format.greenBufferSize() >= 0) {
553         iAttributes[i++] = WGL_GREEN_BITS_ARB;
554         iAttributes[i++] = format.greenBufferSize();
555     }
556     if (format.blueBufferSize() >= 0) {
557         iAttributes[i++] = WGL_BLUE_BITS_ARB;
558         iAttributes[i++] = format.blueBufferSize();
559     }
560     iAttributes[i++] = WGL_ALPHA_BITS_ARB;
561     iAttributes[i++] = format.alphaBufferSize() >= 0 ? format.alphaBufferSize() : 8;
562     if (additional.formatFlags & QWindowsGLAccumBuffer) {
563         iAttributes[i++] = WGL_ACCUM_BITS_ARB;
564         iAttributes[i++] = 16;
565     }
566     iAttributes[i++] = WGL_STENCIL_BITS_ARB;
567     iAttributes[i++] = 8;
568     if (additional.formatFlags & QWindowsGLOverlay) {
569         iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB;
570         iAttributes[i++] = 1;
571     }
572     const int samples = format.samples();
573     const bool sampleBuffersRequested = samples > 1
574             && testFlag(staticContext.extensions, QOpenGLStaticContext::SampleBuffers);
575     int samplesValuePosition = 0;
576     if (sampleBuffersRequested) {
577         iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB;
578         iAttributes[i++] = TRUE;
579         iAttributes[i++] = WGL_SAMPLES_ARB;
580         samplesValuePosition = i;
581         iAttributes[i++] = format.samples();
582     } else {
583         iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB;
584         iAttributes[i++] = FALSE;
585     }
586     // must be the last
587     bool srgbRequested = format.colorSpace() == QSurfaceFormat::sRGBColorSpace;
588     int srgbValuePosition = 0;
589     if (srgbRequested) {
590         srgbValuePosition = i;
591         iAttributes[i++] = WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT;
592         iAttributes[i++] = TRUE;
593     }
594     // If sample or sRGB request cannot be satisfied, reduce request.
595     int pixelFormat = 0;
596     uint numFormats = 0;
597     while (true) {
598         const bool valid =
599             staticContext.wglChoosePixelFormatARB(hdc, iAttributes, nullptr, 1,
600                                                &pixelFormat, &numFormats)
601                 && numFormats >= 1;
602         if (valid || (!sampleBuffersRequested && !srgbRequested))
603             break;
604         if (srgbRequested) {
605             iAttributes[srgbValuePosition] = 0;
606             srgbRequested = false;
607         } else if (sampleBuffersRequested) {
608             if (iAttributes[samplesValuePosition] > 1) {
609                 iAttributes[samplesValuePosition] /= 2;
610             } else if (iAttributes[samplesValuePosition] == 1) {
611                 // Fallback in case it is unable to initialize with any
612                 // samples to avoid falling back to the GDI path
613                 // NB: The sample attributes needs to be at the end for this
614                 // to work correctly
615                 iAttributes[samplesValuePosition - 1] = FALSE;
616                 iAttributes[samplesValuePosition] = 0;
617                 iAttributes[samplesValuePosition + 1] = 0;
618             } else {
619                 break;
620             }
621         }
622     }
623     // Verify if format is acceptable. Note that the returned
624     // formats have been observed to not contain PFD_SUPPORT_OPENGL, ignore.
625     initPixelFormatDescriptor(obtainedPfd);
626     QOpenGLStaticContext::opengl32.describePixelFormat(hdc, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), obtainedPfd);
627     if (!isAcceptableFormat(additional, *obtainedPfd, true)) {
628         qCDebug(lcQpaGl) << __FUNCTION__ << " obtained px #" << pixelFormat
629             << " not acceptable=" << *obtainedPfd;
630         pixelFormat = 0;
631     }
632 
633 #ifndef QT_NO_DEBUG_OUTPUT
634     if (lcQpaGl().isDebugEnabled()) {
635         QString message;
636         QDebug nsp(&message);
637         nsp << __FUNCTION__;
638         if (sampleBuffersRequested)
639             nsp << " samples=" << iAttributes[samplesValuePosition];
640         nsp << " Attributes: " << Qt::hex << Qt::showbase;
641         for (int ii = 0; ii < i; ++ii)
642             nsp << iAttributes[ii] << ',';
643         nsp << Qt::noshowbase << Qt::dec << "\n    obtained px #" << pixelFormat
644             << " of " << numFormats << "\n    " << *obtainedPfd;
645         qCDebug(lcQpaGl) << message;
646     } // Debug
647 #endif
648 
649     return pixelFormat;
650 }
651 
652 static QSurfaceFormat
qSurfaceFormatFromHDC(const QOpenGLStaticContext & staticContext,HDC hdc,int pixelFormat,QWindowsOpenGLAdditionalFormat * additionalIn=nullptr)653     qSurfaceFormatFromHDC(const QOpenGLStaticContext &staticContext,
654                           HDC hdc, int pixelFormat,
655                           QWindowsOpenGLAdditionalFormat *additionalIn = nullptr)
656 {
657     enum { attribSize = 42 };
658 
659     QSurfaceFormat result;
660     result.setRenderableType(QSurfaceFormat::OpenGL);
661     if (!staticContext.hasExtensions())
662         return result;
663     int iAttributes[attribSize];
664     int iValues[attribSize];
665     std::fill(iAttributes, iAttributes + attribSize, int(0));
666     std::fill(iValues, iValues + attribSize, int(0));
667 
668     int i = 0;
669     const bool hasSampleBuffers = testFlag(staticContext.extensions, QOpenGLStaticContext::SampleBuffers);
670     const bool hasSrgbSupport = testFlag(staticContext.extensions, QOpenGLStaticContext::sRGBCapableFramebuffer);
671 
672     iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; // 0
673     iAttributes[i++] = WGL_DEPTH_BITS_ARB; // 1
674     iAttributes[i++] = WGL_PIXEL_TYPE_ARB; // 2
675     iAttributes[i++] = WGL_RED_BITS_ARB; // 3
676     iAttributes[i++] = WGL_GREEN_BITS_ARB; // 4
677     iAttributes[i++] = WGL_BLUE_BITS_ARB; // 5
678     iAttributes[i++] = WGL_ALPHA_BITS_ARB; // 6
679     iAttributes[i++] = WGL_ACCUM_BITS_ARB; // 7
680     iAttributes[i++] = WGL_STENCIL_BITS_ARB; // 8
681     iAttributes[i++] = WGL_STEREO_ARB; // 9
682     iAttributes[i++] = WGL_ACCELERATION_ARB; // 10
683     iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB; // 11
684     if (hasSampleBuffers) {
685         iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; // 12
686         iAttributes[i++] = WGL_SAMPLES_ARB; // 13
687     }
688     if (hasSrgbSupport)
689         iAttributes[i++] = WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT; // 12 or 14
690 
691     if (!staticContext.wglGetPixelFormatAttribIVARB(hdc, pixelFormat, 0, i,
692                                         iAttributes, iValues)) {
693         qErrnoWarning("%s: wglGetPixelFormatAttribIVARB() failed for basic parameters.", __FUNCTION__);
694         return result;
695     }
696     result.setSwapBehavior(iValues[0] ? QSurfaceFormat::DoubleBuffer : QSurfaceFormat::SingleBuffer);
697     result.setDepthBufferSize(iValues[1]);
698     result.setRedBufferSize(iValues[3]);
699     result.setGreenBufferSize(iValues[4]);
700     result.setBlueBufferSize(iValues[5]);
701     result.setAlphaBufferSize(iValues[6]);
702     result.setStencilBufferSize(iValues[8]);
703     if (iValues[9])
704         result.setOption(QSurfaceFormat::StereoBuffers);
705 
706     if (hasSampleBuffers) {
707         result.setSamples(iValues[13]);
708         if (hasSrgbSupport && iValues[14])
709             result.setColorSpace(QSurfaceFormat::sRGBColorSpace);
710     } else {
711         if (hasSrgbSupport && iValues[12])
712             result.setColorSpace(QSurfaceFormat::sRGBColorSpace);
713     }
714     if (additionalIn) {
715         if (iValues[7])
716             additionalIn->formatFlags |= QWindowsGLAccumBuffer;
717         if (iValues[10] == WGL_FULL_ACCELERATION_ARB)
718             additionalIn->formatFlags |= QWindowsGLDirectRendering;
719         if (iValues[11])
720             additionalIn->formatFlags |= QWindowsGLOverlay;
721     }
722     return result;
723 }
724 
createContext(const QOpenGLStaticContext & staticContext,HDC hdc,const QSurfaceFormat & format,const QWindowsOpenGLAdditionalFormat &,HGLRC shared=nullptr)725 static HGLRC createContext(const QOpenGLStaticContext &staticContext,
726                            HDC hdc,
727                            const QSurfaceFormat &format,
728                            const QWindowsOpenGLAdditionalFormat &,
729                            HGLRC shared = nullptr)
730 {
731     enum { attribSize = 11 };
732 
733     if (!staticContext.hasExtensions())
734         return nullptr;
735     int attributes[attribSize];
736     int attribIndex = 0;
737     std::fill(attributes, attributes + attribSize, int(0));
738 
739     // We limit the requested version by the version of the static context as
740     // wglCreateContextAttribsARB fails and returns NULL if the requested context
741     // version is not supported. This means that we will get the closest supported
742     // context format that that which was requested and is supported by the driver
743     const int requestedVersion = qMin((format.majorVersion() << 8) + format.minorVersion(),
744                                       staticContext.defaultFormat.version);
745     const int majorVersion = requestedVersion >> 8;
746     const int minorVersion = requestedVersion & 0xFF;
747 
748     if (requestedVersion > 0x0101) {
749         attributes[attribIndex++] = WGL_CONTEXT_MAJOR_VERSION_ARB;
750         attributes[attribIndex++] = majorVersion;
751         attributes[attribIndex++] = WGL_CONTEXT_MINOR_VERSION_ARB;
752         attributes[attribIndex++] = minorVersion;
753     }
754 
755     int flags = 0;
756     if (format.testOption(QSurfaceFormat::DebugContext))
757         flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
758     if (requestedVersion >= 0x0300) {
759         if (!format.testOption(QSurfaceFormat::DeprecatedFunctions))
760             flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
761     }
762     attributes[attribIndex++] = WGL_CONTEXT_FLAGS_ARB;
763     attributes[attribIndex++] = flags;
764 
765     if (requestedVersion >= 0x0302) {
766         switch (format.profile()) {
767         case QSurfaceFormat::NoProfile:
768             break;
769         case QSurfaceFormat::CoreProfile:
770             attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB;
771             attributes[attribIndex++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
772             break;
773         case QSurfaceFormat::CompatibilityProfile:
774             attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB;
775             attributes[attribIndex++] = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
776             break;
777         }
778     }
779 
780     if (format.testOption(QSurfaceFormat::ResetNotification)) {
781         attributes[attribIndex++] = RESET_NOTIFICATION_STRATEGY_ARB;
782         attributes[attribIndex++] = LOSE_CONTEXT_ON_RESET_ARB;
783     }
784 
785     qCDebug(lcQpaGl) << __FUNCTION__ << "Creating context version"
786         << majorVersion << '.' << minorVersion <<  attribIndex / 2 << "attributes";
787 
788     const HGLRC result =
789         staticContext.wglCreateContextAttribsARB(hdc, shared, attributes);
790     if (!result) {
791         QString message;
792         QDebug(&message).nospace() << __FUNCTION__ << ": wglCreateContextAttribsARB() failed (GL error code: 0x"
793             << Qt::hex << staticContext.opengl32.glGetError() << Qt::dec << ") for format: " << format << ", shared context: " << shared;
794         qErrnoWarning("%s", qPrintable(message));
795     }
796     return result;
797 }
798 
799 } // namespace ARB
800 
801 // Helpers for temporary contexts
createDummyGLWindow()802 static inline HWND createDummyGLWindow()
803 {
804     return QWindowsContext::instance()->
805         createDummyWindow(QStringLiteral("OpenGLDummyWindow"),
806                           L"OpenGLDummyWindow", nullptr, WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
807 }
808 
809 // Create a dummy GL context (see QOpenGLTemporaryContext).
createDummyGLContext(HDC dc)810 static inline HGLRC createDummyGLContext(HDC dc)
811 {
812     if (!dc)
813         return nullptr;
814     PIXELFORMATDESCRIPTOR pixelFormDescriptor;
815     initPixelFormatDescriptor(&pixelFormDescriptor);
816     pixelFormDescriptor.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_GENERIC_FORMAT;
817     pixelFormDescriptor.iPixelType = PFD_TYPE_RGBA;
818     // Use the GDI variant, for the dummy this is fine, even when using something other than opengl32.dll.
819     const int pixelFormat = ChoosePixelFormat(dc, &pixelFormDescriptor);
820     if (!pixelFormat) {
821         qErrnoWarning("%s: ChoosePixelFormat failed.", __FUNCTION__);
822         return nullptr;
823     }
824     if (!QOpenGLStaticContext::opengl32.setPixelFormat(dc, pixelFormat, &pixelFormDescriptor)) {
825         qErrnoWarning("%s: SetPixelFormat failed.", __FUNCTION__);
826         return nullptr;
827     }
828     HGLRC rc = QOpenGLStaticContext::opengl32.wglCreateContext(dc);
829     if (!rc) {
830         qErrnoWarning("%s: wglCreateContext failed.", __FUNCTION__);
831         return nullptr;
832     }
833     return rc;
834 }
835 
currentOpenGLContextData()836 static inline QOpenGLContextData currentOpenGLContextData()
837 {
838     QOpenGLContextData result;
839     result.hdc = QOpenGLStaticContext::opengl32.wglGetCurrentDC();
840     result.renderingContext = QOpenGLStaticContext::opengl32.wglGetCurrentContext();
841     return result;
842 }
843 
createDummyWindowOpenGLContextData()844 static inline QOpenGLContextData createDummyWindowOpenGLContextData()
845 {
846     QOpenGLContextData result;
847     result.hwnd = createDummyGLWindow();
848     result.hdc = GetDC(result.hwnd);
849     result.renderingContext = createDummyGLContext(result.hdc);
850     return result;
851 }
852 
853 /*!
854     \class QOpenGLContextFormat
855     \brief Format options that are related to the context (not pixelformats)
856 
857     Provides utility function to retrieve from currently active
858     context and to apply to a QSurfaceFormat.
859 
860     \internal
861 */
862 
current()863 QWindowsOpenGLContextFormat QWindowsOpenGLContextFormat::current()
864 {
865     QWindowsOpenGLContextFormat result;
866     const QByteArray version = QOpenGLStaticContext::getGlString(GL_VERSION);
867     int major, minor;
868     if (QPlatformOpenGLContext::parseOpenGLVersion(version, major, minor))
869         result.version = (major << 8) + minor;
870     else
871         result.version = 0x0200;
872     result.profile = QSurfaceFormat::NoProfile;
873     if (result.version < 0x0300) {
874         result.options |= QSurfaceFormat::DeprecatedFunctions;
875         return result;
876     }
877     // v3 onwards
878     GLint value = 0;
879     QOpenGLStaticContext::opengl32.glGetIntegerv(GL_CONTEXT_FLAGS, &value);
880     if (!(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT))
881         result.options |= QSurfaceFormat::DeprecatedFunctions;
882     if (value & GL_CONTEXT_FLAG_DEBUG_BIT)
883         result.options |= QSurfaceFormat::DebugContext;
884     if (result.version < 0x0302)
885         return result;
886     // v3.2 onwards: Profiles
887     value = 0;
888     QOpenGLStaticContext::opengl32.glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);
889     if (value & GL_CONTEXT_CORE_PROFILE_BIT)
890         result.profile = QSurfaceFormat::CoreProfile;
891     else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)
892         result.profile = QSurfaceFormat::CompatibilityProfile;
893     return result;
894 }
895 
apply(QSurfaceFormat * format) const896 void QWindowsOpenGLContextFormat::apply(QSurfaceFormat *format) const
897 {
898     format->setMajorVersion(version >> 8);
899     format->setMinorVersion(version & 0xFF);
900     format->setProfile(profile);
901     if (options & QSurfaceFormat::DebugContext)
902         format->setOption(QSurfaceFormat::DebugContext);
903     if (options & QSurfaceFormat::DeprecatedFunctions)
904         format->setOption(QSurfaceFormat::DeprecatedFunctions);
905 }
906 
907 /*!
908     \class QOpenGLTemporaryContext
909     \brief A temporary context that can be instantiated on the stack.
910 
911     Functions like wglGetProcAddress() or glGetString() only work if there
912     is a current GL context.
913 
914     \internal
915 */
916 
917 class QOpenGLTemporaryContext
918 {
919     Q_DISABLE_COPY_MOVE(QOpenGLTemporaryContext)
920 public:
921     QOpenGLTemporaryContext();
922     ~QOpenGLTemporaryContext();
923 
924 private:
925     const QOpenGLContextData m_previous;
926     const QOpenGLContextData m_current;
927 };
928 
QOpenGLTemporaryContext()929 QOpenGLTemporaryContext::QOpenGLTemporaryContext() :
930     m_previous(currentOpenGLContextData()),
931     m_current(createDummyWindowOpenGLContextData())
932 {
933     QOpenGLStaticContext::opengl32.wglMakeCurrent(m_current.hdc, m_current.renderingContext);
934 }
935 
~QOpenGLTemporaryContext()936 QOpenGLTemporaryContext::~QOpenGLTemporaryContext()
937 {
938     QOpenGLStaticContext::opengl32.wglMakeCurrent(m_previous.hdc, m_previous.renderingContext);
939     ReleaseDC(m_current.hwnd, m_current.hdc);
940     DestroyWindow(m_current.hwnd);
941     QOpenGLStaticContext::opengl32.wglDeleteContext(m_current.renderingContext);
942 }
943 
944 /*!
945     \class QWindowsOpenGLAdditionalFormat
946     \brief Additional format information that is not in QSurfaceFormat
947 */
948 
949 /*!
950     \class QOpenGLStaticContext
951     \brief Static Open GL context containing version information, extension function pointers, etc.
952 
953     Functions pending integration in the next version of OpenGL are post-fixed ARB.
954 
955     No WGL or OpenGL functions are called directly from the windows plugin. Instead, the
956     static context loads opengl32.dll and resolves the necessary functions. This allows
957     building the plugin without linking to opengl32 and enables QT_OPENGL_DYNAMIC builds
958     where both the EGL and WGL (this) based implementation of the context are built.
959 
960     \note Initialization requires an active context (see create()).
961 
962     \sa QWindowsGLContext
963     \internal
964 */
965 
966 #define SAMPLE_BUFFER_EXTENSION "GL_ARB_multisample"
967 #define ROBUSTNESS_EXTENSION "GL_ARB_robustness"
968 
QOpenGLStaticContext()969 QOpenGLStaticContext::QOpenGLStaticContext() :
970     vendor(QOpenGLStaticContext::getGlString(GL_VENDOR)),
971     renderer(QOpenGLStaticContext::getGlString(GL_RENDERER)),
972     extensionNames(QOpenGLStaticContext::getGlString(GL_EXTENSIONS)),
973     extensions(0),
974     defaultFormat(QWindowsOpenGLContextFormat::current()),
975     wglGetPixelFormatAttribIVARB(reinterpret_cast<WglGetPixelFormatAttribIVARB>(
976         reinterpret_cast<QFunctionPointer>(QOpenGLStaticContext::opengl32.wglGetProcAddress("wglGetPixelFormatAttribivARB")))),
977     wglChoosePixelFormatARB(reinterpret_cast<WglChoosePixelFormatARB>(
978         reinterpret_cast<QFunctionPointer>(QOpenGLStaticContext::opengl32.wglGetProcAddress("wglChoosePixelFormatARB")))),
979     wglCreateContextAttribsARB(reinterpret_cast<WglCreateContextAttribsARB>(
980         reinterpret_cast<QFunctionPointer>(QOpenGLStaticContext::opengl32.wglGetProcAddress("wglCreateContextAttribsARB")))),
981     wglSwapInternalExt(reinterpret_cast<WglSwapInternalExt>(
982         reinterpret_cast<QFunctionPointer>(QOpenGLStaticContext::opengl32.wglGetProcAddress("wglSwapIntervalEXT")))),
983     wglGetSwapInternalExt(reinterpret_cast<WglGetSwapInternalExt>(
984         reinterpret_cast<QFunctionPointer>(QOpenGLStaticContext::opengl32.wglGetProcAddress("wglGetSwapIntervalEXT")))),
985     wglGetExtensionsStringARB(reinterpret_cast<WglGetExtensionsStringARB>(
986         reinterpret_cast<QFunctionPointer>(QOpenGLStaticContext::opengl32.wglGetProcAddress("wglGetExtensionsStringARB"))))
987 {
988     if (defaultFormat.version < 0x0300) {
989         if (extensionNames.startsWith(SAMPLE_BUFFER_EXTENSION " ")
990                 || extensionNames.indexOf(" " SAMPLE_BUFFER_EXTENSION " ") != -1)
991             extensions |= SampleBuffers;
992         if (extensionNames.startsWith(ROBUSTNESS_EXTENSION " ")
993                 || extensionNames.indexOf(" " ROBUSTNESS_EXTENSION " ") != -1)
994             extensions |= Robustness;
995     } else {
996         typedef const GLubyte * (APIENTRY *glGetStringi_t)(GLenum, GLuint);
997         auto glGetStringi = reinterpret_cast<glGetStringi_t>(
998             reinterpret_cast<QFunctionPointer>(QOpenGLStaticContext::opengl32.wglGetProcAddress("glGetStringi")));
999         if (glGetStringi) {
1000             GLint n = 0;
1001             QOpenGLStaticContext::opengl32.glGetIntegerv(GL_NUM_EXTENSIONS, &n);
1002             for (GLint i = 0; i < n; ++i) {
1003                 const char *p = reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i));
1004                 if (p) {
1005                     if (!strcmp(p, SAMPLE_BUFFER_EXTENSION))
1006                         extensions |= SampleBuffers;
1007                     else if (!strcmp(p, ROBUSTNESS_EXTENSION))
1008                         extensions |= Robustness;
1009                 }
1010             }
1011         }
1012     }
1013 }
1014 
getGlString(unsigned int which)1015 QByteArray QOpenGLStaticContext::getGlString(unsigned int which)
1016 {
1017     if (const GLubyte *s = opengl32.glGetString(which))
1018         return QByteArray(reinterpret_cast<const char*>(s));
1019     return QByteArray();
1020 }
1021 
create(bool softwareRendering)1022 QOpenGLStaticContext *QOpenGLStaticContext::create(bool softwareRendering)
1023 {
1024     if (!opengl32.init(softwareRendering)) {
1025         qWarning("Failed to load and resolve WGL/OpenGL functions");
1026         return nullptr;
1027     }
1028 
1029     // We need a current context for wglGetProcAdress()/getGLString() to work.
1030     QScopedPointer<QOpenGLTemporaryContext> temporaryContext;
1031     if (!QOpenGLStaticContext::opengl32.wglGetCurrentContext())
1032         temporaryContext.reset(new QOpenGLTemporaryContext);
1033     auto *result = new QOpenGLStaticContext;
1034     qCDebug(lcQpaGl) << __FUNCTION__ << *result;
1035     return result;
1036 }
1037 
1038 /*!
1039     \class QWindowsGLContext
1040     \brief Open GL context.
1041 
1042     An Open GL context for use with several windows.
1043     As opposed to other implementations, activating a GL context for
1044     a window requires a HDC allocated for it. The first time this
1045     HDC is created for the window, the pixel format must be applied,
1046     which will affect the window as well. The HDCs are stored in a list of
1047     QOpenGLContextData and are released in doneCurrent().
1048 
1049     \internal
1050 */
1051 
QWindowsGLContext(QOpenGLStaticContext * staticContext,QOpenGLContext * context)1052 QWindowsGLContext::QWindowsGLContext(QOpenGLStaticContext *staticContext,
1053                                      QOpenGLContext *context) :
1054     m_staticContext(staticContext),
1055     m_context(context),
1056     m_renderingContext(nullptr),
1057     m_pixelFormat(0),
1058     m_extensionsUsed(false),
1059     m_swapInterval(-1),
1060     m_ownsContext(true),
1061     m_getGraphicsResetStatus(nullptr),
1062     m_lost(false)
1063 {
1064     if (!m_staticContext) // Something went very wrong. Stop here, isValid() will return false.
1065         return;
1066 
1067     QVariant nativeHandle = context->nativeHandle();
1068     if (!nativeHandle.isNull()) {
1069         // Adopt and existing context.
1070         if (!nativeHandle.canConvert<QWGLNativeContext>()) {
1071             qWarning("QWindowsGLContext: Requires a QWGLNativeContext");
1072             return;
1073         }
1074         auto handle = nativeHandle.value<QWGLNativeContext>();
1075         HGLRC wglcontext = handle.context();
1076         HWND wnd = handle.window();
1077         if (!wglcontext || !wnd) {
1078             qWarning("QWindowsGLContext: No context and window given");
1079             return;
1080         }
1081 
1082         HDC dc = GetDC(wnd);
1083         // A window with an associated pixel format is mandatory.
1084         // When no SetPixelFormat() call has been made, the following will fail.
1085         m_pixelFormat = GetPixelFormat(dc);
1086         bool ok = m_pixelFormat != 0;
1087         if (!ok)
1088             qWarning("QWindowsGLContext: Failed to get pixel format");
1089         ok = DescribePixelFormat(dc, m_pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &m_obtainedPixelFormatDescriptor);
1090         if (!ok) {
1091             qWarning("QWindowsGLContext: Failed to describe pixel format");
1092         } else {
1093             QWindowsOpenGLAdditionalFormat obtainedAdditional;
1094             m_obtainedFormat = GDI::qSurfaceFormatFromPixelFormat(m_obtainedPixelFormatDescriptor, &obtainedAdditional);
1095             m_renderingContext = wglcontext;
1096             ok = updateObtainedParams(dc);
1097         }
1098 
1099         ReleaseDC(wnd, dc);
1100 
1101         if (ok)
1102             m_ownsContext = false;
1103         else
1104             m_renderingContext = nullptr;
1105 
1106         return;
1107     }
1108 
1109     QSurfaceFormat format = context->format();
1110     if (format.renderableType() == QSurfaceFormat::DefaultRenderableType)
1111         format.setRenderableType(QSurfaceFormat::OpenGL);
1112     if (format.renderableType() != QSurfaceFormat::OpenGL)
1113         return;
1114 
1115     // workaround for matrox driver:
1116     // make a cheap call to opengl to force loading of DLL
1117     static bool opengl32dll = false;
1118     if (!opengl32dll) {
1119         GLint params;
1120         staticContext->opengl32.glGetIntegerv(GL_DEPTH_BITS, &params);
1121         opengl32dll = true;
1122     }
1123 
1124     // SetPixelFormat (as of Windows 7) requires a real window.
1125     // Create a dummy one as we are not associated with a window yet.
1126     // Try to find a suitable pixel format using preferably ARB extensions
1127     // (default to GDI) and store that.
1128     HWND dummyWindow = nullptr;
1129     HDC hdc = nullptr;
1130     bool tryExtensions = false;
1131     int obtainedSwapInterval = -1;
1132     do {
1133         dummyWindow = createDummyGLWindow();
1134         if (!dummyWindow)
1135             break;
1136         hdc = GetDC(dummyWindow);
1137         if (!hdc)
1138             break;
1139 
1140         if (QWindowsContext::verbose > 1)
1141             describeFormats(hdc);
1142         // Preferably use direct rendering and ARB extensions (unless pixmap
1143         // or explicitly turned off on command line).
1144         const QWindowsOpenGLAdditionalFormat
1145             requestedAdditional(QWindowsGLDirectRendering);
1146         tryExtensions = m_staticContext->hasExtensions()
1147                 && !testFlag(requestedAdditional.formatFlags, QWindowsGLRenderToPixmap)
1148                 && !(QWindowsIntegration::instance()->options() & QWindowsIntegration::DisableArb);
1149         QWindowsOpenGLAdditionalFormat obtainedAdditional;
1150         if (tryExtensions) {
1151             if (m_staticContext->wglGetExtensionsStringARB) {
1152                 const char *exts = m_staticContext->wglGetExtensionsStringARB(hdc);
1153                 if (exts) {
1154                     qCDebug(lcQpaGl) << __FUNCTION__ << "WGL extensions:" << exts;
1155                     if (strstr(exts, "WGL_EXT_framebuffer_sRGB"))
1156                         m_staticContext->extensions |= QOpenGLStaticContext::sRGBCapableFramebuffer;
1157                 }
1158             }
1159             m_pixelFormat =
1160                 ARB::choosePixelFormat(hdc, *m_staticContext, format,
1161                                        requestedAdditional, &m_obtainedPixelFormatDescriptor);
1162             if (m_pixelFormat > 0) {
1163                 m_obtainedFormat =
1164                     ARB::qSurfaceFormatFromHDC(*m_staticContext, hdc, m_pixelFormat,
1165                                                 &obtainedAdditional);
1166                 m_extensionsUsed = true;
1167             }
1168         } // tryExtensions
1169         if (!m_pixelFormat) { // Failed, try GDI
1170             m_pixelFormat = GDI::choosePixelFormat(hdc, format, requestedAdditional,
1171                                                    &m_obtainedPixelFormatDescriptor);
1172             if (m_pixelFormat)
1173                 m_obtainedFormat =
1174                     GDI::qSurfaceFormatFromPixelFormat(m_obtainedPixelFormatDescriptor,
1175                                                        &obtainedAdditional);
1176         } // try GDI
1177         if (!m_pixelFormat) {
1178             qWarning("%s: Unable find a suitable pixel format.", __FUNCTION__);
1179             break;
1180         }
1181         if (!QOpenGLStaticContext::opengl32.setPixelFormat(hdc, m_pixelFormat, &m_obtainedPixelFormatDescriptor)) {
1182             qErrnoWarning("SetPixelFormat failed.");
1183             break;
1184         }
1185         // Create context with sharing, again preferably using ARB.
1186         HGLRC sharingRenderingContext = nullptr;
1187         if (const QPlatformOpenGLContext *sc = context->shareHandle())
1188             sharingRenderingContext = static_cast<const QWindowsGLContext *>(sc)->renderingContext();
1189 
1190         if (m_extensionsUsed)
1191             m_renderingContext =
1192                 ARB::createContext(*m_staticContext, hdc,
1193                                    format,
1194                                    requestedAdditional,
1195                                    sharingRenderingContext);
1196         if (!m_renderingContext)
1197             m_renderingContext = GDI::createContext(hdc, sharingRenderingContext);
1198 
1199         if (!m_renderingContext) {
1200             qWarning("Unable to create a GL Context.");
1201             break;
1202         }
1203 
1204         // Query obtained parameters and apply swap interval.
1205         if (!updateObtainedParams(hdc, &obtainedSwapInterval))
1206             break;
1207 
1208     } while (false);
1209 
1210     // Make the HGLRC retrievable via QOpenGLContext::nativeHandle().
1211     // Do not provide the window since it is the dummy one and it is about to disappear.
1212     if (m_renderingContext)
1213         context->setNativeHandle(QVariant::fromValue<QWGLNativeContext>(QWGLNativeContext(m_renderingContext, nullptr)));
1214 
1215     if (hdc)
1216         ReleaseDC(dummyWindow, hdc);
1217     if (dummyWindow)
1218         DestroyWindow(dummyWindow);
1219 
1220     qCDebug(lcQpaGl) << __FUNCTION__ << this << (tryExtensions ? "ARB" : "GDI")
1221         << " requested: " << context->format()
1222         << "\n    obtained #" << m_pixelFormat << (m_extensionsUsed ? "ARB" : "GDI") << m_obtainedFormat
1223         << "\n    " << m_obtainedPixelFormatDescriptor << " swap interval: " << obtainedSwapInterval
1224         << "\n    default: " << m_staticContext->defaultFormat
1225         << "\n    HGLRC=" << m_renderingContext;
1226 }
1227 
~QWindowsGLContext()1228 QWindowsGLContext::~QWindowsGLContext()
1229 {
1230     if (m_renderingContext && m_ownsContext)
1231         QOpenGLStaticContext::opengl32.wglDeleteContext(m_renderingContext);
1232     releaseDCs();
1233 }
1234 
updateObtainedParams(HDC hdc,int * obtainedSwapInterval)1235 bool QWindowsGLContext::updateObtainedParams(HDC hdc, int *obtainedSwapInterval)
1236 {
1237     HGLRC prevContext = QOpenGLStaticContext::opengl32.wglGetCurrentContext();
1238     HDC prevSurface = QOpenGLStaticContext::opengl32.wglGetCurrentDC();
1239 
1240     if (!QOpenGLStaticContext::opengl32.wglMakeCurrent(hdc, m_renderingContext)) {
1241         qWarning("Failed to make context current.");
1242         return false;
1243     }
1244 
1245     QWindowsOpenGLContextFormat::current().apply(&m_obtainedFormat);
1246 
1247     if (m_staticContext->wglGetSwapInternalExt && obtainedSwapInterval)
1248         *obtainedSwapInterval = m_staticContext->wglGetSwapInternalExt();
1249 
1250     if (testFlag(m_staticContext->extensions, QOpenGLStaticContext::Robustness)) {
1251         GLint value = 0;
1252         QOpenGLStaticContext::opengl32.glGetIntegerv(RESET_NOTIFICATION_STRATEGY_ARB, &value);
1253         if (value == LOSE_CONTEXT_ON_RESET_ARB)
1254             m_obtainedFormat.setOption(QSurfaceFormat::ResetNotification);
1255         m_getGraphicsResetStatus = reinterpret_cast<GlGetGraphicsResetStatusArbType>(
1256             reinterpret_cast<QFunctionPointer>(QOpenGLStaticContext::opengl32.wglGetProcAddress("glGetGraphicsResetStatusARB")));
1257     }
1258 
1259     QOpenGLStaticContext::opengl32.wglMakeCurrent(prevSurface, prevContext);
1260     return true;
1261 }
1262 
releaseDCs()1263 void QWindowsGLContext::releaseDCs()
1264 {
1265     for (const auto &e : m_windowContexts)
1266         ReleaseDC(e.hwnd, e.hdc);
1267     m_windowContexts.clear();
1268 }
1269 
glWindowOf(QPlatformSurface * s)1270 static inline QWindowsWindow *glWindowOf(QPlatformSurface *s)
1271 {
1272     return static_cast<QWindowsWindow *>(s);
1273 }
1274 
handleOf(QPlatformSurface * s)1275 static inline HWND handleOf(QPlatformSurface *s)
1276 {
1277     return glWindowOf(s)->handle();
1278 }
1279 
1280 // Find a window in a context list.
1281 static inline const QOpenGLContextData *
findByHWND(const std::vector<QOpenGLContextData> & data,HWND hwnd)1282     findByHWND(const std::vector<QOpenGLContextData> &data, HWND hwnd)
1283 {
1284     for (const auto &e : data) {
1285         if (e.hwnd == hwnd)
1286             return &e;
1287     }
1288     return nullptr;
1289 }
1290 
swapBuffers(QPlatformSurface * surface)1291 void QWindowsGLContext::swapBuffers(QPlatformSurface *surface)
1292 {
1293     if (QWindowsContext::verbose > 1)
1294         qCDebug(lcQpaGl) << __FUNCTION__ << surface;
1295 
1296     if (const QOpenGLContextData *contextData = findByHWND(m_windowContexts, handleOf(surface)))
1297         QOpenGLStaticContext::opengl32.swapBuffers(contextData->hdc);
1298     else
1299         qWarning("%s: Cannot find window %p", __FUNCTION__, handleOf(surface));
1300 }
1301 
makeCurrent(QPlatformSurface * surface)1302 bool QWindowsGLContext::makeCurrent(QPlatformSurface *surface)
1303 {
1304 #ifdef DEBUG_GL
1305     if (QWindowsContext::verbose > 1)
1306         qCDebug(lcQpaGl) << __FUNCTION__ << this << m_windowContexts.size() << "contexts";
1307 #endif // DEBUG_GL
1308 
1309     Q_ASSERT(surface->surface()->supportsOpenGL());
1310 
1311     // Do we already have a DC entry for that window?
1312     auto *window = static_cast<QWindowsWindow *>(surface);
1313     window->aboutToMakeCurrent();
1314     const HWND hwnd = window->handle();
1315     if (const QOpenGLContextData *contextData = findByHWND(m_windowContexts, hwnd)) {
1316         // Repeated calls to wglMakeCurrent when vsync is enabled in the driver will
1317         // often result in 100% cpuload. This check is cheap and avoids the problem.
1318         // This is reproducable on NVidia cards and Intel onboard chips.
1319         if (QOpenGLStaticContext::opengl32.wglGetCurrentContext() == contextData->renderingContext
1320                 && QOpenGLStaticContext::opengl32.wglGetCurrentDC() == contextData->hdc) {
1321             return true;
1322         }
1323         return QOpenGLStaticContext::opengl32.wglMakeCurrent(contextData->hdc, contextData->renderingContext);
1324     }
1325     // Create a new entry.
1326     const QOpenGLContextData newContext(m_renderingContext, hwnd, GetDC(hwnd));
1327     if (!newContext.hdc)
1328         return false;
1329     // Initialize pixel format first time. This will apply to
1330     // the HWND as well and  must be done only once.
1331     if (!window->testFlag(QWindowsWindow::OpenGlPixelFormatInitialized)) {
1332         if (!QOpenGLStaticContext::opengl32.setPixelFormat(newContext.hdc, m_pixelFormat, &m_obtainedPixelFormatDescriptor)) {
1333             qErrnoWarning("%s: SetPixelFormat() failed", __FUNCTION__);
1334             ReleaseDC(newContext.hwnd, newContext.hdc);
1335             return false;
1336         }
1337         window->setFlag(QWindowsWindow::OpenGlPixelFormatInitialized);
1338         if (m_obtainedFormat.swapBehavior() == QSurfaceFormat::DoubleBuffer)
1339             window->setFlag(QWindowsWindow::OpenGLDoubleBuffered);
1340     }
1341     m_windowContexts.push_back(newContext);
1342 
1343     m_lost = false;
1344     bool success = QOpenGLStaticContext::opengl32.wglMakeCurrent(newContext.hdc, newContext.renderingContext);
1345     if (!success) {
1346         if (m_getGraphicsResetStatus && m_getGraphicsResetStatus()) {
1347             m_lost = true;
1348             qCDebug(lcQpaGl) << "makeCurrent(): context loss detected" << this;
1349             // Drop the surface. Will recreate on the next makeCurrent.
1350             window->invalidateSurface();
1351         }
1352     }
1353 
1354     // Set the swap interval
1355     if (m_staticContext->wglSwapInternalExt) {
1356         const int interval = surface->format().swapInterval();
1357         if (m_swapInterval != interval)
1358             m_swapInterval = interval;
1359         if (interval >= 0)
1360             m_staticContext->wglSwapInternalExt(interval);
1361     }
1362 
1363     return success;
1364 }
1365 
doneCurrent()1366 void QWindowsGLContext::doneCurrent()
1367 {
1368 #ifdef DEBUG_GL
1369     if (QWindowsContext::verbose > 1)
1370         qCDebug(lcQpaGl) << __FUNCTION__ << this << m_windowContexts.size() << "contexts";
1371 #endif // DEBUG_GL
1372     QOpenGLStaticContext::opengl32.wglMakeCurrent(nullptr, nullptr);
1373     releaseDCs();
1374 }
1375 
getProcAddress(const char * procName)1376 QFunctionPointer QWindowsGLContext::getProcAddress(const char *procName)
1377 {
1378     // Even though we use QFunctionPointer, it does not mean the function can be called.
1379     // It will need to be cast to the proper function type with the correct calling
1380     // convention. QFunctionPointer is nothing more than a glorified void* here.
1381     auto procAddress = reinterpret_cast<QFunctionPointer>(QOpenGLStaticContext::opengl32.wglGetProcAddress(procName));
1382 
1383     // We support AllGLFunctionsQueryable, which means this function must be able to
1384     // return a function pointer even for functions that are in GL.h and exported
1385     // normally from opengl32.dll. wglGetProcAddress() is not guaranteed to work for such
1386     // functions, however in QT_OPENGL_DYNAMIC builds QOpenGLFunctions will just blindly
1387     // call into here for _any_ OpenGL function.
1388     if (procAddress == nullptr || reinterpret_cast<quintptr>(procAddress) < 4u
1389         || procAddress == reinterpret_cast<QFunctionPointer>(-1)) {
1390         procAddress = QOpenGLStaticContext::opengl32.resolve(procName);
1391     }
1392 
1393     if (QWindowsContext::verbose > 1)
1394         qCDebug(lcQpaGl) << __FUNCTION__ <<  procName << QOpenGLStaticContext::opengl32.wglGetCurrentContext() << "returns" << procAddress;
1395 
1396     return reinterpret_cast<QFunctionPointer>(procAddress);
1397 }
1398 
1399 QT_END_NAMESPACE
1400