util/src/opengl/qwindowsurface_gl.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtOpenGL module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    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 #ifdef QT_OPENGL_ES_1_CL
       
    86 #include "qgl_cl_p.h"
       
    87 #endif
       
    88 
       
    89 #ifdef QT_OPENGL_ES
       
    90 #include <private/qegl_p.h>
       
    91 #endif
       
    92 
       
    93 QT_BEGIN_NAMESPACE
       
    94 
       
    95 //
       
    96 // QGLGraphicsSystem
       
    97 //
       
    98 #ifdef Q_WS_WIN
       
    99 extern Q_GUI_EXPORT bool qt_win_owndc_required;
       
   100 #endif
       
   101 QGLGraphicsSystem::QGLGraphicsSystem()
       
   102     : QGraphicsSystem()
       
   103 {
       
   104 #if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
       
   105     // only override the system defaults if the user hasn't already
       
   106     // picked a visual
       
   107     if (X11->visual == 0 && X11->visual_id == -1 && X11->visual_class == -1) {
       
   108         // find a double buffered, RGBA visual that supports OpenGL
       
   109         // and set that as the default visual for windows in Qt
       
   110         int i = 0;
       
   111         int spec[16];
       
   112         spec[i++] = GLX_RGBA;
       
   113         spec[i++] = GLX_DOUBLEBUFFER;
       
   114 
       
   115         if (!qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull()) {
       
   116             spec[i++] = GLX_DEPTH_SIZE;
       
   117             spec[i++] = 8;
       
   118             spec[i++] = GLX_STENCIL_SIZE;
       
   119             spec[i++] = 8;
       
   120             spec[i++] = GLX_SAMPLE_BUFFERS_ARB;
       
   121             spec[i++] = 1;
       
   122             spec[i++] = GLX_SAMPLES_ARB;
       
   123             spec[i++] = 4;
       
   124         }
       
   125 
       
   126         spec[i++] = XNone;
       
   127 
       
   128         XVisualInfo *vi = glXChooseVisual(X11->display, X11->defaultScreen, spec);
       
   129         if (vi) {
       
   130             X11->visual_id = vi->visualid;
       
   131             X11->visual_class = vi->c_class;
       
   132 
       
   133             QGLFormat format;
       
   134             int res;
       
   135             glXGetConfig(X11->display, vi, GLX_LEVEL, &res);
       
   136             format.setPlane(res);
       
   137             glXGetConfig(X11->display, vi, GLX_DOUBLEBUFFER, &res);
       
   138             format.setDoubleBuffer(res);
       
   139             glXGetConfig(X11->display, vi, GLX_DEPTH_SIZE, &res);
       
   140             format.setDepth(res);
       
   141             if (format.depth())
       
   142                 format.setDepthBufferSize(res);
       
   143             glXGetConfig(X11->display, vi, GLX_RGBA, &res);
       
   144             format.setRgba(res);
       
   145             glXGetConfig(X11->display, vi, GLX_RED_SIZE, &res);
       
   146             format.setRedBufferSize(res);
       
   147             glXGetConfig(X11->display, vi, GLX_GREEN_SIZE, &res);
       
   148             format.setGreenBufferSize(res);
       
   149             glXGetConfig(X11->display, vi, GLX_BLUE_SIZE, &res);
       
   150             format.setBlueBufferSize(res);
       
   151             glXGetConfig(X11->display, vi, GLX_ALPHA_SIZE, &res);
       
   152             format.setAlpha(res);
       
   153             if (format.alpha())
       
   154                 format.setAlphaBufferSize(res);
       
   155             glXGetConfig(X11->display, vi, GLX_ACCUM_RED_SIZE, &res);
       
   156             format.setAccum(res);
       
   157             if (format.accum())
       
   158                 format.setAccumBufferSize(res);
       
   159             glXGetConfig(X11->display, vi, GLX_STENCIL_SIZE, &res);
       
   160             format.setStencil(res);
       
   161             if (format.stencil())
       
   162                 format.setStencilBufferSize(res);
       
   163             glXGetConfig(X11->display, vi, GLX_STEREO, &res);
       
   164             format.setStereo(res);
       
   165             glXGetConfig(X11->display, vi, GLX_SAMPLE_BUFFERS_ARB, &res);
       
   166             format.setSampleBuffers(res);
       
   167             if (format.sampleBuffers()) {
       
   168                 glXGetConfig(X11->display, vi, GLX_SAMPLES_ARB, &res);
       
   169                 format.setSamples(res);
       
   170             }
       
   171 
       
   172             QGLWindowSurface::surfaceFormat = format;
       
   173             XFree(vi);
       
   174 
       
   175             printf("using visual class %x, id %x\n", X11->visual_class, X11->visual_id);
       
   176         }
       
   177     }
       
   178 #elif defined(Q_WS_WIN)
       
   179     QGLWindowSurface::surfaceFormat.setDoubleBuffer(true);
       
   180 
       
   181     qt_win_owndc_required = true;
       
   182 #endif
       
   183 }
       
   184 
       
   185 //
       
   186 // QGLWindowSurface
       
   187 //
       
   188 
       
   189 class QGLGlobalShareWidget
       
   190 {
       
   191 public:
       
   192     QGLGlobalShareWidget() : widget(0), initializing(false) {}
       
   193 
       
   194     QGLWidget *shareWidget() {
       
   195         if (!initializing && !widget && !cleanedUp) {
       
   196             initializing = true;
       
   197             widget = new QGLWidget;
       
   198             // We dont need this internal widget to appear in QApplication::topLevelWidgets()
       
   199             if (QWidgetPrivate::allWidgets)
       
   200                 QWidgetPrivate::allWidgets->remove(widget);
       
   201             initializing = false;
       
   202         }
       
   203         return widget;
       
   204     }
       
   205 
       
   206     void cleanup() {
       
   207         QGLWidget *w = widget;
       
   208         cleanedUp = true;
       
   209         widget = 0;
       
   210         delete w;
       
   211     }
       
   212 
       
   213     static bool cleanedUp;
       
   214 
       
   215 private:
       
   216     QGLWidget *widget;
       
   217     bool initializing;
       
   218 };
       
   219 
       
   220 bool QGLGlobalShareWidget::cleanedUp = false;
       
   221 
       
   222 static void qt_cleanup_gl_share_widget();
       
   223 Q_GLOBAL_STATIC_WITH_INITIALIZER(QGLGlobalShareWidget, _qt_gl_share_widget,
       
   224                                  {
       
   225                                      qAddPostRoutine(qt_cleanup_gl_share_widget);
       
   226                                  })
       
   227 
       
   228 static void qt_cleanup_gl_share_widget()
       
   229 {
       
   230     _qt_gl_share_widget()->cleanup();
       
   231 }
       
   232 
       
   233 QGLWidget* qt_gl_share_widget()
       
   234 {
       
   235     if (QGLGlobalShareWidget::cleanedUp)
       
   236         return 0;
       
   237     return _qt_gl_share_widget()->shareWidget();
       
   238 }
       
   239 
       
   240 
       
   241 struct QGLWindowSurfacePrivate
       
   242 {
       
   243     QGLFramebufferObject *fbo;
       
   244     QGLPixelBuffer *pb;
       
   245     GLuint tex_id;
       
   246     GLuint pb_tex_id;
       
   247 
       
   248     int tried_fbo : 1;
       
   249     int tried_pb : 1;
       
   250     int destructive_swap_buffers : 1;
       
   251     int geometry_updated : 1;
       
   252 
       
   253     QGLContext *ctx;
       
   254 
       
   255     QList<QGLContext **> contexts;
       
   256 
       
   257     QRegion paintedRegion;
       
   258     QSize size;
       
   259 
       
   260     QList<QImage> buffers;
       
   261     QGLWindowSurfaceGLPaintDevice glDevice;
       
   262     QGLWindowSurface* q_ptr;
       
   263 };
       
   264 
       
   265 QGLFormat QGLWindowSurface::surfaceFormat;
       
   266 
       
   267 void QGLWindowSurfaceGLPaintDevice::endPaint()
       
   268 {
       
   269     glFlush();
       
   270     QGLPaintDevice::endPaint();
       
   271 }
       
   272 
       
   273 QSize QGLWindowSurfaceGLPaintDevice::size() const
       
   274 {
       
   275     return d->size;
       
   276 }
       
   277 
       
   278 QGLContext* QGLWindowSurfaceGLPaintDevice::context() const
       
   279 {
       
   280     return d->ctx;
       
   281 }
       
   282 
       
   283 
       
   284 int QGLWindowSurfaceGLPaintDevice::metric(PaintDeviceMetric m) const
       
   285 {
       
   286     return qt_paint_device_metric(d->q_ptr->window(), m);
       
   287 }
       
   288 
       
   289 QPaintEngine *QGLWindowSurfaceGLPaintDevice::paintEngine() const
       
   290 {
       
   291     return qt_qgl_paint_engine();
       
   292 }
       
   293 
       
   294 QGLWindowSurface::QGLWindowSurface(QWidget *window)
       
   295     : QWindowSurface(window), d_ptr(new QGLWindowSurfacePrivate)
       
   296 {
       
   297     Q_ASSERT(window->isTopLevel());
       
   298     d_ptr->pb = 0;
       
   299     d_ptr->fbo = 0;
       
   300     d_ptr->ctx = 0;
       
   301 #if defined (QT_OPENGL_ES_2)
       
   302     d_ptr->tried_fbo = true;
       
   303     d_ptr->tried_pb = true;
       
   304 #else
       
   305     d_ptr->tried_fbo = false;
       
   306     d_ptr->tried_pb = false;
       
   307 #endif
       
   308     d_ptr->destructive_swap_buffers = qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull();
       
   309     d_ptr->glDevice.d = d_ptr;
       
   310     d_ptr->q_ptr = this;
       
   311     d_ptr->geometry_updated = false;
       
   312 }
       
   313 
       
   314 QGLWindowSurface::~QGLWindowSurface()
       
   315 {
       
   316     if (d_ptr->ctx)
       
   317         glDeleteTextures(1, &d_ptr->tex_id);
       
   318     foreach(QGLContext **ctx, d_ptr->contexts) {
       
   319         delete *ctx;
       
   320         *ctx = 0;
       
   321     }
       
   322 
       
   323     delete d_ptr->pb;
       
   324     delete d_ptr->fbo;
       
   325     delete d_ptr;
       
   326 }
       
   327 
       
   328 void QGLWindowSurface::deleted(QObject *object)
       
   329 {
       
   330     // Make sure that the fbo is destroyed before destroying its context.
       
   331     delete d_ptr->fbo;
       
   332     d_ptr->fbo = 0;
       
   333 
       
   334     QWidget *widget = qobject_cast<QWidget *>(object);
       
   335     if (widget) {
       
   336         QWidgetPrivate *widgetPrivate = widget->d_func();
       
   337         if (widgetPrivate->extraData()) {
       
   338             union { QGLContext **ctxPtr; void **voidPtr; };
       
   339             voidPtr = &widgetPrivate->extraData()->glContext;
       
   340             int index = d_ptr->contexts.indexOf(ctxPtr);
       
   341             if (index != -1) {
       
   342                 delete *ctxPtr;
       
   343                 *ctxPtr = 0;
       
   344                 d_ptr->contexts.removeAt(index);
       
   345             }
       
   346         }
       
   347     }
       
   348 }
       
   349 
       
   350 void QGLWindowSurface::hijackWindow(QWidget *widget)
       
   351 {
       
   352     QWidgetPrivate *widgetPrivate = widget->d_func();
       
   353     widgetPrivate->createExtra();
       
   354     if (widgetPrivate->extraData()->glContext)
       
   355         return;
       
   356 
       
   357     QGLContext *ctx = new QGLContext(surfaceFormat, widget);
       
   358     ctx->create(qt_gl_share_widget()->context());
       
   359 
       
   360 #if defined(Q_WS_X11) && defined(QT_OPENGL_ES)
       
   361     // Create the EGL surface to draw into.  QGLContext::chooseContext()
       
   362     // does not do this for X11/EGL, but does do it for other platforms.
       
   363     // This probably belongs in qgl_x11egl.cpp.
       
   364     QGLContextPrivate *ctxpriv = ctx->d_func();
       
   365     ctxpriv->eglSurface = ctxpriv->eglContext->createSurface(widget);
       
   366     if (ctxpriv->eglSurface == EGL_NO_SURFACE) {
       
   367         qWarning() << "hijackWindow() could not create EGL surface";
       
   368     }
       
   369     qDebug("QGLWindowSurface - using EGLConfig %d", reinterpret_cast<int>(ctxpriv->eglContext->config()));
       
   370 #endif
       
   371 
       
   372     widgetPrivate->extraData()->glContext = ctx;
       
   373 
       
   374     union { QGLContext **ctxPtr; void **voidPtr; };
       
   375 
       
   376     connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(deleted(QObject*)));
       
   377 
       
   378     voidPtr = &widgetPrivate->extraData()->glContext;
       
   379     d_ptr->contexts << ctxPtr;
       
   380     qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size();
       
   381 }
       
   382 
       
   383 QGLContext *QGLWindowSurface::context() const
       
   384 {
       
   385     return d_ptr->ctx;
       
   386 }
       
   387 
       
   388 QPaintDevice *QGLWindowSurface::paintDevice()
       
   389 {
       
   390     updateGeometry();
       
   391 
       
   392     if (d_ptr->pb)
       
   393         return d_ptr->pb;
       
   394 
       
   395     if (d_ptr->ctx)
       
   396         return &d_ptr->glDevice;
       
   397 
       
   398     QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
       
   399     ctx->makeCurrent();
       
   400     return d_ptr->fbo;
       
   401 }
       
   402 
       
   403 static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &src = QRectF());
       
   404 
       
   405 void QGLWindowSurface::beginPaint(const QRegion &)
       
   406 {
       
   407 }
       
   408 
       
   409 void QGLWindowSurface::endPaint(const QRegion &rgn)
       
   410 {
       
   411     if (context())
       
   412         d_ptr->paintedRegion |= rgn;
       
   413 
       
   414     d_ptr->buffers.clear();
       
   415 }
       
   416 
       
   417 void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
       
   418 {
       
   419     if (context() && widget != window()) {
       
   420         qWarning("No native child widget support in GL window surface without FBOs or pixel buffers");
       
   421         return;
       
   422     }
       
   423 
       
   424     //### Find out why d_ptr->geometry_updated isn't always false.
       
   425     // flush() should not be called when d_ptr->geometry_updated is true. It assumes that either
       
   426     // d_ptr->fbo or d_ptr->pb is allocated and has the correct size.
       
   427     if (d_ptr->geometry_updated)
       
   428         return;
       
   429 
       
   430     QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget();
       
   431     Q_ASSERT(parent);
       
   432 
       
   433     if (!geometry().isValid())
       
   434         return;
       
   435 
       
   436     // Needed to support native child-widgets...
       
   437     hijackWindow(parent);
       
   438 
       
   439     QRect br = rgn.boundingRect().translated(offset);
       
   440     br = br.intersected(window()->rect());
       
   441     QPoint wOffset = qt_qwidget_data(parent)->wrect.topLeft();
       
   442     QRect rect = br.translated(-offset - wOffset);
       
   443 
       
   444     const GLenum target = GL_TEXTURE_2D;
       
   445     Q_UNUSED(target);
       
   446 
       
   447     if (context()) {
       
   448         context()->makeCurrent();
       
   449 
       
   450         if (context()->format().doubleBuffer()) {
       
   451 #if !defined(QT_OPENGL_ES_2)
       
   452             if (d_ptr->destructive_swap_buffers) {
       
   453                 glBindTexture(target, d_ptr->tex_id);
       
   454 
       
   455                 QVector<QRect> rects = d_ptr->paintedRegion.rects();
       
   456                 for (int i = 0; i < rects.size(); ++i) {
       
   457                     QRect br = rects.at(i);
       
   458                     if (br.isEmpty())
       
   459                         continue;
       
   460 
       
   461                     const uint bottom = window()->height() - (br.y() + br.height());
       
   462                     glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
       
   463                 }
       
   464 
       
   465                 glBindTexture(target, 0);
       
   466 
       
   467                 QRegion dirtyRegion = QRegion(window()->rect()) - d_ptr->paintedRegion;
       
   468 
       
   469                 if (!dirtyRegion.isEmpty()) {
       
   470                     glMatrixMode(GL_MODELVIEW);
       
   471                     glLoadIdentity();
       
   472 
       
   473                     glMatrixMode(GL_PROJECTION);
       
   474                     glLoadIdentity();
       
   475 #ifndef QT_OPENGL_ES
       
   476                     glOrtho(0, window()->width(), window()->height(), 0, -999999, 999999);
       
   477 #else
       
   478                     glOrthof(0, window()->width(), window()->height(), 0, -999999, 999999);
       
   479 #endif
       
   480                     glViewport(0, 0, window()->width(), window()->height());
       
   481 
       
   482                     QVector<QRect> rects = dirtyRegion.rects();
       
   483                     glColor4f(1, 1, 1, 1);
       
   484                     for (int i = 0; i < rects.size(); ++i) {
       
   485                         QRect rect = rects.at(i);
       
   486                         if (rect.isEmpty())
       
   487                             continue;
       
   488 
       
   489                         drawTexture(rect, d_ptr->tex_id, window()->size(), rect);
       
   490                     }
       
   491                 }
       
   492             }
       
   493 #endif
       
   494             d_ptr->paintedRegion = QRegion();
       
   495             context()->swapBuffers();
       
   496         } else {
       
   497             glFlush();
       
   498         }
       
   499 
       
   500         return;
       
   501     }
       
   502 
       
   503     QGLContext *previous_ctx = const_cast<QGLContext *>(QGLContext::currentContext());
       
   504     QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext);
       
   505 
       
   506     // QPainter::end() should have unbound the fbo, otherwise something is very wrong...
       
   507     Q_ASSERT(!d_ptr->fbo || !d_ptr->fbo->isBound());
       
   508 
       
   509     if (ctx != previous_ctx) {
       
   510         ctx->makeCurrent();
       
   511     }
       
   512 
       
   513     QSize size = widget->rect().size();
       
   514     if (d_ptr->destructive_swap_buffers && ctx->format().doubleBuffer()) {
       
   515         rect = parent->rect();
       
   516         br = rect.translated(wOffset + offset);
       
   517         size = parent->size();
       
   518     }
       
   519 
       
   520     glDisable(GL_SCISSOR_TEST);
       
   521 
       
   522     if (d_ptr->fbo && (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)) {
       
   523         const int h = d_ptr->fbo->height();
       
   524 
       
   525         const int sx0 = br.left();
       
   526         const int sx1 = br.left() + br.width();
       
   527         const int sy0 = h - (br.top() + br.height());
       
   528         const int sy1 = h - br.top();
       
   529 
       
   530         const int tx0 = rect.left();
       
   531         const int tx1 = rect.left() + rect.width();
       
   532         const int ty0 = parent->height() - (rect.top() + rect.height());
       
   533         const int ty1 = parent->height() - rect.top();
       
   534 
       
   535         if (window() == parent || d_ptr->fbo->format().samples() <= 1) {
       
   536             // glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
       
   537             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle());
       
   538 
       
   539             glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
       
   540                     tx0, ty0, tx1, ty1,
       
   541                     GL_COLOR_BUFFER_BIT,
       
   542                     GL_NEAREST);
       
   543 
       
   544             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
       
   545         } else {
       
   546             // can't do sub-region blits with multisample FBOs
       
   547             QGLFramebufferObject *temp = qgl_fbo_pool()->acquire(d_ptr->fbo->size(), QGLFramebufferObjectFormat());
       
   548 
       
   549             glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, temp->handle());
       
   550             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle());
       
   551 
       
   552             glBlitFramebufferEXT(0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(),
       
   553                     0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(),
       
   554                     GL_COLOR_BUFFER_BIT,
       
   555                     GL_NEAREST);
       
   556 
       
   557             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, temp->handle());
       
   558             glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
       
   559 
       
   560             glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
       
   561                     tx0, ty0, tx1, ty1,
       
   562                     GL_COLOR_BUFFER_BIT,
       
   563                     GL_NEAREST);
       
   564 
       
   565             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
       
   566 
       
   567             qgl_fbo_pool()->release(temp);
       
   568         }
       
   569     }
       
   570 #if !defined(QT_OPENGL_ES_2)
       
   571     else {
       
   572         GLuint texture;
       
   573     if (d_ptr->fbo) {
       
   574         texture = d_ptr->fbo->texture();
       
   575     } else {
       
   576         d_ptr->pb->makeCurrent();
       
   577         glBindTexture(target, d_ptr->pb_tex_id);
       
   578         const uint bottom = window()->height() - (br.y() + br.height());
       
   579         glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
       
   580         texture = d_ptr->pb_tex_id;
       
   581         glBindTexture(target, 0);
       
   582     }
       
   583 
       
   584         glDisable(GL_DEPTH_TEST);
       
   585 
       
   586         if (d_ptr->fbo) {
       
   587             d_ptr->fbo->release();
       
   588         } else {
       
   589             ctx->makeCurrent();
       
   590         }
       
   591 
       
   592         glMatrixMode(GL_MODELVIEW);
       
   593         glLoadIdentity();
       
   594 
       
   595         glMatrixMode(GL_PROJECTION);
       
   596         glLoadIdentity();
       
   597 #ifndef QT_OPENGL_ES
       
   598         glOrtho(0, size.width(), size.height(), 0, -999999, 999999);
       
   599 #else
       
   600         glOrthof(0, size.width(), size.height(), 0, -999999, 999999);
       
   601 #endif
       
   602         glViewport(0, 0, size.width(), size.height());
       
   603 
       
   604         glColor4f(1, 1, 1, 1);
       
   605         drawTexture(rect, texture, window()->size(), br);
       
   606 
       
   607         if (d_ptr->fbo)
       
   608             d_ptr->fbo->bind();
       
   609     }
       
   610 #else
       
   611     // OpenGL/ES 2.0 version of the fbo blit.
       
   612     else if (d_ptr->fbo) {
       
   613         Q_UNUSED(target);
       
   614 
       
   615         GLuint texture = d_ptr->fbo->texture();
       
   616 
       
   617         glDisable(GL_DEPTH_TEST);
       
   618 
       
   619         if (d_ptr->fbo->isBound())
       
   620             d_ptr->fbo->release();
       
   621 
       
   622         glViewport(0, 0, size.width(), size.height());
       
   623 
       
   624         QGLShaderProgram *blitProgram =
       
   625             QGLEngineSharedShaders::shadersForContext(ctx)->blitProgram();
       
   626         blitProgram->bind();
       
   627         blitProgram->setUniformValue("imageTexture", 0 /*QT_IMAGE_TEXTURE_UNIT*/);
       
   628 
       
   629         // The shader manager's blit program does not multiply the
       
   630         // vertices by the pmv matrix, so we need to do the effect
       
   631         // of the orthographic projection here ourselves.
       
   632         QRectF r;
       
   633         qreal w = size.width() ? size.width() : 1.0f;
       
   634         qreal h = size.height() ? size.height() : 1.0f;
       
   635         r.setLeft((rect.left() / w) * 2.0f - 1.0f);
       
   636         if (rect.right() == (size.width() - 1))
       
   637             r.setRight(1.0f);
       
   638         else
       
   639             r.setRight((rect.right() / w) * 2.0f - 1.0f);
       
   640         r.setBottom((rect.top() / h) * 2.0f - 1.0f);
       
   641         if (rect.bottom() == (size.height() - 1))
       
   642             r.setTop(1.0f);
       
   643         else
       
   644             r.setTop((rect.bottom() / w) * 2.0f - 1.0f);
       
   645 
       
   646         drawTexture(r, texture, window()->size(), br);
       
   647     }
       
   648 #endif
       
   649 
       
   650     if (ctx->format().doubleBuffer())
       
   651         ctx->swapBuffers();
       
   652     else
       
   653         glFlush();
       
   654 }
       
   655 
       
   656 
       
   657 void QGLWindowSurface::setGeometry(const QRect &rect)
       
   658 {
       
   659     QWindowSurface::setGeometry(rect);
       
   660     d_ptr->geometry_updated = true;
       
   661 }
       
   662 
       
   663 
       
   664 void QGLWindowSurface::updateGeometry() {
       
   665     if (!d_ptr->geometry_updated)
       
   666         return;
       
   667     d_ptr->geometry_updated = false;
       
   668 
       
   669 
       
   670     QRect rect = geometry();
       
   671     hijackWindow(window());
       
   672     QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
       
   673 
       
   674 #ifdef Q_WS_MAC
       
   675     ctx->updatePaintDevice();
       
   676 #endif
       
   677 
       
   678     const GLenum target = GL_TEXTURE_2D;
       
   679 
       
   680     if (rect.width() <= 0 || rect.height() <= 0)
       
   681         return;
       
   682 
       
   683     if (d_ptr->size == rect.size())
       
   684         return;
       
   685 
       
   686     d_ptr->size = rect.size();
       
   687 
       
   688     if (d_ptr->ctx) {
       
   689 #ifndef QT_OPENGL_ES_2
       
   690         if (d_ptr->destructive_swap_buffers) {
       
   691             glBindTexture(target, d_ptr->tex_id);
       
   692             glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
       
   693             glBindTexture(target, 0);
       
   694         }
       
   695 #endif
       
   696         return;
       
   697     }
       
   698 
       
   699     if (d_ptr->destructive_swap_buffers
       
   700         && (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject)
       
   701         && (d_ptr->fbo || !d_ptr->tried_fbo)
       
   702         && qt_gl_preferGL2Engine())
       
   703     {
       
   704         d_ptr->tried_fbo = true;
       
   705         ctx->d_ptr->internal_context = true;
       
   706         ctx->makeCurrent();
       
   707         delete d_ptr->fbo;
       
   708 
       
   709         QGLFramebufferObjectFormat format;
       
   710         format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
       
   711         format.setInternalTextureFormat(GLenum(GL_RGBA));
       
   712         format.setTextureTarget(target);
       
   713 
       
   714         if (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)
       
   715             format.setSamples(8);
       
   716 
       
   717         d_ptr->fbo = new QGLFramebufferObject(rect.size(), format);
       
   718 
       
   719         if (d_ptr->fbo->isValid()) {
       
   720             qDebug() << "Created Window Surface FBO" << rect.size()
       
   721                      << "with samples" << d_ptr->fbo->format().samples();
       
   722             return;
       
   723         } else {
       
   724             qDebug() << "QGLWindowSurface: Failed to create valid FBO, falling back";
       
   725             delete d_ptr->fbo;
       
   726             d_ptr->fbo = 0;
       
   727         }
       
   728     }
       
   729 
       
   730 #if !defined(QT_OPENGL_ES_2)
       
   731     if (d_ptr->destructive_swap_buffers && (d_ptr->pb || !d_ptr->tried_pb)) {
       
   732         d_ptr->tried_pb = true;
       
   733 
       
   734         if (d_ptr->pb) {
       
   735             d_ptr->pb->makeCurrent();
       
   736             glDeleteTextures(1, &d_ptr->pb_tex_id);
       
   737         }
       
   738 
       
   739         delete d_ptr->pb;
       
   740 
       
   741         d_ptr->pb = new QGLPixelBuffer(rect.width(), rect.height(),
       
   742                                         QGLFormat(QGL::SampleBuffers | QGL::StencilBuffer | QGL::DepthBuffer),
       
   743                                         qt_gl_share_widget());
       
   744 
       
   745         if (d_ptr->pb->isValid()) {
       
   746             qDebug() << "Created Window Surface Pixelbuffer, Sample buffers:" << d_ptr->pb->format().sampleBuffers();
       
   747             d_ptr->pb->makeCurrent();
       
   748 
       
   749             glGenTextures(1, &d_ptr->pb_tex_id);
       
   750             glBindTexture(target, d_ptr->pb_tex_id);
       
   751             glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
       
   752 
       
   753             glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
       
   754             glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
       
   755             glBindTexture(target, 0);
       
   756 
       
   757             glMatrixMode(GL_PROJECTION);
       
   758             glLoadIdentity();
       
   759             glOrtho(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999);
       
   760 
       
   761             d_ptr->pb->d_ptr->qctx->d_func()->internal_context = true;
       
   762             return;
       
   763         } else {
       
   764             qDebug() << "QGLWindowSurface: Failed to create valid pixelbuffer, falling back";
       
   765             delete d_ptr->pb;
       
   766             d_ptr->pb = 0;
       
   767         }
       
   768     }
       
   769 #endif // !defined(QT_OPENGL_ES_2)
       
   770 
       
   771     ctx->makeCurrent();
       
   772 
       
   773 #ifndef QT_OPENGL_ES_2
       
   774     if (d_ptr->destructive_swap_buffers) {
       
   775         glGenTextures(1, &d_ptr->tex_id);
       
   776         glBindTexture(target, d_ptr->tex_id);
       
   777         glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
       
   778 
       
   779         glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
       
   780         glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
       
   781         glBindTexture(target, 0);
       
   782     }
       
   783 #endif
       
   784 
       
   785     qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;;
       
   786     d_ptr->ctx = ctx;
       
   787     d_ptr->ctx->d_ptr->internal_context = true;
       
   788 }
       
   789 
       
   790 bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy)
       
   791 {
       
   792     // this code randomly fails currently for unknown reasons
       
   793     return false;
       
   794 
       
   795     if (!d_ptr->pb)
       
   796         return false;
       
   797 
       
   798     d_ptr->pb->makeCurrent();
       
   799 
       
   800     QRect br = area.boundingRect();
       
   801 
       
   802 #if 0
       
   803     // ## workaround driver issue (scrolling by these deltas is unbearably slow for some reason)
       
   804     // ## maybe we should use glCopyTexSubImage insteadk
       
   805     if (dx == 1 || dx == -1 || dy == 1 || dy == -1 || dy == 2)
       
   806         return false;
       
   807 
       
   808     glRasterPos2i(br.x() + dx, br.y() + br.height() + dy);
       
   809     glCopyPixels(br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), GL_COLOR);
       
   810     return true;
       
   811 #endif
       
   812 
       
   813     const GLenum target = GL_TEXTURE_2D;
       
   814 
       
   815     glBindTexture(target, d_ptr->tex_id);
       
   816     glCopyTexImage2D(target, 0, GL_RGBA, br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), 0);
       
   817     glBindTexture(target, 0);
       
   818 
       
   819     drawTexture(br.translated(dx, dy), d_ptr->tex_id, window()->size());
       
   820 
       
   821     return true;
       
   822 }
       
   823 
       
   824 static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &br)
       
   825 {
       
   826     const GLenum target = GL_TEXTURE_2D;
       
   827     QRectF src = br.isEmpty()
       
   828         ? QRectF(QPointF(), texSize)
       
   829         : QRectF(QPointF(br.x(), texSize.height() - br.bottom()), br.size());
       
   830 
       
   831     if (target == GL_TEXTURE_2D) {
       
   832         qreal width = texSize.width();
       
   833         qreal height = texSize.height();
       
   834 
       
   835         src.setLeft(src.left() / width);
       
   836         src.setRight(src.right() / width);
       
   837         src.setTop(src.top() / height);
       
   838         src.setBottom(src.bottom() / height);
       
   839     }
       
   840 
       
   841     const q_vertexType tx1 = f2vt(src.left());
       
   842     const q_vertexType tx2 = f2vt(src.right());
       
   843     const q_vertexType ty1 = f2vt(src.top());
       
   844     const q_vertexType ty2 = f2vt(src.bottom());
       
   845 
       
   846     q_vertexType texCoordArray[4*2] = {
       
   847         tx1, ty2, tx2, ty2, tx2, ty1, tx1, ty1
       
   848     };
       
   849 
       
   850     q_vertexType vertexArray[4*2];
       
   851     extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array); // qpaintengine_opengl.cpp
       
   852     qt_add_rect_to_array(rect, vertexArray);
       
   853 
       
   854 #if !defined(QT_OPENGL_ES_2)
       
   855     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
   856     glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
       
   857 
       
   858     glBindTexture(target, tex_id);
       
   859     glEnable(target);
       
   860 
       
   861     glEnableClientState(GL_VERTEX_ARRAY);
       
   862     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
       
   863     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
   864     glDisableClientState(GL_VERTEX_ARRAY);
       
   865     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
       
   866 
       
   867     glDisable(target);
       
   868     glBindTexture(target, 0);
       
   869 #else
       
   870     glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexArray);
       
   871     glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, texCoordArray);
       
   872 
       
   873     glBindTexture(target, tex_id);
       
   874 
       
   875     glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
       
   876     glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
       
   877     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
   878     glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
       
   879     glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
       
   880 
       
   881     glBindTexture(target, 0);
       
   882 #endif
       
   883 }
       
   884 
       
   885 QImage *QGLWindowSurface::buffer(const QWidget *widget)
       
   886 {
       
   887     QImage image;
       
   888 
       
   889     if (d_ptr->pb)
       
   890         image = d_ptr->pb->toImage();
       
   891     else if (d_ptr->fbo)
       
   892         image = d_ptr->fbo->toImage();
       
   893 
       
   894     if (image.isNull())
       
   895         return 0;
       
   896 
       
   897     QRect rect = widget->rect();
       
   898     rect.translate(widget->mapTo(widget->window(), QPoint()));
       
   899 
       
   900     QImage subImage = image.copy(rect);
       
   901     d_ptr->buffers << subImage;
       
   902     return &d_ptr->buffers.last();
       
   903 }
       
   904 
       
   905 
       
   906 
       
   907 QT_END_NAMESPACE
       
   908