From 7c6625b105511191f739d07dc658094ff6f682ac Mon Sep 17 00:00:00 2001
From: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
Date: Tue, 3 Nov 2015 11:41:01 +0100
Subject: [PATCH] Support mixing native child widgets with texture-based ones
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Currently QOpenGLWidget and QQuickWidget do not support having native
child widgets inside the same top-level window. In some cases this is
inevitable, f.ex. multimedia may require native windows when used from
widget apps. winId() calls made for various (valid or invalid) reasons
are also problematic.

There are no blockers for supporting this setup, however. By storing
multiple texture lists (one for each subtree where the root is a
native widget), adding the missing markDirtyOnScreen calls, letting
each native widget access the correct texture list (i.e. the one
corresponding to its children) when they are (separately) flushed, and
fixing composeAndFlush() to take the update region and the (native
child) offset into account, it can all be made functional.

The change also fixes the issue of keeping GL-based compositing
enabled even after all render-to-texture widgets in the window become
hidden. Due to the changes of how such widgets are gathered,
composeAndFlush() is not invoked anymore when no such widgets are
discovered for a given native parent. This is great since having
compositing enabled infinitely is an issue for applications like Qt
Creator that implement certain views with QQuickWidgets but cannot
afford the cost of texture uploads in other places (e.g. for the text
editor) on slower machines.

The openglwidget manual test is greatly enhanced to test various
situations (MDI, scroll areas, tab widgets, QOpenGLWidget as native
child, QOpenGLWidget with non-tlw native parent, etc.)

Task-number: QTBUG-48130
Task-number: QTBUG-49172
Change-Id: Iad098359c8bcf749f01c050da0853415e1550eda
Reviewed-by: Paul Olav Tvete <paul.tvete@theqtcompany.com>
Reviewed-by: Morten Johan Sørvig <morten.sorvig@theqtcompany.com>
---
 src/gui/painting/qplatformbackingstore.cpp         |  42 ++--
 src/widgets/kernel/qopenglwidget.cpp               |   6 -
 src/widgets/kernel/qwidget.cpp                     |   3 +
 src/widgets/kernel/qwidget_p.h                     |   9 +-
 src/widgets/kernel/qwidgetbackingstore.cpp         | 228 ++++++++++++++-------
 src/widgets/kernel/qwidgetbackingstore_p.h         |   5 +-
 .../widgets/qopenglwidget/tst_qopenglwidget.cpp    |  74 ++++++-
 tests/manual/qopenglwidget/openglwidget/main.cpp   | 175 +++++++++++++++-
 .../qopenglwidget/openglwidget/openglwidget.cpp    |  23 ++-
 .../qopenglwidget/openglwidget/openglwidget.h      |   5 +-
 10 files changed, 447 insertions(+), 123 deletions(-)

Index: b/src/gui/painting/qplatformbackingstore.cpp
===================================================================
--- a/src/gui/painting/qplatformbackingstore.cpp
+++ b/src/gui/painting/qplatformbackingstore.cpp
@@ -214,14 +214,14 @@ static inline QRect deviceRect(const QRe
     return deviceRect;
 }
 
-static QRegion deviceRegion(const QRegion &region, QWindow *window)
+static QRegion deviceRegion(const QRegion &region, QWindow *window, const QPoint &offset)
 {
-    if (!(window->devicePixelRatio() > 1))
+    if (offset.isNull() && window->devicePixelRatio() <= 1)
         return region;
 
     QVector<QRect> rects;
     foreach (QRect rect, region.rects())
-        rects.append(deviceRect(rect, window));
+        rects.append(deviceRect(rect.translated(offset), window));
 
     QRegion deviceRegion;
     deviceRegion.setRects(rects.constData(), rects.count());
@@ -234,10 +234,12 @@ static inline QRect toBottomLeftRect(con
                  topLeftRect.width(), topLeftRect.height());
 }
 
-static void blit(const QPlatformTextureList *textures, int idx, QWindow *window, const QRect &deviceWindowRect,
-                 QOpenGLTextureBlitter *blitter)
+static void blitTextureForWidget(const QPlatformTextureList *textures, int idx, QWindow *window, const QRect &deviceWindowRect,
+                                 QOpenGLTextureBlitter *blitter, const QPoint &offset)
 {
-    const QRect rectInWindow = textures->geometry(idx);
+    QRect rectInWindow = textures->geometry(idx);
+    // relative to the TLW, not necessarily our window (if the flush is for a native child widget), have to adjust
+    rectInWindow.translate(-offset);
     QRect clipRect = textures->clipRect(idx);
     if (clipRect.isEmpty())
         clipRect = QRect(QPoint(0, 0), rectInWindow.size());
@@ -262,7 +264,9 @@ static void blit(const QPlatformTextureL
     and composes using OpenGL. May be reimplemented in subclasses if there
     is a more efficient native way to do it.
 
-    Note that the \a offset parameter is currently unused.
+    \note \a region is relative to the window which may not be top-level in case
+    \a window corresponds to a native child widget. \a offset is the position of
+    the native child relative to the top-level window.
  */
 
 void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &region,
@@ -270,7 +274,8 @@ void QPlatformBackingStore::composeAndFl
                                             QPlatformTextureList *textures, QOpenGLContext *context,
                                             bool translucentBackground)
 {
-    Q_UNUSED(offset);
+    if (!qt_window_private(window)->receivedExpose)
+        return;
 
     if (!context->makeCurrent(window)) {
         qWarning("composeAndFlush: makeCurrent() failed");
@@ -294,7 +299,7 @@ void QPlatformBackingStore::composeAndFl
     // Textures for renderToTexture widgets.
     for (int i = 0; i < textures->count(); ++i) {
         if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop))
-            blit(textures, i, window, deviceWindowRect, d_ptr->blitter);
+            blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset);
     }
 
     funcs->glEnable(GL_BLEND);
@@ -341,17 +346,26 @@ void QPlatformBackingStore::composeAndFl
         textureId = d_ptr->textureId;
     } else {
         TextureFlags flags = 0;
-        textureId = toTexture(deviceRegion(region, window), &d_ptr->textureSize, &flags);
+        textureId = toTexture(deviceRegion(region, window, offset), &d_ptr->textureSize, &flags);
         d_ptr->needsSwizzle = (flags & TextureSwizzle) != 0;
         if (flags & TextureFlip)
             origin = QOpenGLTextureBlitter::OriginBottomLeft;
     }
 
     if (textureId) {
-        QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(QRect(QPoint(), d_ptr->textureSize), deviceWindowRect);
         if (d_ptr->needsSwizzle)
             d_ptr->blitter->setSwizzleRB(true);
-        d_ptr->blitter->blit(textureId, target, origin);
+        // offset is usually (0, 0) unless we have native child widgets.
+        if (offset.isNull()) {
+            d_ptr->blitter->blit(textureId, QMatrix4x4(), origin);
+        } else {
+            // The backingstore is for the entire tlw. offset tells the position of the native child in the tlw.
+            const QRect srcRect = toBottomLeftRect(deviceWindowRect.translated(offset), d_ptr->textureSize.height());
+            const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(deviceRect(srcRect, window),
+                                                                             d_ptr->textureSize,
+                                                                             origin);
+            d_ptr->blitter->blit(textureId, QMatrix4x4(), source);
+        }
         if (d_ptr->needsSwizzle)
             d_ptr->blitter->setSwizzleRB(false);
     }
@@ -359,7 +373,7 @@ void QPlatformBackingStore::composeAndFl
     // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set.
     for (int i = 0; i < textures->count(); ++i) {
         if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop))
-            blit(textures, i, window, deviceWindowRect, d_ptr->blitter);
+            blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset);
     }
 
     funcs->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
@@ -407,6 +421,8 @@ QImage QPlatformBackingStore::toImage()
 
   If the image has to be flipped (e.g. because the texture is attached to an FBO), \a
   flags will be set to include \c TextureFlip.
+
+  \note \a dirtyRegion is relative to the backingstore so no adjustment is needed.
  */
 GLuint QPlatformBackingStore::toTexture(const QRegion &dirtyRegion, QSize *textureSize, TextureFlags *flags) const
 {
Index: b/src/widgets/kernel/qwidget.cpp
===================================================================
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -61,6 +61,7 @@
 # include <private/qmainwindowlayout_p.h>
 #endif
 #include <qpa/qplatformwindow.h>
+#include <qpa/qplatformbackingstore.h>
 #include "private/qwidgetwindow_p.h"
 #include "qpainter.h"
 #include "qtooltip.h"
@@ -1839,6 +1840,8 @@ void QWidgetPrivate::deleteTLSysExtra()
         delete extra->topextra->backingStore;
         extra->topextra->backingStore = 0;
 #ifndef QT_NO_OPENGL
+        qDeleteAll(extra->topextra->widgetTextures);
+        extra->topextra->widgetTextures.clear();
         if (textureChildSeen && extra->topextra->shareContext)
             extra->topextra->shareContext->doneCurrent();
         delete extra->topextra->shareContext;
Index: b/src/widgets/kernel/qwidget_p.h
===================================================================
--- a/src/widgets/kernel/qwidget_p.h
+++ b/src/widgets/kernel/qwidget_p.h
@@ -75,6 +75,7 @@ class QWidgetBackingStore;
 class QGraphicsProxyWidget;
 class QWidgetItemV2;
 class QOpenGLContext;
+class QPlatformTextureList;
 
 class QStyle;
 
@@ -153,6 +154,8 @@ struct QTLWExtra {
     QWidgetBackingStoreTracker backingStoreTracker;
     QBackingStore *backingStore;
     QPainter *sharedPainter;
+    QWidgetWindow *window;
+    QOpenGLContext *shareContext;
 
     // Implicit pointers (shared_null).
     QString caption; // widget caption
@@ -167,6 +170,9 @@ struct QTLWExtra {
     QRect frameStrut;
     QRect normalGeometry; // used by showMin/maximized/FullScreen
     Qt::WindowFlags savedFlags; // Save widget flags while showing fullscreen
+    int initialScreenIndex; // Screen number when passing a QDesktop[Screen]Widget as parent.
+
+    QVector<QPlatformTextureList *> widgetTextures;
 
     // *************************** Cross-platform bit fields ****************************
     uint opacity : 8;
@@ -210,9 +216,6 @@ struct QTLWExtra {
     // starting position as 0,0 instead of the normal starting position.
     bool wasMaximized;
 #endif
-    QWidgetWindow *window;
-    QOpenGLContext *shareContext;
-    int initialScreenIndex; // Screen number when passing a QDesktop[Screen]Widget as parent.
 };
 
 struct QWExtra {
Index: b/src/widgets/kernel/qwidgetbackingstore.cpp
===================================================================
--- a/src/widgets/kernel/qwidgetbackingstore.cpp
+++ b/src/widgets/kernel/qwidgetbackingstore.cpp
@@ -79,7 +79,6 @@ void QWidgetBackingStore::qt_flush(QWidg
     Q_ASSERT(widget);
     Q_ASSERT(backingStore);
     Q_ASSERT(tlw);
-
 #if !defined(QT_NO_PAINT_DEBUG)
     static int flushUpdate = qEnvironmentVariableIntValue("QT_FLUSH_UPDATE");
     if (flushUpdate > 0)
@@ -105,13 +104,17 @@ void QWidgetBackingStore::qt_flush(QWidg
 
 #ifndef QT_NO_OPENGL
     if (widgetTextures) {
+        Q_ASSERT(!widgetTextures->isEmpty());
+        qt_window_private(tlw->windowHandle())->compositing = true;
         widget->window()->d_func()->sendComposeStatus(widget->window(), false);
         // A window may have alpha even when the app did not request
         // WA_TranslucentBackground. Therefore the compositor needs to know whether the app intends
         // to rely on translucency, in order to decide if it should clear to transparent or opaque.
         const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground);
+        // Use the tlw's context, not widget's. The difference is important with native child
+        // widgets where tlw != widget.
         backingStore->handle()->composeAndFlush(widget->windowHandle(), region, offset, widgetTextures,
-                                                widget->d_func()->shareContext(), translucentBackground);
+                                                tlw->d_func()->shareContext(), translucentBackground);
         widget->window()->d_func()->sendComposeStatus(widget->window(), true);
     } else
 #endif
@@ -741,7 +744,6 @@ void QWidgetBackingStore::updateLists(QW
 QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel)
     : tlw(topLevel),
       dirtyOnScreenWidgets(0),
-      widgetTextures(0),
       fullUpdatePending(0),
       updateRequestSent(0),
       textureListWatcher(0),
@@ -761,9 +763,6 @@ QWidgetBackingStore::~QWidgetBackingStor
     for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c)
         resetWidget(dirtyRenderToTextureWidgets.at(c));
 
-#ifndef QT_NO_OPENGL
-    delete widgetTextures;
-#endif
     delete dirtyOnScreenWidgets;
 }
 
@@ -792,8 +791,9 @@ void QWidgetPrivate::moveRect(const QRec
         destRect = destRect.translated(dx, dy).intersected(clipR);
     const QRect sourceRect(destRect.translated(-dx, -dy));
     const QRect parentRect(rect & clipR);
+    const bool nativeWithTextureChild = textureChildSeen && q->internalWinId();
 
-    bool accelerateMove = accelEnv && isOpaque
+    bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild
 #ifndef QT_NO_GRAPHICSVIEW
                           // No accelerate move for proxy widgets.
                           && !tlw->d_func()->extra->proxyWidget
@@ -913,6 +913,95 @@ void QWidgetPrivate::scrollRect(const QR
     }
 }
 
+#ifndef QT_NO_OPENGL
+static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatformTextureList *widgetTextures, QVector<QWidget *> *nativeChildren)
+{
+    QWidgetPrivate *wd = QWidgetPrivate::get(widget);
+    if (wd->renderToTexture) {
+        QPlatformTextureList::Flags flags = 0;
+        if (widget->testAttribute(Qt::WA_AlwaysStackOnTop))
+            flags |= QPlatformTextureList::StacksOnTop;
+        const QRect rect(widget->mapTo(tlw, QPoint()), widget->size());
+        widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags);
+    }
+
+    for (int i = 0; i < wd->children.size(); ++i) {
+        QWidget *w = qobject_cast<QWidget *>(wd->children.at(i));
+        // Stop at native widgets but store them. Stop at hidden widgets too.
+        if (w && !w->isWindow() && w->internalWinId())
+            nativeChildren->append(w);
+        if (w && !w->isWindow() && !w->internalWinId() && !w->isHidden() && QWidgetPrivate::get(w)->textureChildSeen)
+            findTextureWidgetsRecursively(tlw, w, widgetTextures, nativeChildren);
+    }
+}
+
+static void findAllTextureWidgetsRecursively(QWidget *tlw, QWidget *widget)
+{
+    // textureChildSeen does not take native child widgets into account and that's good.
+    if (QWidgetPrivate::get(widget)->textureChildSeen) {
+        QVector<QWidget *> nativeChildren;
+        QScopedPointer<QPlatformTextureList> tl(new QPlatformTextureList);
+        // Look for texture widgets (incl. widget itself) from 'widget' down,
+        // but skip subtrees with a parent of a native child widget.
+        findTextureWidgetsRecursively(tlw, widget, tl.data(), &nativeChildren);
+        // tl may be empty regardless of textureChildSeen if we have native or hidden children.
+        if (!tl->isEmpty())
+            QWidgetPrivate::get(tlw)->topData()->widgetTextures.append(tl.take());
+        // Native child widgets, if there was any, get their own separate QPlatformTextureList.
+        foreach (QWidget *ncw, nativeChildren) {
+            if (QWidgetPrivate::get(ncw)->textureChildSeen)
+                findAllTextureWidgetsRecursively(tlw, ncw);
+        }
+    }
+}
+
+static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget)
+{
+    foreach (QPlatformTextureList *tl, QWidgetPrivate::get(tlw)->topData()->widgetTextures) {
+        Q_ASSERT(!tl->isEmpty());
+        for (int i = 0; i < tl->count(); ++i) {
+            QWidget *w = static_cast<QWidget *>(tl->source(i));
+            if ((w->internalWinId() && w == widget) || (!w->internalWinId() && w->nativeParentWidget() == widget))
+                return tl;
+        }
+    }
+    return 0;
+}
+
+// Watches one or more QPlatformTextureLists for changes in the lock state and
+// triggers a backingstore sync when all the registered lists turn into
+// unlocked state. This is essential when a custom composeAndFlush()
+// implementation in a platform plugin is not synchronous and keeps
+// holding on to the textures for some time even after returning from there.
+QPlatformTextureListWatcher::QPlatformTextureListWatcher(QWidgetBackingStore *backingStore)
+    : m_backingStore(backingStore)
+{
+}
+
+void QPlatformTextureListWatcher::watch(QPlatformTextureList *textureList)
+{
+    connect(textureList, SIGNAL(locked(bool)), SLOT(onLockStatusChanged(bool)));
+    m_locked[textureList] = textureList->isLocked();
+}
+
+bool QPlatformTextureListWatcher::isLocked() const
+{
+    foreach (bool v, m_locked) {
+        if (v)
+            return true;
+    }
+    return false;
+}
+
+void QPlatformTextureListWatcher::onLockStatusChanged(bool locked)
+{
+    QPlatformTextureList *tl = static_cast<QPlatformTextureList *>(sender());
+    m_locked[tl] = locked;
+    if (!isLocked())
+        m_backingStore->sync();
+}
+#endif // QT_NO_OPENGL
+
 static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra)
 {
     if (!tlw || !tlwExtra || !tlw->testAttribute(Qt::WA_Mapped) || !tlw->isVisible())
@@ -941,7 +1030,7 @@ void QWidgetBackingStore::sync(QWidget *
 
     // Nothing to repaint.
     if (!isDirty() && store->size().isValid()) {
-        qt_flush(exposedWidget, exposedRegion, store, tlw, tlwOffset, widgetTextures, this);
+        qt_flush(exposedWidget, exposedRegion, store, tlw, tlwOffset, widgetTexturesFor(tlw, tlw), this);
         return;
     }
 
@@ -953,45 +1042,6 @@ void QWidgetBackingStore::sync(QWidget *
     doSync();
 }
 
-#ifndef QT_NO_OPENGL
-static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatformTextureList *widgetTextures)
-{
-    QWidgetPrivate *wd = QWidgetPrivate::get(widget);
-    if (wd->renderToTexture) {
-        QPlatformTextureList::Flags flags = 0;
-        if (widget->testAttribute(Qt::WA_AlwaysStackOnTop))
-            flags |= QPlatformTextureList::StacksOnTop;
-        const QRect rect(widget->mapTo(tlw, QPoint()), widget->size());
-        widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags);
-    }
-
-    for (int i = 0; i < wd->children.size(); ++i) {
-        QWidget *w = qobject_cast<QWidget *>(wd->children.at(i));
-        if (w && !w->isWindow() && !w->isHidden() && QWidgetPrivate::get(w)->textureChildSeen)
-            findTextureWidgetsRecursively(tlw, w, widgetTextures);
-    }
-}
-
-QPlatformTextureListWatcher::QPlatformTextureListWatcher(QWidgetBackingStore *backingStore)
-    : m_locked(false),
-      m_backingStore(backingStore)
-{
-}
-
-void QPlatformTextureListWatcher::watch(QPlatformTextureList *textureList)
-{
-    connect(textureList, SIGNAL(locked(bool)), SLOT(onLockStatusChanged(bool)));
-    m_locked = textureList->isLocked();
-}
-
-void QPlatformTextureListWatcher::onLockStatusChanged(bool locked)
-{
-    m_locked = locked;
-    if (!locked)
-        m_backingStore->sync();
-}
-#endif // QT_NO_OPENGL
-
 /*!
     Synchronizes the backing store, i.e. dirty areas are repainted and flushed.
 */
@@ -1019,12 +1069,19 @@ void QWidgetBackingStore::sync()
     if (textureListWatcher && !textureListWatcher->isLocked()) {
         textureListWatcher->deleteLater();
         textureListWatcher = 0;
-    } else if (widgetTextures && widgetTextures->isLocked()) {
-        if (!textureListWatcher)
-            textureListWatcher = new QPlatformTextureListWatcher(this);
-        if (!textureListWatcher->isLocked())
-            textureListWatcher->watch(widgetTextures);
-        return;
+    } else if (!tlwExtra->widgetTextures.isEmpty()) {
+        bool skipSync = false;
+        foreach (QPlatformTextureList *tl, tlwExtra->widgetTextures) {
+            if (tl->isLocked()) {
+                if (!textureListWatcher)
+                    textureListWatcher = new QPlatformTextureListWatcher(this);
+                if (!textureListWatcher->isLocked())
+                    textureListWatcher->watch(tl);
+                skipSync = true;
+            }
+        }
+        if (skipSync) // cannot compose due to widget textures being in use
+            return;
     }
 #endif
 
@@ -1117,13 +1174,14 @@ void QWidgetBackingStore::doSync()
     dirtyWidgets.clear();
 
 #ifndef QT_NO_OPENGL
-    delete widgetTextures;
-    widgetTextures = 0;
-    if (tlw->d_func()->textureChildSeen) {
-        widgetTextures = new QPlatformTextureList;
-        findTextureWidgetsRecursively(tlw, tlw, widgetTextures);
-    }
-    qt_window_private(tlw->windowHandle())->compositing = widgetTextures;
+    // Find all render-to-texture child widgets (including self).
+    // The search is cut at native widget boundaries, meaning that each native child widget
+    // has its own list for the subtree below it.
+    QTLWExtra *tlwExtra = tlw->d_func()->topData();
+    qDeleteAll(tlwExtra->widgetTextures);
+    tlwExtra->widgetTextures.clear();
+    findAllTextureWidgetsRecursively(tlw, tlw);
+    qt_window_private(tlw->windowHandle())->compositing = false; // will get updated in qt_flush()
     fullUpdatePending = false;
 #endif
 
@@ -1143,6 +1201,9 @@ void QWidgetBackingStore::doSync()
         for (int i = 0; i < paintPending.count(); ++i) {
             QWidget *w = paintPending[i];
             w->d_func()->sendPaintEvent(w->rect());
+            QWidget *npw = w->nativeParentWidget();
+            if (w->internalWinId() || (npw && npw != tlw))
+                markDirtyOnScreen(w->rect(), w, w->mapTo(tlw, QPoint()));
         }
 
         // We might have newly exposed areas on the screen if this function was
@@ -1154,18 +1215,23 @@ void QWidgetBackingStore::doSync()
     }
 
 #ifndef QT_NO_OPENGL
-    if (widgetTextures && widgetTextures->count()) {
-        for (int i = 0; i < widgetTextures->count(); ++i) {
-            QWidget *w = static_cast<QWidget *>(widgetTextures->source(i));
+    foreach (QPlatformTextureList *tl, tlwExtra->widgetTextures) {
+        for (int i = 0; i < tl->count(); ++i) {
+            QWidget *w = static_cast<QWidget *>(tl->source(i));
             if (dirtyRenderToTextureWidgets.contains(w)) {
-                const QRect rect = widgetTextures->geometry(i); // mapped to the tlw already
+                const QRect rect = tl->geometry(i); // mapped to the tlw already
                 dirty += rect;
                 toClean += rect;
             }
         }
     }
-    for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i)
-        resetWidget(dirtyRenderToTextureWidgets.at(i));
+    for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i) {
+        QWidget *w = dirtyRenderToTextureWidgets.at(i);
+        resetWidget(w);
+        QWidget *npw = w->nativeParentWidget();
+        if (w->internalWinId() || (npw && npw != tlw))
+            markDirtyOnScreen(w->rect(), w, w->mapTo(tlw, QPoint()));
+    }
     dirtyRenderToTextureWidgets.clear();
 #endif
 
@@ -1235,31 +1301,39 @@ void QWidgetBackingStore::doSync()
 */
 void QWidgetBackingStore::flush(QWidget *widget)
 {
+    const bool hasDirtyOnScreenWidgets = dirtyOnScreenWidgets && !dirtyOnScreenWidgets->isEmpty();
+    bool flushed = false;
+
+    // Flush the region in dirtyOnScreen.
     if (!dirtyOnScreen.isEmpty()) {
         QWidget *target = widget ? widget : tlw;
-        qt_flush(target, dirtyOnScreen, store, tlw, tlwOffset, widgetTextures, this);
+        qt_flush(target, dirtyOnScreen, store, tlw, tlwOffset, widgetTexturesFor(tlw, tlw), this);
         dirtyOnScreen = QRegion();
-#ifndef QT_NO_OPENGL
-        if (widgetTextures && widgetTextures->count())
-            return;
-#endif
+        flushed = true;
     }
 
-    if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty()) {
+    // Render-to-texture widgets are not in dirtyOnScreen so flush if we have not done it above.
+    if (!flushed && !hasDirtyOnScreenWidgets) {
 #ifndef QT_NO_OPENGL
-        if (widgetTextures && widgetTextures->count()) {
-            QWidget *target = widget ? widget : tlw;
-            qt_flush(target, QRegion(), store, tlw, tlwOffset, widgetTextures, this);
+        if (!tlw->d_func()->topData()->widgetTextures.isEmpty()) {
+            QPlatformTextureList *tl = widgetTexturesFor(tlw, tlw);
+            if (tl) {
+                QWidget *target = widget ? widget : tlw;
+                qt_flush(target, QRegion(), store, tlw, tlwOffset, tl, this);
+            }
         }
 #endif
-        return;
     }
 
+    if (!hasDirtyOnScreenWidgets)
+        return;
+
     for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
         QWidget *w = dirtyOnScreenWidgets->at(i);
         QWidgetPrivate *wd = w->d_func();
         Q_ASSERT(wd->needsFlush);
-        qt_flush(w, *wd->needsFlush, store, tlw, tlwOffset, 0, this);
+        QPlatformTextureList *widgetTexturesForNative = wd->textureChildSeen ? widgetTexturesFor(tlw, w) : 0;
+        qt_flush(w, *wd->needsFlush, store, tlw, tlwOffset, widgetTexturesForNative, this);
         *wd->needsFlush = QRegion();
     }
     dirtyOnScreenWidgets->clear();
Index: b/src/widgets/kernel/qwidgetbackingstore_p.h
===================================================================
--- a/src/widgets/kernel/qwidgetbackingstore_p.h
+++ b/src/widgets/kernel/qwidgetbackingstore_p.h
@@ -71,13 +71,13 @@ class QPlatformTextureListWatcher : publ
 public:
     QPlatformTextureListWatcher(QWidgetBackingStore *backingStore);
     void watch(QPlatformTextureList *textureList);
-    bool isLocked() const { return m_locked; }
+    bool isLocked() const;
 
 private slots:
      void onLockStatusChanged(bool locked);
 
 private:
-     bool m_locked;
+     QHash<QPlatformTextureList *, bool> m_locked;
      QWidgetBackingStore *m_backingStore;
 };
 #endif
@@ -128,7 +128,6 @@ private:
     QVector<QWidget *> dirtyRenderToTextureWidgets;
     QVector<QWidget *> *dirtyOnScreenWidgets;
     QList<QWidget *> staticWidgets;
-    QPlatformTextureList *widgetTextures;
     QBackingStore *store;
     uint fullUpdatePending : 1;
     uint updateRequestSent : 1;
Index: b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp
===================================================================
--- a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp
+++ b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp
@@ -57,6 +57,8 @@ private slots:
     void asViewport();
     void requestUpdate();
     void fboRedirect();
+    void showHide();
+    void nativeWindow();
 };
 
 void tst_QOpenGLWidget::create()
@@ -81,7 +83,8 @@ public:
         : QOpenGLWidget(parent),
           m_initCalled(false), m_paintCalled(false), m_resizeCalled(false),
           m_resizeOk(false),
-          m_w(expectedWidth), m_h(expectedHeight) { }
+          m_w(expectedWidth), m_h(expectedHeight),
+          r(1.0f), g(0.0f), b(0.0f) { }
 
     void initializeGL() Q_DECL_OVERRIDE {
         m_initCalled = true;
@@ -89,13 +92,16 @@ public:
     }
     void paintGL() Q_DECL_OVERRIDE {
         m_paintCalled = true;
-        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
+        glClearColor(r, g, b, 1.0f);
         glClear(GL_COLOR_BUFFER_BIT);
     }
     void resizeGL(int w, int h) Q_DECL_OVERRIDE {
         m_resizeCalled = true;
         m_resizeOk = w == m_w && h == m_h;
     }
+    void setClearColor(float r, float g, float b) {
+        this->r = r; this->g = g; this->b = b;
+    }
 
     bool m_initCalled;
     bool m_paintCalled;
@@ -103,6 +109,7 @@ public:
     bool m_resizeOk;
     int m_w;
     int m_h;
+    float r, g, b;
 };
 
 void tst_QOpenGLWidget::clearAndGrab()
@@ -355,6 +362,69 @@ void tst_QOpenGLWidget::fboRedirect()
     QVERIFY(reportedDefaultFbo != widgetFbo);
 }
 
+void tst_QOpenGLWidget::showHide()
+{
+    QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600));
+    w->resize(800, 600);
+    w->show();
+    QTest::qWaitForWindowExposed(w.data());
+
+    w->hide();
+
+    QImage image = w->grabFramebuffer();
+    QVERIFY(!image.isNull());
+    QCOMPARE(image.width(), w->width());
+    QCOMPARE(image.height(), w->height());
+    QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0));
+
+    w->setClearColor(0, 0, 1);
+    w->show();
+    QTest::qWaitForWindowExposed(w.data());
+
+    image = w->grabFramebuffer();
+    QVERIFY(!image.isNull());
+    QCOMPARE(image.width(), w->width());
+    QCOMPARE(image.height(), w->height());
+    QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255));
+}
+
+void tst_QOpenGLWidget::nativeWindow()
+{
+    QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600));
+    w->resize(800, 600);
+    w->show();
+    w->winId();
+    QTest::qWaitForWindowExposed(w.data());
+
+    QImage image = w->grabFramebuffer();
+    QVERIFY(!image.isNull());
+    QCOMPARE(image.width(), w->width());
+    QCOMPARE(image.height(), w->height());
+    QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0));
+    QVERIFY(w->internalWinId());
+
+    // Now as a native child.
+    QWidget nativeParent;
+    nativeParent.resize(800, 600);
+    nativeParent.setAttribute(Qt::WA_NativeWindow);
+    ClearWidget *child = new ClearWidget(0, 800, 600);
+    child->setClearColor(0, 1, 0);
+    child->setParent(&nativeParent);
+    child->resize(400, 400);
+    child->move(23, 34);
+    nativeParent.show();
+    QTest::qWaitForWindowExposed(&nativeParent);
+
+    QVERIFY(nativeParent.internalWinId());
+    QVERIFY(!child->internalWinId());
+
+    image = child->grabFramebuffer();
+    QVERIFY(!image.isNull());
+    QCOMPARE(image.width(), child->width());
+    QCOMPARE(image.height(), child->height());
+    QVERIFY(image.pixel(30, 40) == qRgb(0, 255, 0));
+}
+
 QTEST_MAIN(tst_QOpenGLWidget)
 
 #include "tst_qopenglwidget.moc"
Index: b/tests/manual/qopenglwidget/openglwidget/main.cpp
===================================================================
--- a/tests/manual/qopenglwidget/openglwidget/main.cpp
+++ b/tests/manual/qopenglwidget/openglwidget/main.cpp
@@ -35,13 +35,108 @@
 #include <QApplication>
 #include <QPushButton>
 #include <QMdiArea>
+#include <QMdiSubWindow>
+#include <QMenu>
+#include <QMenuBar>
+#include <QMainWindow>
 #include <QLCDNumber>
+#include <QScrollArea>
+#include <QScrollBar>
+#include <QTabWidget>
+#include <QLabel>
 #include <QTimer>
 #include <QSurfaceFormat>
 #include <QDebug>
+#include <private/qwindow_p.h>
+
+class Tools : public QObject
+{
+    Q_OBJECT
+
+public:
+    Tools(QWidget *root, QWidget *widgetToTurn, const QVector<QWidget *> glwidgets)
+        : m_root(root), m_widgetToTurn(widgetToTurn), m_glWidgets(glwidgets) { }
+    void dump();
+
+private slots:
+    void turnNative();
+    void hideShowAllGL();
+    void dumpCompositingStatus();
+
+signals:
+    void aboutToShowGLWidgets();
+
+private:
+    void dumpWidget(QWidget *w, int indent = 0);
+
+    QWidget *m_root;
+    QWidget *m_widgetToTurn;
+    QVector<QWidget *> m_glWidgets;
+};
+
+void Tools::turnNative()
+{
+    qDebug("Turning into native");
+    m_widgetToTurn->winId();
+    dump();
+}
+
+void Tools::hideShowAllGL()
+{
+    if (m_glWidgets[0]->isVisible()) {
+        qDebug("Hiding all render-to-texture widgets");
+        foreach (QWidget *w, m_glWidgets)
+            w->hide();
+    } else {
+        qDebug("Showing all render-to-texture widgets");
+        emit aboutToShowGLWidgets();
+        foreach (QWidget *w, m_glWidgets)
+            w->show();
+    }
+}
+
+void Tools::dump()
+{
+    qDebug() << "Widget hierarchy";
+    dumpWidget(m_root);
+    qDebug() << "========";
+}
+
+void Tools::dumpWidget(QWidget *w, int indent)
+{
+    QString indentStr;
+    indentStr.fill(' ', indent);
+    qDebug().noquote() << indentStr << w << "winId =" << w->internalWinId();
+    foreach (QObject *obj, w->children()) {
+        if (QWidget *cw = qobject_cast<QWidget *>(obj))
+            dumpWidget(cw, indent + 4);
+    }
+}
+
+void Tools::dumpCompositingStatus()
+{
+    QWindow *w = m_root->window()->windowHandle();
+    qDebug() << "Compositing status for" << w << m_root->window() << "is" << QWindowPrivate::get(w)->compositing;
+}
+
+class TabWidgetResetter : public QObject
+{
+    Q_OBJECT
+public:
+    TabWidgetResetter(QTabWidget *tw) : m_tw(tw) { }
+public slots:
+    void reset() { m_tw->setCurrentIndex(0); }
+private:
+    QTabWidget *m_tw;
+};
 
 int main(int argc, char *argv[])
 {
+    if (argc > 1 && !strcmp(argv[1], "--sharecontext")) {
+        qDebug("Requesting all contexts to share");
+        QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
+    }
+
     QApplication a(argc, argv);
 
     QSurfaceFormat format;
@@ -53,28 +148,86 @@ int main(int argc, char *argv[])
     }
     qDebug() << "Requesting" << format;
 
-    QMdiArea w;
-    w.resize(400,400);
+    QMainWindow wnd;
+    wnd.setObjectName("Main Window");
+    wnd.resize(1024, 768);
+
+    QMdiArea *w = new QMdiArea;
+    w->setObjectName("MDI area");
+    w->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+    wnd.setCentralWidget(w);
 
-    OpenGLWidget *glw = new OpenGLWidget;
+    OpenGLWidget *glw = new OpenGLWidget(33, QVector3D(0, 0, 1));
+    glw->setObjectName("First GL Widget with 33 ms timer");
     glw->setFormat(format);
-    w.addSubWindow(glw);
-    glw->setMinimumSize(100,100);
+    glw->setMinimumSize(100, 100);
+    QMdiSubWindow *sw = w->addSubWindow(glw);
+    sw->setObjectName("First MDI Sub-Window");
+    sw->setWindowTitle("33 ms timer");
 
-    OpenGLWidget *glw2 = new OpenGLWidget;
+    OpenGLWidget *glw2 = new OpenGLWidget(16);
+    glw2->setObjectName("Second GL Widget with 16 ms timer");
     glw2->setFormat(format);
-    glw2->setMinimumSize(100,100);
-    w.addSubWindow(glw2);
+    glw2->setMinimumSize(100, 100);
+    QOpenGLWidget *glw22 = new OpenGLWidget(16);
+    glw22->setObjectName("Second #2 GLWidget");
+    glw22->setParent(glw2);
+    glw22->resize(40, 40);
+    sw = w->addSubWindow(glw2);
+    sw->setObjectName("Second MDI Sub-Window");
+    sw->setWindowTitle("16 ms timer");
+
+    OpenGLWidget *glw3 = new OpenGLWidget(0); // trigger updates continuously, no timer
+    glw3->setObjectName("GL widget in scroll area (possibly native)");
+    glw3->setFormat(format);
+    glw3->setFixedSize(600, 600);
+    QScrollArea *sa = new QScrollArea;
+    sa->setWidget(glw3);
+    sa->setMinimumSize(100, 100);
+    sa->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+    sw = w->addSubWindow(sa);
+    sw->setObjectName("MDI Sub-Window for scroll area");
+    sw->setWindowTitle("Cont. update");
+    sw->resize(300, 300);
+    sa->verticalScrollBar()->setValue(300);
 
     QLCDNumber *lcd = new QLCDNumber;
     lcd->display(1337);
-    lcd->setMinimumSize(300,100);
-    w.addSubWindow(lcd);
+    lcd->setMinimumSize(300, 100);
+    sw = w->addSubWindow(lcd);
+    sw->setObjectName("MDI Sub-Window for LCD widget");
+    sw->setWindowTitle("Ordinary widget");
+
+    QTabWidget *tw = new QTabWidget;
+    QOpenGLWidget *glw4 = new OpenGLWidget(16, QVector3D(1, 0, 0));
+    glw4->setObjectName("GL widget in tab widget");
+    tw->addTab(glw4, "OpenGL");
+    QLabel *label = new QLabel("Another tab");
+    tw->addTab(label, "Not OpenGL");
+    tw->setMinimumSize(100, 100);
+    sw = w->addSubWindow(tw);
+    sw->setObjectName("MDI Sub-Window for tab widget");
+    sw->setWindowTitle("Tabs");
+
+    TabWidgetResetter twr(tw);
+    Tools t(&wnd, glw3, QVector<QWidget *>() << glw << glw2 << glw3 << glw4);
+    QObject::connect(&t, SIGNAL(aboutToShowGLWidgets()), &twr, SLOT(reset()));
+    QMenu *toolsMenu = wnd.menuBar()->addMenu("&Tools");
+    toolsMenu->addAction("&Turn widgets (or some parent) into native", &t, SLOT(turnNative()));
+    toolsMenu->addAction("&Hide/show all OpenGL widgets", &t, SLOT(hideShowAllGL()));
+
+    QTimer compStatusDumpTimer;
+    QObject::connect(&compStatusDumpTimer, SIGNAL(timeout()), &t, SLOT(dumpCompositingStatus()));
+    compStatusDumpTimer.start(5000);
 
-    w.show();
+    wnd.show();
 
     if (glw->isValid())
         qDebug() << "Got" << glw->format();
 
+    t.dump();
+
     return a.exec();
 }
+
+#include "main.moc"
Index: b/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp
===================================================================
--- a/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp
+++ b/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp
@@ -75,16 +75,23 @@ public:
 
     int w,h;
     QWidget *q;
+
+    int m_interval;
+    QVector3D m_rotAxis;
 };
 
 
-OpenGLWidget::OpenGLWidget(QWidget *parent)
+OpenGLWidget::OpenGLWidget(int interval, const QVector3D &rotAxis, QWidget *parent)
     : QOpenGLWidget(parent)
 {
-    d = new OpenGLWidgetPrivate(this);
-    QTimer *timer = new QTimer(this);
-    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
-    timer->start(30);
+    d.reset(new OpenGLWidgetPrivate(this));
+    d->m_interval = interval;
+    d->m_rotAxis = rotAxis;
+    if (interval > 0) {
+        QTimer *timer = new QTimer(this);
+        connect(timer, SIGNAL(timeout()), this, SLOT(update()));
+        timer->start(interval);
+    }
 }
 
 OpenGLWidget::~OpenGLWidget()
@@ -152,7 +159,8 @@ void OpenGLWidgetPrivate::render()
     QMatrix4x4 matrix;
     matrix.perspective(60.0f, 4.0f/3.0f, 0.1f, 100.0f);
     matrix.translate(0, 0, -2);
-    matrix.rotate(100.0f * m_frame / 30/*screen()->refreshRate()*/, 0, 1, 0);
+    const qreal angle = 100.0f * m_frame / 30;
+    matrix.rotate(angle, m_rotAxis);
 
     m_program->setUniformValue(m_matrixUniform, matrix);
 
@@ -182,4 +190,7 @@ void OpenGLWidgetPrivate::render()
     m_program->release();
 
     ++m_frame;
+
+    if (m_interval <= 0)
+        q->update();
 }
Index: b/tests/manual/qopenglwidget/openglwidget/openglwidget.h
===================================================================
--- a/tests/manual/qopenglwidget/openglwidget/openglwidget.h
+++ b/tests/manual/qopenglwidget/openglwidget/openglwidget.h
@@ -35,13 +35,14 @@
 #define OPENGLWIDGET_H
 
 #include <QtWidgets/QOpenGLWidget>
+#include <QtGui/QVector3D>
 
 class OpenGLWidgetPrivate;
 class OpenGLWidget : public QOpenGLWidget
 {
     Q_OBJECT
 public:
-    OpenGLWidget(QWidget *parent = 0);
+    OpenGLWidget(int interval = 30, const QVector3D &rotAxis = QVector3D(0, 1, 0), QWidget *parent = 0);
     ~OpenGLWidget();
 
     void initializeGL();
@@ -49,7 +50,7 @@ public:
     void paintGL();
 
 private:
-    OpenGLWidgetPrivate *d;
+    QScopedPointer<OpenGLWidgetPrivate> d;
 };
 
 #endif // OPENGLWIDGET_H
Index: b/src/widgets/kernel/qopenglwidget.cpp
===================================================================
--- a/src/widgets/kernel/qopenglwidget.cpp
+++ b/src/widgets/kernel/qopenglwidget.cpp
@@ -636,12 +636,6 @@ void QOpenGLWidgetPaintDevice::ensureAct
 
 GLuint QOpenGLWidgetPrivate::textureId() const
 {
-    Q_Q(const QOpenGLWidget);
-    if (!q->isWindow() && q->internalWinId()) {
-        qWarning() << "QOpenGLWidget cannot be used as a native child widget."
-                   << "Consider setting Qt::WA_DontCreateNativeAncestors and Qt::AA_DontCreateNativeWidgetSiblings.";
-        return 0;
-    }
     return resolvedFbo ? resolvedFbo->texture() : (fbo ? fbo->texture() : 0);
 }
 
