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