src/opengl/qwindowsurface_gl.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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 "qdebug.h"
       
    53 
       
    54 #ifdef Q_WS_X11
       
    55 #include <private/qt_x11_p.h>
       
    56 #include <qx11info_x11.h>
       
    57 #include <private/qwidget_p.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             initializing = false;
       
   199         }
       
   200         return widget;
       
   201     }
       
   202 
       
   203     void cleanup() {
       
   204         QGLWidget *w = widget;
       
   205         cleanedUp = true;
       
   206         widget = 0;
       
   207         delete w;
       
   208     }
       
   209 
       
   210     static bool cleanedUp;
       
   211 
       
   212 private:
       
   213     QGLWidget *widget;
       
   214     bool initializing;
       
   215 };
       
   216 
       
   217 bool QGLGlobalShareWidget::cleanedUp = false;
       
   218 
       
   219 static void qt_cleanup_gl_share_widget();
       
   220 Q_GLOBAL_STATIC_WITH_INITIALIZER(QGLGlobalShareWidget, _qt_gl_share_widget,
       
   221                                  {
       
   222                                      qAddPostRoutine(qt_cleanup_gl_share_widget);
       
   223                                  })
       
   224 
       
   225 static void qt_cleanup_gl_share_widget()
       
   226 {
       
   227     _qt_gl_share_widget()->cleanup();
       
   228 }
       
   229 
       
   230 QGLWidget* qt_gl_share_widget()
       
   231 {
       
   232     if (QGLGlobalShareWidget::cleanedUp)
       
   233         return 0;
       
   234     return _qt_gl_share_widget()->shareWidget();
       
   235 }
       
   236 
       
   237 
       
   238 struct QGLWindowSurfacePrivate
       
   239 {
       
   240     QGLFramebufferObject *fbo;
       
   241     QGLPixelBuffer *pb;
       
   242     GLuint tex_id;
       
   243     GLuint pb_tex_id;
       
   244 
       
   245     int tried_fbo : 1;
       
   246     int tried_pb : 1;
       
   247     int destructive_swap_buffers : 1;
       
   248     int geometry_updated : 1;
       
   249 
       
   250     QGLContext *ctx;
       
   251 
       
   252     QList<QGLContext **> contexts;
       
   253 
       
   254     QRegion paintedRegion;
       
   255     QSize size;
       
   256 
       
   257     QList<QImage> buffers;
       
   258     QGLWindowSurfaceGLPaintDevice glDevice;
       
   259     QGLWindowSurface* q_ptr;
       
   260 };
       
   261 
       
   262 QGLFormat QGLWindowSurface::surfaceFormat;
       
   263 
       
   264 void QGLWindowSurfaceGLPaintDevice::endPaint()
       
   265 {
       
   266     glFlush();
       
   267     QGLPaintDevice::endPaint();
       
   268 }
       
   269 
       
   270 QSize QGLWindowSurfaceGLPaintDevice::size() const
       
   271 {
       
   272     return d->size;
       
   273 }
       
   274 
       
   275 QGLContext* QGLWindowSurfaceGLPaintDevice::context() const
       
   276 {
       
   277     return d->ctx;
       
   278 }
       
   279 
       
   280 
       
   281 int QGLWindowSurfaceGLPaintDevice::metric(PaintDeviceMetric m) const
       
   282 {
       
   283     return qt_paint_device_metric(d->q_ptr->window(), m);
       
   284 }
       
   285 
       
   286 QPaintEngine *QGLWindowSurfaceGLPaintDevice::paintEngine() const
       
   287 {
       
   288     return qt_qgl_paint_engine();
       
   289 }
       
   290 
       
   291 QGLWindowSurface::QGLWindowSurface(QWidget *window)
       
   292     : QWindowSurface(window), d_ptr(new QGLWindowSurfacePrivate)
       
   293 {
       
   294     Q_ASSERT(window->isTopLevel());
       
   295     QGLExtensions::init();
       
   296     d_ptr->pb = 0;
       
   297     d_ptr->fbo = 0;
       
   298     d_ptr->ctx = 0;
       
   299 #if defined (QT_OPENGL_ES_2)
       
   300     d_ptr->tried_fbo = true;
       
   301     d_ptr->tried_pb = true;
       
   302 #else
       
   303     d_ptr->tried_fbo = false;
       
   304     d_ptr->tried_pb = false;
       
   305 #endif
       
   306     d_ptr->destructive_swap_buffers = qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull();
       
   307     d_ptr->glDevice.d = d_ptr;
       
   308     d_ptr->q_ptr = this;
       
   309     d_ptr->geometry_updated = false;
       
   310 }
       
   311 
       
   312 QGLWindowSurface::~QGLWindowSurface()
       
   313 {
       
   314     if (d_ptr->ctx)
       
   315         glDeleteTextures(1, &d_ptr->tex_id);
       
   316     foreach(QGLContext **ctx, d_ptr->contexts) {
       
   317         delete *ctx;
       
   318         *ctx = 0;
       
   319     }
       
   320 
       
   321     delete d_ptr->pb;
       
   322     delete d_ptr->fbo;
       
   323     delete d_ptr;
       
   324 }
       
   325 
       
   326 void QGLWindowSurface::deleted(QObject *object)
       
   327 {
       
   328     // Make sure that the fbo is destroyed before destroying its context.
       
   329     delete d_ptr->fbo;
       
   330     d_ptr->fbo = 0;
       
   331 
       
   332     QWidget *widget = qobject_cast<QWidget *>(object);
       
   333     if (widget) {
       
   334         QWidgetPrivate *widgetPrivate = widget->d_func();
       
   335         if (widgetPrivate->extraData()) {
       
   336             union { QGLContext **ctxPtr; void **voidPtr; };
       
   337             voidPtr = &widgetPrivate->extraData()->glContext;
       
   338             int index = d_ptr->contexts.indexOf(ctxPtr);
       
   339             if (index != -1) {
       
   340                 delete *ctxPtr;
       
   341                 *ctxPtr = 0;
       
   342                 d_ptr->contexts.removeAt(index);
       
   343             }
       
   344         }
       
   345     }
       
   346 }
       
   347 
       
   348 void QGLWindowSurface::hijackWindow(QWidget *widget)
       
   349 {
       
   350     QWidgetPrivate *widgetPrivate = widget->d_func();
       
   351     widgetPrivate->createExtra();
       
   352     if (widgetPrivate->extraData()->glContext)
       
   353         return;
       
   354 
       
   355     QGLContext *ctx = new QGLContext(surfaceFormat, widget);
       
   356     ctx->create(qt_gl_share_widget()->context());
       
   357 
       
   358 #if defined(Q_WS_X11) && defined(QT_OPENGL_ES)
       
   359     // Create the EGL surface to draw into.  QGLContext::chooseContext()
       
   360     // does not do this for X11/EGL, but does do it for other platforms.
       
   361     // This probably belongs in qgl_x11egl.cpp.
       
   362     QGLContextPrivate *ctxpriv = ctx->d_func();
       
   363     ctxpriv->eglSurface = ctxpriv->eglContext->createSurface(widget);
       
   364     if (ctxpriv->eglSurface == EGL_NO_SURFACE) {
       
   365         qWarning() << "hijackWindow() could not create EGL surface";
       
   366     }
       
   367     qDebug("QGLWindowSurface - using EGLConfig %d", ctxpriv->eglContext->config());
       
   368 #endif
       
   369 
       
   370     widgetPrivate->extraData()->glContext = ctx;
       
   371 
       
   372     union { QGLContext **ctxPtr; void **voidPtr; };
       
   373 
       
   374     connect(widget, SIGNAL(destroyed(QObject *)), this, SLOT(deleted(QObject *)));
       
   375 
       
   376     voidPtr = &widgetPrivate->extraData()->glContext;
       
   377     d_ptr->contexts << ctxPtr;
       
   378     qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size();
       
   379 }
       
   380 
       
   381 QGLContext *QGLWindowSurface::context() const
       
   382 {
       
   383     return d_ptr->ctx;
       
   384 }
       
   385 
       
   386 QPaintDevice *QGLWindowSurface::paintDevice()
       
   387 {
       
   388     updateGeometry();
       
   389 
       
   390     if (d_ptr->pb)
       
   391         return d_ptr->pb;
       
   392 
       
   393     if (d_ptr->ctx)
       
   394         return &d_ptr->glDevice;
       
   395 
       
   396     QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
       
   397     ctx->makeCurrent();
       
   398     return d_ptr->fbo;
       
   399 }
       
   400 
       
   401 static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &src = QRectF());
       
   402 
       
   403 void QGLWindowSurface::beginPaint(const QRegion &)
       
   404 {
       
   405 }
       
   406 
       
   407 void QGLWindowSurface::endPaint(const QRegion &rgn)
       
   408 {
       
   409     if (context())
       
   410         d_ptr->paintedRegion |= rgn;
       
   411 
       
   412     d_ptr->buffers.clear();
       
   413 }
       
   414 
       
   415 void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
       
   416 {
       
   417     if (context() && widget != window()) {
       
   418         qWarning("No native child widget support in GL window surface without FBOs or pixel buffers");
       
   419         return;
       
   420     }
       
   421 
       
   422     //### Find out why d_ptr->geometry_updated isn't always false.
       
   423     // flush() should not be called when d_ptr->geometry_updated is true. It assumes that either
       
   424     // d_ptr->fbo or d_ptr->pb is allocated and has the correct size.
       
   425     if (d_ptr->geometry_updated)
       
   426         return;
       
   427 
       
   428     QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget();
       
   429     Q_ASSERT(parent);
       
   430 
       
   431     if (!geometry().isValid())
       
   432         return;
       
   433 
       
   434     // Needed to support native child-widgets...
       
   435     hijackWindow(parent);
       
   436 
       
   437     QRect br = rgn.boundingRect().translated(offset);
       
   438     br = br.intersected(window()->rect());
       
   439     QPoint wOffset = qt_qwidget_data(parent)->wrect.topLeft();
       
   440     QRect rect = br.translated(-offset - wOffset);
       
   441 
       
   442     const GLenum target = GL_TEXTURE_2D;
       
   443     Q_UNUSED(target);
       
   444 
       
   445     if (context()) {
       
   446         context()->makeCurrent();
       
   447 
       
   448         if (context()->format().doubleBuffer()) {
       
   449 #if !defined(QT_OPENGL_ES_2)
       
   450             if (d_ptr->destructive_swap_buffers) {
       
   451                 glBindTexture(target, d_ptr->tex_id);
       
   452 
       
   453                 QVector<QRect> rects = d_ptr->paintedRegion.rects();
       
   454                 for (int i = 0; i < rects.size(); ++i) {
       
   455                     QRect br = rects.at(i);
       
   456                     if (br.isEmpty())
       
   457                         continue;
       
   458 
       
   459                     const uint bottom = window()->height() - (br.y() + br.height());
       
   460                     glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
       
   461                 }
       
   462 
       
   463                 glBindTexture(target, 0);
       
   464 
       
   465                 QRegion dirtyRegion = QRegion(window()->rect()) - d_ptr->paintedRegion;
       
   466 
       
   467                 if (!dirtyRegion.isEmpty()) {
       
   468                     glMatrixMode(GL_MODELVIEW);
       
   469                     glLoadIdentity();
       
   470 
       
   471                     glMatrixMode(GL_PROJECTION);
       
   472                     glLoadIdentity();
       
   473 #ifndef QT_OPENGL_ES
       
   474                     glOrtho(0, window()->width(), window()->height(), 0, -999999, 999999);
       
   475 #else
       
   476                     glOrthof(0, window()->width(), window()->height(), 0, -999999, 999999);
       
   477 #endif
       
   478                     glViewport(0, 0, window()->width(), window()->height());
       
   479 
       
   480                     QVector<QRect> rects = dirtyRegion.rects();
       
   481                     glColor4f(1, 1, 1, 1);
       
   482                     for (int i = 0; i < rects.size(); ++i) {
       
   483                         QRect rect = rects.at(i);
       
   484                         if (rect.isEmpty())
       
   485                             continue;
       
   486 
       
   487                         drawTexture(rect, d_ptr->tex_id, window()->size(), rect);
       
   488                     }
       
   489                 }
       
   490             }
       
   491 #endif
       
   492             d_ptr->paintedRegion = QRegion();
       
   493 
       
   494             context()->swapBuffers();
       
   495         } else {
       
   496             glFlush();
       
   497         }
       
   498 
       
   499         return;
       
   500     }
       
   501 
       
   502     QGLContext *previous_ctx = const_cast<QGLContext *>(QGLContext::currentContext());
       
   503     QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext);
       
   504 
       
   505     // QPainter::end() should have unbound the fbo, otherwise something is very wrong...
       
   506     Q_ASSERT(!d_ptr->fbo || !d_ptr->fbo->isBound());
       
   507 
       
   508     if (ctx != previous_ctx) {
       
   509         ctx->makeCurrent();
       
   510     }
       
   511 
       
   512     QSize size = widget->rect().size();
       
   513     if (d_ptr->destructive_swap_buffers && ctx->format().doubleBuffer()) {
       
   514         rect = parent->rect();
       
   515         br = rect.translated(wOffset + offset);
       
   516         size = parent->size();
       
   517     }
       
   518 
       
   519     glDisable(GL_SCISSOR_TEST);
       
   520 
       
   521     if (d_ptr->fbo && (QGLExtensions::glExtensions & QGLExtensions::FramebufferBlit)) {
       
   522         const int h = d_ptr->fbo->height();
       
   523 
       
   524         const int sx0 = br.left();
       
   525         const int sx1 = br.left() + br.width();
       
   526         const int sy0 = h - (br.top() + br.height());
       
   527         const int sy1 = h - br.top();
       
   528 
       
   529         const int tx0 = rect.left();
       
   530         const int tx1 = rect.left() + rect.width();
       
   531         const int ty0 = parent->height() - (rect.top() + rect.height());
       
   532         const int ty1 = parent->height() - rect.top();
       
   533 
       
   534         if (window() == parent || d_ptr->fbo->format().samples() <= 1) {
       
   535             // glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
       
   536             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle());
       
   537 
       
   538             glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
       
   539                     tx0, ty0, tx1, ty1,
       
   540                     GL_COLOR_BUFFER_BIT,
       
   541                     GL_NEAREST);
       
   542 
       
   543             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
       
   544         } else {
       
   545             // can't do sub-region blits with multisample FBOs
       
   546             QGLFramebufferObject *temp = qgl_fbo_pool()->acquire(d_ptr->fbo->size(), QGLFramebufferObjectFormat());
       
   547 
       
   548             glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, temp->handle());
       
   549             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle());
       
   550 
       
   551             glBlitFramebufferEXT(0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(),
       
   552                     0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(),
       
   553                     GL_COLOR_BUFFER_BIT,
       
   554                     GL_NEAREST);
       
   555 
       
   556             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, temp->handle());
       
   557             glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
       
   558 
       
   559             glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
       
   560                     tx0, ty0, tx1, ty1,
       
   561                     GL_COLOR_BUFFER_BIT,
       
   562                     GL_NEAREST);
       
   563 
       
   564             glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
       
   565 
       
   566             qgl_fbo_pool()->release(temp);
       
   567         }
       
   568     }
       
   569 #if !defined(QT_OPENGL_ES_2)
       
   570     else {
       
   571         GLuint texture;
       
   572     if (d_ptr->fbo) {
       
   573         texture = d_ptr->fbo->texture();
       
   574     } else {
       
   575         d_ptr->pb->makeCurrent();
       
   576         glBindTexture(target, d_ptr->pb_tex_id);
       
   577         const uint bottom = window()->height() - (br.y() + br.height());
       
   578         glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
       
   579         texture = d_ptr->pb_tex_id;
       
   580         glBindTexture(target, 0);
       
   581     }
       
   582 
       
   583         glDisable(GL_DEPTH_TEST);
       
   584 
       
   585         if (d_ptr->fbo) {
       
   586             d_ptr->fbo->release();
       
   587         } else {
       
   588             ctx->makeCurrent();
       
   589         }
       
   590 
       
   591         glMatrixMode(GL_MODELVIEW);
       
   592         glLoadIdentity();
       
   593 
       
   594         glMatrixMode(GL_PROJECTION);
       
   595         glLoadIdentity();
       
   596 #ifndef QT_OPENGL_ES
       
   597         glOrtho(0, size.width(), size.height(), 0, -999999, 999999);
       
   598 #else
       
   599         glOrthof(0, size.width(), size.height(), 0, -999999, 999999);
       
   600 #endif
       
   601         glViewport(0, 0, size.width(), size.height());
       
   602 
       
   603         glColor4f(1, 1, 1, 1);
       
   604         drawTexture(rect, texture, window()->size(), br);
       
   605 
       
   606         if (d_ptr->fbo)
       
   607             d_ptr->fbo->bind();
       
   608     }
       
   609 #else
       
   610     // OpenGL/ES 2.0 version of the fbo blit.
       
   611     else if (d_ptr->fbo) {
       
   612         Q_UNUSED(target);
       
   613 
       
   614         GLuint texture = d_ptr->fbo->texture();
       
   615 
       
   616         glDisable(GL_DEPTH_TEST);
       
   617 
       
   618         if (d_ptr->fbo->isBound())
       
   619             d_ptr->fbo->release();
       
   620 
       
   621         glViewport(0, 0, size.width(), size.height());
       
   622 
       
   623         QGLShaderProgram *blitProgram =
       
   624             QGLEngineSharedShaders::shadersForContext(ctx)->blitProgram();
       
   625         blitProgram->enable();
       
   626         blitProgram->setUniformValue("imageTexture", 0 /*QT_IMAGE_TEXTURE_UNIT*/);
       
   627 
       
   628         // The shader manager's blit program does not multiply the
       
   629         // vertices by the pmv matrix, so we need to do the effect
       
   630         // of the orthographic projection here ourselves.
       
   631         QRectF r;
       
   632         qreal w = size.width() ? size.width() : 1.0f;
       
   633         qreal h = size.height() ? size.height() : 1.0f;
       
   634         r.setLeft((rect.left() / w) * 2.0f - 1.0f);
       
   635         if (rect.right() == (size.width() - 1))
       
   636             r.setRight(1.0f);
       
   637         else
       
   638             r.setRight((rect.right() / w) * 2.0f - 1.0f);
       
   639         r.setBottom((rect.top() / h) * 2.0f - 1.0f);
       
   640         if (rect.bottom() == (size.height() - 1))
       
   641             r.setTop(1.0f);
       
   642         else
       
   643             r.setTop((rect.bottom() / w) * 2.0f - 1.0f);
       
   644 
       
   645         drawTexture(r, texture, window()->size(), br);
       
   646     }
       
   647 #endif
       
   648 
       
   649     if (ctx->format().doubleBuffer())
       
   650         ctx->swapBuffers();
       
   651     else
       
   652         glFlush();
       
   653 }
       
   654 
       
   655 
       
   656 void QGLWindowSurface::setGeometry(const QRect &rect)
       
   657 {
       
   658     QWindowSurface::setGeometry(rect);
       
   659     d_ptr->geometry_updated = true;
       
   660 }
       
   661 
       
   662 
       
   663 void QGLWindowSurface::updateGeometry() {
       
   664     if (!d_ptr->geometry_updated)
       
   665         return;
       
   666     d_ptr->geometry_updated = false;
       
   667 
       
   668 
       
   669     QRect rect = geometry();
       
   670     hijackWindow(window());
       
   671     QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
       
   672 
       
   673 #ifdef Q_WS_MAC
       
   674     ctx->updatePaintDevice();
       
   675 #endif
       
   676 
       
   677     const GLenum target = GL_TEXTURE_2D;
       
   678 
       
   679     if (rect.width() <= 0 || rect.height() <= 0)
       
   680         return;
       
   681 
       
   682     if (d_ptr->size == rect.size())
       
   683         return;
       
   684 
       
   685     d_ptr->size = rect.size();
       
   686 
       
   687     if (d_ptr->ctx) {
       
   688         if (d_ptr->destructive_swap_buffers) {
       
   689             glBindTexture(target, d_ptr->tex_id);
       
   690             glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
       
   691             glBindTexture(target, 0);
       
   692         }
       
   693         return;
       
   694     }
       
   695 
       
   696     if (d_ptr->destructive_swap_buffers
       
   697         && (QGLExtensions::glExtensions & QGLExtensions::FramebufferObject)
       
   698         && (d_ptr->fbo || !d_ptr->tried_fbo)
       
   699         && qt_gl_preferGL2Engine())
       
   700     {
       
   701         d_ptr->tried_fbo = true;
       
   702         ctx->d_ptr->internal_context = true;
       
   703         ctx->makeCurrent();
       
   704         delete d_ptr->fbo;
       
   705 
       
   706         QGLFramebufferObjectFormat format;
       
   707         format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
       
   708         format.setInternalTextureFormat(GLenum(GL_RGBA));
       
   709         format.setTextureTarget(target);
       
   710 
       
   711         if (QGLExtensions::glExtensions & QGLExtensions::FramebufferBlit)
       
   712             format.setSamples(8);
       
   713 
       
   714         d_ptr->fbo = new QGLFramebufferObject(rect.size(), format);
       
   715 
       
   716         if (d_ptr->fbo->isValid()) {
       
   717             qDebug() << "Created Window Surface FBO" << rect.size()
       
   718                      << "with samples" << d_ptr->fbo->format().samples();
       
   719             return;
       
   720         } else {
       
   721             qDebug() << "QGLWindowSurface: Failed to create valid FBO, falling back";
       
   722             delete d_ptr->fbo;
       
   723             d_ptr->fbo = 0;
       
   724         }
       
   725     }
       
   726 
       
   727 #if !defined(QT_OPENGL_ES_2)
       
   728     if (d_ptr->destructive_swap_buffers && (d_ptr->pb || !d_ptr->tried_pb)) {
       
   729         d_ptr->tried_pb = true;
       
   730 
       
   731         if (d_ptr->pb) {
       
   732             d_ptr->pb->makeCurrent();
       
   733             glDeleteTextures(1, &d_ptr->pb_tex_id);
       
   734         }
       
   735 
       
   736         delete d_ptr->pb;
       
   737 
       
   738         d_ptr->pb = new QGLPixelBuffer(rect.width(), rect.height(),
       
   739                                         QGLFormat(QGL::SampleBuffers | QGL::StencilBuffer | QGL::DepthBuffer),
       
   740                                         qt_gl_share_widget());
       
   741 
       
   742         if (d_ptr->pb->isValid()) {
       
   743             qDebug() << "Created Window Surface Pixelbuffer, Sample buffers:" << d_ptr->pb->format().sampleBuffers();
       
   744             d_ptr->pb->makeCurrent();
       
   745 
       
   746             glGenTextures(1, &d_ptr->pb_tex_id);
       
   747             glBindTexture(target, d_ptr->pb_tex_id);
       
   748             glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
       
   749 
       
   750             glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
       
   751             glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
       
   752             glBindTexture(target, 0);
       
   753 
       
   754             glMatrixMode(GL_PROJECTION);
       
   755             glLoadIdentity();
       
   756 #ifndef QT_OPENGL_ES
       
   757             glOrtho(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999);
       
   758 #else
       
   759             glOrthof(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999);
       
   760 #endif
       
   761 
       
   762             d_ptr->pb->d_ptr->qctx->d_func()->internal_context = true;
       
   763             return;
       
   764         } else {
       
   765             qDebug() << "QGLWindowSurface: Failed to create valid pixelbuffer, falling back";
       
   766             delete d_ptr->pb;
       
   767             d_ptr->pb = 0;
       
   768         }
       
   769     }
       
   770 #endif // !defined(QT_OPENGL_ES_2)
       
   771 
       
   772     ctx->makeCurrent();
       
   773 
       
   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 
       
   784     qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;;
       
   785     d_ptr->ctx = ctx;
       
   786     d_ptr->ctx->d_ptr->internal_context = true;
       
   787 }
       
   788 
       
   789 bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy)
       
   790 {
       
   791     // this code randomly fails currently for unknown reasons
       
   792     return false;
       
   793 
       
   794     if (!d_ptr->pb)
       
   795         return false;
       
   796 
       
   797     d_ptr->pb->makeCurrent();
       
   798 
       
   799     QRect br = area.boundingRect();
       
   800 
       
   801 #if 0
       
   802     // ## workaround driver issue (scrolling by these deltas is unbearably slow for some reason)
       
   803     // ## maybe we should use glCopyTexSubImage insteadk
       
   804     if (dx == 1 || dx == -1 || dy == 1 || dy == -1 || dy == 2)
       
   805         return false;
       
   806 
       
   807     glRasterPos2i(br.x() + dx, br.y() + br.height() + dy);
       
   808     glCopyPixels(br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), GL_COLOR);
       
   809     return true;
       
   810 #endif
       
   811 
       
   812     const GLenum target = GL_TEXTURE_2D;
       
   813 
       
   814     glBindTexture(target, d_ptr->tex_id);
       
   815     glCopyTexImage2D(target, 0, GL_RGBA, br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), 0);
       
   816     glBindTexture(target, 0);
       
   817 
       
   818     drawTexture(br.translated(dx, dy), d_ptr->tex_id, window()->size());
       
   819 
       
   820     return true;
       
   821 }
       
   822 
       
   823 static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &br)
       
   824 {
       
   825     const GLenum target = GL_TEXTURE_2D;
       
   826     QRectF src = br.isEmpty()
       
   827         ? QRectF(QPointF(), texSize)
       
   828         : QRectF(QPointF(br.x(), texSize.height() - br.bottom()), br.size());
       
   829 
       
   830     if (target == GL_TEXTURE_2D) {
       
   831         qreal width = texSize.width();
       
   832         qreal height = texSize.height();
       
   833 
       
   834         src.setLeft(src.left() / width);
       
   835         src.setRight(src.right() / width);
       
   836         src.setTop(src.top() / height);
       
   837         src.setBottom(src.bottom() / height);
       
   838     }
       
   839 
       
   840     const q_vertexType tx1 = f2vt(src.left());
       
   841     const q_vertexType tx2 = f2vt(src.right());
       
   842     const q_vertexType ty1 = f2vt(src.top());
       
   843     const q_vertexType ty2 = f2vt(src.bottom());
       
   844 
       
   845     q_vertexType texCoordArray[4*2] = {
       
   846         tx1, ty2, tx2, ty2, tx2, ty1, tx1, ty1
       
   847     };
       
   848 
       
   849     q_vertexType vertexArray[4*2];
       
   850     extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array); // qpaintengine_opengl.cpp
       
   851     qt_add_rect_to_array(rect, vertexArray);
       
   852 
       
   853 #if !defined(QT_OPENGL_ES_2)
       
   854     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
   855     glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
       
   856 
       
   857     glBindTexture(target, tex_id);
       
   858     glEnable(target);
       
   859 
       
   860     glEnableClientState(GL_VERTEX_ARRAY);
       
   861     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
       
   862     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
   863     glDisableClientState(GL_VERTEX_ARRAY);
       
   864     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
       
   865 
       
   866     glDisable(target);
       
   867     glBindTexture(target, 0);
       
   868 #else
       
   869     glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexArray);
       
   870     glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, texCoordArray);
       
   871 
       
   872     glBindTexture(target, tex_id);
       
   873 
       
   874     glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
       
   875     glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
       
   876     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
   877     glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
       
   878     glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
       
   879 
       
   880     glBindTexture(target, 0);
       
   881 #endif
       
   882 }
       
   883 
       
   884 QImage *QGLWindowSurface::buffer(const QWidget *widget)
       
   885 {
       
   886     QImage image;
       
   887 
       
   888     if (d_ptr->pb)
       
   889         image = d_ptr->pb->toImage();
       
   890     else if (d_ptr->fbo)
       
   891         image = d_ptr->fbo->toImage();
       
   892 
       
   893     if (image.isNull())
       
   894         return 0;
       
   895 
       
   896     QRect rect = widget->rect();
       
   897     rect.translate(widget->mapTo(widget->window(), QPoint()));
       
   898 
       
   899     QImage subImage = image.copy(rect);
       
   900     d_ptr->buffers << subImage;
       
   901     return &d_ptr->buffers.last();
       
   902 }
       
   903 
       
   904 
       
   905 
       
   906 QT_END_NAMESPACE
       
   907