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 QtGui 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 "qopenglwindow.h"
41 #include "qpaintdevicewindow_p.h"
42 #include <QtGui/QOpenGLFramebufferObject>
43 #include <QtGui/QOpenGLPaintDevice>
44 #include <QtGui/QOpenGLFunctions>
45 #include <QtGui/QOpenGLTextureBlitter>
46 #include <QtGui/private/qopenglextensions_p.h>
47 #include <QtGui/private/qopenglcontext_p.h>
48 #include <QtGui/QMatrix4x4>
49 #include <QtGui/QOffscreenSurface>
50 
51 QT_BEGIN_NAMESPACE
52 
53 /*!
54   \class QOpenGLWindow
55   \inmodule QtGui
56   \since 5.4
57   \brief The QOpenGLWindow class is a convenience subclass of QWindow to perform OpenGL painting.
58 
59   QOpenGLWindow is an enhanced QWindow that allows easily creating windows that
60   perform OpenGL rendering using an API that is compatible with QOpenGLWidget
61   and is similar to the legacy QGLWidget. Unlike QOpenGLWidget, QOpenGLWindow
62   has no dependency on the widgets module and offers better performance.
63 
64   A typical application will subclass QOpenGLWindow and reimplement the following
65   virtual functions:
66 
67   \list
68 
69   \li initializeGL() to perform OpenGL resource initialization
70 
71   \li resizeGL() to set up the transformation matrices and other window size dependent resources
72 
73   \li paintGL() to issue OpenGL commands or draw using QPainter
74 
75   \endlist
76 
77   To schedule a repaint, call the update() function. Note that this will not
78   immediately result in a call to paintGL(). Calling update() multiple times in
79   a row will not change the behavior in any way.
80 
81   This is a slot so it can be connected to a \l QTimer::timeout() signal to
82   perform animation. Note however that in the modern OpenGL world it is a much
83   better choice to rely on synchronization to the vertical refresh rate of the
84   display. See \l{QSurfaceFormat::setSwapInterval()}{setSwapInterval()} on a
85   description of the swap interval. With a swap interval of \c 1, which is the
86   case on most systems by default, the
87   \l{QOpenGLContext::swapBuffers()}{swapBuffers()} call, that is executed
88   internally by QOpenGLWindow after each repaint, will block and wait for
89   vsync. This means that whenever the swap is done, an update can be scheduled
90   again by calling update(), without relying on timers.
91 
92   To request a specific configuration for the context, use setFormat()
93   like for any other QWindow. This allows, among others, requesting a
94   given OpenGL version and profile, or enabling depth and stencil
95   buffers.
96 
97   Unlike QWindow, QOpenGLWindow allows opening a painter on itself and perform
98   QPainter-based drawing.
99 
100   QOpenGLWindow supports multiple update behaviors. The default,
101   \c NoPartialUpdate is equivalent to a regular, OpenGL-based QWindow or the
102   legacy QGLWidget. In contrast, \c PartialUpdateBlit and \c PartialUpdateBlend are
103   more in line with QOpenGLWidget's way of working, where there is always an
104   extra, dedicated framebuffer object present. These modes allow, by
105   sacrificing some performance, redrawing only a smaller area on each paint and
106   having the rest of the content preserved from of the previous frame. This is
107   useful for applications than render incrementally using QPainter, because
108   this way they do not have to redraw the entire window content on each
109   paintGL() call.
110 
111   Similarly to QOpenGLWidget, QOpenGLWindow supports the Qt::AA_ShareOpenGLContexts
112   attribute. When enabled, the OpenGL contexts of all QOpenGLWindow instances will share
113   with each other. This allows accessing each other's shareable OpenGL resources.
114 
115   For more information on graphics in Qt, see \l {Graphics}.
116  */
117 
118 /*!
119   \enum QOpenGLWindow::UpdateBehavior
120 
121   This enum describes the update strategy of the QOpenGLWindow.
122 
123   \value NoPartialUpdate Indicates that the entire window surface will
124   redrawn on each update and so no additional framebuffers are needed.
125   This is the setting used in most cases and is equivalent to how drawing
126   directly via QWindow would function.
127 
128   \value PartialUpdateBlit Indicates that the drawing performed in paintGL()
129   does not cover the entire window. In this case an extra framebuffer object
130   is created under the hood, and rendering performed in paintGL() will target
131   this framebuffer. This framebuffer is then blitted onto the window surface's
132   default framebuffer after each paint. This allows having QPainter-based drawing
133   code in paintGL() which only repaints a smaller area at a time, because, unlike
134   NoPartialUpdate, the previous content is preserved.
135 
136   \value PartialUpdateBlend Similar to PartialUpdateBlit, but instead of using
137   framebuffer blits, the contents of the extra framebuffer is rendered by
138   drawing a textured quad with blending enabled. This, unlike PartialUpdateBlit,
139   allows alpha blended content and works even when the glBlitFramebuffer is
140   not available. Performance-wise this setting is likely to be somewhat slower
141   than PartialUpdateBlit.
142  */
143 
144 /*!
145     \fn void QOpenGLWindow::frameSwapped()
146 
147     This signal is emitted after the potentially blocking
148     \l{QOpenGLContext::swapBuffers()}{buffer swap} has been done. Applications
149     that wish to continuously repaint synchronized to the vertical refresh,
150     should issue an update() upon this signal. This allows for a much smoother
151     experience compared to the traditional usage of timers.
152 */
153 
154 // GLES2 builds won't have these constants with the suffixless names
155 #ifndef GL_READ_FRAMEBUFFER
156 #define GL_READ_FRAMEBUFFER 0x8CA8
157 #endif
158 #ifndef GL_DRAW_FRAMEBUFFER
159 #define GL_DRAW_FRAMEBUFFER 0x8CA9
160 #endif
161 
162 class QOpenGLWindowPaintDevice : public QOpenGLPaintDevice
163 {
164 public:
QOpenGLWindowPaintDevice(QOpenGLWindow * window)165     QOpenGLWindowPaintDevice(QOpenGLWindow *window) : m_window(window) { }
166     void ensureActiveTarget() override;
167 
168     QOpenGLWindow *m_window;
169 };
170 
171 class QOpenGLWindowPrivate : public QPaintDeviceWindowPrivate
172 {
173     Q_DECLARE_PUBLIC(QOpenGLWindow)
174 public:
QOpenGLWindowPrivate(QOpenGLContext * shareContext,QOpenGLWindow::UpdateBehavior updateBehavior)175     QOpenGLWindowPrivate(QOpenGLContext *shareContext, QOpenGLWindow::UpdateBehavior updateBehavior)
176         : updateBehavior(updateBehavior)
177         , hasFboBlit(false)
178         , shareContext(shareContext)
179     {
180         if (!shareContext)
181             this->shareContext = qt_gl_global_share_context();
182     }
183 
184     ~QOpenGLWindowPrivate();
185 
get(QOpenGLWindow * w)186     static QOpenGLWindowPrivate *get(QOpenGLWindow *w) { return w->d_func(); }
187 
188     void bindFBO();
189     void initialize();
190 
191     void beginPaint(const QRegion &region) override;
192     void endPaint() override;
193     void flush(const QRegion &region) override;
194 
195     QOpenGLWindow::UpdateBehavior updateBehavior;
196     bool hasFboBlit;
197     QScopedPointer<QOpenGLContext> context;
198     QOpenGLContext *shareContext;
199     QScopedPointer<QOpenGLFramebufferObject> fbo;
200     QScopedPointer<QOpenGLWindowPaintDevice> paintDevice;
201     QOpenGLTextureBlitter blitter;
202     QColor backgroundColor;
203     QScopedPointer<QOffscreenSurface> offscreenSurface;
204 };
205 
~QOpenGLWindowPrivate()206 QOpenGLWindowPrivate::~QOpenGLWindowPrivate()
207 {
208     Q_Q(QOpenGLWindow);
209     if (q->isValid()) {
210         q->makeCurrent(); // this works even when the platformwindow is destroyed
211         paintDevice.reset(nullptr);
212         fbo.reset(nullptr);
213         blitter.destroy();
214         q->doneCurrent();
215     }
216 }
217 
initialize()218 void QOpenGLWindowPrivate::initialize()
219 {
220     Q_Q(QOpenGLWindow);
221 
222     if (context)
223         return;
224 
225     if (!q->handle())
226         qWarning("Attempted to initialize QOpenGLWindow without a platform window");
227 
228     context.reset(new QOpenGLContext);
229     context->setShareContext(shareContext);
230     context->setFormat(q->requestedFormat());
231     if (!context->create())
232         qWarning("QOpenGLWindow::beginPaint: Failed to create context");
233     if (!context->makeCurrent(q))
234         qWarning("QOpenGLWindow::beginPaint: Failed to make context current");
235 
236     paintDevice.reset(new QOpenGLWindowPaintDevice(q));
237     if (updateBehavior == QOpenGLWindow::PartialUpdateBlit)
238         hasFboBlit = QOpenGLFramebufferObject::hasOpenGLFramebufferBlit();
239 
240     q->initializeGL();
241 }
242 
beginPaint(const QRegion & region)243 void QOpenGLWindowPrivate::beginPaint(const QRegion &region)
244 {
245     Q_UNUSED(region);
246     Q_Q(QOpenGLWindow);
247 
248     initialize();
249     context->makeCurrent(q);
250 
251     const int deviceWidth = q->width() * q->devicePixelRatio();
252     const int deviceHeight = q->height() * q->devicePixelRatio();
253     const QSize deviceSize(deviceWidth, deviceHeight);
254     if (updateBehavior > QOpenGLWindow::NoPartialUpdate) {
255         if (!fbo || fbo->size() != deviceSize) {
256             QOpenGLFramebufferObjectFormat fboFormat;
257             fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
258             const int samples = q->requestedFormat().samples();
259             if (samples > 0) {
260                 if (updateBehavior != QOpenGLWindow::PartialUpdateBlend)
261                     fboFormat.setSamples(samples);
262                 else
263                     qWarning("QOpenGLWindow: PartialUpdateBlend does not support multisampling");
264             }
265             fbo.reset(new QOpenGLFramebufferObject(deviceSize, fboFormat));
266             markWindowAsDirty();
267         }
268     } else {
269         markWindowAsDirty();
270     }
271 
272     paintDevice->setSize(QSize(deviceWidth, deviceHeight));
273     paintDevice->setDevicePixelRatio(q->devicePixelRatio());
274     context->functions()->glViewport(0, 0, deviceWidth, deviceHeight);
275 
276     context->functions()->glBindFramebuffer(GL_FRAMEBUFFER, context->defaultFramebufferObject());
277 
278     q->paintUnderGL();
279 
280     if (updateBehavior > QOpenGLWindow::NoPartialUpdate)
281         fbo->bind();
282 }
283 
endPaint()284 void QOpenGLWindowPrivate::endPaint()
285 {
286     Q_Q(QOpenGLWindow);
287 
288     if (updateBehavior > QOpenGLWindow::NoPartialUpdate)
289         fbo->release();
290 
291     context->functions()->glBindFramebuffer(GL_FRAMEBUFFER, context->defaultFramebufferObject());
292 
293     if (updateBehavior == QOpenGLWindow::PartialUpdateBlit && hasFboBlit) {
294         const int deviceWidth = q->width() * q->devicePixelRatio();
295         const int deviceHeight = q->height() * q->devicePixelRatio();
296         QOpenGLExtensions extensions(context.data());
297         extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo->handle());
298         extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, context->defaultFramebufferObject());
299         extensions.glBlitFramebuffer(0, 0, deviceWidth, deviceHeight,
300                                      0, 0, deviceWidth, deviceHeight,
301                                      GL_COLOR_BUFFER_BIT, GL_NEAREST);
302     } else if (updateBehavior > QOpenGLWindow::NoPartialUpdate) {
303         if (updateBehavior == QOpenGLWindow::PartialUpdateBlend) {
304             context->functions()->glEnable(GL_BLEND);
305             context->functions()->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
306         }
307         if (!blitter.isCreated())
308             blitter.create();
309 
310         QRect windowRect(QPoint(0, 0), fbo->size());
311         QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(windowRect, windowRect);
312         blitter.bind();
313         blitter.blit(fbo->texture(), target, QOpenGLTextureBlitter::OriginBottomLeft);
314         blitter.release();
315 
316         if (updateBehavior == QOpenGLWindow::PartialUpdateBlend)
317             context->functions()->glDisable(GL_BLEND);
318     }
319 
320     q->paintOverGL();
321 }
322 
bindFBO()323 void QOpenGLWindowPrivate::bindFBO()
324 {
325     if (updateBehavior > QOpenGLWindow::NoPartialUpdate)
326         fbo->bind();
327     else
328         QOpenGLFramebufferObject::bindDefault();
329 }
330 
flush(const QRegion & region)331 void QOpenGLWindowPrivate::flush(const QRegion &region)
332 {
333     Q_UNUSED(region);
334     Q_Q(QOpenGLWindow);
335     context->swapBuffers(q);
336     emit q->frameSwapped();
337 }
338 
ensureActiveTarget()339 void QOpenGLWindowPaintDevice::ensureActiveTarget()
340 {
341     QOpenGLWindowPrivate::get(m_window)->bindFBO();
342 }
343 
344 /*!
345   Constructs a new QOpenGLWindow with the given \a parent and \a updateBehavior.
346 
347   \sa QOpenGLWindow::UpdateBehavior
348  */
QOpenGLWindow(QOpenGLWindow::UpdateBehavior updateBehavior,QWindow * parent)349 QOpenGLWindow::QOpenGLWindow(QOpenGLWindow::UpdateBehavior updateBehavior, QWindow *parent)
350     : QPaintDeviceWindow(*(new QOpenGLWindowPrivate(nullptr, updateBehavior)), parent)
351 {
352     setSurfaceType(QSurface::OpenGLSurface);
353 }
354 
355 /*!
356   Constructs a new QOpenGLWindow with the given \a parent and \a updateBehavior. The QOpenGLWindow's context will share with \a shareContext.
357 
358   \sa QOpenGLWindow::UpdateBehavior shareContext
359 */
QOpenGLWindow(QOpenGLContext * shareContext,UpdateBehavior updateBehavior,QWindow * parent)360 QOpenGLWindow::QOpenGLWindow(QOpenGLContext *shareContext, UpdateBehavior updateBehavior, QWindow *parent)
361     : QPaintDeviceWindow(*(new QOpenGLWindowPrivate(shareContext, updateBehavior)), parent)
362 {
363     setSurfaceType(QSurface::OpenGLSurface);
364 }
365 
366 /*!
367   Destroys the QOpenGLWindow instance, freeing its resources.
368 
369   The OpenGLWindow's context is made current in the destructor, allowing for
370   safe destruction of any child object that may need to release OpenGL
371   resources belonging to the context provided by this window.
372 
373   \warning if you have objects wrapping OpenGL resources (such as
374   QOpenGLBuffer, QOpenGLShaderProgram, etc.) as members of a QOpenGLWindow
375   subclass, you may need to add a call to makeCurrent() in that subclass'
376   destructor as well. Due to the rules of C++ object destruction, those objects
377   will be destroyed \e{before} calling this function (but after that the
378   destructor of the subclass has run), therefore making the OpenGL context
379   current in this function happens too late for their safe disposal.
380 
381   \sa makeCurrent
382 
383   \since 5.5
384 */
~QOpenGLWindow()385 QOpenGLWindow::~QOpenGLWindow()
386 {
387     makeCurrent();
388 }
389 
390 /*!
391   \return the update behavior for this QOpenGLWindow.
392 */
updateBehavior() const393 QOpenGLWindow::UpdateBehavior QOpenGLWindow::updateBehavior() const
394 {
395     Q_D(const QOpenGLWindow);
396     return d->updateBehavior;
397 }
398 
399 /*!
400   \return \c true if the window's OpenGL resources, like the context, have
401   been successfully initialized. Note that the return value is always \c false
402   until the window becomes exposed (shown).
403 */
isValid() const404 bool QOpenGLWindow::isValid() const
405 {
406     Q_D(const QOpenGLWindow);
407     return d->context && d->context->isValid();
408 }
409 
410 /*!
411   Prepares for rendering OpenGL content for this window by making the
412   corresponding context current and binding the framebuffer object, if there is
413   one, in that context context.
414 
415   It is not necessary to call this function in most cases, because it is called
416   automatically before invoking paintGL(). It is provided nonetheless to support
417   advanced, multi-threaded scenarios where a thread different than the GUI or main
418   thread may want to update the surface or framebuffer contents. See QOpenGLContext
419   for more information on threading related issues.
420 
421   This function is suitable for calling also when the underlying platform window
422   is already destroyed. This means that it is safe to call this function from
423   a QOpenGLWindow subclass' destructor. If there is no native window anymore,
424   an offscreen surface is used instead. This ensures that OpenGL resource
425   cleanup operations in the destructor will always work, as long as
426   this function is called first.
427 
428   \sa QOpenGLContext, context(), paintGL(), doneCurrent()
429  */
makeCurrent()430 void QOpenGLWindow::makeCurrent()
431 {
432     Q_D(QOpenGLWindow);
433 
434     if (!isValid())
435         return;
436 
437     // The platform window may be destroyed at this stage and therefore
438     // makeCurrent() may not safely be called with 'this'.
439     if (handle()) {
440         d->context->makeCurrent(this);
441     } else {
442         if (!d->offscreenSurface) {
443             d->offscreenSurface.reset(new QOffscreenSurface(screen()));
444             d->offscreenSurface->setFormat(d->context->format());
445             d->offscreenSurface->create();
446         }
447         d->context->makeCurrent(d->offscreenSurface.data());
448     }
449 
450     d->bindFBO();
451 }
452 
453 /*!
454   Releases the context.
455 
456   It is not necessary to call this function in most cases, since the widget
457   will make sure the context is bound and released properly when invoking
458   paintGL().
459 
460   \sa makeCurrent()
461  */
doneCurrent()462 void QOpenGLWindow::doneCurrent()
463 {
464     Q_D(QOpenGLWindow);
465 
466     if (!isValid())
467         return;
468 
469     d->context->doneCurrent();
470 }
471 
472 /*!
473   \return The QOpenGLContext used by this window or \c 0 if not yet initialized.
474  */
context() const475 QOpenGLContext *QOpenGLWindow::context() const
476 {
477     Q_D(const QOpenGLWindow);
478     return d->context.data();
479 }
480 
481 /*!
482   \return The QOpenGLContext requested to be shared with this window's QOpenGLContext.
483 */
shareContext() const484 QOpenGLContext *QOpenGLWindow::shareContext() const
485 {
486     Q_D(const QOpenGLWindow);
487     return d->shareContext;
488 }
489 
490 /*!
491   The framebuffer object handle used by this window.
492 
493   When the update behavior is set to \c NoPartialUpdate, there is no separate
494   framebuffer object. In this case the returned value is the ID of the
495   default framebuffer.
496 
497   Otherwise the value of the ID of the framebuffer object or \c 0 if not
498   yet initialized.
499  */
defaultFramebufferObject() const500 GLuint QOpenGLWindow::defaultFramebufferObject() const
501 {
502     Q_D(const QOpenGLWindow);
503     if (d->updateBehavior > NoPartialUpdate && d->fbo)
504         return d->fbo->handle();
505     else if (QOpenGLContext *ctx = QOpenGLContext::currentContext())
506         return ctx->defaultFramebufferObject();
507     else
508         return 0;
509 }
510 
511 extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
512 
513 /*!
514   Returns a copy of the framebuffer.
515 
516   \note This is a potentially expensive operation because it relies on
517   glReadPixels() to read back the pixels. This may be slow and can stall the
518   GPU pipeline.
519 
520   \note When used together with update behavior \c NoPartialUpdate, the returned
521   image may not contain the desired content when called after the front and back
522   buffers have been swapped (unless preserved swap is enabled in the underlying
523   windowing system interface). In this mode the function reads from the back
524   buffer and the contents of that may not match the content on the screen (the
525   front buffer). In this case the only place where this function can safely be
526   used is paintGL() or paintOverGL().
527  */
grabFramebuffer()528 QImage QOpenGLWindow::grabFramebuffer()
529 {
530     if (!isValid())
531         return QImage();
532 
533     makeCurrent();
534 
535     const bool hasAlpha = format().hasAlpha();
536     QImage img = qt_gl_read_framebuffer(size() * devicePixelRatio(), hasAlpha, hasAlpha);
537     img.setDevicePixelRatio(devicePixelRatio());
538     return img;
539 }
540 
541 /*!
542   This virtual function is called once before the first call to paintGL() or
543   resizeGL(). Reimplement it in a subclass.
544 
545   This function should set up any required OpenGL resources and state.
546 
547   There is no need to call makeCurrent() because this has already been done
548   when this function is called. Note however that the framebuffer, in case
549   partial update mode is used, is not yet available at this stage, so avoid
550   issuing draw calls from here. Defer such calls to paintGL() instead.
551 
552   \sa paintGL(), resizeGL()
553  */
initializeGL()554 void QOpenGLWindow::initializeGL()
555 {
556 }
557 
558 /*!
559   This virtual function is called whenever the widget has been resized.
560   Reimplement it in a subclass. The new size is passed in \a w and \a h.
561 
562   \note This is merely a convenience function in order to provide an API that is
563   compatible with QOpenGLWidget. Unlike with QOpenGLWidget, derived classes are
564   free to choose to override resizeEvent() instead of this function.
565 
566   \note Avoid issuing OpenGL commands from this function as there may not be a
567   context current when it is invoked. If it cannot be avoided, call makeCurrent().
568 
569   \note Scheduling updates from here is not necessary. The windowing systems
570   will send expose events that trigger an update automatically.
571 
572   \sa initializeGL(), paintGL()
573  */
resizeGL(int w,int h)574 void QOpenGLWindow::resizeGL(int w, int h)
575 {
576     Q_UNUSED(w);
577     Q_UNUSED(h);
578 }
579 
580 /*!
581   This virtual function is called whenever the window contents needs to be
582   painted. Reimplement it in a subclass.
583 
584   There is no need to call makeCurrent() because this has already
585   been done when this function is called.
586 
587   Before invoking this function, the context and the framebuffer, if there is
588   one, are bound, and the viewport is set up by a call to glViewport(). No
589   other state is set and no clearing or drawing is performed by the framework.
590 
591   \note When using a partial update behavior, like \c PartialUpdateBlend, the
592   output of the previous paintGL() call is preserved and, after the additional
593   drawing perfomed in the current invocation of the function, the content is
594   blitted or blended over the content drawn directly to the window in
595   paintUnderGL().
596 
597   \sa initializeGL(), resizeGL(), paintUnderGL(), paintOverGL(), UpdateBehavior
598  */
paintGL()599 void QOpenGLWindow::paintGL()
600 {
601 }
602 
603 /*!
604   The virtual function is called before each invocation of paintGL().
605 
606   When the update mode is set to \c NoPartialUpdate, there is no difference
607   between this function and paintGL(), performing rendering in either of them
608   leads to the same result.
609 
610   The difference becomes significant when using \c PartialUpdateBlend, where an
611   extra framebuffer object is used. There, paintGL() targets this additional
612   framebuffer object, which preserves its contents, while paintUnderGL() and
613   paintOverGL() target the default framebuffer, i.e. directly the window
614   surface, the contents of which is lost after each displayed frame.
615 
616   \note Avoid relying on this function when the update behavior is
617   \c PartialUpdateBlit. This mode involves blitting the extra framebuffer used by
618   paintGL() onto the default framebuffer after each invocation of paintGL(),
619   thus overwriting all drawing generated in this function.
620 
621   \sa paintGL(), paintOverGL(), UpdateBehavior
622  */
paintUnderGL()623 void QOpenGLWindow::paintUnderGL()
624 {
625 }
626 
627 /*!
628   This virtual function is called after each invocation of paintGL().
629 
630   When the update mode is set to NoPartialUpdate, there is no difference
631   between this function and paintGL(), performing rendering in either of them
632   leads to the same result.
633 
634   Like paintUnderGL(), rendering in this function targets the default
635   framebuffer of the window, regardless of the update behavior. It gets called
636   after paintGL() has returned and the blit (PartialUpdateBlit) or quad drawing
637   (PartialUpdateBlend) has been done.
638 
639   \sa paintGL(), paintUnderGL(), UpdateBehavior
640  */
paintOverGL()641 void QOpenGLWindow::paintOverGL()
642 {
643 }
644 
645 /*!
646   Paint \a event handler. Calls paintGL().
647 
648   \sa paintGL()
649  */
paintEvent(QPaintEvent * event)650 void QOpenGLWindow::paintEvent(QPaintEvent *event)
651 {
652     Q_UNUSED(event);
653     paintGL();
654 }
655 
656 /*!
657   Resize \a event handler. Calls resizeGL().
658 
659   \sa resizeGL()
660  */
resizeEvent(QResizeEvent * event)661 void QOpenGLWindow::resizeEvent(QResizeEvent *event)
662 {
663     Q_UNUSED(event);
664     Q_D(QOpenGLWindow);
665     d->initialize();
666     resizeGL(width(), height());
667 }
668 
669 /*!
670   \internal
671  */
metric(PaintDeviceMetric metric) const672 int QOpenGLWindow::metric(PaintDeviceMetric metric) const
673 {
674     Q_D(const QOpenGLWindow);
675 
676     switch (metric) {
677         case PdmDepth:
678             if (d->paintDevice)
679                 return d->paintDevice->depth();
680             break;
681         default:
682             break;
683     }
684     return QPaintDeviceWindow::metric(metric);
685 }
686 
687 /*!
688   \internal
689  */
redirected(QPoint *) const690 QPaintDevice *QOpenGLWindow::redirected(QPoint *) const
691 {
692     Q_D(const QOpenGLWindow);
693     if (QOpenGLContext::currentContext() == d->context.data())
694         return d->paintDevice.data();
695     return nullptr;
696 }
697 
698 QT_END_NAMESPACE
699