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