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 \note Displaying a QOpenGLWidget requires an alpha channel in the associated
405 top-level window's backing store due to the way composition with other
406 QWidget-based content works. If there is no alpha channel, the content
407 rendered by the QOpenGLWidget will not be visible. This can become
408 particularly relevant on Linux/X11 in remote display setups (such as, with
409 Xvnc), when using a color depth lower than 24. For example, a color depth of
410 16 will typically map to using a backing store image with the format
411 QImage::Format_RGB16 (RGB565), leaving no room for an alpha
412 channel. Therefore, if experiencing problems with getting the contents of a
413 QOpenGLWidget composited correctly with other the widgets in the window, make
414 sure the server (such as, vncserver) is configured with a 24 or 32 bit depth
415 instead of 16.
416
417 \section1 Alternatives
418
419 Adding a QOpenGLWidget into a window turns on OpenGL-based
420 compositing for the entire window. In some special cases this may
421 not be ideal, and the old QGLWidget-style behavior with a separate,
422 native child window is desired. Desktop applications that understand
423 the limitations of this approach (for example when it comes to
424 overlaps, transparency, scroll views and MDI areas), can use
425 QOpenGLWindow with QWidget::createWindowContainer(). This is a
426 modern alternative to QGLWidget and is faster than QOpenGLWidget due
427 to the lack of the additional composition step. It is strongly
428 recommended to limit the usage of this approach to cases where there
429 is no other choice. Note that this option is not suitable for most
430 embedded and mobile platforms, and it is known to have issues on
431 certain desktop platforms (e.g. \macos) too. The stable,
432 cross-platform solution is always QOpenGLWidget.
433
434 \e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other
435 countries.}
436
437 \sa QOpenGLFunctions, QOpenGLWindow, Qt::AA_ShareOpenGLContexts, UpdateBehavior
438*/
439
440/*!
441 \fn void QOpenGLWidget::aboutToCompose()
442
443 This signal is emitted when the widget's top-level window is about to begin
444 composing the textures of its QOpenGLWidget children and the other widgets.
445*/
446
447/*!
448 \fn void QOpenGLWidget::frameSwapped()
449
450 This signal is emitted after the widget's top-level window has finished
451 composition and returned from its potentially blocking
452 QOpenGLContext::swapBuffers() call.
453*/
454
455/*!
456 \fn void QOpenGLWidget::aboutToResize()
457
458 This signal is emitted when the widget's size is changed and therefore the
459 framebuffer object is going to be recreated.
460*/
461
462/*!
463 \fn void QOpenGLWidget::resized()
464
465 This signal is emitted right after the framebuffer object has been recreated
466 due to resizing the widget.
467*/
468
469/*!
470 \enum QOpenGLWidget::UpdateBehavior
471 \since 5.5
472
473 This enum describes the update semantics of QOpenGLWidget.
474
475 \value NoPartialUpdate QOpenGLWidget will discard the
476 contents of the color buffer and the ancillary buffers after the
477 QOpenGLWidget is rendered to screen. This is the same behavior that can be
478 expected by calling QOpenGLContext::swapBuffers with a default opengl
479 enabled QWindow as the argument. NoPartialUpdate can have some performance
480 benefits on certain hardware architectures common in the mobile and
481 embedded space when a framebuffer object is used as the rendering target.
482 The framebuffer object is invalidated between frames with
483 glDiscardFramebufferEXT if supported or a glClear. Please see the
484 documentation of EXT_discard_framebuffer for more information:
485 https://www.khronos.org/registry/gles/extensions/EXT/EXT_discard_framebuffer.txt
486
487 \value PartialUpdate The framebuffer objects color buffer and ancillary
488 buffers are not invalidated between frames.
489
490 \sa updateBehavior(), setUpdateBehavior()
491*/
492
493class QOpenGLWidgetPaintDevicePrivate : public QOpenGLPaintDevicePrivate
494{
495public:
496 explicit QOpenGLWidgetPaintDevicePrivate(QOpenGLWidget *widget)
497 : QOpenGLPaintDevicePrivate(QSize()),
498 w(widget) { }
499
500 void beginPaint() override;
501 void endPaint() override;
502
503 QOpenGLWidget *w;
504};
505
506class QOpenGLWidgetPaintDevice : public QOpenGLPaintDevice
507{
508public:
509 explicit QOpenGLWidgetPaintDevice(QOpenGLWidget *widget)
510 : QOpenGLPaintDevice(*new QOpenGLWidgetPaintDevicePrivate(widget)) { }
511 void ensureActiveTarget() override;
512};
513
514class QOpenGLWidgetPrivate : public QWidgetPrivate
515{
516 Q_DECLARE_PUBLIC(QOpenGLWidget)
517public:
518 QOpenGLWidgetPrivate() = default;
519
520 void reset();
521 void recreateFbo();
522
523 GLuint textureId() const override;
524 QPlatformTextureList::Flags textureListFlags() override;
525
526 void initialize();
527 void invokeUserPaint();
528 void render();
529
530 void invalidateFbo();
531
532 QImage grabFramebuffer() override;
533 void beginBackingStorePainting() override { inBackingStorePaint = true; }
534 void endBackingStorePainting() override { inBackingStorePaint = false; }
535 void beginCompose() override;
536 void endCompose() override;
537 void initializeViewportFramebuffer() override;
538 void resizeViewportFramebuffer() override;
539 void resolveSamples() override;
540
541 QOpenGLContext *context = nullptr;
542 QOpenGLFramebufferObject *fbo = nullptr;
543 QOpenGLFramebufferObject *resolvedFbo = nullptr;
544 QOffscreenSurface *surface = nullptr;
545 QOpenGLPaintDevice *paintDevice = nullptr;
546 int requestedSamples = 0;
547 GLenum textureFormat = 0;
548 QSurfaceFormat requestedFormat = QSurfaceFormat::defaultFormat();
549 QOpenGLWidget::UpdateBehavior updateBehavior = QOpenGLWidget::NoPartialUpdate;
550 bool initialized = false;
551 bool fakeHidden = false;
552 bool inBackingStorePaint = false;
553 bool hasBeenComposed = false;
554 bool flushPending = false;
555 bool inPaintGL = false;
556};
557
558void QOpenGLWidgetPaintDevicePrivate::beginPaint()
559{
560 // NB! autoFillBackground is and must be false by default. Otherwise we would clear on
561 // every QPainter begin() which is not desirable. This is only for legacy use cases,
562 // like using QOpenGLWidget as the viewport of a graphics view, that expect clearing
563 // with the palette's background color.
564 if (w->autoFillBackground()) {
565 QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
566 if (w->format().hasAlpha()) {
567 f->glClearColor(0, 0, 0, 0);
568 } else {
569 QColor c = w->palette().brush(w->backgroundRole()).color();
570 float alpha = c.alphaF();
571 f->glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha);
572 }
573 f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
574 }
575}
576
577void QOpenGLWidgetPaintDevicePrivate::endPaint()
578{
579 QOpenGLWidgetPrivate *wd = static_cast<QOpenGLWidgetPrivate *>(QWidgetPrivate::get(w));
580 if (!wd->initialized)
581 return;
582
583 if (!wd->inPaintGL)
584 QOpenGLContextPrivate::get(wd->context)->defaultFboRedirect = 0;
585}
586
587void QOpenGLWidgetPaintDevice::ensureActiveTarget()
588{
589 QOpenGLWidgetPaintDevicePrivate *d = static_cast<QOpenGLWidgetPaintDevicePrivate *>(d_ptr.data());
590 QOpenGLWidgetPrivate *wd = static_cast<QOpenGLWidgetPrivate *>(QWidgetPrivate::get(d->w));
591 if (!wd->initialized)
592 return;
593
594 if (QOpenGLContext::currentContext() != wd->context)
595 d->w->makeCurrent();
596 else
597 wd->fbo->bind();
598
599 if (!wd->inPaintGL)
600 QOpenGLContextPrivate::get(wd->context)->defaultFboRedirect = wd->fbo->handle();
601
602 // When used as a viewport, drawing is done via opening a QPainter on the widget
603 // without going through paintEvent(). We will have to make sure a glFlush() is done
604 // before the texture is accessed also in this case.
605 wd->flushPending = true;
606}
607
608GLuint QOpenGLWidgetPrivate::textureId() const
609{
610 return resolvedFbo ? resolvedFbo->texture() : (fbo ? fbo->texture() : 0);
611}
612
613#ifndef GL_SRGB
614#define GL_SRGB 0x8C40
615#endif
616#ifndef GL_SRGB8
617#define GL_SRGB8 0x8C41
618#endif
619#ifndef GL_SRGB_ALPHA
620#define GL_SRGB_ALPHA 0x8C42
621#endif
622#ifndef GL_SRGB8_ALPHA8
623#define GL_SRGB8_ALPHA8 0x8C43
624#endif
625
626QPlatformTextureList::Flags QOpenGLWidgetPrivate::textureListFlags()
627{
628 QPlatformTextureList::Flags flags = QWidgetPrivate::textureListFlags();
629 switch (textureFormat) {
630 case GL_SRGB:
631 case GL_SRGB8:
632 case GL_SRGB_ALPHA:
633 case GL_SRGB8_ALPHA8:
634 flags |= QPlatformTextureList::TextureIsSrgb;
635 break;
636 default:
637 break;
638 }
639 return flags;
640}
641
642void QOpenGLWidgetPrivate::reset()
643{
644 Q_Q(QOpenGLWidget);
645
646 // Destroy the OpenGL resources first. These need the context to be current.
647 if (initialized)
648 q->makeCurrent();
649
650 delete paintDevice;
651 paintDevice = nullptr;
652 delete fbo;
653 fbo = nullptr;
654 delete resolvedFbo;
655 resolvedFbo = nullptr;
656
657 if (initialized)
658 q->doneCurrent();
659
660 // Delete the context first, then the surface. Slots connected to
661 // the context's aboutToBeDestroyed() may still call makeCurrent()
662 // to perform some cleanup.
663 delete context;
664 context = nullptr;
665 delete surface;
666 surface = nullptr;
667 initialized = fakeHidden = inBackingStorePaint = false;
668}
669
670void QOpenGLWidgetPrivate::recreateFbo()
671{
672 Q_Q(QOpenGLWidget);
673
674 emit q->aboutToResize();
675
676 context->makeCurrent(surface);
677
678 delete fbo;
679 fbo = nullptr;
680 delete resolvedFbo;
681 resolvedFbo = nullptr;
682
683 int samples = requestedSamples;
684 QOpenGLExtensions *extfuncs = static_cast<QOpenGLExtensions *>(context->functions());
685 if (!extfuncs->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
686 samples = 0;
687
688 QOpenGLFramebufferObjectFormat format;
689 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
690 format.setSamples(samples);
691 if (textureFormat)
692 format.setInternalTextureFormat(textureFormat);
693
694 const QSize deviceSize = q->size() * q->devicePixelRatio();
695 fbo = new QOpenGLFramebufferObject(deviceSize, format);
696 if (samples > 0)
697 resolvedFbo = new QOpenGLFramebufferObject(deviceSize);
698
699 textureFormat = fbo->format().internalTextureFormat();
700
701 fbo->bind();
702 context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
703 flushPending = true; // Make sure the FBO is initialized before use
704
705 paintDevice->setSize(deviceSize);
706 paintDevice->setDevicePixelRatio(q->devicePixelRatio());
707
708 emit q->resized();
709}
710
711void QOpenGLWidgetPrivate::beginCompose()
712{
713 Q_Q(QOpenGLWidget);
714 if (flushPending) {
715 flushPending = false;
716 q->makeCurrent();
717 static_cast<QOpenGLExtensions *>(context->functions())->flushShared();
718 }
719 hasBeenComposed = true;
720 emit q->aboutToCompose();
721}
722
723void QOpenGLWidgetPrivate::endCompose()
724{
725 Q_Q(QOpenGLWidget);
726 emit q->frameSwapped();
727}
728
729void QOpenGLWidgetPrivate::initialize()
730{
731 Q_Q(QOpenGLWidget);
732 if (initialized)
733 return;
734
735 // If no global shared context get our toplevel's context with which we
736 // will share in order to make the texture usable by the underlying window's backingstore.
737 QWidget *tlw = q->window();
738 QOpenGLContext *shareContext = qt_gl_global_share_context();
739 if (!shareContext)
740 shareContext = get(tlw)->shareContext();
741 // If shareContext is null, showing content on-screen will not work.
742 // However, offscreen rendering and grabFramebuffer() will stay fully functional.
743
744 // Do not include the sample count. Requesting a multisampled context is not necessary
745 // since we render into an FBO, never to an actual surface. What's more, attempting to
746 // create a pbuffer with a multisampled config crashes certain implementations. Just
747 // avoid the entire hassle, the result is the same.
748 requestedSamples = requestedFormat.samples();
749 requestedFormat.setSamples(0);
750
751 QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext);
752 ctx->setFormat(requestedFormat);
753 if (shareContext) {
754 ctx->setShareContext(shareContext);
755 ctx->setScreen(shareContext->screen());
756 }
757 if (Q_UNLIKELY(!ctx->create())) {
758 qWarning("QOpenGLWidget: Failed to create context");
759 return;
760 }
761
762 // Propagate settings that make sense only for the tlw. Note that this only
763 // makes sense for properties that get picked up even after the native
764 // window is created.
765 if (tlw->windowHandle()) {
766 QSurfaceFormat tlwFormat = tlw->windowHandle()->format();
767 if (requestedFormat.swapInterval() != tlwFormat.swapInterval()) {
768 // Most platforms will pick up the changed swap interval on the next
769 // makeCurrent or swapBuffers.
770 tlwFormat.setSwapInterval(requestedFormat.swapInterval());
771 tlw->windowHandle()->setFormat(tlwFormat);
772 }
773 if (requestedFormat.swapBehavior() != tlwFormat.swapBehavior()) {
774 tlwFormat.setSwapBehavior(requestedFormat.swapBehavior());
775 tlw->windowHandle()->setFormat(tlwFormat);
776 }
777 }
778
779 // The top-level window's surface is not good enough since it causes way too
780 // much trouble with regards to the QSurfaceFormat for example. So just like
781 // in QQuickWidget, use a dedicated QOffscreenSurface.
782 surface = new QOffscreenSurface;
783 surface->setFormat(ctx->format());
784 surface->setScreen(ctx->screen());
785 surface->create();
786
787 if (Q_UNLIKELY(!ctx->makeCurrent(surface))) {
788 qWarning("QOpenGLWidget: Failed to make context current");
789 return;
790 }
791
792 paintDevice = new QOpenGLWidgetPaintDevice(q);
793 paintDevice->setSize(q->size() * q->devicePixelRatio());
794 paintDevice->setDevicePixelRatio(q->devicePixelRatio());
795
796 context = ctx.take();
797 initialized = true;
798
799 q->initializeGL();
800}
801
802void QOpenGLWidgetPrivate::resolveSamples()
803{
804 Q_Q(QOpenGLWidget);
805 if (resolvedFbo) {
806 q->makeCurrent();
807 QRect rect(QPoint(0, 0), fbo->size());
808 QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
809 flushPending = true;
810 }
811}
812
813void QOpenGLWidgetPrivate::invokeUserPaint()
814{
815 Q_Q(QOpenGLWidget);
816
817 QOpenGLContext *ctx = QOpenGLContext::currentContext();
818 Q_ASSERT(ctx && fbo);
819
820 QOpenGLFunctions *f = ctx->functions();
821 QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = fbo->handle();
822
823 f->glViewport(0, 0, q->width() * q->devicePixelRatio(), q->height() * q->devicePixelRatio());
824 inPaintGL = true;
825 q->paintGL();
826 inPaintGL = false;
827 flushPending = true;
828
829 QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = 0;
830}
831
832void QOpenGLWidgetPrivate::render()
833{
834 Q_Q(QOpenGLWidget);
835
836 if (fakeHidden || !initialized)
837 return;
838
839 q->makeCurrent();
840
841 if (updateBehavior == QOpenGLWidget::NoPartialUpdate && hasBeenComposed) {
842 invalidateFbo();
843 hasBeenComposed = false;
844 }
845
846 invokeUserPaint();
847}
848
849void QOpenGLWidgetPrivate::invalidateFbo()
850{
851 QOpenGLExtensions *f = static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions());
852 if (f->hasOpenGLExtension(QOpenGLExtensions::DiscardFramebuffer)) {
853 const int gl_color_attachment0 = 0x8CE0; // GL_COLOR_ATTACHMENT0
854 const int gl_depth_attachment = 0x8D00; // GL_DEPTH_ATTACHMENT
855 const int gl_stencil_attachment = 0x8D20; // GL_STENCIL_ATTACHMENT
856#ifdef Q_OS_WASM
857 // webgl does not allow separate depth and stencil attachments
858 // QTBUG-69913
859 const int gl_depth_stencil_attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT
860
861 const GLenum attachments[] = {
862 gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment, gl_depth_stencil_attachment
863 };
864#else
865 const GLenum attachments[] = {
866 gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment
867 };
868#endif
869 f->glDiscardFramebufferEXT(GL_FRAMEBUFFER, sizeof attachments / sizeof *attachments, attachments);
870 } else {
871 f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
872 }
873}
874
875QImage QOpenGLWidgetPrivate::grabFramebuffer()
876{
877 Q_Q(QOpenGLWidget);
878
879 initialize();
880 if (!initialized)
881 return QImage();
882
883 if (!fbo) // could be completely offscreen, without ever getting a resize event
884 recreateFbo();
885
886 if (!inPaintGL)
887 render();
888
889 if (resolvedFbo) {
890 resolveSamples();
891 resolvedFbo->bind();
892 } else {
893 q->makeCurrent();
894 }
895
896 const bool hasAlpha = q->format().hasAlpha();
897 QImage res = qt_gl_read_framebuffer(q->size() * q->devicePixelRatio(), hasAlpha, hasAlpha);
898 res.setDevicePixelRatio(q->devicePixelRatio());
899
900 // While we give no guarantees of what is going to be left bound, prefer the
901 // multisample fbo instead of the resolved one. Clients may continue to
902 // render straight after calling this function.
903 if (resolvedFbo)
904 q->makeCurrent();
905
906 return res;
907}
908
909void QOpenGLWidgetPrivate::initializeViewportFramebuffer()
910{
911 Q_Q(QOpenGLWidget);
912 // Legacy behavior for compatibility with QGLWidget when used as a graphics view
913 // viewport: enable clearing on each painter begin.
914 q->setAutoFillBackground(true);
915}
916
917void QOpenGLWidgetPrivate::resizeViewportFramebuffer()
918{
919 Q_Q(QOpenGLWidget);
920 if (!initialized)
921 return;
922
923 if (!fbo || q->size() * q->devicePixelRatio() != fbo->size()) {
924 recreateFbo();
925 q->update();
926 }
927}
928
929/*!
930 Constructs a widget which is a child of \a parent, with widget flags set to \a f.
931 */
932QOpenGLWidget::QOpenGLWidget(QWidget *parent, Qt::WindowFlags f)
933 : QWidget(*(new QOpenGLWidgetPrivate), parent, f)
934{
935 Q_D(QOpenGLWidget);
936 if (Q_UNLIKELY(!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface)))
937 qWarning("QOpenGLWidget is not supported on this platform.");
938 else
939 d->setRenderToTexture();
940}
941
942/*!
943 Destroys the QOpenGLWidget instance, freeing its resources.
944
945 The QOpenGLWidget's context is made current in the destructor, allowing for
946 safe destruction of any child object that may need to release OpenGL
947 resources belonging to the context provided by this widget.
948
949 \warning if you have objects wrapping OpenGL resources (such as
950 QOpenGLBuffer, QOpenGLShaderProgram, etc.) as members of a OpenGLWidget
951 subclass, you may need to add a call to makeCurrent() in that subclass'
952 destructor as well. Due to the rules of C++ object destruction, those objects
953 will be destroyed \e{before} calling this function (but after that the
954 destructor of the subclass has run), therefore making the OpenGL context
955 current in this function happens too late for their safe disposal.
956
957 \sa makeCurrent
958*/
959QOpenGLWidget::~QOpenGLWidget()
960{
961 Q_D(QOpenGLWidget);
962 d->reset();
963}
964
965/*!
966 Sets this widget's update behavior to \a updateBehavior.
967 \since 5.5
968*/
969void QOpenGLWidget::setUpdateBehavior(UpdateBehavior updateBehavior)
970{
971 Q_D(QOpenGLWidget);
972 d->updateBehavior = updateBehavior;
973}
974
975/*!
976 \return the update behavior of the widget.
977 \since 5.5
978*/
979QOpenGLWidget::UpdateBehavior QOpenGLWidget::updateBehavior() const
980{
981 Q_D(const QOpenGLWidget);
982 return d->updateBehavior;
983}
984
985/*!
986 Sets the requested surface \a format.
987
988 When the format is not explicitly set via this function, the format returned by
989 QSurfaceFormat::defaultFormat() will be used. This means that when having multiple
990 OpenGL widgets, individual calls to this function can be replaced by one single call to
991 QSurfaceFormat::setDefaultFormat() before creating the first widget.
992
993 \note Requesting an alpha buffer via this function will not lead to the
994 desired results when the intention is to make other widgets beneath visible.
995 Instead, use Qt::WA_AlwaysStackOnTop to enable semi-transparent QOpenGLWidget
996 instances with other widgets visible underneath. Keep in mind however that
997 this breaks the stacking order, so it will no longer be possible to have
998 other widgets on top of the QOpenGLWidget.
999
1000 \sa format(), Qt::WA_AlwaysStackOnTop, QSurfaceFormat::setDefaultFormat()
1001 */
1002void QOpenGLWidget::setFormat(const QSurfaceFormat &format)
1003{
1004 Q_D(QOpenGLWidget);
1005 if (Q_UNLIKELY(d->initialized)) {
1006 qWarning("QOpenGLWidget: Already initialized, setting the format has no effect");
1007 return;
1008 }
1009
1010 d->requestedFormat = format;
1011}
1012
1013/*!
1014 Returns the context and surface format used by this widget and its toplevel
1015 window.
1016
1017 After the widget and its toplevel have both been created, resized and shown,
1018 this function will return the actual format of the context. This may differ
1019 from the requested format if the request could not be fulfilled by the
1020 platform. It is also possible to get larger color buffer sizes than
1021 requested.
1022
1023 When the widget's window and the related OpenGL resources are not yet
1024 initialized, the return value is the format that has been set via
1025 setFormat().
1026
1027 \sa setFormat(), context()
1028 */
1029QSurfaceFormat QOpenGLWidget::format() const
1030{
1031 Q_D(const QOpenGLWidget);
1032 return d->initialized ? d->context->format() : d->requestedFormat;
1033}
1034
1035/*!
1036 Sets a custom internal texture format of \a texFormat.
1037
1038 When working with sRGB framebuffers, it will be necessary to specify a
1039 format like \c{GL_SRGB8_ALPHA8}. This can be achieved by calling this
1040 function.
1041
1042 \note This function has no effect if called after the widget has already
1043 been shown and thus it performed initialization.
1044
1045 \note This function will typically have to be used in combination with a
1046 QSurfaceFormat::setDefaultFormat() call that sets the color space to
1047 QSurfaceFormat::sRGBColorSpace.
1048
1049 \since 5.10
1050 */
1051void QOpenGLWidget::setTextureFormat(GLenum texFormat)
1052{
1053 Q_D(QOpenGLWidget);
1054 if (Q_UNLIKELY(d->initialized)) {
1055 qWarning("QOpenGLWidget: Already initialized, setting the internal texture format has no effect");
1056 return;
1057 }
1058
1059 d->textureFormat = texFormat;
1060}
1061
1062/*!
1063 \return the active internal texture format if the widget has already
1064 initialized, the requested format if one was set but the widget has not yet
1065 been made visible, or \nullptr if setTextureFormat() was not called and the
1066 widget has not yet been made visible.
1067
1068 \since 5.10
1069 */
1070GLenum QOpenGLWidget::textureFormat() const
1071{
1072 Q_D(const QOpenGLWidget);
1073 return d->textureFormat;
1074}
1075
1076/*!
1077 \return \e true if the widget and OpenGL resources, like the context, have
1078 been successfully initialized. Note that the return value is always false
1079 until the widget is shown.
1080*/
1081bool QOpenGLWidget::isValid() const
1082{
1083 Q_D(const QOpenGLWidget);
1084 return d->initialized && d->context->isValid();
1085}
1086
1087/*!
1088 Prepares for rendering OpenGL content for this widget by making the
1089 corresponding context current and binding the framebuffer object in that
1090 context.
1091
1092 It is not necessary to call this function in most cases, because it
1093 is called automatically before invoking paintGL().
1094
1095 \sa context(), paintGL(), doneCurrent()
1096 */
1097void QOpenGLWidget::makeCurrent()
1098{
1099 Q_D(QOpenGLWidget);
1100 if (!d->initialized)
1101 return;
1102
1103 d->context->makeCurrent(d->surface);
1104
1105 if (d->fbo) // there may not be one if we are in reset()
1106 d->fbo->bind();
1107}
1108
1109/*!
1110 Releases the context.
1111
1112 It is not necessary to call this function in most cases, since the
1113 widget will make sure the context is bound and released properly
1114 when invoking paintGL().
1115 */
1116void QOpenGLWidget::doneCurrent()
1117{
1118 Q_D(QOpenGLWidget);
1119 if (!d->initialized)
1120 return;
1121
1122 d->context->doneCurrent();
1123}
1124
1125/*!
1126 \return The QOpenGLContext used by this widget or \c 0 if not yet initialized.
1127
1128 \note The context and the framebuffer object used by the widget changes when
1129 reparenting the widget via setParent().
1130
1131 \sa QOpenGLContext::setShareContext(), defaultFramebufferObject()
1132 */
1133QOpenGLContext *QOpenGLWidget::context() const
1134{
1135 Q_D(const QOpenGLWidget);
1136 return d->context;
1137}
1138
1139/*!
1140 \return The framebuffer object handle or \c 0 if not yet initialized.
1141
1142 \note The framebuffer object belongs to the context returned by context()
1143 and may not be accessible from other contexts.
1144
1145 \note The context and the framebuffer object used by the widget changes when
1146 reparenting the widget via setParent(). In addition, the framebuffer object
1147 changes on each resize.
1148
1149 \sa context()
1150 */
1151GLuint QOpenGLWidget::defaultFramebufferObject() const
1152{
1153 Q_D(const QOpenGLWidget);
1154 return d->fbo ? d->fbo->handle() : 0;
1155}
1156
1157/*!
1158 This virtual function is called once before the first call to
1159 paintGL() or resizeGL(). Reimplement it in a subclass.
1160
1161 This function should set up any required OpenGL resources and state.
1162
1163 There is no need to call makeCurrent() because this has already been
1164 done when this function is called. Note however that the framebuffer
1165 is not yet available at this stage, so avoid issuing draw calls from
1166 here. Defer such calls to paintGL() instead.
1167
1168 \sa paintGL(), resizeGL()
1169*/
1170void QOpenGLWidget::initializeGL()
1171{
1172}
1173
1174/*!
1175 This virtual function is called whenever the widget has been
1176 resized. Reimplement it in a subclass. The new size is passed in
1177 \a w and \a h.
1178
1179 There is no need to call makeCurrent() because this has already been
1180 done when this function is called. Additionally, the framebuffer is
1181 also bound.
1182
1183 \sa initializeGL(), paintGL()
1184*/
1185void QOpenGLWidget::resizeGL(int w, int h)
1186{
1187 Q_UNUSED(w);
1188 Q_UNUSED(h);
1189}
1190
1191/*!
1192 This virtual function is called whenever the widget needs to be
1193 painted. Reimplement it in a subclass.
1194
1195 There is no need to call makeCurrent() because this has already
1196 been done when this function is called.
1197
1198 Before invoking this function, the context and the framebuffer are
1199 bound, and the viewport is set up by a call to glViewport(). No
1200 other state is set and no clearing or drawing is performed by the
1201 framework.
1202
1203 \sa initializeGL(), resizeGL()
1204*/
1205void QOpenGLWidget::paintGL()
1206{
1207}
1208
1209/*!
1210 Handles resize events that are passed in the \a e event parameter.
1211 Calls the virtual function resizeGL().
1212
1213 \note Avoid overriding this function in derived classes. If that is not
1214 feasible, make sure that QOpenGLWidget's implementation is invoked
1215 too. Otherwise the underlying framebuffer object and related resources will
1216 not get resized properly and will lead to incorrect rendering.
1217*/
1218void QOpenGLWidget::resizeEvent(QResizeEvent *e)
1219{
1220 Q_D(QOpenGLWidget);
1221
1222 if (e->size().isEmpty()) {
1223 d->fakeHidden = true;
1224 return;
1225 }
1226 d->fakeHidden = false;
1227
1228 d->initialize();
1229 if (!d->initialized)
1230 return;
1231
1232 d->recreateFbo();
1233 resizeGL(width(), height());
1234 d->sendPaintEvent(QRect(QPoint(0, 0), size()));
1235}
1236
1237/*!
1238 Handles paint events.
1239
1240 Calling QWidget::update() will lead to sending a paint event \a e,
1241 and thus invoking this function. (NB this is asynchronous and will
1242 happen at some point after returning from update()). This function
1243 will then, after some preparation, call the virtual paintGL() to
1244 update the contents of the QOpenGLWidget's framebuffer. The widget's
1245 top-level window will then composite the framebuffer's texture with
1246 the rest of the window.
1247*/
1248void QOpenGLWidget::paintEvent(QPaintEvent *e)
1249{
1250 Q_UNUSED(e);
1251 Q_D(QOpenGLWidget);
1252 if (!d->initialized)
1253 return;
1254
1255 if (updatesEnabled())
1256 d->render();
1257}
1258
1259/*!
1260 Renders and returns a 32-bit RGB image of the framebuffer.
1261
1262 \note This is a potentially expensive operation because it relies on glReadPixels()
1263 to read back the pixels. This may be slow and can stall the GPU pipeline.
1264*/
1265QImage QOpenGLWidget::grabFramebuffer()
1266{
1267 Q_D(QOpenGLWidget);
1268 return d->grabFramebuffer();
1269}
1270
1271/*!
1272 \reimp
1273*/
1274int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const
1275{
1276 Q_D(const QOpenGLWidget);
1277 if (d->inBackingStorePaint)
1278 return QWidget::metric(metric);
1279
1280 auto window = d->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel);
1281 QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen();
1282
1283 const float dpmx = qt_defaultDpiX() * 100. / 2.54;
1284 const float dpmy = qt_defaultDpiY() * 100. / 2.54;
1285
1286 switch (metric) {
1287 case PdmWidth:
1288 return width();
1289 case PdmHeight:
1290 return height();
1291 case PdmDepth:
1292 return 32;
1293 case PdmWidthMM:
1294 if (screen)
1295 return width() * screen->physicalSize().width() / screen->geometry().width();
1296 else
1297 return width() * 1000 / dpmx;
1298 case PdmHeightMM:
1299 if (screen)
1300 return height() * screen->physicalSize().height() / screen->geometry().height();
1301 else
1302 return height() * 1000 / dpmy;
1303 case PdmNumColors:
1304 return 0;
1305 case PdmDpiX:
1306 if (screen)
1307 return qRound(screen->logicalDotsPerInchX());
1308 else
1309 return qRound(dpmx * 0.0254);
1310 case PdmDpiY:
1311 if (screen)
1312 return qRound(screen->logicalDotsPerInchY());
1313 else
1314 return qRound(dpmy * 0.0254);
1315 case PdmPhysicalDpiX:
1316 if (screen)
1317 return qRound(screen->physicalDotsPerInchX());
1318 else
1319 return qRound(dpmx * 0.0254);
1320 case PdmPhysicalDpiY:
1321 if (screen)
1322 return qRound(screen->physicalDotsPerInchY());
1323 else
1324 return qRound(dpmy * 0.0254);
1325 case PdmDevicePixelRatio:
1326 if (window)
1327 return int(window->devicePixelRatio());
1328 else
1329 return 1.0;
1330 case PdmDevicePixelRatioScaled:
1331 if (window)
1332 return int(window->devicePixelRatio() * devicePixelRatioFScale());
1333 else
1334 return int(devicePixelRatioFScale());
1335 default:
1336 qWarning("QOpenGLWidget::metric(): unknown metric %d", metric);
1337 return 0;
1338 }
1339}
1340
1341/*!
1342 \reimp
1343*/
1344QPaintDevice *QOpenGLWidget::redirected(QPoint *p) const
1345{
1346 Q_D(const QOpenGLWidget);
1347 if (d->inBackingStorePaint)
1348 return QWidget::redirected(p);
1349
1350 return d->paintDevice;
1351}
1352
1353/*!
1354 \reimp
1355*/
1356QPaintEngine *QOpenGLWidget::paintEngine() const
1357{
1358 Q_D(const QOpenGLWidget);
1359 // QWidget needs to "punch a hole" into the backingstore. This needs the
1360 // normal paint engine and device, not the GL one. So in this mode, behave
1361 // like a normal widget.
1362 if (d->inBackingStorePaint)
1363 return QWidget::paintEngine();
1364
1365 if (!d->initialized)
1366 return nullptr;
1367
1368 return d->paintDevice->paintEngine();
1369}
1370
1371/*!
1372 \reimp
1373*/
1374bool QOpenGLWidget::event(QEvent *e)
1375{
1376 Q_D(QOpenGLWidget);
1377 switch (e->type()) {
1378 case QEvent::WindowChangeInternal:
1379 if (QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts))
1380 break;
1381 if (d->initialized)
1382 d->reset();
1383 if (isHidden())
1384 break;
1385 Q_FALLTHROUGH();
1386 case QEvent::Show: // reparenting may not lead to a resize so reinitalize on Show too
1387 if (d->initialized && window()->windowHandle()
1388 && d->context->shareContext() != QWidgetPrivate::get(window())->shareContext())
1389 {
1390 // Special case: did grabFramebuffer() for a hidden widget that then became visible.
1391 // Recreate all resources since the context now needs to share with the TLW's.
1392 if (!QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts))
1393 d->reset();
1394 }
1395 if (!d->initialized && !size().isEmpty() && window()->windowHandle()) {
1396 d->initialize();
1397 if (d->initialized) {
1398 d->recreateFbo();
1399 // QTBUG-89812: generate a paint event, like resize would do,
1400 // otherwise a QOpenGLWidget in a QDockWidget may not show the
1401 // content upon (un)docking.
1402 d->sendPaintEvent(QRect(QPoint(0, 0), size()));
1403 }
1404 }
1405 break;
1406 case QEvent::ScreenChangeInternal:
1407 if (d->initialized && d->paintDevice->devicePixelRatio() != devicePixelRatio())
1408 d->recreateFbo();
1409 break;
1410 default:
1411 break;
1412 }
1413 return QWidget::event(e);
1414}
1415
1416Q_CONSTRUCTOR_FUNCTION(qt_registerDefaultPlatformBackingStoreOpenGLSupport);
1417
1418QT_END_NAMESPACE
1419
1420#include "moc_qopenglwidget.cpp"
1421