summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/painting/qplatformbackingstore.cpp42
-rw-r--r--src/widgets/kernel/qopenglwidget.cpp6
-rw-r--r--src/widgets/kernel/qwidget.cpp3
-rw-r--r--src/widgets/kernel/qwidget_p.h9
-rw-r--r--src/widgets/kernel/qwidgetbackingstore.cpp228
-rw-r--r--src/widgets/kernel/qwidgetbackingstore_p.h5
-rw-r--r--tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp74
-rw-r--r--tests/manual/qopenglwidget/openglwidget/main.cpp175
-rw-r--r--tests/manual/qopenglwidget/openglwidget/openglwidget.cpp23
-rw-r--r--tests/manual/qopenglwidget/openglwidget/openglwidget.h5
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 &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;
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 &region,
@@ -282,7 +286,8 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &regi
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 &regi
// 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 &regi
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 &regi
// 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 &region, 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 &region, 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