diff options
-rw-r--r-- | src/gui/painting/qplatformbackingstore.cpp | 42 | ||||
-rw-r--r-- | src/widgets/kernel/qopenglwidget.cpp | 6 | ||||
-rw-r--r-- | src/widgets/kernel/qwidget.cpp | 3 | ||||
-rw-r--r-- | src/widgets/kernel/qwidget_p.h | 9 | ||||
-rw-r--r-- | src/widgets/kernel/qwidgetbackingstore.cpp | 228 | ||||
-rw-r--r-- | src/widgets/kernel/qwidgetbackingstore_p.h | 5 | ||||
-rw-r--r-- | tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp | 74 | ||||
-rw-r--r-- | tests/manual/qopenglwidget/openglwidget/main.cpp | 175 | ||||
-rw-r--r-- | tests/manual/qopenglwidget/openglwidget/openglwidget.cpp | 23 | ||||
-rw-r--r-- | tests/manual/qopenglwidget/openglwidget/openglwidget.h | 5 |
10 files changed, 447 insertions, 123 deletions
diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp index dd02e24676..5f873bfe7e 100644 --- a/src/gui/painting/qplatformbackingstore.cpp +++ b/src/gui/painting/qplatformbackingstore.cpp @@ -224,16 +224,16 @@ static inline QRect deviceRect(const QRect &rect, QWindow *window) return deviceRect; } -static QRegion deviceRegion(const QRegion ®ion, QWindow *window) +static QRegion deviceRegion(const QRegion ®ion, QWindow *window, const QPoint &offset) { - if (!(window->devicePixelRatio() > 1)) + if (offset.isNull() && window->devicePixelRatio() <= 1) return region; QVector<QRect> rects; const QVector<QRect> regionRects = region.rects(); rects.reserve(regionRects.count()); foreach (const QRect &rect, regionRects) - rects.append(deviceRect(rect, window)); + rects.append(deviceRect(rect.translated(offset), window)); QRegion deviceRegion; deviceRegion.setRects(rects.constData(), rects.count()); @@ -246,10 +246,12 @@ static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight) 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()); @@ -274,7 +276,9 @@ static void blit(const QPlatformTextureList *textures, int idx, QWindow *window, 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 ®ion, @@ -282,7 +286,8 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i QPlatformTextureList *textures, QOpenGLContext *context, bool translucentBackground) { - Q_UNUSED(offset); + if (!qt_window_private(window)->receivedExpose) + return; if (!context->makeCurrent(window)) { qWarning("composeAndFlush: makeCurrent() failed"); @@ -306,7 +311,7 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i // 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); @@ -348,17 +353,26 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i 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); } @@ -366,7 +380,7 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i // 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->glDisable(GL_BLEND); @@ -413,6 +427,8 @@ QImage QPlatformBackingStore::toImage() const 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 { diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp index 389539bb18..3a4a3a0d63 100644 --- a/src/widgets/kernel/qopenglwidget.cpp +++ b/src/widgets/kernel/qopenglwidget.cpp @@ -645,12 +645,6 @@ void QOpenGLWidgetPaintDevice::ensureActiveTarget() 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); } diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 4037310088..0d07eedc0b 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -60,6 +60,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" @@ -1834,6 +1835,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; diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index fe65cb19c7..a78cf099ac 100644 --- 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 { diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp index 69958636fd..55b8513072 100644 --- a/src/widgets/kernel/qwidgetbackingstore.cpp +++ b/src/widgets/kernel/qwidgetbackingstore.cpp @@ -79,7 +79,6 @@ void QWidgetBackingStore::qt_flush(QWidget *widget, const QRegion ®ion, QBack 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(QWidget *widget, const QRegion ®ion, QBack #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(QWidget *cur) QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel) : tlw(topLevel), dirtyOnScreenWidgets(0), - widgetTextures(0), fullUpdatePending(0), updateRequestSent(0), textureListWatcher(0), @@ -761,9 +763,6 @@ QWidgetBackingStore::~QWidgetBackingStore() 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 QRect &rect, int dx, int dy) 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 QRect &rect, int dx, int dy) } } +#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 *exposedWidget, const QRegion &exposedReg // 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 *exposedWidget, const QRegion &exposedReg 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(); diff --git a/src/widgets/kernel/qwidgetbackingstore_p.h b/src/widgets/kernel/qwidgetbackingstore_p.h index b7ee7e4168..c45e60ef6e 100644 --- a/src/widgets/kernel/qwidgetbackingstore_p.h +++ b/src/widgets/kernel/qwidgetbackingstore_p.h @@ -71,13 +71,13 @@ class QPlatformTextureListWatcher : public QObject 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; diff --git a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp index 638fad6206..a4a0045265 100644 --- 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" diff --git a/tests/manual/qopenglwidget/openglwidget/main.cpp b/tests/manual/qopenglwidget/openglwidget/main.cpp index aaa48ea60a..a56cea1dfe 100644 --- 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"); - w.show(); + 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); + + wnd.show(); if (glw->isValid()) qDebug() << "Got" << glw->format(); + t.dump(); + return a.exec(); } + +#include "main.moc" diff --git a/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp b/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp index d47e12edc8..4d2463b84d 100644 --- 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(); } diff --git a/tests/manual/qopenglwidget/openglwidget/openglwidget.h b/tests/manual/qopenglwidget/openglwidget/openglwidget.h index 4dc5fde067..a1d5490845 100644 --- 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 |