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, ¶ms);
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