1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtOpenGL 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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include <QtGui/QApplication>
43 #include <QtGui/QColormap>
44 #include <QtGui/QDesktopWidget>
45 #include <QtGui/QPaintDevice>
46 #include <QtGui/QWidget>
47 
48 #include <qglframebufferobject.h>
49 #include <qglpixelbuffer.h>
50 #include <qcolormap.h>
51 #include <qdesktopwidget.h>
52 #include <private/qwidget_p.h>
53 #include "qdebug.h"
54 
55 #ifdef Q_WS_X11
56 #include <private/qt_x11_p.h>
57 #include <qx11info_x11.h>
58 
59 #ifndef QT_OPENGL_ES
60 #include <GL/glx.h>
61 #include <X11/Xlib.h>
62 #endif
63 #endif //Q_WS_X11
64 
65 #include <private/qglextensions_p.h>
66 #include <private/qwindowsurface_gl_p.h>
67 
68 #include <private/qgl_p.h>
69 
70 #include <private/qglpixelbuffer_p.h>
71 #include <private/qgraphicssystem_gl_p.h>
72 
73 #include <private/qpaintengineex_opengl2_p.h>
74 #include <private/qpixmapdata_gl_p.h>
75 
76 #ifndef QT_OPENGL_ES_2
77 #include <private/qpaintengine_opengl_p.h>
78 #endif
79 
80 #ifndef GLX_ARB_multisample
81 #define GLX_SAMPLE_BUFFERS_ARB  100000
82 #define GLX_SAMPLES_ARB         100001
83 #endif
84 
85 #ifndef QT_NO_EGL
86 #include <private/qeglcontext_p.h>
87 #endif
88 
89 QT_BEGIN_NAMESPACE
90 
91 //
92 // QGLGraphicsSystem
93 //
94 #ifdef Q_WS_WIN
95 extern Q_GUI_EXPORT bool qt_win_owndc_required;
96 #endif
QGLGraphicsSystem(bool useX11GL)97 QGLGraphicsSystem::QGLGraphicsSystem(bool useX11GL)
98     : QGraphicsSystem(), m_useX11GL(useX11GL)
99 {
100 #if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
101     // only override the system defaults if the user hasn't already
102     // picked a visual
103     if (X11->visual == 0 && X11->visual_id == -1 && X11->visual_class == -1) {
104         // find a double buffered, RGBA visual that supports OpenGL
105         // and set that as the default visual for windows in Qt
106         int i = 0;
107         int spec[16];
108         spec[i++] = GLX_RGBA;
109         spec[i++] = GLX_DOUBLEBUFFER;
110 
111         if (!qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull()) {
112             spec[i++] = GLX_DEPTH_SIZE;
113             spec[i++] = 8;
114             spec[i++] = GLX_STENCIL_SIZE;
115             spec[i++] = 8;
116             spec[i++] = GLX_SAMPLE_BUFFERS_ARB;
117             spec[i++] = 1;
118             spec[i++] = GLX_SAMPLES_ARB;
119             spec[i++] = 4;
120         }
121 
122         spec[i++] = XNone;
123 
124         XVisualInfo *vi = glXChooseVisual(X11->display, X11->defaultScreen, spec);
125         if (vi) {
126             X11->visual_id = vi->visualid;
127             X11->visual_class = vi->c_class;
128 
129             QGLFormat format;
130             int res;
131             glXGetConfig(X11->display, vi, GLX_LEVEL, &res);
132             format.setPlane(res);
133             glXGetConfig(X11->display, vi, GLX_DOUBLEBUFFER, &res);
134             format.setDoubleBuffer(res);
135             glXGetConfig(X11->display, vi, GLX_DEPTH_SIZE, &res);
136             format.setDepth(res);
137             if (format.depth())
138                 format.setDepthBufferSize(res);
139             glXGetConfig(X11->display, vi, GLX_RGBA, &res);
140             format.setRgba(res);
141             glXGetConfig(X11->display, vi, GLX_RED_SIZE, &res);
142             format.setRedBufferSize(res);
143             glXGetConfig(X11->display, vi, GLX_GREEN_SIZE, &res);
144             format.setGreenBufferSize(res);
145             glXGetConfig(X11->display, vi, GLX_BLUE_SIZE, &res);
146             format.setBlueBufferSize(res);
147             glXGetConfig(X11->display, vi, GLX_ALPHA_SIZE, &res);
148             format.setAlpha(res);
149             if (format.alpha())
150                 format.setAlphaBufferSize(res);
151             glXGetConfig(X11->display, vi, GLX_ACCUM_RED_SIZE, &res);
152             format.setAccum(res);
153             if (format.accum())
154                 format.setAccumBufferSize(res);
155             glXGetConfig(X11->display, vi, GLX_STENCIL_SIZE, &res);
156             format.setStencil(res);
157             if (format.stencil())
158                 format.setStencilBufferSize(res);
159             glXGetConfig(X11->display, vi, GLX_STEREO, &res);
160             format.setStereo(res);
161             glXGetConfig(X11->display, vi, GLX_SAMPLE_BUFFERS_ARB, &res);
162             format.setSampleBuffers(res);
163             if (format.sampleBuffers()) {
164                 glXGetConfig(X11->display, vi, GLX_SAMPLES_ARB, &res);
165                 format.setSamples(res);
166             }
167 
168             QGLWindowSurface::surfaceFormat = format;
169             XFree(vi);
170 
171             printf("using visual class %x, id %x\n", X11->visual_class, X11->visual_id);
172         }
173     }
174 #elif defined(Q_WS_WIN)
175     QGLWindowSurface::surfaceFormat.setDoubleBuffer(true);
176 
177     qt_win_owndc_required = true;
178 #endif
179 }
180 
181 //
182 // QGLWindowSurface
183 //
184 class QGLGlobalShareWidget
185 {
186 public:
QGLGlobalShareWidget()187     QGLGlobalShareWidget() : widget(0), init(false) {
188         created = true;
189     }
190 
shareWidget()191     QGLWidget *shareWidget() {
192         if (!init && !widget && !cleanedUp) {
193             init = true;
194             widget = new QGLWidget(QGLFormat(QGL::SingleBuffer | QGL::NoDepthBuffer | QGL::NoStencilBuffer));
195 #ifdef Q_OS_SYMBIAN
196             if (!widget->context()->isValid()) {
197                 delete widget;
198                 widget = 0;
199                 init = false;
200                 return 0;
201             }
202 #endif
203 
204             widget->resize(1, 1);
205 
206             // We don't need this internal widget to appear in QApplication::topLevelWidgets()
207             if (QWidgetPrivate::allWidgets)
208                 QWidgetPrivate::allWidgets->remove(widget);
209             init = false;
210         }
211         return widget;
212     }
213 
214     // destroys the share widget and prevents recreation
cleanup()215     void cleanup() {
216         QGLWidget *w = widget;
217         cleanedUp = true;
218         widget = 0;
219         delete w;
220     }
221 
222     // destroys the share widget, but allows it to be recreated later on
destroy()223     void destroy() {
224         if (cleanedUp)
225             return;
226 
227         QGLWidget *w = widget;
228 
229         // prevent potential recursions
230         cleanedUp = true;
231         widget = 0;
232         delete w;
233         cleanedUp = false;
234     }
235 
initializing()236     bool initializing()
237     {
238         return init;
239     }
240 
241     static bool cleanedUp;
242     static bool created;
243 
244 private:
245     QGLWidget *widget;
246     bool init;
247 };
248 
249 bool QGLGlobalShareWidget::cleanedUp = false;
250 bool QGLGlobalShareWidget::created = false;
251 
252 static void qt_cleanup_gl_share_widget();
253 Q_GLOBAL_STATIC_WITH_INITIALIZER(QGLGlobalShareWidget, _qt_gl_share_widget,
254                                  {
255                                      qAddPostRoutine(qt_cleanup_gl_share_widget);
256                                  })
257 
qt_cleanup_gl_share_widget()258 static void qt_cleanup_gl_share_widget()
259 {
260     if (QGLGlobalShareWidget::created)
261         _qt_gl_share_widget()->cleanup();
262 }
263 
qt_gl_share_widget()264 QGLWidget* qt_gl_share_widget()
265 {
266     if (QGLGlobalShareWidget::cleanedUp)
267         return 0;
268     return _qt_gl_share_widget()->shareWidget();
269 }
270 
qt_destroy_gl_share_widget()271 void qt_destroy_gl_share_widget()
272 {
273     if (QGLGlobalShareWidget::created)
274         _qt_gl_share_widget()->destroy();
275 }
276 
qt_initializing_gl_share_widget()277 bool qt_initializing_gl_share_widget()
278 {
279     if (QGLGlobalShareWidget::created)
280         return _qt_gl_share_widget()->initializing();
281     return false;
282 }
283 
qt_gl_share_context()284 const QGLContext *qt_gl_share_context()
285 {
286     QGLWidget *widget = qt_gl_share_widget();
287     if (widget)
288         return widget->context();
289     return 0;
290 }
291 
292 struct QGLWindowSurfacePrivate
293 {
294     QGLFramebufferObject *fbo;
295     QGLPixelBuffer *pb;
296     GLuint tex_id;
297     GLuint pb_tex_id;
298 
299     int tried_fbo : 1;
300     int tried_pb : 1;
301     int destructive_swap_buffers : 1;
302     int geometry_updated : 1;
303     int did_paint : 1;
304 
305     QGLContext *ctx;
306 
307     QList<QGLContext **> contexts;
308 
309     QRegion paintedRegion;
310     QSize size;
311 
312     QSize textureSize;
313 
314     QList<QImage> buffers;
315     QGLWindowSurfaceGLPaintDevice glDevice;
316     QGLWindowSurface* q_ptr;
317 
318     bool swap_region_support;
319 };
320 
321 QGLFormat QGLWindowSurface::surfaceFormat;
322 QGLWindowSurface::SwapMode QGLWindowSurface::swapBehavior = QGLWindowSurface::AutomaticSwap;
323 
size() const324 QSize QGLWindowSurfaceGLPaintDevice::size() const
325 {
326     return d->size;
327 }
328 
context() const329 QGLContext* QGLWindowSurfaceGLPaintDevice::context() const
330 {
331     return d->ctx;
332 }
333 
334 
metric(PaintDeviceMetric m) const335 int QGLWindowSurfaceGLPaintDevice::metric(PaintDeviceMetric m) const
336 {
337     return qt_paint_device_metric(d->q_ptr->window(), m);
338 }
339 
paintEngine() const340 QPaintEngine *QGLWindowSurfaceGLPaintDevice::paintEngine() const
341 {
342     return qt_qgl_paint_engine();
343 }
344 
QGLWindowSurface(QWidget * window)345 QGLWindowSurface::QGLWindowSurface(QWidget *window)
346     : QWindowSurface(window), d_ptr(new QGLWindowSurfacePrivate)
347 {
348 //    Q_ASSERT(window->isTopLevel());
349     d_ptr->pb = 0;
350     d_ptr->fbo = 0;
351     d_ptr->ctx = 0;
352     d_ptr->tex_id = 0;
353 #if defined (QT_OPENGL_ES_2)
354     d_ptr->tried_fbo = true;
355     d_ptr->tried_pb = true;
356 #else
357     d_ptr->tried_fbo = false;
358     d_ptr->tried_pb = false;
359 #endif
360     d_ptr->destructive_swap_buffers = qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull();
361     d_ptr->glDevice.d = d_ptr;
362     d_ptr->q_ptr = this;
363     d_ptr->geometry_updated = false;
364     d_ptr->did_paint = false;
365     d_ptr->swap_region_support = false;
366 }
367 
~QGLWindowSurface()368 QGLWindowSurface::~QGLWindowSurface()
369 {
370     if (d_ptr->ctx)
371         glDeleteTextures(1, &d_ptr->tex_id);
372 #ifndef Q_WS_QPA // Don't delete the contexts. Destroying the window does that for us
373     foreach(QGLContext **ctx, d_ptr->contexts) {
374         delete *ctx;
375         *ctx = 0;
376     }
377 #endif
378     delete d_ptr->pb;
379     delete d_ptr->fbo;
380     delete d_ptr;
381 
382     if (QGLGlobalShareWidget::cleanedUp)
383         return;
384 
385 #ifdef Q_OS_SYMBIAN
386     // Destroy the context if necessary.
387     if (qt_gl_share_widget() && !qt_gl_share_context()->isSharing())
388         qt_destroy_gl_share_widget();
389 #endif
390 }
391 
deleted(QObject * object)392 void QGLWindowSurface::deleted(QObject *object)
393 {
394     QWidget *widget = qobject_cast<QWidget *>(object);
395     if (widget) {
396         if (widget == window()) {
397             // Make sure that the fbo is destroyed before destroying its context.
398             delete d_ptr->fbo;
399             d_ptr->fbo = 0;
400         }
401 
402 #ifndef Q_WS_QPA //no need to specifically delete the QGLContext as it will be deleted by QWidget
403         QWidgetPrivate *widgetPrivate = widget->d_func();
404         if (widgetPrivate->extraData()) {
405             union { QGLContext **ctxPtrPtr; void **voidPtrPtr; };
406             voidPtrPtr = &widgetPrivate->extraData()->glContext;
407             int index = d_ptr->contexts.indexOf(ctxPtrPtr);
408             if (index != -1) {
409                 delete *ctxPtrPtr;
410                 *ctxPtrPtr = 0;
411                 d_ptr->contexts.removeAt(index);
412             }
413         }
414 #endif
415     }
416 }
417 
hijackWindow(QWidget * widget)418 void QGLWindowSurface::hijackWindow(QWidget *widget)
419 {
420     QWidgetPrivate *widgetPrivate = widget->d_func();
421     widgetPrivate->createExtra();
422     if (widgetPrivate->extraData()->glContext)
423         return;
424 
425     QGLContext *ctx = NULL;
426 
427     // For translucent top-level widgets we need alpha in the format.
428     if (widget->testAttribute(Qt::WA_TranslucentBackground)) {
429         QGLFormat modFormat(surfaceFormat);
430         modFormat.setSampleBuffers(false);
431         modFormat.setSamples(0);
432         modFormat.setAlpha(true);
433         ctx = new QGLContext(modFormat, widget);
434     } else
435         ctx = new QGLContext(surfaceFormat, widget);
436 
437     ctx->create(qt_gl_share_context());
438 #ifdef Q_OS_SYMBIAN
439     if (!ctx->isValid()) {
440         delete ctx;
441         return;
442     }
443 #endif
444 #ifndef QT_NO_EGL
445     static bool checkedForNOKSwapRegion = false;
446     static bool haveNOKSwapRegion = false;
447 
448     if (!checkedForNOKSwapRegion) {
449         haveNOKSwapRegion = QEgl::hasExtension("EGL_NOK_swap_region2");
450         checkedForNOKSwapRegion = true;
451 
452         if (haveNOKSwapRegion)
453             qDebug() << "Found EGL_NOK_swap_region2 extension. Using partial updates.";
454     }
455 
456     d_ptr->destructive_swap_buffers = true;
457     if (ctx->d_func()->eglContext->configAttrib(EGL_SURFACE_TYPE)&EGL_SWAP_BEHAVIOR_PRESERVED_BIT) {
458         EGLint swapBehavior;
459         if (eglQuerySurface(ctx->d_func()->eglContext->display(), ctx->d_func()->eglSurface
460                             , EGL_SWAP_BEHAVIOR, &swapBehavior)) {
461             d_ptr->destructive_swap_buffers = (swapBehavior != EGL_BUFFER_PRESERVED);
462         }
463     }
464 
465     d_ptr->swap_region_support = haveNOKSwapRegion;
466 #endif
467 
468     widgetPrivate->extraData()->glContext = ctx;
469 
470     union { QGLContext **ctxPtrPtr; void **voidPtrPtr; };
471 
472     connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(deleted(QObject*)));
473 
474     voidPtrPtr = &widgetPrivate->extraData()->glContext;
475     d_ptr->contexts << ctxPtrPtr;
476 
477 #ifndef Q_OS_SYMBIAN
478     qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size();
479 #endif
480 }
481 
context() const482 QGLContext *QGLWindowSurface::context() const
483 {
484     return d_ptr->ctx;
485 }
486 
paintDevice()487 QPaintDevice *QGLWindowSurface::paintDevice()
488 {
489     updateGeometry();
490 
491 #ifdef Q_OS_SYMBIAN
492     // On symbian we always return glDevice, even if it's invalid
493     return &d_ptr->glDevice;
494 #else
495     if (d_ptr->pb)
496         return d_ptr->pb;
497 
498     if (d_ptr->ctx)
499         return &d_ptr->glDevice;
500 
501     QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
502     ctx->makeCurrent();
503 
504     Q_ASSERT(d_ptr->fbo);
505     return d_ptr->fbo;
506 #endif
507 }
508 
509 static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &src = QRectF());
510 
beginPaint(const QRegion &)511 void QGLWindowSurface::beginPaint(const QRegion &)
512 {
513     d_ptr->did_paint = true;
514     updateGeometry();
515 
516     int clearFlags = 0;
517 
518     QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
519 
520     if (!ctx)
521         return;
522 
523     if (ctx->d_func()->workaround_needsFullClearOnEveryFrame)
524         clearFlags = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
525     else if (ctx->format().alpha())
526         clearFlags = GL_COLOR_BUFFER_BIT;
527 
528     if (clearFlags) {
529         if (d_ptr->fbo)
530             d_ptr->fbo->bind();
531 
532         glClearColor(0.0, 0.0, 0.0, 0.0);
533         glClear(clearFlags);
534 
535         if (d_ptr->fbo)
536             d_ptr->fbo->release();
537     }
538 }
539 
endPaint(const QRegion & rgn)540 void QGLWindowSurface::endPaint(const QRegion &rgn)
541 {
542     if (context())
543         d_ptr->paintedRegion |= rgn;
544 
545     d_ptr->buffers.clear();
546 }
547 
blitTexture(QGLContext * ctx,GLuint texture,const QSize & viewport,const QSize & texSize,const QRect & targetRect,const QRect & sourceRect)548 static void blitTexture(QGLContext *ctx, GLuint texture, const QSize &viewport, const QSize &texSize, const QRect &targetRect, const QRect &sourceRect)
549 {
550     glDisable(GL_DEPTH_TEST);
551     glDisable(GL_SCISSOR_TEST);
552     glDisable(GL_BLEND);
553 
554     glViewport(0, 0, viewport.width(), viewport.height());
555 
556     QGLShaderProgram *blitProgram =
557         QGLEngineSharedShaders::shadersForContext(ctx)->blitProgram();
558     blitProgram->bind();
559     blitProgram->setUniformValue("imageTexture", 0 /*QT_IMAGE_TEXTURE_UNIT*/);
560 
561     // The shader manager's blit program does not multiply the
562     // vertices by the pmv matrix, so we need to do the effect
563     // of the orthographic projection here ourselves.
564     QRectF r;
565     qreal w = viewport.width();
566     qreal h = viewport.height();
567     r.setLeft((targetRect.left() / w) * 2.0f - 1.0f);
568     if (targetRect.right() == (viewport.width() - 1))
569         r.setRight(1.0f);
570     else
571         r.setRight((targetRect.right() / w) * 2.0f - 1.0f);
572     r.setBottom((targetRect.top() / h) * 2.0f - 1.0f);
573     if (targetRect.bottom() == (viewport.height() - 1))
574         r.setTop(1.0f);
575     else
576         r.setTop((targetRect.bottom() / w) * 2.0f - 1.0f);
577 
578     drawTexture(r, texture, texSize, sourceRect);
579 }
580 
581 
flush(QWidget * widget,const QRegion & rgn,const QPoint & offset)582 void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
583 {
584     //### Find out why d_ptr->geometry_updated isn't always false.
585     // flush() should not be called when d_ptr->geometry_updated is true. It assumes that either
586     // d_ptr->fbo or d_ptr->pb is allocated and has the correct size.
587     if (d_ptr->geometry_updated)
588         return;
589 
590     // did_paint is set to true in ::beginPaint. ::beginPaint means that we
591     // at least cleared the background (= painted something). In EGL API it's a
592     // mistake to call swapBuffers if nothing was painted unless
593     // EGL_BUFFER_PRESERVED is set. This check protects the flush func from
594     // being executed if it's for nothing.
595     if (!d_ptr->destructive_swap_buffers && !d_ptr->did_paint)
596         return;
597 
598 #ifdef Q_OS_SYMBIAN
599     if (window() != widget) {
600         // For performance reasons we don't support
601         // flushing native child widgets on Symbian.
602         // It breaks overlapping native child widget
603         // rendering in some cases but we prefer performance.
604         return;
605     }
606 #endif
607 
608 
609     QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget();
610     Q_ASSERT(parent);
611 
612 #if !defined(Q_WS_QPA)
613     if (!geometry().isValid())
614         return;
615 #else
616     if (!size().isValid())
617         return;
618 #endif
619 
620     // Needed to support native child-widgets...
621     hijackWindow(parent);
622 
623     QRect br = rgn.boundingRect().translated(offset);
624     br = br.intersected(window()->rect());
625     QPoint wOffset = qt_qwidget_data(parent)->wrect.topLeft();
626     QRect rect = br.translated(-offset - wOffset);
627 
628     const GLenum target = GL_TEXTURE_2D;
629     Q_UNUSED(target);
630 
631     if (QGLWindowSurface::swapBehavior == QGLWindowSurface::KillSwap)
632         return;
633 
634     if (context()) {
635         context()->makeCurrent();
636 
637         if (context()->format().doubleBuffer()) {
638 #if !defined(QT_OPENGL_ES_2)
639             if (d_ptr->destructive_swap_buffers) {
640                 glBindTexture(target, d_ptr->tex_id);
641 
642                 QVector<QRect> rects = d_ptr->paintedRegion.rects();
643                 for (int i = 0; i < rects.size(); ++i) {
644                     QRect br = rects.at(i);
645                     if (br.isEmpty())
646                         continue;
647 
648                     const uint bottom = window()->height() - (br.y() + br.height());
649                     glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
650                 }
651 
652                 glBindTexture(target, 0);
653 
654                 QRegion dirtyRegion = QRegion(window()->rect()) - d_ptr->paintedRegion;
655 
656                 if (!dirtyRegion.isEmpty()) {
657                     glMatrixMode(GL_MODELVIEW);
658                     glLoadIdentity();
659 
660                     glMatrixMode(GL_PROJECTION);
661                     glLoadIdentity();
662 #ifndef QT_OPENGL_ES
663                     glOrtho(0, window()->width(), window()->height(), 0, -999999, 999999);
664 #else
665                     glOrthof(0, window()->width(), window()->height(), 0, -999999, 999999);
666 #endif
667                     glViewport(0, 0, window()->width(), window()->height());
668 
669                     QVector<QRect> rects = dirtyRegion.rects();
670                     glColor4f(1, 1, 1, 1);
671                     for (int i = 0; i < rects.size(); ++i) {
672                         QRect rect = rects.at(i);
673                         if (rect.isEmpty())
674                             continue;
675 
676                         drawTexture(rect, d_ptr->tex_id, window()->size(), rect);
677                     }
678                 }
679             }
680 #endif
681             bool doingPartialUpdate = false;
682             if (d_ptr->swap_region_support) {
683                 if (QGLWindowSurface::swapBehavior == QGLWindowSurface::AutomaticSwap)
684                     doingPartialUpdate = br.width() * br.height() < parent->geometry().width() * parent->geometry().height() * 0.2;
685                 else if (QGLWindowSurface::swapBehavior == QGLWindowSurface::AlwaysPartialSwap)
686                     doingPartialUpdate = true;
687             }
688 
689             QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext);
690 #ifdef Q_OS_SYMBIAN
691             if (!ctx)
692                 return;
693 #endif
694 
695             if (widget != window()) {
696                 if (initializeOffscreenTexture(window()->size()))
697                     qWarning() << "QGLWindowSurface: Flushing to native child widget, may lead to significant performance loss";
698                 glBindTexture(target, d_ptr->tex_id);
699 
700                 const uint bottom = window()->height() - (br.y() + br.height());
701                 glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
702 
703                 glBindTexture(target, 0);
704 
705                 ctx->makeCurrent();
706                 if (doingPartialUpdate)
707                     blitTexture(ctx, d_ptr->tex_id, parent->size(), window()->size(), rect, br);
708                 else
709                     blitTexture(ctx, d_ptr->tex_id, parent->size(), window()->size(), parent->rect(), parent->rect().translated(offset + wOffset));
710             }
711 
712             if (doingPartialUpdate)
713                 ctx->d_func()->swapRegion(br);
714             else
715                 ctx->swapBuffers();
716 
717             d_ptr->paintedRegion = QRegion();
718         } else {
719             glFlush();
720         }
721         return;
722     }
723 
724     QGLContext *previous_ctx = const_cast<QGLContext *>(QGLContext::currentContext());
725     QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext);
726 #ifdef Q_OS_SYMBIAN
727     if (!ctx)
728         return;
729 #endif
730 
731     // QPainter::end() should have unbound the fbo, otherwise something is very wrong...
732     Q_ASSERT(!d_ptr->fbo || !d_ptr->fbo->isBound());
733 
734     if (ctx != previous_ctx) {
735         ctx->makeCurrent();
736     }
737 
738     QSize size = widget->rect().size();
739     if (d_ptr->destructive_swap_buffers && ctx->format().doubleBuffer()) {
740         rect = parent->rect();
741         br = rect.translated(wOffset + offset);
742         size = parent->size();
743     }
744 
745     glDisable(GL_SCISSOR_TEST);
746 
747     if (d_ptr->fbo && (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)) {
748         const int h = d_ptr->fbo->height();
749 
750         const int sx0 = br.left();
751         const int sx1 = br.left() + br.width();
752         const int sy0 = h - (br.top() + br.height());
753         const int sy1 = h - br.top();
754 
755         const int tx0 = rect.left();
756         const int tx1 = rect.left() + rect.width();
757         const int ty0 = parent->height() - (rect.top() + rect.height());
758         const int ty1 = parent->height() - rect.top();
759 
760         if (window() == parent || d_ptr->fbo->format().samples() <= 1) {
761             if (ctx->d_ptr->current_fbo != 0)
762                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
763 
764             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle());
765 
766             glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
767                     tx0, ty0, tx1, ty1,
768                     GL_COLOR_BUFFER_BIT,
769                     GL_NEAREST);
770 
771             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
772         } else {
773 #ifndef Q_OS_SYMBIAN // We don't have FBO pool on Symbian
774             // can't do sub-region blits with multisample FBOs
775             QGLFramebufferObject *temp = qgl_fbo_pool()->acquire(d_ptr->fbo->size(), QGLFramebufferObjectFormat());
776 
777             glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, temp->handle());
778             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle());
779 
780             glBlitFramebufferEXT(0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(),
781                     0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(),
782                     GL_COLOR_BUFFER_BIT,
783                     GL_NEAREST);
784 
785             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, temp->handle());
786             glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
787 
788             glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
789                     tx0, ty0, tx1, ty1,
790                     GL_COLOR_BUFFER_BIT,
791                     GL_NEAREST);
792 
793             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
794 
795             qgl_fbo_pool()->release(temp);
796 #endif // Q_OS_SYMBIAN
797         }
798 
799         ctx->d_ptr->current_fbo = 0;
800     }
801 #if !defined(QT_OPENGL_ES_2)
802     else {
803         GLuint texture;
804     if (d_ptr->fbo) {
805         texture = d_ptr->fbo->texture();
806     } else {
807         d_ptr->pb->makeCurrent();
808         glBindTexture(target, d_ptr->pb_tex_id);
809         const uint bottom = window()->height() - (br.y() + br.height());
810         glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
811         texture = d_ptr->pb_tex_id;
812         glBindTexture(target, 0);
813     }
814 
815         glDisable(GL_DEPTH_TEST);
816 
817         if (d_ptr->fbo) {
818             d_ptr->fbo->release();
819         } else {
820             ctx->makeCurrent();
821         }
822 
823         glMatrixMode(GL_MODELVIEW);
824         glLoadIdentity();
825 
826         glMatrixMode(GL_PROJECTION);
827         glLoadIdentity();
828 #ifndef QT_OPENGL_ES
829         glOrtho(0, size.width(), size.height(), 0, -999999, 999999);
830 #else
831         glOrthof(0, size.width(), size.height(), 0, -999999, 999999);
832 #endif
833         glViewport(0, 0, size.width(), size.height());
834 
835         glColor4f(1, 1, 1, 1);
836         drawTexture(rect, texture, window()->size(), br);
837 
838         if (d_ptr->fbo)
839             d_ptr->fbo->bind();
840     }
841 #else
842     // OpenGL/ES 2.0 version of the fbo blit.
843     else if (d_ptr->fbo) {
844         Q_UNUSED(target);
845 
846         if (d_ptr->fbo->isBound())
847             d_ptr->fbo->release();
848 
849         blitTexture(ctx, d_ptr->fbo->texture(), size, window()->size(), rect, br);
850     }
851 #endif
852 
853     if (ctx->format().doubleBuffer())
854         ctx->swapBuffers();
855     else
856         glFlush();
857 
858     d_ptr->did_paint = false;
859 }
860 
861 
862 #if !defined(Q_WS_QPA)
setGeometry(const QRect & rect)863 void QGLWindowSurface::setGeometry(const QRect &rect)
864 {
865     QWindowSurface::setGeometry(rect);
866     d_ptr->geometry_updated = true;
867 }
868 #else
resize(const QSize & size)869 void QGLWindowSurface::resize(const QSize &size)
870 {
871     QWindowSurface::resize(size);
872     d_ptr->geometry_updated = true;
873 }
874 #endif
875 
updateGeometry()876 void QGLWindowSurface::updateGeometry() {
877     if (!d_ptr->geometry_updated)
878         return;
879     d_ptr->geometry_updated = false;
880 
881     bool hijack(true);
882     QWidgetPrivate *wd = window()->d_func();
883     if (wd->extraData() && wd->extraData()->glContext) {
884 #ifdef Q_OS_SYMBIAN // Symbian needs to recreate the context when native window size changes
885         if (d_ptr->size != geometry().size()) {
886             QGLContext *ctx = reinterpret_cast<QGLContext *>(wd->extraData()->glContext);
887 
888             if (ctx == QGLContext::currentContext())
889                  ctx->doneCurrent();
890 
891             ctx->d_func()->destroyEglSurfaceForDevice();
892 
893             // Delete other contexts (shouldn't happen too often, if at all)
894             while (d_ptr->contexts.size()) {
895                 QGLContext **ctxPtrPtr = d_ptr->contexts.takeFirst();
896                 if ((*ctxPtrPtr) != ctx)
897                     delete *ctxPtrPtr;
898             }
899             union { QGLContext **ctxPtrPtr; void **voidPtrPtr; };
900             voidPtrPtr = &wd->extraData()->glContext;
901             d_ptr->contexts << ctxPtrPtr;
902 
903             ctx->d_func()->eglSurface = ctx->d_func()->eglContext->createSurface(window());
904 
905             // Swap behaviour has been checked already in previous hijackWindow call.
906             // Reset swap behaviour based on that flag.
907             if (d_ptr->destructive_swap_buffers) {
908                 eglSurfaceAttrib(QEgl::display(), ctx->d_func()->eglSurfaceForDevice(),
909                                     EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
910 
911                 if (eglGetError() != EGL_SUCCESS)
912                     qWarning("QGLWindowSurface::updateGeometry() - could not re-enable destroyed swap behaviour");
913             } else {
914                 eglSurfaceAttrib(QEgl::display(), ctx->d_func()->eglSurfaceForDevice(),
915                                     EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
916 
917                 if (eglGetError() != EGL_SUCCESS)
918                     qWarning("QGLWindowSurface::updateGeometry() - could not re-enable preserved swap behaviour");
919             }
920         }
921 #endif
922         hijack = false; // we already have gl context for widget
923     }
924 
925     if (hijack)
926         hijackWindow(window());
927 
928     QGLContext *ctx = reinterpret_cast<QGLContext *>(wd->extraData()->glContext);
929 #ifdef Q_OS_SYMBIAN
930     if (!ctx)
931         return;
932 #endif
933 #ifdef Q_WS_MAC
934     ctx->updatePaintDevice();
935 #endif
936 
937     QSize surfSize = geometry().size();
938 
939     if (surfSize.width() <= 0 || surfSize.height() <= 0)
940         return;
941 
942     if (d_ptr->size == surfSize)
943         return;
944 
945     d_ptr->size = surfSize;
946 
947     if (d_ptr->ctx) {
948 #ifndef QT_OPENGL_ES_2
949         if (d_ptr->destructive_swap_buffers)
950             initializeOffscreenTexture(surfSize);
951 #endif
952         return;
953     }
954 
955     const GLenum target = GL_TEXTURE_2D;
956     if (d_ptr->destructive_swap_buffers
957         && (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject)
958         && (d_ptr->fbo || !d_ptr->tried_fbo)
959         && qt_gl_preferGL2Engine())
960     {
961         d_ptr->tried_fbo = true;
962         ctx->d_ptr->internal_context = true;
963         ctx->makeCurrent();
964         delete d_ptr->fbo;
965 
966         QGLFramebufferObjectFormat format;
967         format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
968         format.setInternalTextureFormat(GLenum(GL_RGBA));
969         format.setTextureTarget(target);
970 
971         if (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)
972             format.setSamples(8);
973 
974         d_ptr->fbo = new QGLFramebufferObject(surfSize, format);
975 
976         if (d_ptr->fbo->isValid()) {
977             qDebug() << "Created Window Surface FBO" << surfSize
978                      << "with samples" << d_ptr->fbo->format().samples();
979             return;
980         } else {
981             qDebug() << "QGLWindowSurface: Failed to create valid FBO, falling back";
982             delete d_ptr->fbo;
983             d_ptr->fbo = 0;
984         }
985     }
986 
987 #if !defined(QT_OPENGL_ES_2) && !defined(Q_WS_QPA) //QPA doesn't support pixelbuffers
988     if (d_ptr->destructive_swap_buffers && (d_ptr->pb || !d_ptr->tried_pb)) {
989         d_ptr->tried_pb = true;
990 
991         if (d_ptr->pb) {
992             d_ptr->pb->makeCurrent();
993             glDeleteTextures(1, &d_ptr->pb_tex_id);
994         }
995 
996         delete d_ptr->pb;
997 
998         d_ptr->pb = new QGLPixelBuffer(surfSize.width(), surfSize.height(),
999                                         QGLFormat(QGL::SampleBuffers | QGL::StencilBuffer | QGL::DepthBuffer),
1000                                         qt_gl_share_widget());
1001 
1002         if (d_ptr->pb->isValid()) {
1003             qDebug() << "Created Window Surface Pixelbuffer, Sample buffers:" << d_ptr->pb->format().sampleBuffers();
1004             d_ptr->pb->makeCurrent();
1005 
1006             glGenTextures(1, &d_ptr->pb_tex_id);
1007             glBindTexture(target, d_ptr->pb_tex_id);
1008             glTexImage2D(target, 0, GL_RGBA, surfSize.width(), surfSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
1009 
1010             glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1011             glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1012             glBindTexture(target, 0);
1013 
1014             glMatrixMode(GL_PROJECTION);
1015             glLoadIdentity();
1016             glOrtho(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999);
1017 
1018             d_ptr->pb->d_ptr->qctx->d_func()->internal_context = true;
1019             return;
1020         } else {
1021             qDebug() << "QGLWindowSurface: Failed to create valid pixelbuffer, falling back";
1022             delete d_ptr->pb;
1023             d_ptr->pb = 0;
1024         }
1025     }
1026 #endif // !defined(QT_OPENGL_ES_2) !defined(Q_WS_QPA)
1027 
1028     ctx->makeCurrent();
1029 
1030 #ifndef QT_OPENGL_ES_2
1031     if (d_ptr->destructive_swap_buffers)
1032         initializeOffscreenTexture(surfSize);
1033 #endif
1034 #ifndef Q_OS_SYMBIAN
1035     qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;
1036 #endif
1037     d_ptr->ctx = ctx;
1038     d_ptr->ctx->d_ptr->internal_context = true;
1039 }
1040 
initializeOffscreenTexture(const QSize & size)1041 bool QGLWindowSurface::initializeOffscreenTexture(const QSize &size)
1042 {
1043     if (size == d_ptr->textureSize)
1044         return false;
1045 
1046     glGenTextures(1, &d_ptr->tex_id);
1047     glBindTexture(GL_TEXTURE_2D, d_ptr->tex_id);
1048     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
1049 
1050     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1051     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1052     glBindTexture(GL_TEXTURE_2D, 0);
1053 
1054     d_ptr->textureSize = size;
1055     return true;
1056 }
1057 
scroll(const QRegion & area,int dx,int dy)1058 bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy)
1059 {
1060     // this code randomly fails currently for unknown reasons
1061     return false;
1062 
1063     if (!d_ptr->pb)
1064         return false;
1065 
1066     d_ptr->pb->makeCurrent();
1067 
1068     QRect br = area.boundingRect();
1069 
1070 #if 0
1071     // ## workaround driver issue (scrolling by these deltas is unbearably slow for some reason)
1072     // ## maybe we should use glCopyTexSubImage insteadk
1073     if (dx == 1 || dx == -1 || dy == 1 || dy == -1 || dy == 2)
1074         return false;
1075 
1076     glRasterPos2i(br.x() + dx, br.y() + br.height() + dy);
1077     glCopyPixels(br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), GL_COLOR);
1078     return true;
1079 #endif
1080 
1081     const GLenum target = GL_TEXTURE_2D;
1082 
1083     glBindTexture(target, d_ptr->tex_id);
1084     glCopyTexImage2D(target, 0, GL_RGBA, br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), 0);
1085     glBindTexture(target, 0);
1086 
1087     drawTexture(br.translated(dx, dy), d_ptr->tex_id, window()->size());
1088 
1089     return true;
1090 }
1091 
drawTexture(const QRectF & rect,GLuint tex_id,const QSize & texSize,const QRectF & br)1092 static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &br)
1093 {
1094     const GLenum target = GL_TEXTURE_2D;
1095     QRectF src = br.isEmpty()
1096         ? QRectF(QPointF(), texSize)
1097         : QRectF(QPointF(br.x(), texSize.height() - br.bottom()), br.size());
1098 
1099     if (target == GL_TEXTURE_2D) {
1100         qreal width = texSize.width();
1101         qreal height = texSize.height();
1102 
1103         src.setLeft(src.left() / width);
1104         src.setRight(src.right() / width);
1105         src.setTop(src.top() / height);
1106         src.setBottom(src.bottom() / height);
1107     }
1108 
1109     const GLfloat tx1 = src.left();
1110     const GLfloat tx2 = src.right();
1111     const GLfloat ty1 = src.top();
1112     const GLfloat ty2 = src.bottom();
1113 
1114     GLfloat texCoordArray[4*2] = {
1115         tx1, ty2, tx2, ty2, tx2, ty1, tx1, ty1
1116     };
1117 
1118     GLfloat vertexArray[4*2];
1119     extern void qt_add_rect_to_array(const QRectF &r, GLfloat *array); // qpaintengine_opengl.cpp
1120     qt_add_rect_to_array(rect, vertexArray);
1121 
1122 #if !defined(QT_OPENGL_ES_2)
1123     glVertexPointer(2, GL_FLOAT, 0, vertexArray);
1124     glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
1125 
1126     glBindTexture(target, tex_id);
1127     glEnable(target);
1128 
1129     glEnableClientState(GL_VERTEX_ARRAY);
1130     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1131     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1132     glDisableClientState(GL_VERTEX_ARRAY);
1133     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1134 
1135     glDisable(target);
1136     glBindTexture(target, 0);
1137 #else
1138     glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexArray);
1139     glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, texCoordArray);
1140 
1141     glBindTexture(target, tex_id);
1142 
1143     glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1144     glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
1145     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1146     glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1147     glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
1148 
1149     glBindTexture(target, 0);
1150 #endif
1151 }
1152 
buffer(const QWidget * widget)1153 QImage *QGLWindowSurface::buffer(const QWidget *widget)
1154 {
1155     QImage image;
1156 
1157     if (d_ptr->pb)
1158         image = d_ptr->pb->toImage();
1159     else if (d_ptr->fbo)
1160         image = d_ptr->fbo->toImage();
1161 
1162     if (image.isNull())
1163         return 0;
1164 
1165     QRect rect = widget->rect();
1166     rect.translate(widget->mapTo(widget->window(), QPoint()));
1167 
1168     QImage subImage = image.copy(rect);
1169     d_ptr->buffers << subImage;
1170     return &d_ptr->buffers.last();
1171 }
1172 
features() const1173 QWindowSurface::WindowSurfaceFeatures QGLWindowSurface::features() const
1174 {
1175     WindowSurfaceFeatures features = 0;
1176     if (!d_ptr->destructive_swap_buffers || d_ptr->swap_region_support)
1177         features |= PartialUpdates;
1178     if (!d_ptr->destructive_swap_buffers)
1179         features |= PreservedContents;
1180     return features;
1181 }
1182 
1183 QT_END_NAMESPACE
1184 
1185