src/opengl/qwindowsurface_x11gl.cpp
changeset 30 5dc02b23752f
parent 18 2f34d5167611
--- a/src/opengl/qwindowsurface_x11gl.cpp	Wed Jun 23 19:07:03 2010 +0300
+++ b/src/opengl/qwindowsurface_x11gl.cpp	Tue Jul 06 15:10:48 2010 +0300
@@ -51,14 +51,16 @@
 QT_BEGIN_NAMESPACE
 
 QX11GLWindowSurface::QX11GLWindowSurface(QWidget* window)
-    : QWindowSurface(window), m_GC(0), m_window(window)
+    : QWindowSurface(window), m_windowGC(0), m_pixmapGC(0), m_window(window)
 {
 }
 
 QX11GLWindowSurface::~QX11GLWindowSurface()
 {
-    if (m_GC)
-        XFree(m_GC);
+    if (m_windowGC)
+        XFree(m_windowGC);
+    if (m_pixmapGC)
+        XFree(m_pixmapGC);
 }
 
 QPaintDevice *QX11GLWindowSurface::paintDevice()
@@ -70,78 +72,142 @@
 
 void QX11GLWindowSurface::flush(QWidget *widget, const QRegion &widgetRegion, const QPoint &offset)
 {
-//    qDebug("QX11GLWindowSurface::flush()");
-    QTime startTime = QTime::currentTime();
+    // We don't need to know the widget which initiated the flush. Instead we just use the offset
+    // to translate the widgetRegion:
+    Q_UNUSED(widget);
+
     if (m_backBuffer.isNull()) {
-        qDebug("QHarmattanWindowSurface::flush() - backBuffer is null, not flushing anything");
+        qDebug("QX11GLWindowSurface::flush() - backBuffer is null, not flushing anything");
         return;
     }
 
-    QPoint widgetOffset = qt_qwidget_data(widget)->wrect.topLeft();
-    QRegion windowRegion(widgetRegion);
-    QRect boundingRect = widgetRegion.boundingRect();
-    if (!widgetOffset.isNull())
-        windowRegion.translate(-widgetOffset);
-    QRect windowBoundingRect = windowRegion.boundingRect();
+    Q_ASSERT(window()->size() != m_backBuffer.size());
+
+    // Wait for all GL rendering to the back buffer pixmap to complete before trying to
+    // copy it to the window. We do this by making sure the pixmap's context is current
+    // and then call eglWaitClient. The EGL 1.4 spec says eglWaitClient doesn't have to
+    // block, just that "All rendering calls...are guaranteed to be executed before native
+    // rendering calls". This makes it potentially less expensive than glFinish.
+    QGLContext* ctx = static_cast<QX11GLPixmapData*>(m_backBuffer.data_ptr().data())->context();
+    if (QGLContext::currentContext() != ctx && ctx && ctx->isValid())
+        ctx->makeCurrent();
+    eglWaitClient();
+
+    if (m_windowGC == 0) {
+        XGCValues attribs;
+        attribs.graphics_exposures = False;
+        m_windowGC = XCreateGC(X11->display, m_window->handle(), GCGraphicsExposures, &attribs);
+    }
 
     int rectCount;
-    XRectangle *rects = (XRectangle *)qt_getClipRects(windowRegion, rectCount);
+    XRectangle *rects = (XRectangle *)qt_getClipRects(widgetRegion, rectCount);
     if (rectCount <= 0)
         return;
-//         qDebug() << "XSetClipRectangles";
-//         for  (int i = 0; i < num; ++i)
-//             qDebug() << ' ' << i << rects[i].x << rects[i].x << rects[i].y << rects[i].width << rects[i].height;
+
+    XSetClipRectangles(X11->display, m_windowGC, 0, 0, rects, rectCount, YXBanded);
 
-    if (m_GC == 0) {
-        m_GC = XCreateGC(X11->display, m_window->handle(), 0, 0);
-        XSetGraphicsExposures(X11->display, m_GC, False);
-    }
+    QRect dirtyRect = widgetRegion.boundingRect().translated(-offset);
+    XCopyArea(X11->display, m_backBuffer.handle(), m_window->handle(), m_windowGC,
+              dirtyRect.x(), dirtyRect.y(), dirtyRect.width(), dirtyRect.height(),
+              dirtyRect.x(), dirtyRect.y());
 
-    XSetClipRectangles(X11->display, m_GC, 0, 0, rects, rectCount, YXBanded);
-    XCopyArea(X11->display, m_backBuffer.handle(), m_window->handle(), m_GC,
-              boundingRect.x() + offset.x(), boundingRect.y() + offset.y(),
-              boundingRect.width(), boundingRect.height(),
-              windowBoundingRect.x(), windowBoundingRect.y());
+    // Make sure the blit of the update from the back buffer to the window completes
+    // before allowing rendering to start again to the back buffer. Otherwise the GPU
+    // might start rendering to the back buffer again while the blit takes place.
+    eglWaitNative(EGL_CORE_NATIVE_ENGINE);
 }
 
 void QX11GLWindowSurface::setGeometry(const QRect &rect)
 {
     if (rect.width() > m_backBuffer.size().width() || rect.height() > m_backBuffer.size().height()) {
+        QX11GLPixmapData *pd = new QX11GLPixmapData;
         QSize newSize = rect.size();
-//        QSize newSize(1024,512);
-        qDebug() << "QX11GLWindowSurface::setGeometry() - creating a pixmap of size" << newSize;
-        QX11GLPixmapData *pd = new QX11GLPixmapData;
         pd->resize(newSize.width(), newSize.height());
         m_backBuffer = QPixmap(pd);
+        if (window()->testAttribute(Qt::WA_TranslucentBackground))
+            m_backBuffer.fill(Qt::transparent);
+        if (m_pixmapGC) {
+            XFreeGC(X11->display, m_pixmapGC);
+            m_pixmapGC = 0;
+        }
     }
 
-//    if (gc)
-//        XFreeGC(X11->display, gc);
-//    gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
-//    XSetGraphicsExposures(X11->display, gc, False);
     QWindowSurface::setGeometry(rect);
 }
 
 bool QX11GLWindowSurface::scroll(const QRegion &area, int dx, int dy)
 {
-    Q_UNUSED(area);
-    Q_UNUSED(dx);
-    Q_UNUSED(dy);
-    return false;
-}
+    if (m_backBuffer.isNull())
+        return false;
+
+    Q_ASSERT(m_backBuffer.data_ptr()->classId() == QPixmapData::X11Class);
+
+    // Make sure all GL rendering is complete before starting the scroll operation:
+    QGLContext* ctx = static_cast<QX11GLPixmapData*>(m_backBuffer.data_ptr().data())->context();
+    if (QGLContext::currentContext() != ctx && ctx && ctx->isValid())
+        ctx->makeCurrent();
+    eglWaitClient();
 
-/*
-void QX11GLWindowSurface::beginPaint(const QRegion &region)
-{
+    if (!m_pixmapGC)
+        m_pixmapGC = XCreateGC(X11->display, m_backBuffer.handle(), 0, 0);
+
+    foreach (const QRect& rect, area.rects()) {
+        XCopyArea(X11->display, m_backBuffer.handle(), m_backBuffer.handle(), m_pixmapGC,
+                  rect.x(), rect.y(), rect.width(), rect.height(),
+                  rect.x()+dx, rect.y()+dy);
+    }
+
+    // Make sure the scroll operation is complete before allowing GL rendering to resume
+    eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+
+    return true;
 }
 
-void QX11GLWindowSurface::endPaint(const QRegion &region)
+
+QPixmap QX11GLWindowSurface::grabWidget(const QWidget *widget, const QRect& rect) const
 {
+    if (!widget || m_backBuffer.isNull())
+        return QPixmap();
+
+    QRect srcRect;
+
+    // make sure the rect is inside the widget & clip to widget's rect
+    if (!rect.isEmpty())
+        srcRect = rect & widget->rect();
+    else
+        srcRect = widget->rect();
+
+    if (srcRect.isEmpty())
+        return QPixmap();
+
+    // If it's a child widget we have to translate the coordinates
+    if (widget != window())
+        srcRect.translate(widget->mapTo(window(), QPoint(0, 0)));
+
+    QPixmap::x11SetDefaultScreen(widget->x11Info().screen());
+
+    QX11PixmapData *pmd = new QX11PixmapData(QPixmapData::PixmapType);
+    pmd->resize(srcRect.width(), srcRect.height());
+    QPixmap px(pmd);
+
+    GC tmpGc = XCreateGC(X11->display, m_backBuffer.handle(), 0, 0);
+
+    // Make sure all GL rendering is complete before copying the window
+    QGLContext* ctx = static_cast<QX11GLPixmapData*>(m_backBuffer.pixmapData())->context();
+    if (QGLContext::currentContext() != ctx && ctx && ctx->isValid())
+        ctx->makeCurrent();
+    eglWaitClient();
+
+    // Copy srcRect from the backing store to the new pixmap
+    XSetGraphicsExposures(X11->display, tmpGc, False);
+    XCopyArea(X11->display, m_backBuffer.handle(), px.handle(), tmpGc,
+              srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(), 0, 0);
+    XFreeGC(X11->display, tmpGc);
+
+    // Wait until the copy has finised before allowing more rendering into the back buffer
+    eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+
+    return px;
 }
 
-QImage *QX11GLWindowSurface::buffer(const QWidget *widget)
-{
-}
-*/
-
 QT_END_NAMESPACE