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

source code of qtbase/src/opengl/qopenglwindow.cpp