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