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 QtOpenGLWidgets module 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 "qopenglwidget.h"
41#include <QtGui/QOpenGLContext>
42#include <QtGui/QOffscreenSurface>
43#include <QtGui/QOpenGLFunctions>
44#include <QtGui/QWindow>
45#include <QtGui/QGuiApplication>
46#include <QtGui/QScreen>
47#include <QtGui/qpa/qplatformwindow.h>
48#include <QtGui/qpa/qplatformintegration.h>
49#include <QtOpenGL/QOpenGLFramebufferObject>
50#include <QtOpenGL/QOpenGLPaintDevice>
51
52#include <QtGui/private/qguiapplication_p.h>
53#include <QtGui/private/qopenglextensions_p.h>
54#include <QtGui/private/qfont_p.h>
55#include <QtGui/private/qopenglcontext_p.h>
56#include <QtOpenGL/private/qopenglframebufferobject_p.h>
57#include <QtOpenGL/private/qopenglpaintdevice_p.h>
58#include <QtOpenGL/qpa/qplatformbackingstoreopenglsupport.h>
59
60#include <QtWidgets/private/qwidget_p.h>
61
62QT_BEGIN_NAMESPACE
63
64/*!
65 \class QOpenGLWidget
66 \inmodule QtOpenGLWidgets
67 \since 5.4
68
69 \brief The QOpenGLWidget class is a widget for rendering OpenGL graphics.
70
71 QOpenGLWidget provides functionality for displaying OpenGL graphics
72 integrated into a Qt application. It is very simple to use: Make
73 your class inherit from it and use the subclass like any other
74 QWidget, except that you have the choice between using QPainter and
75 standard OpenGL rendering commands.
76
77 QOpenGLWidget provides three convenient virtual functions that you
78 can reimplement in your subclass to perform the typical OpenGL
79 tasks:
80
81 \list
82 \li paintGL() - Renders the OpenGL scene. Gets called whenever the widget
83 needs to be updated.
84 \li resizeGL() - Sets up the OpenGL viewport, projection, etc. Gets
85 called whenever the widget has been resized (and also when it
86 is shown for the first time because all newly created widgets get a
87 resize event automatically).
88 \li initializeGL() - Sets up the OpenGL resources and state. Gets called
89 once before the first time resizeGL() or paintGL() is called.
90 \endlist
91
92 If you need to trigger a repaint from places other than paintGL() (a
93 typical example is when using \l{QTimer}{timers} to animate scenes),
94 you should call the widget's update() function to schedule an update.
95
96 Your widget's OpenGL rendering context is made current when
97 paintGL(), resizeGL(), or initializeGL() is called. If you need to
98 call the standard OpenGL API functions from other places (e.g. in
99 your widget's constructor or in your own paint functions), you
100 must call makeCurrent() first.
101
102 All rendering happens into an OpenGL framebuffer
103 object. makeCurrent() ensure that it is bound in the context. Keep
104 this in mind when creating and binding additional framebuffer
105 objects in the rendering code in paintGL(). Never re-bind the
106 framebuffer with ID 0. Instead, call defaultFramebufferObject() to
107 get the ID that should be bound.
108
109 QOpenGLWidget allows using different OpenGL versions and profiles
110 when the platform supports it. Just set the requested format via
111 setFormat(). Keep in mind however that having multiple QOpenGLWidget
112 instances in the same window requires that they all use the same
113 format, or at least formats that do not make the contexts
114 non-sharable. To overcome this issue, prefer using
115 QSurfaceFormat::setDefaultFormat() instead of setFormat().
116
117 \note Calling QSurfaceFormat::setDefaultFormat() before constructing
118 the QApplication instance is mandatory on some platforms (for example,
119 \macos) when an OpenGL core profile context is requested. This is to
120 ensure that resource sharing between contexts stays functional as all
121 internal contexts are created using the correct version and profile.
122
123 \section1 Painting Techniques
124
125 As described above, subclass QOpenGLWidget to render pure 3D content in the
126 following way:
127
128 \list
129
130 \li Reimplement the initializeGL() and resizeGL() functions to
131 set up the OpenGL state and provide a perspective transformation.
132
133 \li Reimplement paintGL() to paint the 3D scene, calling only
134 OpenGL functions.
135
136 \endlist
137
138 It is also possible to draw 2D graphics onto a QOpenGLWidget subclass using QPainter:
139
140 \list
141
142 \li In paintGL(), instead of issuing OpenGL commands, construct a QPainter
143 object for use on the widget.
144
145 \li Draw primitives using QPainter's member functions.
146
147 \li Direct OpenGL commands can still be issued. However, you must make sure
148 these are enclosed by a call to the painter's beginNativePainting() and
149 endNativePainting().
150
151 \endlist
152
153 When performing drawing using QPainter only, it is also possible to perform
154 the painting like it is done for ordinary widgets: by reimplementing paintEvent().
155
156 \list
157
158 \li Reimplement the paintEvent() function.
159
160 \li Construct a QPainter object targeting the widget. Either pass the widget to the
161 constructor or the QPainter::begin() function.
162
163 \li Draw primitives using QPainter's member functions.
164
165 \li Painting finishes then the QPainter instance is destroyed. Alternatively,
166 call QPainter::end() explicitly.
167
168 \endlist
169
170 \section1 OpenGL Function Calls, Headers and QOpenGLFunctions
171
172 When making OpenGL function calls, it is strongly recommended to avoid calling
173 the functions directly. Instead, prefer using QOpenGLFunctions (when making
174 portable applications) or the versioned variants (for example,
175 QOpenGLFunctions_3_2_Core and similar, when targeting modern, desktop-only
176 OpenGL). This way the application will work correctly in all Qt build
177 configurations, including the ones that perform dynamic OpenGL implementation
178 loading which means applications are not directly linking to an GL
179 implementation and thus direct function calls are not feasible.
180
181 In paintGL() the current context is always accessible by caling
182 QOpenGLContext::currentContext(). From this context an already initialized,
183 ready-to-be-used QOpenGLFunctions instance is retrievable by calling
184 QOpenGLContext::functions(). An alternative to prefixing every GL call is to
185 inherit from QOpenGLFunctions and call
186 QOpenGLFunctions::initializeOpenGLFunctions() in initializeGL().
187
188 As for the OpenGL headers, note that in most cases there will be no need to
189 directly include any headers like GL.h. The OpenGL-related Qt headers will
190 include qopengl.h which will in turn include an appropriate header for the
191 system. This might be an OpenGL ES 3.x or 2.0 header, the highest version that
192 is available, or a system-provided gl.h. In addition, a copy of the extension
193 headers (called glext.h on some systems) is provided as part of Qt both for
194 OpenGL and OpenGL ES. These will get included automatically on platforms where
195 feasible. This means that constants and function pointer typedefs from ARB,
196 EXT, OES extensions are automatically available.
197
198 \section1 Code Examples
199
200 To get started, the simplest QOpenGLWidget subclass could like like the following:
201
202 \snippet code/doc_gui_widgets_qopenglwidget.cpp 0
203
204 Alternatively, the prefixing of each and every OpenGL call can be avoided by deriving
205 from QOpenGLFunctions instead:
206
207 \snippet code/doc_gui_widgets_qopenglwidget.cpp 1
208
209 To get a context compatible with a given OpenGL version or profile, or to
210 request depth and stencil buffers, call setFormat():
211
212 \snippet code/doc_gui_widgets_qopenglwidget.cpp 2
213
214 With OpenGL 3.0+ contexts, when portability is not important, the versioned
215 QOpenGLFunctions variants give easy access to all the modern OpenGL functions
216 available in a given version:
217
218 \snippet code/doc_gui_widgets_qopenglwidget.cpp 3
219
220 As described above, it is simpler and more robust to set the requested format
221 globally so that it applies to all windows and contexts during the lifetime of
222 the application. Below is an example of this:
223
224 \snippet code/doc_gui_widgets_qopenglwidget.cpp 6
225
226 \section1 Multisampling
227
228 To enable multisampling, set the number of requested samples on the
229 QSurfaceFormat that is passed to setFormat(). On systems that do not support
230 it the request may get ignored.
231
232 Multisampling support requires support for multisampled renderbuffers and
233 framebuffer blits. On OpenGL ES 2.0 implementations it is likely that these
234 will not be present. This means that multisampling will not be available. With
235 modern OpenGL versions and OpenGL ES 3.0 and up this is usually not a problem
236 anymore.
237
238 \section1 Threading
239
240 Performing offscreen rendering on worker threads, for example to generate
241 textures that are then used in the GUI/main thread in paintGL(), are supported
242 by exposing the widget's QOpenGLContext so that additional contexts sharing
243 with it can be created on each thread.
244
245 Drawing directly to the QOpenGLWidget's framebuffer outside the GUI/main
246 thread is possible by reimplementing paintEvent() to do nothing. The context's
247 thread affinity has to be changed via QObject::moveToThread(). After that,
248 makeCurrent() and doneCurrent() are usable on the worker thread. Be careful to
249 move the context back to the GUI/main thread afterwards.
250
251 Triggering a buffer swap just for the QOpenGLWidget is not possible since there
252 is no real, onscreen native surface for it. It is up to the widget stack to
253 manage composition and buffer swaps on the gui thread. When a thread is done
254 updating the framebuffer, call update() \b{on the GUI/main thread} to
255 schedule composition.
256
257 Extra care has to be taken to avoid using the framebuffer when the GUI/main
258 thread is performing compositing. The signals aboutToCompose() and
259 frameSwapped() will be emitted when the composition is starting and
260 ending. They are emitted on the GUI/main thread. This means that by using a
261 direct connection aboutToCompose() can block the GUI/main thread until the
262 worker thread has finished its rendering. After that, the worker thread must
263 perform no further rendering until the frameSwapped() signal is emitted. If
264 this is not acceptable, the worker thread has to implement a double buffering
265 mechanism. This involves drawing using an alternative render target, that is
266 fully controlled by the thread, e.g. an additional framebuffer object, and
267 blitting to the QOpenGLWidget's framebuffer at a suitable time.
268
269 \section1 Context Sharing
270
271 When multiple QOpenGLWidgets are added as children to the same top-level
272 widget, their contexts will share with each other. This does not apply for
273 QOpenGLWidget instances that belong to different windows.
274
275 This means that all QOpenGLWidgets in the same window can access each other's
276 sharable resources, like textures, and there is no need for an extra "global
277 share" context.
278
279 To set up sharing between QOpenGLWidget instances belonging to different
280 windows, set the Qt::AA_ShareOpenGLContexts application attribute before
281 instantiating QApplication. This will trigger sharing between all
282 QOpenGLWidget instances without any further steps.
283
284 Creating extra QOpenGLContext instances that share resources like textures
285 with the QOpenGLWidget's context is also possible. Simply pass the pointer
286 returned from context() to QOpenGLContext::setShareContext() before calling
287 QOpenGLContext::create(). The resulting context can also be used on a
288 different thread, allowing threaded generation of textures and asynchronous
289 texture uploads.
290
291 Note that QOpenGLWidget expects a standard conformant implementation of
292 resource sharing when it comes to the underlying graphics drivers. For
293 example, some drivers, in particular for mobile and embedded hardware, have
294 issues with setting up sharing between an existing context and others that are
295 created later. Some other drivers may behave in unexpected ways when trying to
296 utilize shared resources between different threads.
297
298 \section1 Resource Initialization and Cleanup
299
300 The QOpenGLWidget's associated OpenGL context is guaranteed to be current
301 whenever initializeGL() and paintGL() are invoked. Do not attempt to create
302 OpenGL resources before initializeGL() is called. For example, attempting to
303 compile shaders, initialize vertex buffer objects or upload texture data will
304 fail when done in a subclass's constructor. These operations must be deferred
305 to initializeGL(). Some of Qt's OpenGL helper classes, like QOpenGLBuffer or
306 QOpenGLVertexArrayObject, have a matching deferred behavior: they can be
307 instantiated without a context, but all initialization is deferred until a
308 create(), or similar, call. This means that they can be used as normal
309 (non-pointer) member variables in a QOpenGLWidget subclass, but the create()
310 or similar function can only be called from initializeGL(). Be aware however
311 that not all classes are designed like this. When in doubt, make the member
312 variable a pointer and create and destroy the instance dynamically in
313 initializeGL() and the destructor, respectively.
314
315 Releasing the resources also needs the context to be current. Therefore
316 destructors that perform such cleanup are expected to call makeCurrent()
317 before moving on to destroy any OpenGL resources or wrappers. Avoid deferred
318 deletion via \l{QObject::deleteLater()}{deleteLater()} or the parenting
319 mechanism of QObject. There is no guarantee the correct context will be
320 current at the time the instance in question is really destroyed.
321
322 A typical subclass will therefore often look like the following when it comes
323 to resource initialization and destruction:
324
325 \snippet code/doc_gui_widgets_qopenglwidget.cpp 4
326
327 This is naturally not the only possible solution. One alternative is to use
328 the \l{QOpenGLContext::aboutToBeDestroyed()}{aboutToBeDestroyed()} signal of
329 QOpenGLContext. By connecting a slot, using direct connection, to this signal,
330 it is possible to perform cleanup whenever the underlying native context
331 handle, or the entire QOpenGLContext instance, is going to be released. The
332 following snippet is in principle equivalent to the previous one:
333
334 \snippet code/doc_gui_widgets_qopenglwidget.cpp 5
335
336 \note For widgets that change their associated top-level window multiple times
337 during their lifetime, a combined approach is essential. Whenever the widget
338 or a parent of it gets reparented so that the top-level window becomes
339 different, the widget's associated context is destroyed and a new one is
340 created. This is then followed by a call to initializeGL() where all OpenGL
341 resources must get reinitialized. Due to this the only option to perform
342 proper cleanup is to connect to the context's aboutToBeDestroyed()
343 signal. Note that the context in question may not be the current one when the
344 signal gets emitted. Therefore it is good practice to call makeCurrent() in
345 the connected slot. Additionally, the same cleanup steps must be performed
346 from the derived class' destructor, since the slot connected to the signal
347 will not get invoked when the widget is being destroyed.
348
349 \note When Qt::AA_ShareOpenGLContexts is set, the widget's context never
350 changes, not even when reparenting because the widget's associated texture is
351 guaranteed to be accessible also from the new top-level's context.
352
353 Proper cleanup is especially important due to context sharing. Even though
354 each QOpenGLWidget's associated context is destroyed together with the
355 QOpenGLWidget, the sharable resources in that context, like textures, will
356 stay valid until the top-level window, in which the QOpenGLWidget lived, is
357 destroyed. Additionally, settings like Qt::AA_ShareOpenGLContexts and some Qt
358 modules may trigger an even wider scope for sharing contexts, potentially
359 leading to keeping the resources in question alive for the entire lifetime of
360 the application. Therefore the safest and most robust is always to perform
361 explicit cleanup for all resources and resource wrappers used in the
362 QOpenGLWidget.
363
364 \section1 Limitations
365
366 Putting other widgets underneath and making the QOpenGLWidget transparent will
367 not lead to the expected results: The widgets underneath will not be
368 visible. This is because in practice the QOpenGLWidget is drawn before all
369 other regular, non-OpenGL widgets, and so see-through type of solutions are
370 not feasible. Other type of layouts, like having widgets on top of the
371 QOpenGLWidget, will function as expected.
372
373 When absolutely necessary, this limitation can be overcome by setting the
374 Qt::WA_AlwaysStackOnTop attribute on the QOpenGLWidget. Be aware however that
375 this breaks stacking order, for example it will not be possible to have other
376 widgets on top of the QOpenGLWidget, so it should only be used in situations
377 where a semi-transparent QOpenGLWidget with other widgets visible underneath
378 is required.
379
380 Note that this does not apply when there are no other widgets underneath and
381 the intention is to have a semi-transparent window. In that case the
382 traditional approach of setting Qt::WA_TranslucentBackground
383 on the top-level window is sufficient. Note that if the transparent areas are
384 only desired in the QOpenGLWidget, then Qt::WA_NoSystemBackground will need
385 to be turned back to \c false after enabling Qt::WA_TranslucentBackground.
386 Additionally, requesting an alpha channel for the QOpenGLWidget's context via
387 setFormat() may be necessary too, depending on the system.
388
389 QOpenGLWidget supports multiple update behaviors, just like QOpenGLWindow. In
390 preserved mode the rendered content from the previous paintGL() call is
391 available in the next one, allowing incremental rendering. In non-preserved
392 mode the content is lost and paintGL() implementations are expected to redraw
393 everything in the view.
394
395 Before Qt 5.5 the default behavior of QOpenGLWidget was to preserve the
396 rendered contents between paintGL() calls. Since Qt 5.5 the default behavior
397 is non-preserved because this provides better performance and the majority of
398 applications have no need for the previous content. This also resembles the
399 semantics of an OpenGL-based QWindow and matches the default behavior of
400 QOpenGLWindow in that the color and ancillary buffers are invalidated for
401 each frame. To restore the preserved behavior, call setUpdateBehavior() with
402 \c PartialUpdate.
403
404 \section1 Alternatives
405
406 Adding a QOpenGLWidget into a window turns on OpenGL-based
407 compositing for the entire window. In some special cases this may
408 not be ideal, and the old QGLWidget-style behavior with a separate,
409 native child window is desired. Desktop applications that understand
410 the limitations of this approach (for example when it comes to
411 overlaps, transparency, scroll views and MDI areas), can use
412 QOpenGLWindow with QWidget::createWindowContainer(). This is a
413 modern alternative to QGLWidget and is faster than QOpenGLWidget due
414 to the lack of the additional composition step. It is strongly
415 recommended to limit the usage of this approach to cases where there
416 is no other choice. Note that this option is not suitable for most
417 embedded and mobile platforms, and it is known to have issues on
418 certain desktop platforms (e.g. \macos) too. The stable,
419 cross-platform solution is always QOpenGLWidget.
420
421 \e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other
422 countries.}
423
424 \sa QOpenGLFunctions, QOpenGLWindow, Qt::AA_ShareOpenGLContexts, UpdateBehavior
425*/
426
427/*!
428 \fn void QOpenGLWidget::aboutToCompose()
429
430 This signal is emitted when the widget's top-level window is about to begin
431 composing the textures of its QOpenGLWidget children and the other widgets.
432*/
433
434/*!
435 \fn void QOpenGLWidget::frameSwapped()
436
437 This signal is emitted after the widget's top-level window has finished
438 composition and returned from its potentially blocking
439 QOpenGLContext::swapBuffers() call.
440*/
441
442/*!
443 \fn void QOpenGLWidget::aboutToResize()
444
445 This signal is emitted when the widget's size is changed and therefore the
446 framebuffer object is going to be recreated.
447*/
448
449/*!
450 \fn void QOpenGLWidget::resized()
451
452 This signal is emitted right after the framebuffer object has been recreated
453 due to resizing the widget.
454*/
455
456/*!
457 \enum QOpenGLWidget::UpdateBehavior
458 \since 5.5
459
460 This enum describes the update semantics of QOpenGLWidget.
461
462 \value NoPartialUpdate QOpenGLWidget will discard the
463 contents of the color buffer and the ancillary buffers after the
464 QOpenGLWidget is rendered to screen. This is the same behavior that can be
465 expected by calling QOpenGLContext::swapBuffers with a default opengl
466 enabled QWindow as the argument. NoPartialUpdate can have some performance
467 benefits on certain hardware architectures common in the mobile and
468 embedded space when a framebuffer object is used as the rendering target.
469 The framebuffer object is invalidated between frames with
470 glDiscardFramebufferEXT if supported or a glClear. Please see the
471 documentation of EXT_discard_framebuffer for more information:
472 https://www.khronos.org/registry/gles/extensions/EXT/EXT_discard_framebuffer.txt
473
474 \value PartialUpdate The framebuffer objects color buffer and ancillary
475 buffers are not invalidated between frames.
476
477 \sa updateBehavior(), setUpdateBehavior()
478*/
479
480class QOpenGLWidgetPaintDevicePrivate : public QOpenGLPaintDevicePrivate
481{
482public:
483 QOpenGLWidgetPaintDevicePrivate(QOpenGLWidget *widget)
484 : QOpenGLPaintDevicePrivate(QSize()),
485 w(widget) { }
486
487 void beginPaint() override;
488 void endPaint() override;
489
490 QOpenGLWidget *w;
491};
492
493class QOpenGLWidgetPaintDevice : public QOpenGLPaintDevice
494{
495public:
496 QOpenGLWidgetPaintDevice(QOpenGLWidget *widget)
497 : QOpenGLPaintDevice(*new QOpenGLWidgetPaintDevicePrivate(widget)) { }
498 void ensureActiveTarget() override;
499};
500
501class QOpenGLWidgetPrivate : public QWidgetPrivate
502{
503 Q_DECLARE_PUBLIC(QOpenGLWidget)
504public:
505 QOpenGLWidgetPrivate()
506 : context(nullptr),
507 fbo(nullptr),
508 resolvedFbo(nullptr),
509 surface(nullptr),
510 initialized(false),
511 fakeHidden(false),
512 inBackingStorePaint(false),
513 hasBeenComposed(false),
514 flushPending(false),
515 paintDevice(nullptr),
516 updateBehavior(QOpenGLWidget::NoPartialUpdate),
517 requestedSamples(0),
518 inPaintGL(false),
519 textureFormat(0)
520 {
521 requestedFormat = QSurfaceFormat::defaultFormat();
522 }
523
524 void reset();
525 void recreateFbo();
526
527 GLuint textureId() const override;
528 QPlatformTextureList::Flags textureListFlags() override;
529
530 void initialize();
531 void invokeUserPaint();
532 void render();
533
534 void invalidateFbo();
535
536 QImage grabFramebuffer() override;
537 void beginBackingStorePainting() override { inBackingStorePaint = true; }
538 void endBackingStorePainting() override { inBackingStorePaint = false; }
539 void beginCompose() override;
540 void endCompose() override;
541 void initializeViewportFramebuffer() override;
542 void resizeViewportFramebuffer() override;
543 void resolveSamples() override;
544
545 QOpenGLContext *context;
546 QOpenGLFramebufferObject *fbo;
547 QOpenGLFramebufferObject *resolvedFbo;
548 QOffscreenSurface *surface;
549 bool initialized;
550 bool fakeHidden;
551 bool inBackingStorePaint;
552 bool hasBeenComposed;
553 bool flushPending;
554 QOpenGLPaintDevice *paintDevice;
555 QSurfaceFormat requestedFormat;
556 QOpenGLWidget::UpdateBehavior updateBehavior;
557 int requestedSamples;
558 bool inPaintGL;
559 GLenum textureFormat;
560};
561
562void QOpenGLWidgetPaintDevicePrivate::beginPaint()
563{
564 // NB! autoFillBackground is and must be false by default. Otherwise we would clear on
565 // every QPainter begin() which is not desirable. This is only for legacy use cases,
566 // like using QOpenGLWidget as the viewport of a graphics view, that expect clearing
567 // with the palette's background color.
568 if (w->autoFillBackground()) {
569 QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
570 if (w->format().hasAlpha()) {
571 f->glClearColor(0, 0, 0, 0);
572 } else {
573 QColor c = w->palette().brush(w->backgroundRole()).color();
574 float alpha = c.alphaF();
575 f->glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha);
576 }
577 f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
578 }
579}
580
581void QOpenGLWidgetPaintDevicePrivate::endPaint()
582{
583 QOpenGLWidgetPrivate *wd = static_cast<QOpenGLWidgetPrivate *>(QWidgetPrivate::get(w));
584 if (!wd->initialized)
585 return;
586
587 if (!wd->inPaintGL)
588 QOpenGLContextPrivate::get(wd->context)->defaultFboRedirect = 0;
589}
590
591void QOpenGLWidgetPaintDevice::ensureActiveTarget()
592{
593 QOpenGLWidgetPaintDevicePrivate *d = static_cast<QOpenGLWidgetPaintDevicePrivate *>(d_ptr.data());
594 QOpenGLWidgetPrivate *wd = static_cast<QOpenGLWidgetPrivate *>(QWidgetPrivate::get(d->w));
595 if (!wd->initialized)
596 return;
597
598 if (QOpenGLContext::currentContext() != wd->context)
599 d->w->makeCurrent();
600 else
601 wd->fbo->bind();
602
603 if (!wd->inPaintGL)
604 QOpenGLContextPrivate::get(wd->context)->defaultFboRedirect = wd->fbo->handle();
605
606 // When used as a viewport, drawing is done via opening a QPainter on the widget
607 // without going through paintEvent(). We will have to make sure a glFlush() is done
608 // before the texture is accessed also in this case.
609 wd->flushPending = true;
610}
611
612GLuint QOpenGLWidgetPrivate::textureId() const
613{
614 return resolvedFbo ? resolvedFbo->texture() : (fbo ? fbo->texture() : 0);
615}
616
617#ifndef GL_SRGB
618#define GL_SRGB 0x8C40
619#endif
620#ifndef GL_SRGB8
621#define GL_SRGB8 0x8C41
622#endif
623#ifndef GL_SRGB_ALPHA
624#define GL_SRGB_ALPHA 0x8C42
625#endif
626#ifndef GL_SRGB8_ALPHA8
627#define GL_SRGB8_ALPHA8 0x8C43
628#endif
629
630QPlatformTextureList::Flags QOpenGLWidgetPrivate::textureListFlags()
631{
632 QPlatformTextureList::Flags flags = QWidgetPrivate::textureListFlags();
633 switch (textureFormat) {
634 case GL_SRGB:
635 case GL_SRGB8:
636 case GL_SRGB_ALPHA:
637 case GL_SRGB8_ALPHA8:
638 flags |= QPlatformTextureList::TextureIsSrgb;
639 break;
640 default:
641 break;
642 }
643 return flags;
644}
645
646void QOpenGLWidgetPrivate::reset()
647{
648 Q_Q(QOpenGLWidget);
649
650 // Destroy the OpenGL resources first. These need the context to be current.
651 if (initialized)
652 q->makeCurrent();
653
654 delete paintDevice;
655 paintDevice = nullptr;
656 delete fbo;
657 fbo = nullptr;
658 delete resolvedFbo;
659 resolvedFbo = nullptr;
660
661 if (initialized)
662 q->doneCurrent();
663
664 // Delete the context first, then the surface. Slots connected to
665 // the context's aboutToBeDestroyed() may still call makeCurrent()
666 // to perform some cleanup.
667 delete context;
668 context = nullptr;
669 delete surface;
670 surface = nullptr;
671 initialized = fakeHidden = inBackingStorePaint = false;
672}
673
674void QOpenGLWidgetPrivate::recreateFbo()
675{
676 Q_Q(QOpenGLWidget);
677
678 emit q->aboutToResize();
679
680 context->makeCurrent(surface);
681
682 delete fbo;
683 fbo = nullptr;
684 delete resolvedFbo;
685 resolvedFbo = nullptr;
686
687 int samples = requestedSamples;
688 QOpenGLExtensions *extfuncs = static_cast<QOpenGLExtensions *>(context->functions());
689 if (!extfuncs->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
690 samples = 0;
691
692 QOpenGLFramebufferObjectFormat format;
693 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
694 format.setSamples(samples);
695 if (textureFormat)
696 format.setInternalTextureFormat(textureFormat);
697
698 const QSize deviceSize = q->size() * q->devicePixelRatio();
699 fbo = new QOpenGLFramebufferObject(deviceSize, format);
700 if (samples > 0)
701 resolvedFbo = new QOpenGLFramebufferObject(deviceSize);
702
703 textureFormat = fbo->format().internalTextureFormat();
704
705 fbo->bind();
706 context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
707 flushPending = true; // Make sure the FBO is initialized before use
708
709 paintDevice->setSize(deviceSize);
710 paintDevice->setDevicePixelRatio(q->devicePixelRatio());
711
712 emit q->resized();
713}
714
715void QOpenGLWidgetPrivate::beginCompose()
716{
717 Q_Q(QOpenGLWidget);
718 if (flushPending) {
719 flushPending = false;
720 q->makeCurrent();
721 static_cast<QOpenGLExtensions *>(context->functions())->flushShared();
722 }
723 hasBeenComposed = true;
724 emit q->aboutToCompose();
725}
726
727void QOpenGLWidgetPrivate::endCompose()
728{
729 Q_Q(QOpenGLWidget);
730 emit q->frameSwapped();
731}
732
733void QOpenGLWidgetPrivate::initialize()
734{
735 Q_Q(QOpenGLWidget);
736 if (initialized)
737 return;
738
739 // If no global shared context get our toplevel's context with which we
740 // will share in order to make the texture usable by the underlying window's backingstore.
741 QWidget *tlw = q->window();
742 QOpenGLContext *shareContext = qt_gl_global_share_context();
743 if (!shareContext)
744 shareContext = get(tlw)->shareContext();
745 // If shareContext is null, showing content on-screen will not work.
746 // However, offscreen rendering and grabFramebuffer() will stay fully functional.
747
748 // Do not include the sample count. Requesting a multisampled context is not necessary
749 // since we render into an FBO, never to an actual surface. What's more, attempting to
750 // create a pbuffer with a multisampled config crashes certain implementations. Just
751 // avoid the entire hassle, the result is the same.
752 requestedSamples = requestedFormat.samples();
753 requestedFormat.setSamples(0);
754
755 QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext);
756 ctx->setFormat(requestedFormat);
757 if (shareContext) {
758 ctx->setShareContext(shareContext);
759 ctx->setScreen(shareContext->screen());
760 }
761 if (Q_UNLIKELY(!ctx->create())) {
762 qWarning("QOpenGLWidget: Failed to create context");
763 return;
764 }
765
766 // Propagate settings that make sense only for the tlw. Note that this only
767 // makes sense for properties that get picked up even after the native
768 // window is created.
769 if (tlw->windowHandle()) {
770 QSurfaceFormat tlwFormat = tlw->windowHandle()->format();
771 if (requestedFormat.swapInterval() != tlwFormat.swapInterval()) {
772 // Most platforms will pick up the changed swap interval on the next
773 // makeCurrent or swapBuffers.
774 tlwFormat.setSwapInterval(requestedFormat.swapInterval());
775 tlw->windowHandle()->setFormat(tlwFormat);
776 }
777 if (requestedFormat.swapBehavior() != tlwFormat.swapBehavior()) {
778 tlwFormat.setSwapBehavior(requestedFormat.swapBehavior());
779 tlw->windowHandle()->setFormat(tlwFormat);
780 }
781 }
782
783 // The top-level window's surface is not good enough since it causes way too
784 // much trouble with regards to the QSurfaceFormat for example. So just like
785 // in QQuickWidget, use a dedicated QOffscreenSurface.
786 surface = new QOffscreenSurface;
787 surface->setFormat(ctx->format());
788 surface->setScreen(ctx->screen());
789 surface->create();
790
791 if (Q_UNLIKELY(!ctx->makeCurrent(surface))) {
792 qWarning("QOpenGLWidget: Failed to make context current");
793 return;
794 }
795
796 paintDevice = new QOpenGLWidgetPaintDevice(q);
797 paintDevice->setSize(q->size() * q->devicePixelRatio());
798 paintDevice->setDevicePixelRatio(q->devicePixelRatio());
799
800 context = ctx.take();
801 initialized = true;
802
803 q->initializeGL();
804}
805
806void QOpenGLWidgetPrivate::resolveSamples()
807{
808 Q_Q(QOpenGLWidget);
809 if (resolvedFbo) {
810 q->makeCurrent();
811 QRect rect(QPoint(0, 0), fbo->size());
812 QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
813 flushPending = true;
814 }
815}
816
817void QOpenGLWidgetPrivate::invokeUserPaint()
818{
819 Q_Q(QOpenGLWidget);
820
821 QOpenGLContext *ctx = QOpenGLContext::currentContext();
822 Q_ASSERT(ctx && fbo);
823
824 QOpenGLFunctions *f = ctx->functions();
825 QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = fbo->handle();
826
827 f->glViewport(0, 0, q->width() * q->devicePixelRatio(), q->height() * q->devicePixelRatio());
828 inPaintGL = true;
829 q->paintGL();
830 inPaintGL = false;
831 flushPending = true;
832
833 QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = 0;
834}
835
836void QOpenGLWidgetPrivate::render()
837{
838 Q_Q(QOpenGLWidget);
839
840 if (fakeHidden || !initialized)
841 return;
842
843 q->makeCurrent();
844
845 if (updateBehavior == QOpenGLWidget::NoPartialUpdate && hasBeenComposed) {
846 invalidateFbo();
847 hasBeenComposed = false;
848 }
849
850 invokeUserPaint();
851}
852
853void QOpenGLWidgetPrivate::invalidateFbo()
854{
855 QOpenGLExtensions *f = static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions());
856 if (f->hasOpenGLExtension(QOpenGLExtensions::DiscardFramebuffer)) {
857 const int gl_color_attachment0 = 0x8CE0; // GL_COLOR_ATTACHMENT0
858 const int gl_depth_attachment = 0x8D00; // GL_DEPTH_ATTACHMENT
859 const int gl_stencil_attachment = 0x8D20; // GL_STENCIL_ATTACHMENT
860#ifdef Q_OS_WASM
861 // webgl does not allow separate depth and stencil attachments
862 // QTBUG-69913
863 const int gl_depth_stencil_attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT
864
865 const GLenum attachments[] = {
866 gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment, gl_depth_stencil_attachment
867 };
868#else
869 const GLenum attachments[] = {
870 gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment
871 };
872#endif
873 f->glDiscardFramebufferEXT(GL_FRAMEBUFFER, sizeof attachments / sizeof *attachments, attachments);
874 } else {
875 f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
876 }
877}
878
879QImage QOpenGLWidgetPrivate::grabFramebuffer()
880{
881 Q_Q(QOpenGLWidget);
882
883 initialize();
884 if (!initialized)
885 return QImage();
886
887 if (!fbo) // could be completely offscreen, without ever getting a resize event
888 recreateFbo();
889
890 if (!inPaintGL)
891 render();
892
893 if (resolvedFbo) {
894 resolveSamples();
895 resolvedFbo->bind();
896 } else {
897 q->makeCurrent();
898 }
899
900 const bool hasAlpha = q->format().hasAlpha();
901 QImage res = qt_gl_read_framebuffer(q->size() * q->devicePixelRatio(), hasAlpha, hasAlpha);
902 res.setDevicePixelRatio(q->devicePixelRatio());
903
904 // While we give no guarantees of what is going to be left bound, prefer the
905 // multisample fbo instead of the resolved one. Clients may continue to
906 // render straight after calling this function.
907 if (resolvedFbo)
908 q->makeCurrent();
909
910 return res;
911}
912
913void QOpenGLWidgetPrivate::initializeViewportFramebuffer()
914{
915 Q_Q(QOpenGLWidget);
916 // Legacy behavior for compatibility with QGLWidget when used as a graphics view
917 // viewport: enable clearing on each painter begin.
918 q->setAutoFillBackground(true);
919}
920
921void QOpenGLWidgetPrivate::resizeViewportFramebuffer()
922{
923 Q_Q(QOpenGLWidget);
924 if (!initialized)
925 return;
926
927 if (!fbo || q->size() * q->devicePixelRatio() != fbo->size()) {
928 recreateFbo();
929 q->update();
930 }
931}
932
933/*!
934 Constructs a widget which is a child of \a parent, with widget flags set to \a f.
935 */
936QOpenGLWidget::QOpenGLWidget(QWidget *parent, Qt::WindowFlags f)
937 : QWidget(*(new QOpenGLWidgetPrivate), parent, f)
938{
939 Q_D(QOpenGLWidget);
940 if (Q_UNLIKELY(!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface)))
941 qWarning("QOpenGLWidget is not supported on this platform.");
942 else
943 d->setRenderToTexture();
944}
945
946/*!
947 Destroys the QOpenGLWidget instance, freeing its resources.
948
949 The QOpenGLWidget's context is made current in the destructor, allowing for
950 safe destruction of any child object that may need to release OpenGL
951 resources belonging to the context provided by this widget.
952
953 \warning if you have objects wrapping OpenGL resources (such as
954 QOpenGLBuffer, QOpenGLShaderProgram, etc.) as members of a OpenGLWidget
955 subclass, you may need to add a call to makeCurrent() in that subclass'
956 destructor as well. Due to the rules of C++ object destruction, those objects
957 will be destroyed \e{before} calling this function (but after that the
958 destructor of the subclass has run), therefore making the OpenGL context
959 current in this function happens too late for their safe disposal.
960
961 \sa makeCurrent
962*/
963QOpenGLWidget::~QOpenGLWidget()
964{
965 Q_D(QOpenGLWidget);
966 d->reset();
967}
968
969/*!
970 Sets this widget's update behavior to \a updateBehavior.
971 \since 5.5
972*/
973void QOpenGLWidget::setUpdateBehavior(UpdateBehavior updateBehavior)
974{
975 Q_D(QOpenGLWidget);
976 d->updateBehavior = updateBehavior;
977}
978
979/*!
980 \return the update behavior of the widget.
981 \since 5.5
982*/
983QOpenGLWidget::UpdateBehavior QOpenGLWidget::updateBehavior() const
984{
985 Q_D(const QOpenGLWidget);
986 return d->updateBehavior;
987}
988
989/*!
990 Sets the requested surface \a format.
991
992 When the format is not explicitly set via this function, the format returned by
993 QSurfaceFormat::defaultFormat() will be used. This means that when having multiple
994 OpenGL widgets, individual calls to this function can be replaced by one single call to
995 QSurfaceFormat::setDefaultFormat() before creating the first widget.
996
997 \note Requesting an alpha buffer via this function will not lead to the
998 desired results when the intention is to make other widgets beneath visible.
999 Instead, use Qt::WA_AlwaysStackOnTop to enable semi-transparent QOpenGLWidget
1000 instances with other widgets visible underneath. Keep in mind however that
1001 this breaks the stacking order, so it will no longer be possible to have
1002 other widgets on top of the QOpenGLWidget.
1003
1004 \sa format(), Qt::WA_AlwaysStackOnTop, QSurfaceFormat::setDefaultFormat()
1005 */
1006void QOpenGLWidget::setFormat(const QSurfaceFormat &format)
1007{
1008 Q_D(QOpenGLWidget);
1009 if (Q_UNLIKELY(d->initialized)) {
1010 qWarning("QOpenGLWidget: Already initialized, setting the format has no effect");
1011 return;
1012 }
1013
1014 d->requestedFormat = format;
1015}
1016
1017/*!
1018 Returns the context and surface format used by this widget and its toplevel
1019 window.
1020
1021 After the widget and its toplevel have both been created, resized and shown,
1022 this function will return the actual format of the context. This may differ
1023 from the requested format if the request could not be fulfilled by the
1024 platform. It is also possible to get larger color buffer sizes than
1025 requested.
1026
1027 When the widget's window and the related OpenGL resources are not yet
1028 initialized, the return value is the format that has been set via
1029 setFormat().
1030
1031 \sa setFormat(), context()
1032 */
1033QSurfaceFormat QOpenGLWidget::format() const
1034{
1035 Q_D(const QOpenGLWidget);
1036 return d->initialized ? d->context->format() : d->requestedFormat;
1037}
1038
1039/*!
1040 Sets a custom internal texture format of \a texFormat.
1041
1042 When working with sRGB framebuffers, it will be necessary to specify a
1043 format like \c{GL_SRGB8_ALPHA8}. This can be achieved by calling this
1044 function.
1045
1046 \note This function has no effect if called after the widget has already
1047 been shown and thus it performed initialization.
1048
1049 \note This function will typically have to be used in combination with a
1050 QSurfaceFormat::setDefaultFormat() call that sets the color space to
1051 QSurfaceFormat::sRGBColorSpace.
1052
1053 \since 5.10
1054 */
1055void QOpenGLWidget::setTextureFormat(GLenum texFormat)
1056{
1057 Q_D(QOpenGLWidget);
1058 if (Q_UNLIKELY(d->initialized)) {
1059 qWarning("QOpenGLWidget: Already initialized, setting the internal texture format has no effect");
1060 return;
1061 }
1062
1063 d->textureFormat = texFormat;
1064}
1065
1066/*!
1067 \return the active internal texture format if the widget has already
1068 initialized, the requested format if one was set but the widget has not yet
1069 been made visible, or \nullptr if setTextureFormat() was not called and the
1070 widget has not yet been made visible.
1071
1072 \since 5.10
1073 */
1074GLenum QOpenGLWidget::textureFormat() const
1075{
1076 Q_D(const QOpenGLWidget);
1077 return d->textureFormat;
1078}
1079
1080/*!
1081 \return \e true if the widget and OpenGL resources, like the context, have
1082 been successfully initialized. Note that the return value is always false
1083 until the widget is shown.
1084*/
1085bool QOpenGLWidget::isValid() const
1086{
1087 Q_D(const QOpenGLWidget);
1088 return d->initialized && d->context->isValid();
1089}
1090
1091/*!
1092 Prepares for rendering OpenGL content for this widget by making the
1093 corresponding context current and binding the framebuffer object in that
1094 context.
1095
1096 It is not necessary to call this function in most cases, because it
1097 is called automatically before invoking paintGL().
1098
1099 \sa context(), paintGL(), doneCurrent()
1100 */
1101void QOpenGLWidget::makeCurrent()
1102{
1103 Q_D(QOpenGLWidget);
1104 if (!d->initialized)
1105 return;
1106
1107 d->context->makeCurrent(d->surface);
1108
1109 if (d->fbo) // there may not be one if we are in reset()
1110 d->fbo->bind();
1111}
1112
1113/*!
1114 Releases the context.
1115
1116 It is not necessary to call this function in most cases, since the
1117 widget will make sure the context is bound and released properly
1118 when invoking paintGL().
1119 */
1120void QOpenGLWidget::doneCurrent()
1121{
1122 Q_D(QOpenGLWidget);
1123 if (!d->initialized)
1124 return;
1125
1126 d->context->doneCurrent();
1127}
1128
1129/*!
1130 \return The QOpenGLContext used by this widget or \c 0 if not yet initialized.
1131
1132 \note The context and the framebuffer object used by the widget changes when
1133 reparenting the widget via setParent().
1134
1135 \sa QOpenGLContext::setShareContext(), defaultFramebufferObject()
1136 */
1137QOpenGLContext *QOpenGLWidget::context() const
1138{
1139 Q_D(const QOpenGLWidget);
1140 return d->context;
1141}
1142
1143/*!
1144 \return The framebuffer object handle or \c 0 if not yet initialized.
1145
1146 \note The framebuffer object belongs to the context returned by context()
1147 and may not be accessible from other contexts.
1148
1149 \note The context and the framebuffer object used by the widget changes when
1150 reparenting the widget via setParent(). In addition, the framebuffer object
1151 changes on each resize.
1152
1153 \sa context()
1154 */
1155GLuint QOpenGLWidget::defaultFramebufferObject() const
1156{
1157 Q_D(const QOpenGLWidget);
1158 return d->fbo ? d->fbo->handle() : 0;
1159}
1160
1161/*!
1162 This virtual function is called once before the first call to
1163 paintGL() or resizeGL(). Reimplement it in a subclass.
1164
1165 This function should set up any required OpenGL resources and state.
1166
1167 There is no need to call makeCurrent() because this has already been
1168 done when this function is called. Note however that the framebuffer
1169 is not yet available at this stage, so avoid issuing draw calls from
1170 here. Defer such calls to paintGL() instead.
1171
1172 \sa paintGL(), resizeGL()
1173*/
1174void QOpenGLWidget::initializeGL()
1175{
1176}
1177
1178/*!
1179 This virtual function is called whenever the widget has been
1180 resized. Reimplement it in a subclass. The new size is passed in
1181 \a w and \a h.
1182
1183 There is no need to call makeCurrent() because this has already been
1184 done when this function is called. Additionally, the framebuffer is
1185 also bound.
1186
1187 \sa initializeGL(), paintGL()
1188*/
1189void QOpenGLWidget::resizeGL(int w, int h)
1190{
1191 Q_UNUSED(w);
1192 Q_UNUSED(h);
1193}
1194
1195/*!
1196 This virtual function is called whenever the widget needs to be
1197 painted. Reimplement it in a subclass.
1198
1199 There is no need to call makeCurrent() because this has already
1200 been done when this function is called.
1201
1202 Before invoking this function, the context and the framebuffer are
1203 bound, and the viewport is set up by a call to glViewport(). No
1204 other state is set and no clearing or drawing is performed by the
1205 framework.
1206
1207 \sa initializeGL(), resizeGL()
1208*/
1209void QOpenGLWidget::paintGL()
1210{
1211}
1212
1213/*!
1214 Handles resize events that are passed in the \a e event parameter.
1215 Calls the virtual function resizeGL().
1216
1217 \note Avoid overriding this function in derived classes. If that is not
1218 feasible, make sure that QOpenGLWidget's implementation is invoked
1219 too. Otherwise the underlying framebuffer object and related resources will
1220 not get resized properly and will lead to incorrect rendering.
1221*/
1222void QOpenGLWidget::resizeEvent(QResizeEvent *e)
1223{
1224 Q_D(QOpenGLWidget);
1225
1226 if (e->size().isEmpty()) {
1227 d->fakeHidden = true;
1228 return;
1229 }
1230 d->fakeHidden = false;
1231
1232 d->initialize();
1233 if (!d->initialized)
1234 return;
1235
1236 d->recreateFbo();
1237 resizeGL(width(), height());
1238 d->sendPaintEvent(QRect(QPoint(0, 0), size()));
1239}
1240
1241/*!
1242 Handles paint events.
1243
1244 Calling QWidget::update() will lead to sending a paint event \a e,
1245 and thus invoking this function. (NB this is asynchronous and will
1246 happen at some point after returning from update()). This function
1247 will then, after some preparation, call the virtual paintGL() to
1248 update the contents of the QOpenGLWidget's framebuffer. The widget's
1249 top-level window will then composite the framebuffer's texture with
1250 the rest of the window.
1251*/
1252void QOpenGLWidget::paintEvent(QPaintEvent *e)
1253{
1254 Q_UNUSED(e);
1255 Q_D(QOpenGLWidget);
1256 if (!d->initialized)
1257 return;
1258
1259 if (updatesEnabled())
1260 d->render();
1261}
1262
1263/*!
1264 Renders and returns a 32-bit RGB image of the framebuffer.
1265
1266 \note This is a potentially expensive operation because it relies on glReadPixels()
1267 to read back the pixels. This may be slow and can stall the GPU pipeline.
1268*/
1269QImage QOpenGLWidget::grabFramebuffer()
1270{
1271 Q_D(QOpenGLWidget);
1272 return d->grabFramebuffer();
1273}
1274
1275/*!
1276 \reimp
1277*/
1278int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const
1279{
1280 Q_D(const QOpenGLWidget);
1281 if (d->inBackingStorePaint)
1282 return QWidget::metric(metric);
1283
1284 auto window = d->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel);
1285 QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen();
1286
1287 const float dpmx = qt_defaultDpiX() * 100. / 2.54;
1288 const float dpmy = qt_defaultDpiY() * 100. / 2.54;
1289
1290 switch (metric) {
1291 case PdmWidth:
1292 return width();
1293 case PdmHeight:
1294 return height();
1295 case PdmDepth:
1296 return 32;
1297 case PdmWidthMM:
1298 if (screen)
1299 return width() * screen->physicalSize().width() / screen->geometry().width();
1300 else
1301 return width() * 1000 / dpmx;
1302 case PdmHeightMM:
1303 if (screen)
1304 return height() * screen->physicalSize().height() / screen->geometry().height();
1305 else
1306 return height() * 1000 / dpmy;
1307 case PdmNumColors:
1308 return 0;
1309 case PdmDpiX:
1310 if (screen)
1311 return qRound(screen->logicalDotsPerInchX());
1312 else
1313 return qRound(dpmx * 0.0254);
1314 case PdmDpiY:
1315 if (screen)
1316 return qRound(screen->logicalDotsPerInchY());
1317 else
1318 return qRound(dpmy * 0.0254);
1319 case PdmPhysicalDpiX:
1320 if (screen)
1321 return qRound(screen->physicalDotsPerInchX());
1322 else
1323 return qRound(dpmx * 0.0254);
1324 case PdmPhysicalDpiY:
1325 if (screen)
1326 return qRound(screen->physicalDotsPerInchY());
1327 else
1328 return qRound(dpmy * 0.0254);
1329 case PdmDevicePixelRatio:
1330 if (window)
1331 return int(window->devicePixelRatio());
1332 else
1333 return 1.0;
1334 case PdmDevicePixelRatioScaled:
1335 if (window)
1336 return int(window->devicePixelRatio() * devicePixelRatioFScale());
1337 else
1338 return int(devicePixelRatioFScale());
1339 default:
1340 qWarning("QOpenGLWidget::metric(): unknown metric %d", metric);
1341 return 0;
1342 }
1343}
1344
1345/*!
1346 \reimp
1347*/
1348QPaintDevice *QOpenGLWidget::redirected(QPoint *p) const
1349{
1350 Q_D(const QOpenGLWidget);
1351 if (d->inBackingStorePaint)
1352 return QWidget::redirected(p);
1353
1354 return d->paintDevice;
1355}
1356
1357/*!
1358 \reimp
1359*/
1360QPaintEngine *QOpenGLWidget::paintEngine() const
1361{
1362 Q_D(const QOpenGLWidget);
1363 // QWidget needs to "punch a hole" into the backingstore. This needs the
1364 // normal paint engine and device, not the GL one. So in this mode, behave
1365 // like a normal widget.
1366 if (d->inBackingStorePaint)
1367 return QWidget::paintEngine();
1368
1369 if (!d->initialized)
1370 return nullptr;
1371
1372 return d->paintDevice->paintEngine();
1373}
1374
1375/*!
1376 \reimp
1377*/
1378bool QOpenGLWidget::event(QEvent *e)
1379{
1380 Q_D(QOpenGLWidget);
1381 switch (e->type()) {
1382 case QEvent::WindowChangeInternal:
1383 if (QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts))
1384 break;
1385 if (d->initialized)
1386 d->reset();
1387 if (isHidden())
1388 break;
1389 Q_FALLTHROUGH();
1390 case QEvent::Show: // reparenting may not lead to a resize so reinitalize on Show too
1391 if (d->initialized && window()->windowHandle()
1392 && d->context->shareContext() != QWidgetPrivate::get(window())->shareContext())
1393 {
1394 // Special case: did grabFramebuffer() for a hidden widget that then became visible.
1395 // Recreate all resources since the context now needs to share with the TLW's.
1396 if (!QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts))
1397 d->reset();
1398 }
1399 if (!d->initialized && !size().isEmpty() && window()->windowHandle()) {
1400 d->initialize();
1401 if (d->initialized)
1402 d->recreateFbo();
1403 }
1404 break;
1405 case QEvent::ScreenChangeInternal:
1406 if (d->initialized && d->paintDevice->devicePixelRatio() != devicePixelRatio())
1407 d->recreateFbo();
1408 break;
1409 default:
1410 break;
1411 }
1412 return QWidget::event(e);
1413}
1414
1415Q_CONSTRUCTOR_FUNCTION(qt_registerDefaultPlatformBackingStoreOpenGLSupport);
1416
1417QT_END_NAMESPACE
1418
1419#include "moc_qopenglwidget.cpp"
1420