aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@digia.com>2013-10-17 14:53:33 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-10-30 08:29:49 +0100
commit906d5c5c40183468f9521277c6244a6c46730de6 (patch)
tree0eb46a8f88d59993ab659e2dc07970d1ce2f0d73
parentc084d32d92b2df55532fa1599e590c29bf2b5bfb (diff)
Use one render loop per QQuickWindow
See the task for the full reasoning behind this patch. The threaded renderloop has been refactored to have one window per thread. This is mostly a simplification of the current code path where for loops over multiple windows are turned into if (window). The QSGContext has been split into two classes, QSGRenderContext for which there is one per OpenGLContext. The rest of the patch is name changes and a couple of cleanups in the hopes of simplifying this change. Task-number: QTBUG-33993 Change-Id: I31c81f9694d7da7474a72333169be38de62613c4 Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
-rw-r--r--src/quick/designer/designerwindowmanager.cpp5
-rw-r--r--src/quick/designer/designerwindowmanager_p.h3
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp2
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp2
-rw-r--r--src/quick/items/qquickborderimage.cpp2
-rw-r--r--src/quick/items/qquickimage.cpp16
-rw-r--r--src/quick/items/qquickitem_p.h7
-rw-r--r--src/quick/items/qquickshadereffectsource.cpp31
-rw-r--r--src/quick/items/qquickshadereffectsource_p.h5
-rw-r--r--src/quick/items/qquicktext.cpp7
-rw-r--r--src/quick/items/qquicktextedit.cpp2
-rw-r--r--src/quick/items/qquicktextinput.cpp2
-rw-r--r--src/quick/items/qquicktextnode.cpp15
-rw-r--r--src/quick/items/qquicktextnode_p.h3
-rw-r--r--src/quick/items/qquickwindow.cpp34
-rw-r--r--src/quick/items/qquickwindow_p.h2
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp3
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h2
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.cpp2
-rw-r--r--src/quick/scenegraph/coreapi/qsgnodeupdater_p.h5
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer.cpp17
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer_p.h8
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp5
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h4
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp550
-rw-r--r--src/quick/scenegraph/qsgcontext_p.h102
-rw-r--r--src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp9
-rw-r--r--src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h2
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp9
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode.cpp2
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p.h6
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp17
-rw-r--r--src/quick/scenegraph/qsgrenderloop_p.h2
-rw-r--r--src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp5
-rw-r--r--src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h1
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp473
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop_p.h28
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop.cpp14
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop_p.h5
-rw-r--r--src/quick/scenegraph/scenegraph.pri2
-rw-r--r--src/quick/scenegraph/util/qsgatlastexture.cpp40
-rw-r--r--src/quick/scenegraph/util/qsgatlastexture_p.h10
-rw-r--r--src/quick/scenegraph/util/qsgpainternode.cpp6
-rw-r--r--src/quick/scenegraph/util/qsgpainternode_p.h2
-rw-r--r--tests/auto/quick/nodes/tst_nodestest.cpp2
-rw-r--r--tests/auto/quick/nokeywords/tst_nokeywords.cpp1
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp14
-rw-r--r--tests/auto/quick/scenegraph/data/logo-big.jpgbin0 -> 14174 bytes
-rw-r--r--tests/auto/quick/scenegraph/data/logo-small.jpgbin0 -> 691 bytes
-rw-r--r--tests/auto/quick/scenegraph/data/manyWindows_dftext.qml (renamed from src/quick/scenegraph/qsgflashnode_p.h)29
-rw-r--r--tests/auto/quick/scenegraph/data/manyWindows_image.qml (renamed from src/quick/scenegraph/qsgflashnode.cpp)28
-rw-r--r--tests/auto/quick/scenegraph/data/manyWindows_ntext.qml51
-rw-r--r--tests/auto/quick/scenegraph/data/manyWindows_rects.qml70
-rw-r--r--tests/auto/quick/scenegraph/scenegraph.pro11
-rw-r--r--tests/auto/quick/scenegraph/tst_scenegraph.cpp209
55 files changed, 1128 insertions, 756 deletions
diff --git a/src/quick/designer/designerwindowmanager.cpp b/src/quick/designer/designerwindowmanager.cpp
index 76789cd3a8..c4a95d254b 100644
--- a/src/quick/designer/designerwindowmanager.cpp
+++ b/src/quick/designer/designerwindowmanager.cpp
@@ -40,7 +40,7 @@
****************************************************************************/
#include "designerwindowmanager_p.h"
-
+#include "private/qquickwindow_p.h"
#include <QtGui/QOpenGLContext>
#include <QtQuick/QQuickWindow>
@@ -51,6 +51,7 @@ QT_BEGIN_NAMESPACE
DesignerWindowManager::DesignerWindowManager()
: m_sgContext(QSGContext::createDefaultContext())
{
+ m_renderContext.reset(new QSGRenderContext(m_sgContext.data()));
}
void DesignerWindowManager::show(QQuickWindow *window)
@@ -74,7 +75,7 @@ void DesignerWindowManager::makeOpenGLContext(QQuickWindow *window)
m_openGlContext->create();
if (!m_openGlContext->makeCurrent(window))
qWarning("QQuickWindow: makeCurrent() failed...");
- m_sgContext->initialize(m_openGlContext.data());
+ m_renderContext->initialize(m_openGlContext.data());
} else {
m_openGlContext->makeCurrent(window);
}
diff --git a/src/quick/designer/designerwindowmanager_p.h b/src/quick/designer/designerwindowmanager_p.h
index a822ead64e..1bab8c8508 100644
--- a/src/quick/designer/designerwindowmanager_p.h
+++ b/src/quick/designer/designerwindowmanager_p.h
@@ -64,6 +64,7 @@ QT_BEGIN_NAMESPACE
class QQuickWindow;
class QSGContext;
+class QSGRenderContext;
class QAnimationDriver;
class QOpenGLContext;
@@ -91,12 +92,14 @@ public:
QAnimationDriver *animationDriver() const { return 0; }
QSGContext *sceneGraphContext() const;
+ QSGRenderContext *createRenderContext(QSGContext *) const { return m_renderContext.data(); }
static void createOpenGLContext(QQuickWindow *window);
private:
QScopedPointer<QOpenGLContext> m_openGlContext;
QScopedPointer<QSGContext> m_sgContext;
+ QScopedPointer<QSGRenderContext> m_renderContext;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index d9feb8d8eb..ba3592986c 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -112,7 +112,7 @@ QSGTexture *QQuickCanvasPixmap::texture()
Q_ASSERT(m_pixmap->textureFactory());
m_texture = m_pixmap->textureFactory()->createTexture(m_window);
} else {
- m_texture = QQuickWindowPrivate::get(m_window)->context->createTexture(m_image);
+ m_texture = m_window->createTextureFromImage(m_image, QQuickWindow::TextureCanUseAtlas);
}
}
return m_texture;
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index 2b2504daf2..3088c41cd7 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -4112,7 +4112,7 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_texture->moveToThread(renderThread);
if (m_renderTarget == QQuickCanvasItem::FramebufferObject && renderThread != sceneGraphThread) {
- QOpenGLContext *cc = QQuickWindowPrivate::get(window)->context->glContext();
+ QOpenGLContext *cc = QQuickWindowPrivate::get(window)->context->openglContext();
m_surface = window;
m_glContext = new QOpenGLContext;
m_glContext->setFormat(cc->format());
diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp
index a2e4e91755..0bed5e96a2 100644
--- a/src/quick/items/qquickborderimage.cpp
+++ b/src/quick/items/qquickborderimage.cpp
@@ -551,7 +551,7 @@ QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
{
Q_D(QQuickBorderImage);
- QSGTexture *texture = d->sceneGraphContext()->textureForFactory(d->pix.textureFactory(), window());
+ QSGTexture *texture = d->sceneGraphRenderContext()->textureForFactory(d->pix.textureFactory(), window());
if (!texture || width() <= 0 || height() <= 0) {
delete oldNode;
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index d6be13f3c0..0708304051 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -532,17 +532,17 @@ QRectF QQuickImage::boundingRect() const
QSGTextureProvider *QQuickImage::textureProvider() const
{
Q_D(const QQuickImage);
+
+ if (!d->window || !d->sceneGraphRenderContext() || QThread::currentThread() != d->sceneGraphRenderContext()->thread()) {
+ qWarning("QQuickImage::textureProvider: can only be queried on the rendering thread of an exposed window");
+ return 0;
+ }
+
if (!d->provider) {
- // Make sure it gets thread affinity on the rendering thread so deletion works properly..
- Q_ASSERT_X(d->window
- && d->sceneGraphContext()
- && QThread::currentThread() == d->sceneGraphContext()->thread(),
- "QQuickImage::textureProvider",
- "Cannot be used outside the GUI thread");
QQuickImagePrivate *dd = const_cast<QQuickImagePrivate *>(d);
dd->provider = new QQuickImageTextureProvider;
dd->provider->m_smooth = d->smooth;
- dd->provider->m_texture = d->sceneGraphContext()->textureForFactory(d->pix.textureFactory(), window());
+ dd->provider->m_texture = d->sceneGraphRenderContext()->textureForFactory(d->pix.textureFactory(), window());
}
return d->provider;
@@ -552,7 +552,7 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
Q_D(QQuickImage);
- QSGTexture *texture = d->sceneGraphContext()->textureForFactory(d->pix.textureFactory(), window());
+ QSGTexture *texture = d->sceneGraphRenderContext()->textureForFactory(d->pix.textureFactory(), window());
// Copy over the current texture state into the texture provider...
if (d->provider) {
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index f731acabbb..7faf39f8e5 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -468,6 +468,7 @@ public:
QQuickWindow *window;
int windowRefCount;
inline QSGContext *sceneGraphContext() const;
+ inline QSGRenderContext *sceneGraphRenderContext() const;
QQuickItem *parentItem;
QQmlNotifier parentNotifier;
@@ -840,6 +841,12 @@ Qt::MouseButtons QQuickItemPrivate::acceptedMouseButtons() const
QSGContext *QQuickItemPrivate::sceneGraphContext() const
{
Q_ASSERT(window);
+ return static_cast<QQuickWindowPrivate *>(QObjectPrivate::get(window))->context->sceneGraphContext();
+}
+
+QSGRenderContext *QQuickItemPrivate::sceneGraphRenderContext() const
+{
+ Q_ASSERT(window);
return static_cast<QQuickWindowPrivate *>(QObjectPrivate::get(window))->context;
}
diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp
index e86550d731..c05545c292 100644
--- a/src/quick/items/qquickshadereffectsource.cpp
+++ b/src/quick/items/qquickshadereffectsource.cpp
@@ -45,6 +45,7 @@
#include "qquickwindow_p.h"
#include <private/qsgadaptationlayer_p.h>
#include <QtQuick/private/qsgrenderer_p.h>
+#include <qsgsimplerectnode.h>
#include "qopenglframebufferobject.h"
#include "qmath.h"
@@ -143,7 +144,7 @@ QQuickShaderEffectTexture::QQuickShaderEffectTexture(QQuickItem *shaderSource)
#ifdef QSG_DEBUG_FBO_OVERLAY
, m_debugOverlay(0)
#endif
- , m_context(QQuickItemPrivate::get(shaderSource)->sceneGraphContext())
+ , m_context(QQuickItemPrivate::get(shaderSource)->sceneGraphRenderContext())
, m_mipmap(false)
, m_live(true)
, m_recursive(false)
@@ -326,7 +327,7 @@ void QQuickShaderEffectTexture::grab()
|| (!m_fbo->format().mipmap() && m_mipmap))
{
if (!m_multisamplingChecked) {
- if (m_context->glContext()->format().samples() <= 1) {
+ if (m_context->openglContext()->format().samples() <= 1) {
m_multisampling = false;
} else {
QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
@@ -342,7 +343,7 @@ void QQuickShaderEffectTexture::grab()
QOpenGLFramebufferObjectFormat format;
format.setInternalTextureFormat(m_format);
- format.setSamples(m_context->glContext()->format().samples());
+ format.setSamples(m_context->openglContext()->format().samples());
m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format);
m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo);
} else {
@@ -385,20 +386,16 @@ void QQuickShaderEffectTexture::grab()
#ifdef QSG_DEBUG_FBO_OVERLAY
if (qmlFboOverlay()) {
if (!m_debugOverlay)
- m_debugOverlay = m_context->createRectangleNode();
+ m_debugOverlay = new QSGSimpleRectNode();
m_debugOverlay->setRect(QRectF(0, 0, m_size.width(), m_size.height()));
m_debugOverlay->setColor(QColor(0xff, 0x00, 0x80, 0x40));
- m_debugOverlay->setPenColor(QColor());
- m_debugOverlay->setPenWidth(0);
- m_debugOverlay->setRadius(0);
- m_debugOverlay->update();
root->appendChildNode(m_debugOverlay);
}
#endif
m_dirtyTexture = false;
- QOpenGLContext *ctx = m_context->glContext();
+ QOpenGLContext *ctx = m_context->openglContext();
m_renderer->setDeviceRect(m_size);
m_renderer->setViewportRect(m_size);
QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height());
@@ -591,8 +588,8 @@ void QQuickShaderEffectSource::ensureTexture()
return;
Q_ASSERT_X(QQuickItemPrivate::get(this)->window
- && QQuickItemPrivate::get(this)->sceneGraphContext()
- && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
+ && QQuickItemPrivate::get(this)->sceneGraphRenderContext()
+ && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphRenderContext()->thread(),
"QQuickShaderEffectSource::ensureTexture",
"Cannot be used outside the rendering thread");
@@ -603,13 +600,13 @@ void QQuickShaderEffectSource::ensureTexture()
QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
{
+ const QQuickItemPrivate *d = QQuickItemPrivate::get(this);
+ if (!d->window || !d->sceneGraphRenderContext() || QThread::currentThread() != d->sceneGraphRenderContext()->thread()) {
+ qWarning("QQuickShaderEffectSource::textureProvider: can only be queried on the rendering thread of an exposed window");
+ return 0;
+ }
+
if (!m_provider) {
- // Make sure it gets thread affinity on the rendering thread so deletion works properly..
- Q_ASSERT_X(QQuickItemPrivate::get(this)->window
- && QQuickItemPrivate::get(this)->sceneGraphContext()
- && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
- "QQuickShaderEffectSource::textureProvider",
- "Cannot be used outside the rendering thread");
const_cast<QQuickShaderEffectSource *>(this)->m_provider = new QQuickShaderEffectSourceTextureProvider();
const_cast<QQuickShaderEffectSource *>(this)->ensureTexture();
connect(m_texture, SIGNAL(updateRequested()), m_provider, SIGNAL(textureChanged()));
diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h
index 7d1a9b7304..85b58e67e4 100644
--- a/src/quick/items/qquickshadereffectsource_p.h
+++ b/src/quick/items/qquickshadereffectsource_p.h
@@ -60,6 +60,7 @@ QT_BEGIN_NAMESPACE
class QSGNode;
class UpdatePaintNodeData;
class QOpenGLFramebufferObject;
+class QSGSimpleRectNode;
class QQuickShaderEffectSourceTextureProvider;
@@ -139,10 +140,10 @@ private:
QSharedPointer<QSGDepthStencilBuffer> m_depthStencilBuffer;
#ifdef QSG_DEBUG_FBO_OVERLAY
- QSGRectangleNode *m_debugOverlay;
+ QSGSimpleRectNode *m_debugOverlay;
#endif
- QSGContext *m_context;
+ QSGRenderContext *m_context;
uint m_mipmap : 1;
uint m_live : 1;
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index fbda2df2dc..6335c83b2b 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -2241,11 +2241,10 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
const qreal dy = QQuickTextUtil::alignedY(d->layedOutTextRect.height(), height(), d->vAlign);
QQuickTextNode *node = 0;
- if (!oldNode) {
- node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
- } else {
+ if (!oldNode)
+ node = new QQuickTextNode(this);
+ else
node = static_cast<QQuickTextNode *>(oldNode);
- }
node->setUseNativeRenderer(d->renderType == NativeRendering && d->window->devicePixelRatio() <= 1);
node->deleteContent();
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index be2cd37dee..ffc732621d 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -2370,7 +2370,7 @@ void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QSGTransformNode *root, QQu
QQuickTextNode *QQuickTextEditPrivate::createTextNode()
{
Q_Q(QQuickTextEdit);
- QQuickTextNode* node = new QQuickTextNode(QQuickItemPrivate::get(q)->sceneGraphContext(), q);
+ QQuickTextNode* node = new QQuickTextNode(q);
node->setUseNativeRenderer(renderType == QQuickTextEdit::NativeRendering && window->devicePixelRatio() <= 1);
node->initEngine(color, selectedTextColor, selectionColor);
return node;
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 1848fbb80b..93ea677d2c 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -1834,7 +1834,7 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
QQuickTextNode *node = static_cast<QQuickTextNode *>(oldNode);
if (node == 0)
- node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
+ node = new QQuickTextNode(this);
d->textNode = node;
if (!d->textLayoutDirty && oldNode != 0) {
diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp
index 1f4ca34b13..6acfaaf396 100644
--- a/src/quick/items/qquicktextnode.cpp
+++ b/src/quick/items/qquicktextnode.cpp
@@ -47,6 +47,7 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qsgdistancefieldglyphnode_p.h>
#include <private/qquickclipnode_p.h>
+#include <private/qquickitem_p.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <QtCore/qpoint.h>
@@ -80,8 +81,8 @@ namespace {
/*!
Creates an empty QQuickTextNode
*/
-QQuickTextNode::QQuickTextNode(QSGContext *context, QQuickItem *ownerElement)
- : m_context(context), m_cursorNode(0), m_ownerElement(ownerElement), m_useNativeRenderer(false)
+QQuickTextNode::QQuickTextNode(QQuickItem *ownerElement)
+ : m_cursorNode(0), m_ownerElement(ownerElement), m_useNativeRenderer(false)
{
#ifdef QSG_RUNTIME_DESCRIPTION
qsgnode_set_description(this, QLatin1String("text"));
@@ -141,9 +142,10 @@ QSGGlyphNode *QQuickTextNode::addGlyphs(const QPointF &position, const QGlyphRun
QQuickText::TextStyle style, const QColor &styleColor,
QSGNode *parentNode)
{
+ QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
QSGGlyphNode *node = m_useNativeRenderer
- ? m_context->createNativeGlyphNode()
- : m_context->createGlyphNode();
+ ? sg->sceneGraphContext()->createNativeGlyphNode(sg)
+ : sg->sceneGraphContext()->createGlyphNode(sg);
node->setOwnerElement(m_ownerElement);
node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
node->setStyle(style);
@@ -189,8 +191,9 @@ void QQuickTextNode::initEngine(const QColor& textColor, const QColor& selectedT
void QQuickTextNode::addImage(const QRectF &rect, const QImage &image)
{
- QSGImageNode *node = m_context->createImageNode();
- QSGTexture *texture = m_context->createTexture(image);
+ QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
+ QSGImageNode *node = sg->sceneGraphContext()->createImageNode();
+ QSGTexture *texture = sg->createTexture(image);
m_textures.append(texture);
node->setTargetRect(rect);
node->setInnerTargetRect(rect);
diff --git a/src/quick/items/qquicktextnode_p.h b/src/quick/items/qquicktextnode_p.h
index 64f396eb10..0bff0d5cff 100644
--- a/src/quick/items/qquicktextnode_p.h
+++ b/src/quick/items/qquicktextnode_p.h
@@ -77,7 +77,7 @@ public:
};
Q_DECLARE_FLAGS(Decorations, Decoration)
- QQuickTextNode(QSGContext *, QQuickItem *ownerElement);
+ QQuickTextNode(QQuickItem *ownerElement);
~QQuickTextNode();
static bool isComplexRichText(QTextDocument *);
@@ -110,7 +110,6 @@ private:
void initEngine(const QColor &textColor, const QColor &selectedTextColor, const QColor &selectionColor, const QColor& anchorColor = QColor()
, const QPointF &position = QPointF());
- QSGContext *m_context;
QSGSimpleRectNode *m_cursorNode;
QList<QSGTexture *> m_textures;
QQuickItem *m_ownerElement;
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index fd0313aed5..ed97325c85 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -50,8 +50,6 @@
#include <QtQuick/private/qsgrenderer_p.h>
#include <QtQuick/private/qsgtexture_p.h>
-#include <QtQuick/private/qsgflashnode_p.h>
-
#include <private/qsgrenderloop_p.h>
#include <private/qquickanimatorcontroller_p.h>
@@ -416,9 +414,10 @@ void QQuickWindowPrivate::init(QQuickWindow *c)
contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
windowManager = QSGRenderLoop::instance();
- context = windowManager->sceneGraphContext();
+ QSGContext *sg = windowManager->sceneGraphContext();
+ context = windowManager->createRenderContext(sg);
q->setSurfaceType(QWindow::OpenGLSurface);
- q->setFormat(context->defaultSurfaceFormat());
+ q->setFormat(sg->defaultSurfaceFormat());
animationController = new QQuickAnimatorController();
animationController->window = q;
@@ -2319,10 +2318,6 @@ void QQuickWindowPrivate::updateDirtyNodes()
void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
{
-#ifdef QML_RUNTIME_TESTING
- bool didFlash = false;
-#endif
-
QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
quint32 dirty = itemPriv->dirtyAttributes;
itemPriv->dirtyAttributes = 0;
@@ -2546,19 +2541,6 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
}
#endif
-#ifdef QML_RUNTIME_TESTING
- if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
- QSGFlashNode *flash = new QSGFlashNode();
- flash->setRect(item->boundingRect());
- itemPriv->childContainerNode()->appendChildNode(flash);
- didFlash = true;
- }
- Q_Q(QQuickWindow);
- if (didFlash) {
- q->maybeUpdate();
- }
-#endif
-
}
void QQuickWindow::maybeUpdate()
@@ -2598,9 +2580,7 @@ void QQuickWindow::setTransientParent_helper(QQuickWindow *window)
QOpenGLContext *QQuickWindow::openglContext() const
{
Q_D(const QQuickWindow);
- if (d->context->isReady())
- return d->context->glContext();
- return 0;
+ return d->context->openglContext();
}
/*!
@@ -2796,7 +2776,7 @@ QImage QQuickWindow::grabWindow()
Q_D(QQuickWindow);
if (!isVisible()) {
- if (d->context->isReady()) {
+ if (d->context->openglContext()) {
qWarning("QQuickWindow::grabWindow: scene graph already in use");
return QImage();
}
@@ -2993,7 +2973,7 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateTextureOptions options) const
{
Q_D(const QQuickWindow);
- if (d->context && d->context->isReady()) {
+ if (d->context && d->context->openglContext()) {
if (options & TextureCanUseAtlas)
return d->context->createTexture(image);
else
@@ -3021,7 +3001,7 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateText
QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
{
Q_D(const QQuickWindow);
- if (d->context && d->context->isReady()) {
+ if (d->context && d->context->openglContext()) {
QSGPlainTexture *texture = new QSGPlainTexture();
texture->setTextureId(id);
texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index c02f7107d4..2cf98bfe21 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -194,7 +194,7 @@ public:
void fireFrameSwapped() { Q_EMIT q_func()->frameSwapped(); }
- QSGContext *context;
+ QSGRenderContext *context;
QSGRenderer *renderer;
QSGRenderLoop *windowManager;
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index bf31b94e46..8514203f57 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -283,7 +283,6 @@ Updater::Updater(Renderer *r)
void Updater::updateStates(QSGNode *n)
{
- m_toplevel_alpha = 1;
m_current_clip = 0;
m_added = 0;
@@ -683,7 +682,7 @@ static int qsg_countNodesInBatches(const QDataBuffer<Batch *> &batches)
return sum;
}
-Renderer::Renderer(QSGContext *ctx)
+Renderer::Renderer(QSGRenderContext *ctx)
: QSGRenderer(ctx)
, m_opaqueRenderList(64)
, m_alphaRenderList(64)
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
index 94e8ba5d96..a205306b77 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
@@ -389,7 +389,7 @@ public:
class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer
{
public:
- Renderer(QSGContext *);
+ Renderer(QSGRenderContext *);
~Renderer();
protected:
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
index 25d256158e..d186823958 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
@@ -478,7 +478,7 @@ QRect QSGMaterialShader::RenderState::deviceRect() const
QOpenGLContext *QSGMaterialShader::RenderState::context() const
{
- return static_cast<const QSGRenderer *>(m_data)->glContext();
+ return static_cast<const QSGRenderer *>(m_data)->context()->openglContext();
}
diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h
index d0adbc5dd2..4c3be61a76 100644
--- a/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h
+++ b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h
@@ -64,9 +64,6 @@ public:
virtual void updateStates(QSGNode *n);
virtual bool isNodeBlocked(QSGNode *n, QSGNode *root) const;
- void setToplevelOpacity(qreal alpha) { m_opacity_stack.last() = alpha; }
- qreal toplevelOpacity() const { return m_opacity_stack.last(); }
-
protected:
virtual void enterTransformNode(QSGTransformNode *);
virtual void leaveTransformNode(QSGTransformNode *);
@@ -88,8 +85,6 @@ protected:
const QSGClipNode *m_current_clip;
int m_force_update;
-
- qreal m_toplevel_alpha;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
index 216c32f027..7d982cee36 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
@@ -133,7 +133,7 @@ void QSGBindableFboId::bind() const
*/
-QSGRenderer::QSGRenderer(QSGContext *context)
+QSGRenderer::QSGRenderer(QSGRenderContext *context)
: QObject()
, m_clear_color(Qt::transparent)
, m_clear_mode(ClearColorBuffer | ClearDepthBuffer)
@@ -162,20 +162,6 @@ QSGRenderer::~QSGRenderer()
}
/*!
- Returns the scene graph context for this renderer.
-
- \internal
- */
-
-QSGContext *QSGRenderer::context()
-{
- return m_context;
-}
-
-
-
-
-/*!
Returns the node updater that this renderer uses to update states in the
scene graph.
@@ -398,7 +384,6 @@ void QSGRenderer::preprocess()
preprocessTime = frameTimer.nsecsElapsed();
#endif
- nodeUpdater()->setToplevelOpacity(context()->renderAlpha());
nodeUpdater()->updateStates(m_root_node);
#ifndef QSG_NO_RENDER_TIMING
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
index a1ebfa0946..43811e6d5e 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
@@ -84,7 +84,7 @@ public:
};
Q_DECLARE_FLAGS(ClearMode, ClearModeBit)
- QSGRenderer(QSGContext *context);
+ QSGRenderer(QSGRenderContext *context);
virtual ~QSGRenderer();
void setRootNode(QSGRootNode *node);
@@ -117,9 +117,7 @@ public:
void setClearColor(const QColor &color);
QColor clearColor() const { return m_clear_color; }
- QOpenGLContext *glContext() const { Q_ASSERT(m_context); return m_context->glContext(); }
-
- QSGContext *context();
+ QSGRenderContext *context() const { return m_context; }
void renderScene();
void renderScene(const QSGBindable &bindable);
@@ -161,7 +159,7 @@ protected:
QRect m_current_scissor_rect;
int m_current_stencil_value;
- QSGContext *m_context;
+ QSGRenderContext *m_context;
private:
QSGRootNode *m_root_node;
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index cdf63be5f2..8585caa22d 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -60,9 +60,8 @@ static QElapsedTimer qsg_render_timer;
QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture;
-QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
- : ctx(c)
- , m_manager(man)
+QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, const QRawFont &font)
+ : m_manager(man)
, m_pendingGlyphs(64)
{
Q_ASSERT(font.isValid());
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index ebeb449be5..f9e59f9c7f 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -149,7 +149,7 @@ public:
class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphCache
{
public:
- QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font);
+ QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, const QRawFont &font);
virtual ~QSGDistanceFieldGlyphCache();
struct Metrics {
@@ -246,8 +246,6 @@ protected:
GlyphData &glyphData(glyph_t glyph);
- QOpenGLContext *ctx;
-
private:
QSGDistanceFieldGlyphCacheManager *m_manager;
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index bb8e3c4b36..558711ea0e 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -72,11 +72,8 @@
#include <private/qqmlprofilerservice_p.h>
-DEFINE_BOOL_CONFIG_OPTION(qmlFlashMode, QML_FLASH_MODE)
-DEFINE_BOOL_CONFIG_OPTION(qmlTranslucentMode, QML_TRANSLUCENT_MODE)
DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
-
/*
Comments about this class from Gunnar:
@@ -94,49 +91,32 @@ DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
to defining plugin interfaces..
*/
-
QT_BEGIN_NAMESPACE
class QSGContextPrivate : public QObjectPrivate
{
public:
QSGContextPrivate()
- : gl(0)
- , depthStencilBufferManager(0)
- , distanceFieldCacheManager(0)
- #if !defined(QT_OPENGL_ES) || defined(QT_OPENGL_ES_2_ANGLE)
- , distanceFieldAntialiasing(QSGGlyphNode::HighQualitySubPixelAntialiasing)
- #else
- , distanceFieldAntialiasing(QSGGlyphNode::GrayAntialiasing)
- #endif
- , atlasManager(0)
- , flashMode(qmlFlashMode())
+ : antialiasingMethod(QSGContext::UndecidedAntialiasing)
, distanceFieldDisabled(qmlDisableDistanceField())
- , msaa(false)
+ , distanceFieldAntialiasing(
+#if !defined(QT_OPENGL_ES) || defined(QT_OPENGL_ES_2_ANGLE)
+ QSGGlyphNode::HighQualitySubPixelAntialiasing
+#else
+ QSGGlyphNode::GrayAntialiasing
+#endif
+ )
{
- renderAlpha = qmlTranslucentMode() ? 0.5 : 1;
}
~QSGContextPrivate()
{
}
- QOpenGLContext *gl;
-
- QMutex textureMutex;
- QHash<QQuickTextureFactory *, QSGTexture *> textures;
- QSGDepthStencilBufferManager *depthStencilBufferManager;
- QSGDistanceFieldGlyphCacheManager *distanceFieldCacheManager;
-
- QSGDistanceFieldGlyphNode::AntialiasingMode distanceFieldAntialiasing;
-
- QSGAtlasTexture::Manager *atlasManager;
-
- bool flashMode;
- float renderAlpha;
+ QMutex mutex;
+ QSGContext::AntialiasingMethod antialiasingMethod;
bool distanceFieldDisabled;
-
- bool msaa;
+ QSGDistanceFieldGlyphNode::AntialiasingMode distanceFieldAntialiasing;
};
class QSGTextureCleanupEvent : public QEvent
@@ -188,143 +168,158 @@ QSGContext::QSGContext(QObject *parent) :
QSGContext::~QSGContext()
{
- invalidate();
}
-
-
-void QSGContext::invalidate()
+void QSGContext::renderContextInitialized(QSGRenderContext *renderContext)
{
Q_D(QSGContext);
- d->textureMutex.lock();
- qDeleteAll(d->textures.values());
- d->textures.clear();
- d->textureMutex.unlock();
- delete d->depthStencilBufferManager;
- d->depthStencilBufferManager = 0;
- delete d->distanceFieldCacheManager;
- d->distanceFieldCacheManager = 0;
- d->gl = 0;
-
- emit invalidated();
-
- /* The cleanup of the atlas textures is a bit intruiging.
- As part of the cleanup in the threaded render loop, we
- do:
- 1. call this function
- 2. call QCoreApp::sendPostedEvents() to immediately process
- any pending deferred deletes.
- 3. delete the GL context.
- As textures need the atlas manager while cleaning up, the
- manager needs to be cleaned up after the textures, so
- we post a deleteLater here at the very bottom so it gets
- deferred deleted last.
-
- Another alternative would be to use a QPointer in
- QSGAtlasTexture::Texture, but this seemed simpler.
- */
-
- if (d->atlasManager) {
- d->atlasManager->deleteLater();
- d->atlasManager = 0;
+ d->mutex.lock();
+ if (d->antialiasingMethod == UndecidedAntialiasing) {
+ QByteArray aaType = qgetenv("QSG_ANTIALIASING_METHOD");
+ if (aaType == "msaa") {
+ d->antialiasingMethod = MsaaAntialiasing;
+ } else if (aaType == "vertex") {
+ d->antialiasingMethod = VertexAntialiasing;
+ } else {
+ if (renderContext->openglContext()->format().samples() > 0)
+ d->antialiasingMethod = MsaaAntialiasing;
+ else
+ d->antialiasingMethod = VertexAntialiasing;
+ }
}
+ d->mutex.unlock();
}
+void QSGContext::renderContextInvalidated(QSGRenderContext *)
+{
+}
-QSGTexture *QSGContext::textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window)
+/*!
+ Factory function for scene graph backends of the Rectangle element.
+ */
+QSGRectangleNode *QSGContext::createRectangleNode()
{
Q_D(QSGContext);
- if (!factory)
- return 0;
-
- d->textureMutex.lock();
- QSGTexture *texture = d->textures.value(factory);
- if (!texture) {
- if (QQuickDefaultTextureFactory *dtf = qobject_cast<QQuickDefaultTextureFactory *>(factory))
- texture = createTexture(dtf->image());
- else
- texture = factory->createTexture(window);
- d->textures.insert(factory, texture);
- connect(factory, SIGNAL(destroyed(QObject *)), this, SLOT(textureFactoryDestroyed(QObject *)), Qt::DirectConnection);
- }
- d->textureMutex.unlock();
- return texture;
+ return d->antialiasingMethod == MsaaAntialiasing
+ ? new QSGMultisampleAntialiasing::RectangleNode
+ : new QSGDefaultRectangleNode;
}
+/*!
+ Factory function for scene graph backends of the Image element.
+ */
+QSGImageNode *QSGContext::createImageNode()
+{
+ Q_D(QSGContext);
+ return d->antialiasingMethod == MsaaAntialiasing
+ ? new QSGMultisampleAntialiasing::ImageNode
+ : new QSGDefaultImageNode;
+}
-void QSGContext::textureFactoryDestroyed(QObject *o)
+/*!
+ Factory function for scene graph backends of the Text elements which supports native
+ text rendering. Used in special cases where native look and feel is a main objective.
+*/
+QSGGlyphNode *QSGContext::createNativeGlyphNode(QSGRenderContext *rc)
{
+#if defined(QT_OPENGL_ES) && !defined(QT_OPENGL_ES_2_ANGLE)
Q_D(QSGContext);
- QQuickTextureFactory *f = static_cast<QQuickTextureFactory *>(o);
+ if (d->distanceFieldDisabled)
+ return new QSGDefaultGlyphNode;
+ else
+ return createGlyphNode(rc);
+#else
+ Q_UNUSED(rc);
+ return new QSGDefaultGlyphNode;
+#endif
+}
- d->textureMutex.lock();
- QSGTexture *t = d->textures.take(f);
- d->textureMutex.unlock();
+/*!
+ Factory function for scene graph backends of the Text elements;
+ */
+QSGGlyphNode *QSGContext::createGlyphNode(QSGRenderContext *rc)
+{
+ Q_D(QSGContext);
- if (t) {
- if (t->thread() == thread())
- t->deleteLater();
- else
- QCoreApplication::postEvent(this, new QSGTextureCleanupEvent(t));
+ if (d->distanceFieldDisabled) {
+ return createNativeGlyphNode(rc);
+ } else {
+ QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(rc);
+ node->setPreferredAntialiasingMode(d->distanceFieldAntialiasing);
+ return node;
}
}
-
-QOpenGLContext *QSGContext::glContext() const
+QSurfaceFormat QSGContext::defaultSurfaceFormat() const
{
- Q_D(const QSGContext);
- return d->gl;
+ QSurfaceFormat format;
+ format.setDepthBufferSize(24);
+ format.setStencilBufferSize(8);
+ if (QQuickWindow::hasDefaultAlphaBuffer())
+ format.setAlphaBufferSize(8);
+ format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
+ return format;
}
/*!
- Initializes the scene graph context with the GL context \a context. This also
- emits the ready() signal so that the QML graph can start building scene graph nodes.
+ Returns the minimum supported framebuffer object size.
*/
-void QSGContext::initialize(QOpenGLContext *context)
+
+QSize QSGContext::minimumFBOSize() const
{
- Q_D(QSGContext);
+#ifdef Q_OS_MAC
+ return QSize(33, 33);
+#else
+ return QSize(1, 1);
+#endif
+}
- QByteArray aaType = qgetenv("QSG_ANTIALIASING_METHOD");
- if (aaType == "msaa") {
- d->msaa = true;
- } else if (aaType == "vertex") {
- d->msaa = false;
- } else {
- if (context->format().samples() > 0)
- d->msaa = true;
- else
- d->msaa = false;
- }
- // Sanity check the surface format, in case it was overridden by the application
- QSurfaceFormat requested = defaultSurfaceFormat();
- QSurfaceFormat actual = context->format();
- if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0)
- qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors");
- if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
- qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors");
- d->atlasManager = new QSGAtlasTexture::Manager();
+/*!
+ Sets whether or not the scene graph should use the distance field technique to render text
+ */
+void QSGContext::setDistanceFieldEnabled(bool enabled)
+{
+ d_func()->distanceFieldDisabled = !enabled;
+}
- Q_ASSERT(!d->gl);
- d->gl = context;
- emit initialized();
+/*!
+ Returns true if the scene graph uses the distance field technique to render text
+ */
+bool QSGContext::isDistanceFieldEnabled() const
+{
+ return !d_func()->distanceFieldDisabled;
}
+
+
/*!
- Returns if the scene graph context is ready or not, meaning that it has a valid
- GL context.
+ Creates a new animation driver.
*/
-bool QSGContext::isReady() const
+
+QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent)
+{
+ return new QAnimationDriver(parent);
+}
+
+QSGRenderContext::QSGRenderContext(QSGContext *context)
+ : m_gl(0)
+ , m_sg(context)
+ , m_atlasManager(0)
+ , m_depthStencilManager(0)
+ , m_distanceFieldCacheManager(0)
{
- Q_D(const QSGContext);
- return d->gl;
}
+QSGRenderContext::~QSGRenderContext()
+{
+ invalidate();
+}
-void QSGContext::renderNextFrame(QSGRenderer *renderer, GLuint fboId)
+void QSGRenderContext::renderNextFrame(QSGRenderer *renderer, GLuint fboId)
{
if (fboId) {
QSGBindableFboId bindable(fboId);
@@ -336,36 +331,14 @@ void QSGContext::renderNextFrame(QSGRenderer *renderer, GLuint fboId)
}
/*!
- Factory function for scene graph backends of the Rectangle element.
- */
-QSGRectangleNode *QSGContext::createRectangleNode()
-{
- Q_D(QSGContext);
- return d->msaa ? new QSGMultisampleAntialiasing::RectangleNode
- : new QSGDefaultRectangleNode;
-}
-
-/*!
- Factory function for scene graph backends of the Image element.
- */
-QSGImageNode *QSGContext::createImageNode()
-{
- Q_D(QSGContext);
- return d->msaa ? new QSGMultisampleAntialiasing::ImageNode
- : new QSGDefaultImageNode;
-}
-
-/*!
Factory function for scene graph backends of the distance-field glyph cache.
*/
-QSGDistanceFieldGlyphCache *QSGContext::distanceFieldGlyphCache(const QRawFont &font)
+QSGDistanceFieldGlyphCache *QSGRenderContext::distanceFieldGlyphCache(const QRawFont &font)
{
- Q_D(QSGContext);
-
- if (!d->distanceFieldCacheManager)
- d->distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager;
+ if (!m_distanceFieldCacheManager)
+ m_distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager;
- QSGDistanceFieldGlyphCache *cache = d->distanceFieldCacheManager->cache(font);
+ QSGDistanceFieldGlyphCache *cache = m_distanceFieldCacheManager->cache(font);
if (!cache) {
QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
if (platformIntegration != 0
@@ -387,125 +360,114 @@ QSGDistanceFieldGlyphCache *QSGContext::distanceFieldGlyphCache(const QRawFont &
QPlatformSharedGraphicsCache::Alpha8);
cache = new QSGSharedDistanceFieldGlyphCache(keyName,
- sharedGraphicsCache,
- d->distanceFieldCacheManager,
- glContext(),
- font);
+ sharedGraphicsCache,
+ m_distanceFieldCacheManager,
+ font);
}
}
}
if (!cache)
- cache = new QSGDefaultDistanceFieldGlyphCache(d->distanceFieldCacheManager, glContext(), font);
- d->distanceFieldCacheManager->insertCache(font, cache);
+ cache = new QSGDefaultDistanceFieldGlyphCache(m_distanceFieldCacheManager, font);
+ m_distanceFieldCacheManager->insertCache(font, cache);
}
+
return cache;
}
-/*!
- Factory function for scene graph backends of the Text elements which supports native
- text rendering. Used in special cases where native look and feel is a main objective.
-*/
-QSGGlyphNode *QSGContext::createNativeGlyphNode()
+#define QSG_RENDERCONTEXT_PROPERTY "_q_sgrendercontext"
+
+QSGRenderContext *QSGRenderContext::from(QOpenGLContext *context)
{
-#if defined(QT_OPENGL_ES) && !defined(QT_OPENGL_ES_2_ANGLE)
- Q_D(QSGContext);
- if (d->distanceFieldDisabled)
- return new QSGDefaultGlyphNode;
- else
- return createGlyphNode();
-#else
- return new QSGDefaultGlyphNode;
-#endif
+ return qobject_cast<QSGRenderContext *>(context->property(QSG_RENDERCONTEXT_PROPERTY).value<QObject *>());
}
-/*!
- Factory function for scene graph backends of the Text elements;
- */
-QSGGlyphNode *QSGContext::createGlyphNode()
+void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine)
{
- Q_D(QSGContext);
-
- if (d->distanceFieldDisabled) {
- return createNativeGlyphNode();
- } else {
- QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(this);
- node->setPreferredAntialiasingMode(d->distanceFieldAntialiasing);
- return node;
- }
+ m_fontEnginesToClean << engine;
}
/*!
- Factory function for the scene graph renderers.
-
- The renderers are used for the toplevel renderer and once for every
- QQuickShaderEffectSource used in the QML scene.
+ Initializes the scene graph render context with the GL context \a context. This also
+ emits the ready() signal so that the QML graph can start building scene graph nodes.
*/
-QSGRenderer *QSGContext::createRenderer()
+void QSGRenderContext::initialize(QOpenGLContext *context)
{
- return new QSGBatchRenderer::Renderer(this);
-}
-
+ // Sanity check the surface format, in case it was overridden by the application
+ QSurfaceFormat requested = m_sg->defaultSurfaceFormat();
+ QSurfaceFormat actual = context->format();
+ if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0)
+ qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors");
+ if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
+ qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors");
+ if (!m_atlasManager)
+ m_atlasManager = new QSGAtlasTexture::Manager();
+ Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!");
+ m_gl = context;
+ m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this));
+ m_sg->renderContextInitialized(this);
-QSurfaceFormat QSGContext::defaultSurfaceFormat() const
-{
- QSurfaceFormat format;
- format.setDepthBufferSize(24);
- format.setStencilBufferSize(8);
- if (QQuickWindow::hasDefaultAlphaBuffer())
- format.setAlphaBufferSize(8);
- format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
- return format;
+ emit initialized();
}
-
-/*!
- Factory function for texture objects.
-
- If \a image is a valid image, the QSGTexture::setImage function
- will be called with \a image as argument.
- */
-
-QSGTexture *QSGContext::createTexture(const QImage &image) const
+void QSGRenderContext::invalidate()
{
- Q_D(const QSGContext);
- QSGTexture *at = d->atlasManager->create(image);
- if (at)
- return at;
- return createTextureNoAtlas(image);
-}
+ if (!m_gl)
+ return;
+
+ qDeleteAll(m_textures.values());
+ m_textures.clear();
+
+ /* The cleanup of the atlas textures is a bit intriguing.
+ As part of the cleanup in the threaded render loop, we
+ do:
+ 1. call this function
+ 2. call QCoreApp::sendPostedEvents() to immediately process
+ any pending deferred deletes.
+ 3. delete the GL context.
+
+ As textures need the atlas manager while cleaning up, the
+ manager needs to be cleaned up after the textures, so
+ we post a deleteLater here at the very bottom so it gets
+ deferred deleted last.
+
+ Another alternative would be to use a QPointer in
+ QSGAtlasTexture::Texture, but this seemed simpler.
+ */
+ m_atlasManager->invalidate();
+ m_atlasManager->deleteLater();
+ m_atlasManager = 0;
+
+ // The following piece of code will read/write to the font engine's caches,
+ // potentially from different threads. However, this is safe because this
+ // code is only called from QQuickWindow's shutdown which is called
+ // only when the GUI is blocked, and multiple threads will call it in
+ // sequence. (see qsgdefaultglyphnode_p.cpp's init())
+ for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(),
+ end = m_fontEnginesToClean.constEnd(); it != end; ++it) {
+ (*it)->clearGlyphCache(m_gl);
+ }
-QSGTexture *QSGContext::createTextureNoAtlas(const QImage &image) const
-{
- QSGPlainTexture *t = new QSGPlainTexture();
- if (!image.isNull())
- t->setImage(image);
- return t;
-}
+ delete m_depthStencilManager;
+ m_depthStencilManager = 0;
-/*!
- Returns the minimum supported framebuffer object size.
- */
-
-QSize QSGContext::minimumFBOSize() const
-{
-#ifdef Q_OS_MAC
- return QSize(33, 33);
-#else
- return QSize(1, 1);
-#endif
-}
+ delete m_distanceFieldCacheManager;
+ m_distanceFieldCacheManager = 0;
+ m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant());
+ m_gl = 0;
+ m_sg->renderContextInvalidated(this);
+ emit invalidated();
+}
/*!
Returns a shared pointer to a depth stencil buffer that can be used with \a fbo.
*/
-QSharedPointer<QSGDepthStencilBuffer> QSGContext::depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo)
+QSharedPointer<QSGDepthStencilBuffer> QSGRenderContext::depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo)
{
- Q_D(QSGContext);
- if (!d->gl)
+ if (!m_gl)
return QSharedPointer<QSGDepthStencilBuffer>();
QSGDepthStencilBufferManager *manager = depthStencilBufferManager();
QSGDepthStencilBuffer::Format format;
@@ -514,7 +476,7 @@ QSharedPointer<QSGDepthStencilBuffer> QSGContext::depthStencilBufferForFbo(QOpen
format.attachments = QSGDepthStencilBuffer::DepthAttachment | QSGDepthStencilBuffer::StencilAttachment;
QSharedPointer<QSGDepthStencilBuffer> buffer = manager->bufferForFormat(format);
if (buffer.isNull()) {
- buffer = QSharedPointer<QSGDepthStencilBuffer>(new QSGDefaultDepthStencilBuffer(d->gl, format));
+ buffer = QSharedPointer<QSGDepthStencilBuffer>(new QSGDefaultDepthStencilBuffer(m_gl, format));
manager->insertBuffer(buffer);
}
return buffer;
@@ -524,89 +486,81 @@ QSharedPointer<QSGDepthStencilBuffer> QSGContext::depthStencilBufferForFbo(QOpen
Returns a pointer to the context's depth/stencil buffer manager. This is useful for custom
implementations of \l depthStencilBufferForFbo().
*/
-QSGDepthStencilBufferManager *QSGContext::depthStencilBufferManager()
+QSGDepthStencilBufferManager *QSGRenderContext::depthStencilBufferManager()
{
- Q_D(QSGContext);
- if (!d->gl)
+ if (!m_gl)
return 0;
- if (!d->depthStencilBufferManager)
- d->depthStencilBufferManager = new QSGDepthStencilBufferManager(d->gl);
- return d->depthStencilBufferManager;
+ if (!m_depthStencilManager)
+ m_depthStencilManager = new QSGDepthStencilBufferManager(m_gl);
+ return m_depthStencilManager;
}
/*!
- Sets whether the scene graph should render with flashing update rectangles or not
- */
-
-void QSGContext::setFlashModeEnabled(bool enabled)
-{
- d_func()->flashMode = enabled;
-}
-
+ Factory function for texture objects.
-/*!
- Returns true if the scene graph should be rendered with flashing update rectangles
+ If \a image is a valid image, the QSGTexture::setImage function
+ will be called with \a image as argument.
*/
-bool QSGContext::isFlashModeEnabled() const
+
+QSGTexture *QSGRenderContext::createTexture(const QImage &image) const
{
- return d_func()->flashMode;
+ QSGTexture *t = m_atlasManager->create(image);
+ if (t)
+ return t;
+ return createTextureNoAtlas(image);
}
-
-/*!
- Sets the toplevel opacity for rendering. This value will be multiplied into all
- drawing calls where possible.
-
- The default value is 1. Any other value will cause artifacts and is primarily
- useful for debugging.
- */
-void QSGContext::setRenderAlpha(qreal renderAlpha)
+QSGTexture *QSGRenderContext::createTextureNoAtlas(const QImage &image) const
{
- d_func()->renderAlpha = renderAlpha;
+ QSGPlainTexture *t = new QSGPlainTexture();
+ if (!image.isNull())
+ t->setImage(image);
+ return t;
}
-
/*!
- Returns the toplevel opacity used for rendering.
-
- The default value is 1.
+ Factory function for the scene graph renderers.
- \sa setRenderAlpha()
+ The renderers are used for the toplevel renderer and once for every
+ QQuickShaderEffectSource used in the QML scene.
*/
-qreal QSGContext::renderAlpha() const
+QSGRenderer *QSGRenderContext::createRenderer()
{
- return d_func()->renderAlpha;
+ return new QSGBatchRenderer::Renderer(this);
}
-
-/*!
- Sets whether or not the scene graph should use the distance field technique to render text
- */
-void QSGContext::setDistanceFieldEnabled(bool enabled)
+QSGTexture *QSGRenderContext::textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window)
{
- d_func()->distanceFieldDisabled = !enabled;
-}
-
+ if (!factory)
+ return 0;
-/*!
- Returns true if the scene graph uses the distance field technique to render text
- */
-bool QSGContext::isDistanceFieldEnabled() const
-{
- return !d_func()->distanceFieldDisabled;
-}
+ m_mutex.lock();
+ QSGTexture *texture = m_textures.value(factory);
+ m_mutex.unlock();
+ if (!texture) {
+ if (QQuickDefaultTextureFactory *dtf = qobject_cast<QQuickDefaultTextureFactory *>(factory))
+ texture = createTexture(dtf->image());
+ else
+ texture = factory->createTexture(window);
+ m_mutex.lock();
+ m_textures.insert(factory, texture);
+ m_mutex.unlock();
-/*!
- Creates a new animation driver.
- */
+ connect(factory, SIGNAL(destroyed(QObject *)), this, SLOT(textureFactoryDestroyed(QObject *)), Qt::DirectConnection);
+ }
+ return texture;
+}
-QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent)
+void QSGRenderContext::textureFactoryDestroyed(QObject *o)
{
- return new QAnimationDriver(parent);
+ m_mutex.lock();
+ QSGTexture *t = m_textures.take(static_cast<QQuickTextureFactory *>(o));
+ m_mutex.unlock();
+ if (t)
+ t->deleteLater();
}
-
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h
index 2057c0031d..a11a4856c4 100644
--- a/src/quick/scenegraph/qsgcontext_p.h
+++ b/src/quick/scenegraph/qsgcontext_p.h
@@ -56,6 +56,9 @@
QT_BEGIN_NAMESPACE
+namespace QSGAtlasTexture {
+ class Manager;
+}
class QSGContextPrivate;
class QSGRectangleNode;
@@ -73,66 +76,95 @@ class QOpenGLContext;
class QOpenGLFramebufferObject;
class QQuickTextureFactory;
+class QSGDistanceFieldGlyphCacheManager;
+class QSGContext;
-class Q_QUICK_PRIVATE_EXPORT QSGContext : public QObject
+
+class Q_QUICK_PRIVATE_EXPORT QSGRenderContext : public QObject
{
Q_OBJECT
- Q_DECLARE_PRIVATE(QSGContext)
-
public:
- explicit QSGContext(QObject *parent = 0);
- ~QSGContext();
+ QSGRenderContext(QSGContext *context);
+ ~QSGRenderContext();
+
+ QOpenGLContext *openglContext() const { return m_gl; }
+ QSGContext *sceneGraphContext() const { return m_sg; }
virtual void initialize(QOpenGLContext *context);
virtual void invalidate();
- QOpenGLContext *glContext() const;
-
- bool isReady() const;
-
virtual void renderNextFrame(QSGRenderer *renderer, GLuint fboId);
- virtual QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font);
+ virtual QSharedPointer<QSGDepthStencilBuffer> depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo);
+ QSGDepthStencilBufferManager *depthStencilBufferManager();
- virtual QSGRectangleNode *createRectangleNode();
- virtual QSGImageNode *createImageNode();
- virtual QSGGlyphNode *createGlyphNode();
- virtual QSGGlyphNode *createNativeGlyphNode();
- virtual QSGRenderer *createRenderer();
+ virtual QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font);
+ QSGTexture *textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window);
virtual QSGTexture *createTexture(const QImage &image) const;
virtual QSGTexture *createTextureNoAtlas(const QImage &image) const;
- virtual QSize minimumFBOSize() const;
- virtual QSharedPointer<QSGDepthStencilBuffer> depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo);
- QSGDepthStencilBufferManager *depthStencilBufferManager();
+ virtual QSGRenderer *createRenderer();
- virtual QSurfaceFormat defaultSurfaceFormat() const;
+ void registerFontengineForCleanup(QFontEngine *engine);
- QSGTexture *textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window);
+ static QSGRenderContext *from(QOpenGLContext *context);
- static QSGContext *createDefaultContext();
+Q_SIGNALS:
+ void initialized();
+ void invalidated();
- void setFlashModeEnabled(bool enabled);
- bool isFlashModeEnabled() const;
- void setRenderAlpha(qreal renderAlpha);
- qreal renderAlpha() const;
+public Q_SLOTS:
+ void textureFactoryDestroyed(QObject *o);
- void setDistanceFieldEnabled(bool enabled);
- bool isDistanceFieldEnabled() const;
+protected:
+ QOpenGLContext *m_gl;
+ QSGContext *m_sg;
- virtual QAnimationDriver *createAnimationDriver(QObject *parent);
+ QMutex m_mutex;
+ QHash<QQuickTextureFactory *, QSGTexture *> m_textures;
+ QSGAtlasTexture::Manager *m_atlasManager;
- static QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image);
- static QSGRenderLoop *createWindowManager();
+ QSGDepthStencilBufferManager *m_depthStencilManager;
+ QSGDistanceFieldGlyphCacheManager *m_distanceFieldCacheManager;
+ QSet<QFontEngine *> m_fontEnginesToClean;
+};
-public Q_SLOTS:
- void textureFactoryDestroyed(QObject *o);
-Q_SIGNALS:
- void initialized();
- void invalidated();
+class Q_QUICK_PRIVATE_EXPORT QSGContext : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGContext)
+
+public:
+ enum AntialiasingMethod {
+ UndecidedAntialiasing,
+ VertexAntialiasing,
+ MsaaAntialiasing
+ };
+
+ explicit QSGContext(QObject *parent = 0);
+ ~QSGContext();
+
+ virtual void renderContextInitialized(QSGRenderContext *renderContext);
+ virtual void renderContextInvalidated(QSGRenderContext *renderContext);
+
+ virtual QSGRectangleNode *createRectangleNode();
+ virtual QSGImageNode *createImageNode();
+ virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc);
+ virtual QSGGlyphNode *createNativeGlyphNode(QSGRenderContext *rc);
+ virtual QAnimationDriver *createAnimationDriver(QObject *parent);
+
+ virtual QSize minimumFBOSize() const;
+ virtual QSurfaceFormat defaultSurfaceFormat() const;
+
+ void setDistanceFieldEnabled(bool enabled);
+ bool isDistanceFieldEnabled() const;
+
+ static QSGContext *createDefaultContext();
+ static QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image);
+ static QSGRenderLoop *createWindowManager();
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
index bc09ed83d7..635f07f2ea 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
@@ -52,8 +52,8 @@ QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(qmlUseGlyphCacheWorkaround, QML_USE_GLYPHCACHE_WORKAROUND)
-QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
- : QSGDistanceFieldGlyphCache(man, c, font)
+QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, const QRawFont &font)
+ : QSGDistanceFieldGlyphCache(man, font)
, m_maxTextureSize(0)
, m_maxTextureCount(3)
, m_blitProgram(0)
@@ -239,6 +239,9 @@ static void freeFramebufferFunc(QOpenGLFunctions *funcs, GLuint id)
void QSGDefaultDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int width, int height)
{
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ Q_ASSERT(ctx);
+
int oldWidth = texInfo->size.width();
int oldHeight = texInfo->size.height();
if (width == oldWidth && height == oldHeight)
@@ -361,7 +364,7 @@ bool QSGDefaultDistanceFieldGlyphCache::useWorkaround() const
static bool set = false;
static bool useWorkaround = false;
if (!set) {
- QOpenGLContextPrivate *ctx_p = static_cast<QOpenGLContextPrivate *>(QOpenGLContextPrivate::get(ctx));
+ QOpenGLContextPrivate *ctx_p = static_cast<QOpenGLContextPrivate *>(QOpenGLContextPrivate::get(QOpenGLContext::currentContext()));
useWorkaround = ctx_p->workaround_brokenFBOReadBack
|| qmlUseGlyphCacheWorkaround(); // on some hardware the workaround is faster (see QTBUG-29264)
set = true;
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
index cd6fa00852..54b0bcb889 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
@@ -54,7 +54,7 @@ class QOpenGLSharedResourceGuard;
class Q_QUICK_PRIVATE_EXPORT QSGDefaultDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
{
public:
- QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font);
+ QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, const QRawFont &font);
virtual ~QSGDefaultDistanceFieldGlyphCache();
void requestGlyphs(const QSet<glyph_t> &glyphs);
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index 39d9832f58..bf92bf8b39 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -480,6 +480,12 @@ void QSGTextMaskMaterial::init(int cacheType)
QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
Q_ASSERT(ctx != 0);
+ // The following piece of code will read/write to the font engine's caches,
+ // potentially from different threads. However, this is safe because this
+ // code is only called from QQuickItem::updatePaintNode() which is called
+ // only when the GUI is blocked, and multiple threads will call it in
+ // sequence. See also QSGRenderContext::invalidate
+
QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
if (fontD->fontEngine != 0) {
if (cacheType < 0) {
@@ -494,6 +500,9 @@ void QSGTextMaskMaterial::init(int cacheType)
m_glyphCache = new QOpenGLTextureGlyphCache(QFontEngineGlyphCache::Type(cacheType),
QTransform());
fontD->fontEngine->setGlyphCache(ctx, m_glyphCache.data());
+ QSGRenderContext *sg = QSGRenderContext::from(ctx);
+ Q_ASSERT(sg);
+ sg->registerFontengineForCleanup(fontD->fontEngine);
}
}
}
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
index 876a9ea9ad..49b56494db 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
@@ -46,7 +46,7 @@
QT_BEGIN_NAMESPACE
-QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGContext *context)
+QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGRenderContext *context)
: m_glyphNodeType(RootGlyphNode)
, m_context(context)
, m_material(0)
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
index 2558413675..98a65d7347 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
@@ -49,13 +49,13 @@
QT_BEGIN_NAMESPACE
-class QSGContext;
+class QSGRenderContext;
class QSGDistanceFieldGlyphCacheManager;
class QSGDistanceFieldTextMaterial;
class QSGDistanceFieldGlyphNode: public QSGGlyphNode, public QSGDistanceFieldGlyphConsumer
{
public:
- QSGDistanceFieldGlyphNode(QSGContext *context);
+ QSGDistanceFieldGlyphNode(QSGRenderContext *context);
~QSGDistanceFieldGlyphNode();
virtual QPointF baseLine() const { return m_baseLine; }
@@ -86,7 +86,7 @@ private:
DistanceFieldGlyphNodeType m_glyphNodeType;
QColor m_color;
QPointF m_baseLine;
- QSGContext *m_context;
+ QSGRenderContext *m_context;
QSGDistanceFieldTextMaterial *m_material;
QPointF m_originalPosition;
QPointF m_position;
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index 163d1d35c5..fc1e98d87f 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -87,13 +87,13 @@ class QSGGuiThreadRenderLoop : public QSGRenderLoop
Q_OBJECT
public:
QSGGuiThreadRenderLoop();
+ ~QSGGuiThreadRenderLoop();
void show(QQuickWindow *window);
void hide(QQuickWindow *window);
void windowDestroyed(QQuickWindow *window);
- void initializeGL();
void renderWindow(QQuickWindow *window);
void exposureChanged(QQuickWindow *window);
QImage grab(QQuickWindow *window);
@@ -106,6 +106,7 @@ public:
QAnimationDriver *animationDriver() const { return 0; }
QSGContext *sceneGraphContext() const;
+ QSGRenderContext *createRenderContext(QSGContext *) const { return rc; }
bool event(QEvent *);
@@ -118,6 +119,7 @@ public:
QOpenGLContext *gl;
QSGContext *sg;
+ QSGRenderContext *rc;
QImage grabContent;
@@ -140,7 +142,6 @@ bool QSGRenderLoop::useConsistentTiming()
QSGRenderLoop *QSGRenderLoop::instance()
{
if (!s_instance) {
-
s_instance = QSGContext::createWindowManager();
if (useConsistentTiming())
@@ -202,8 +203,14 @@ QSGGuiThreadRenderLoop::QSGGuiThreadRenderLoop()
, eventPending(false)
{
sg = QSGContext::createDefaultContext();
+ rc = new QSGRenderContext(sg);
}
+QSGGuiThreadRenderLoop::~QSGGuiThreadRenderLoop()
+{
+ delete rc;
+ delete sg;
+}
void QSGGuiThreadRenderLoop::show(QQuickWindow *window)
{
@@ -226,7 +233,7 @@ void QSGGuiThreadRenderLoop::hide(QQuickWindow *window)
if (m_windows.size() == 0) {
if (!cd->persistentSceneGraph) {
- sg->invalidate();
+ rc->invalidate();
if (!cd->persistentGLContext) {
delete gl;
gl = 0;
@@ -239,7 +246,7 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
{
hide(window);
if (m_windows.size() == 0) {
- sg->invalidate();
+ rc->invalidate();
delete gl;
gl = 0;
}
@@ -264,7 +271,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
current = gl->makeCurrent(window);
}
if (current)
- sg->initialize(gl);
+ QQuickWindowPrivate::get(window)->context->initialize(gl);
} else {
current = gl->makeCurrent(window);
}
diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h
index 933a8428f1..7b06399f08 100644
--- a/src/quick/scenegraph/qsgrenderloop_p.h
+++ b/src/quick/scenegraph/qsgrenderloop_p.h
@@ -49,6 +49,7 @@ QT_BEGIN_NAMESPACE
class QQuickWindow;
class QSGContext;
+class QSGRenderContext;
class QAnimationDriver;
class Q_QUICK_PRIVATE_EXPORT QSGRenderLoop : public QObject
@@ -72,6 +73,7 @@ public:
virtual QAnimationDriver *animationDriver() const = 0;
virtual QSGContext *sceneGraphContext() const = 0;
+ virtual QSGRenderContext *createRenderContext(QSGContext *) const = 0;
virtual void releaseResources(QQuickWindow *window) = 0;
diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp
index 34a884348d..20e1210b68 100644
--- a/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp
@@ -199,9 +199,8 @@ namespace {
QSGSharedDistanceFieldGlyphCache::QSGSharedDistanceFieldGlyphCache(const QByteArray &cacheId,
QPlatformSharedGraphicsCache *sharedGraphicsCache,
QSGDistanceFieldGlyphCacheManager *man,
- QOpenGLContext *c,
const QRawFont &font)
- : QSGDistanceFieldGlyphCache(man, c, font)
+ : QSGDistanceFieldGlyphCache(man, font)
, m_cacheId(cacheId)
, m_sharedGraphicsCache(sharedGraphicsCache)
, m_isInSceneGraphUpdate(false)
@@ -228,6 +227,8 @@ QSGSharedDistanceFieldGlyphCache::QSGSharedDistanceFieldGlyphCache(const QByteAr
this, SLOT(reportItemsInvalidated(QByteArray,QVector<quint32>)),
Qt::DirectConnection);
+ QOpenGLContext *c = QOpenGLContext::currentContext();
+ Q_ASSERT(c);
QQuickWindow *window = static_cast<QQuickWindow *>(c->surface());
Q_ASSERT(window != 0);
diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h
index d72d2a740f..a8d70be732 100644
--- a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h
@@ -55,7 +55,6 @@ public:
explicit QSGSharedDistanceFieldGlyphCache(const QByteArray &cacheId,
QPlatformSharedGraphicsCache *sharedGraphicsCache,
QSGDistanceFieldGlyphCacheManager *man,
- QOpenGLContext *c,
const QRawFont &font);
~QSGSharedDistanceFieldGlyphCache();
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 7421db1db1..bb0bd67d6b 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -84,6 +84,17 @@
---
+ There is one thread per window and one opengl context per thread.
+
+ ---
+
+ The render thread has affinity to the GUI thread until a window
+ is shown. From that moment and until the window is destroyed, it
+ will have affinity to the render thread. (moved back at the end
+ of run for cleanup).
+
+ ---
+
The render loop is active while any window is exposed. All visible
windows are tracked, but only exposed windows are actually added to
the render thread and rendered. That means that if all windows are
@@ -98,6 +109,7 @@ QT_BEGIN_NAMESPACE
// #define QSG_RENDER_LOOP_DEBUG
// #define QSG_RENDER_LOOP_DEBUG_FULL
+
#ifdef QSG_RENDER_LOOP_DEBUG
#define QSG_RENDER_LOOP_DEBUG_BASIC
#endif
@@ -276,23 +288,25 @@ class QSGRenderThread : public QThread
{
Q_OBJECT
public:
-
- QSGRenderThread(QSGThreadedRenderLoop *w)
+ QSGRenderThread(QSGThreadedRenderLoop *w, QSGRenderContext *renderContext)
: wm(w)
, gl(0)
- , sg(QSGContext::createDefaultContext())
+ , sgrc(renderContext)
, animatorDriver(0)
, pendingUpdate(0)
, sleeping(false)
, syncResultedInChanges(false)
- , guiIsLocked(false)
- , shouldExit(false)
+ , active(false)
+ , window(0)
, stopEventProcessing(false)
{
- sg->moveToThread(this);
vsyncDelta = qsgrl_animation_interval();
}
+ ~QSGRenderThread()
+ {
+ delete sgrc;
+ }
void invalidateOpenGL(QQuickWindow *window, bool inDestructor);
void initializeOpenGL();
@@ -307,7 +321,7 @@ public:
{
if (sleeping)
stopEventProcessing = true;
- if (m_windows.size() > 0)
+ if (window)
pendingUpdate |= RepaintRequest;
}
@@ -329,16 +343,15 @@ public:
QSGThreadedRenderLoop *wm;
QOpenGLContext *gl;
- QSGContext *sg;
+ QSGRenderContext *sgrc;
QAnimationDriver *animatorDriver;
uint pendingUpdate;
- uint sleeping;
- uint syncResultedInChanges;
+ bool sleeping;
+ bool syncResultedInChanges;
- volatile bool guiIsLocked;
- volatile bool shouldExit;
+ volatile bool active;
float vsyncDelta;
@@ -347,11 +360,8 @@ public:
QElapsedTimer m_timer;
- struct Window {
- QQuickWindow *window;
- QSize size;
- };
- QList<Window> m_windows;
+ QQuickWindow *window; // Will be 0 when window is not exposed
+ QSize windowSize;
// Local event queue stuff...
bool stopEventProcessing;
@@ -368,33 +378,29 @@ bool QSGRenderThread::event(QEvent *e)
pendingUpdate |= RepaintRequest;
- if (Window *w = windowFor(m_windows, se->window)) {
- w->size = se->size;
+ Q_ASSERT(!window || window == se->window);
+
+ windowSize = se->size;
+ if (window) {
RLDEBUG1(" Render: - window already added...");
return true;
}
- Window window;
- window.window = se->window;
- window.size = se->size;
- m_windows << window;
+ window = se->window;
connect(animatorDriver, SIGNAL(started()), QQuickWindowPrivate::get(se->window)->animationController, SLOT(animationsStarted()));
return true; }
case WM_Obscure: {
RLDEBUG1(" Render: WM_Obscure");
+
WMWindowEvent *ce = static_cast<WMWindowEvent *>(e);
- for (int i=0; i<m_windows.size(); ++i) {
- if (m_windows.at(i).window == ce->window) {
- RLDEBUG1(" Render: - removed one...");
- m_windows.removeAt(i);
- disconnect(animatorDriver, SIGNAL(started()), QQuickWindowPrivate::get(ce->window)->animationController, SLOT(animationsStarted()));
- break;
- }
- }
+ Q_ASSERT(!window || window == ce->window);
- if (sleeping && m_windows.size())
- stopEventProcessing = true;
+ if (window) {
+ RLDEBUG1(" Render: - removed one...");
+ window = 0;
+ disconnect(animatorDriver, SIGNAL(started()), QQuickWindowPrivate::get(ce->window)->animationController, SLOT(animationsStarted()));
+ }
return true; }
@@ -402,23 +408,20 @@ bool QSGRenderThread::event(QEvent *e)
RLDEBUG(" Render: WM_RequestSync");
if (sleeping)
stopEventProcessing = true;
- if (m_windows.size() > 0)
+ if (window)
pendingUpdate |= SyncRequest;
return true;
case WM_TryRelease: {
RLDEBUG1(" Render: WM_TryRelease");
mutex.lock();
- WMTryReleaseEvent *wme = static_cast<WMTryReleaseEvent *>(e);
- if (m_windows.size() == 0) {
+ if (!window) {
+ WMTryReleaseEvent *wme = static_cast<WMTryReleaseEvent *>(e);
RLDEBUG1(" Render: - setting exit flag and invalidating GL");
invalidateOpenGL(wme->window, wme->inDestructor);
- shouldExit = !gl;
+ active = gl;
if (sleeping)
stopEventProcessing = true;
- } else if (wme->window == gl->surface()) {
- RLDEBUG1(" Render: - destroying the current window. Calling doneCurrent()...");
- gl->doneCurrent();
} else {
RLDEBUG1(" Render: - not releasing anything because we have active windows...");
}
@@ -430,20 +433,20 @@ bool QSGRenderThread::event(QEvent *e)
case WM_Grab: {
RLDEBUG1(" Render: WM_Grab");
WMGrabEvent *ce = static_cast<WMGrabEvent *>(e);
- Window *w = windowFor(m_windows, ce->window);
+ Q_ASSERT(ce->window == window);
mutex.lock();
- if (w) {
- gl->makeCurrent(ce->window);
+ if (window) {
+ gl->makeCurrent(window);
RLDEBUG1(" Render: - syncing scene graph");
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(w->window);
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
d->syncSceneGraph();
RLDEBUG1(" Render: - rendering scene graph");
- QQuickWindowPrivate::get(ce->window)->renderSceneGraph(w->size);
+ QQuickWindowPrivate::get(window)->renderSceneGraph(windowSize);
RLDEBUG1(" Render: - grabbing result...");
- *ce->image = qt_gl_read_framebuffer(w->size, false, false);
+ *ce->image = qt_gl_read_framebuffer(windowSize, false, false);
}
RLDEBUG1(" Render: - waking gui to handle grab result");
waitCondition.wakeOne();
@@ -476,38 +479,27 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor)
}
- bool persistentGL = false;
- bool persistentSG = false;
-
- // GUI is locked so accessing the wm and window here is safe
- for (int i=0; i<wm->m_windows.size(); ++i) {
- const QSGThreadedRenderLoop::Window &w = wm->m_windows.at(i);
- if (!inDestructor || w.window != window) {
- persistentSG |= w.window->isPersistentSceneGraph();
- persistentGL |= w.window->isPersistentOpenGLContext();
- }
- }
+ bool wipeSG = inDestructor || !window->isPersistentSceneGraph();
+ bool wipeGL = inDestructor || (wipeSG && !window->isPersistentOpenGLContext());
gl->makeCurrent(window);
- // The canvas nodes must be cleanded up regardless if we are in the destructor..
- if (!persistentSG || inDestructor) {
+ // The canvas nodes must be cleaned up regardless if we are in the destructor..
+ if (wipeSG) {
QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
dd->cleanupNodesOnShutdown();
- }
-
- // We're not doing any cleanup in this case...
- if (persistentSG) {
+ } else {
RLDEBUG1(" Render: - persistent SG, avoiding cleanup");
return;
}
- sg->invalidate();
+ sgrc->invalidate();
+ QCoreApplication::processEvents();
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
gl->doneCurrent();
RLDEBUG1(" Render: - invalidated scenegraph..");
- if (!persistentGL) {
+ if (wipeGL) {
delete gl;
gl = 0;
RLDEBUG1(" Render: - invalidated OpenGL");
@@ -525,16 +517,11 @@ void QSGRenderThread::sync()
RLDEBUG(" Render: sync()");
mutex.lock();
- Q_ASSERT_X(guiIsLocked, "QSGRenderThread::sync()", "sync triggered on bad terms as gui is not already locked...");
+ Q_ASSERT_X(wm->m_locked, "QSGRenderThread::sync()", "sync triggered on bad terms as gui is not already locked...");
- for (int i=0; i<m_windows.size(); ++i) {
- Window &w = const_cast<Window &>(m_windows.at(i));
- if (w.size.width() == 0 || w.size.height() == 0) {
- RLDEBUG(" Render: - window has bad size, waiting...");
- continue;
- }
- gl->makeCurrent(w.window);
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window);
+ if (windowSize.width() > 0 && windowSize.height() > 0) {
+ gl->makeCurrent(window);
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
bool hadRenderer = d->renderer != 0;
d->syncSceneGraph();
if (!hadRenderer && d->renderer) {
@@ -542,12 +529,16 @@ void QSGRenderThread::sync()
syncResultedInChanges = true;
connect(d->renderer, SIGNAL(sceneGraphChanged()), this, SLOT(sceneGraphChanged()), Qt::DirectConnection);
}
- }
- QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
-
- RLDEBUG(" Render: - unlocking after sync");
+ // Process deferred deletes now, directly after the sync as
+ // deleteLater on the GUI must now also have resulted in SG changes
+ // and the delete is a safe operation.
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ } else {
+ RLDEBUG(" Render: - window has bad size, waiting...");
+ }
+ RLDEBUG(" Render: - window has bad size, waiting...");
waitCondition.wakeOne();
mutex.unlock();
}
@@ -583,7 +574,6 @@ void QSGRenderThread::syncAndRender()
int waitTime = vsyncDelta - (int) waitTimer.elapsed();
if (waitTime > 0)
msleep(waitTime);
- emit wm->timeToIncubate();
return;
}
@@ -596,28 +586,26 @@ void QSGRenderThread::syncAndRender()
if (animatorDriver->isRunning())
animatorDriver->advance();
- for (int i=0; i<m_windows.size(); ++i) {
- Window &w = const_cast<Window &>(m_windows.at(i));
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window);
- if (!d->renderer || w.size.width() == 0 || w.size.height() == 0) {
- RLDEBUG(" Render: - Window not yet ready, skipping render...");
- continue;
- }
- gl->makeCurrent(w.window);
- d->renderSceneGraph(w.size);
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
+ if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0) {
+ gl->makeCurrent(window);
+ d->renderSceneGraph(windowSize);
#ifndef QSG_NO_RENDER_TIMING
- if (profileFrames && i == 0)
+ if (profileFrames)
renderTime = threadTimer.nsecsElapsed();
#endif
- gl->swapBuffers(w.window);
+ gl->swapBuffers(window);
d->fireFrameSwapped();
+ } else {
+ RLDEBUG(" Render: - Window not yet ready, skipping render...");
}
+
RLDEBUG(" Render: - rendering done");
- emit wm->timeToIncubate();
#ifndef QSG_NO_RENDER_TIMING
if (qsg_render_timing)
- qDebug("Render Thread: framedelta=%d, sync=%d, first render=%d, after final swap=%d",
+ qDebug("Render Thread: window=%p, framedelta=%d, sync=%d, first render=%d, after final swap=%d",
+ window,
int(sinceLastTime/1000000),
int(syncTime/1000000),
int((renderTime - syncTime)/1000000),
@@ -668,16 +656,16 @@ void QSGRenderThread::processEventsAndWaitForMore()
void QSGRenderThread::run()
{
RLDEBUG1(" Render: run()");
- animatorDriver = sg->createAnimationDriver(0);
+ animatorDriver = sgrc->sceneGraphContext()->createAnimationDriver(0);
animatorDriver->install();
QUnifiedTimer::instance(true)->setConsistentTiming(QSGRenderLoop::useConsistentTiming());
- while (!shouldExit) {
+ while (active) {
- if (m_windows.size() > 0) {
- if (!sg->isReady()) {
- gl->makeCurrent(m_windows.at(0).window);
- sg->initialize(gl);
+ if (window) {
+ if (!sgrc->openglContext()) {
+ gl->makeCurrent(window);
+ sgrc->initialize(gl);
}
syncAndRender();
}
@@ -685,14 +673,12 @@ void QSGRenderThread::run()
processEvents();
QCoreApplication::processEvents();
- if (!shouldExit
- && (pendingUpdate == 0 || m_windows.size() == 0)) {
+ if (active && (pendingUpdate == 0 || !window)) {
RLDEBUG(" Render: enter event loop (going to sleep)");
sleeping = true;
processEventsAndWaitForMore();
sleeping = false;
}
-
}
Q_ASSERT_X(!gl, "QSGRenderThread::run()", "The OpenGL context should be cleaned up before exiting the render thread...");
@@ -701,21 +687,20 @@ void QSGRenderThread::run()
delete animatorDriver;
animatorDriver = 0;
+
+ sgrc->moveToThread(wm->thread());
+ moveToThread(wm->thread());
}
QSGThreadedRenderLoop::QSGThreadedRenderLoop()
- : m_animation_timer(0)
- , m_update_timer(0)
- , m_sync_triggered_update(false)
+ : sg(QSGContext::createDefaultContext())
+ , m_animation_timer(0)
{
#if defined(QSG_RENDER_LOOP_DEBUG_BASIC) || defined (QSG_RENDER_LOOP_DEBUG_FULL)
qsgrl_timer.start();
#endif
- m_thread = new QSGRenderThread(this);
- m_thread->moveToThread(m_thread);
-
- m_animation_driver = m_thread->sg->createAnimationDriver(this);
+ m_animation_driver = sg->createAnimationDriver(this);
m_exhaust_delay = get_env_int("QML_EXHAUST_DELAY", 5);
@@ -726,11 +711,16 @@ QSGThreadedRenderLoop::QSGThreadedRenderLoop()
RLDEBUG1("GUI: QSGThreadedRenderLoop() created");
}
-void QSGThreadedRenderLoop::maybePostPolishRequest()
+QSGRenderContext *QSGThreadedRenderLoop::createRenderContext(QSGContext *sg) const
{
- if (m_update_timer == 0) {
+ return new QSGRenderContext(sg);
+}
+
+void QSGThreadedRenderLoop::maybePostPolishRequest(Window *w)
+{
+ if (w->timerId == 0) {
RLDEBUG("GUI: - posting update");
- m_update_timer = startTimer(m_exhaust_delay, Qt::PreciseTimer);
+ w->timerId = startTimer(m_exhaust_delay, Qt::PreciseTimer);
}
}
@@ -741,7 +731,7 @@ QAnimationDriver *QSGThreadedRenderLoop::animationDriver() const
QSGContext *QSGThreadedRenderLoop::sceneGraphContext() const
{
- return m_thread->sg;
+ return sg;
}
bool QSGThreadedRenderLoop::anyoneShowing() const
@@ -762,24 +752,39 @@ bool QSGThreadedRenderLoop::interleaveIncubation() const
void QSGThreadedRenderLoop::animationStarted()
{
RLDEBUG("GUI: animationStarted()");
- if (!anyoneShowing() && m_animation_timer == 0)
- m_animation_timer = startTimer(qsgrl_animation_interval());
- maybePostPolishRequest();
+ startOrStopAnimationTimer();
+
+ for (int i=0; i<m_windows.size(); ++i)
+ maybePostPolishRequest(const_cast<Window *>(&m_windows.at(i)));
}
void QSGThreadedRenderLoop::animationStopped()
{
RLDEBUG("GUI: animationStopped()");
- if (!anyoneShowing()) {
+ startOrStopAnimationTimer();
+}
+
+
+void QSGThreadedRenderLoop::startOrStopAnimationTimer()
+{
+ int exposedWindows = 0;
+ for (int i=0; i<m_windows.size(); ++i) {
+ const Window &w = m_windows.at(i);
+ if (w.window->isVisible() && w.window->isExposed())
+ ++exposedWindows;
+ }
+
+ if (m_animation_timer != 0 && (exposedWindows == 1 || !m_animation_driver->isRunning())) {
killTimer(m_animation_timer);
m_animation_timer = 0;
+
+ } else if (m_animation_timer == 0 && exposedWindows != 1 && m_animation_driver->isRunning()) {
+ m_animation_timer = startTimer(qsgrl_animation_interval());
}
}
-
-
/*
- Adds this window to the list of tracked windowes in this window
+ Adds this window to the list of tracked windows in this window
manager. show() does not trigger rendering to start, that happens
in expose.
*/
@@ -788,8 +793,16 @@ void QSGThreadedRenderLoop::show(QQuickWindow *window)
{
RLDEBUG1("GUI: show()");
+ if (windowFor(m_windows, window)) {
+ RLDEBUG1("GUI: - already showing");
+ return;
+ }
+
Window win;
win.window = window;
+ win.thread = new QSGRenderThread(this, QQuickWindowPrivate::get(window)->context);
+ win.timerId = 0;
+ win.updateDuringSync = false;
m_windows << win;
}
@@ -806,16 +819,9 @@ void QSGThreadedRenderLoop::hide(QQuickWindow *window)
RLDEBUG1("GUI: hide()");
if (window->isExposed())
- handleObscurity(window);
+ handleObscurity(windowFor(m_windows, window));
releaseResources(window);
-
- for (int i=0; i<m_windows.size(); ++i) {
- if (m_windows.at(i).window == window) {
- m_windows.removeAt(i);
- break;
- }
- }
}
@@ -832,6 +838,18 @@ void QSGThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
hide(window);
releaseResources(window, true);
+ for (int i=0; i<m_windows.size(); ++i) {
+ if (m_windows.at(i).window == window) {
+ QSGRenderThread *thread = m_windows.at(i).thread;
+ while (thread->isRunning())
+ QThread::yieldCurrentThread();
+ Q_ASSERT(thread->thread() == QThread::currentThread());
+ delete thread;
+ m_windows.removeAt(i);
+ break;
+ }
+ }
+
RLDEBUG1("GUI: - done with windowDestroyed()");
}
@@ -839,13 +857,14 @@ void QSGThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
void QSGThreadedRenderLoop::exposureChanged(QQuickWindow *window)
{
RLDEBUG1("GUI: exposureChanged()");
- if (windowFor(m_windows, window) == 0)
+ Window *w = windowFor(m_windows, window);
+ if (!w)
return;
if (window->isExposed()) {
- handleExposure(window);
+ handleExposure(w);
} else {
- handleObscurity(window);
+ handleObscurity(w);
}
}
@@ -854,49 +873,53 @@ void QSGThreadedRenderLoop::exposureChanged(QQuickWindow *window)
Will post an event to the render thread that this window should
start to render.
*/
-void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
+void QSGThreadedRenderLoop::handleExposure(Window *w)
{
RLDEBUG1("GUI: handleExposure");
// Because we are going to bind a GL context to it, make sure it
// is created.
- if (!window->handle())
- window->create();
-
- m_thread->postEvent(new WMExposeEvent(window));
+ if (!w->window->handle())
+ w->window->create();
// Start render thread if it is not running
- if (!m_thread->isRunning()) {
- m_thread->shouldExit = false;
+ if (!w->thread->isRunning()) {
RLDEBUG1("GUI: - starting render thread...");
- if (!m_thread->gl) {
- QOpenGLContext *ctx = new QOpenGLContext();
- ctx->setFormat(window->requestedFormat());
- ctx->create();
- ctx->moveToThread(m_thread);
- m_thread->gl = ctx;
+ if (!w->thread->gl) {
+ w->thread->gl = new QOpenGLContext();
+ w->thread->gl->setFormat(w->window->requestedFormat());
+ if (!w->thread->gl->create()) {
+ delete w->thread->gl;
+ w->thread->gl = 0;
+ qWarning("QtQuick: failed to create OpenGL context");
+ return;
+ }
+
+ w->thread->gl->moveToThread(w->thread);
+ RLDEBUG1("GUI: - OpenGL context created...");
}
- QQuickAnimatorController *controller = QQuickWindowPrivate::get(window)->animationController;
- if (controller->thread() != m_thread)
- controller->moveToThread(m_thread);
+ QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController;
+ if (controller->thread() != w->thread)
+ controller->moveToThread(w->thread);
- m_thread->start();
+ w->thread->active = true;
+ if (w->thread->thread() == QThread::currentThread()) {
+ w->thread->sgrc->moveToThread(w->thread);
+ w->thread->moveToThread(w->thread);
+ }
+ w->thread->start();
} else {
RLDEBUG1("GUI: - render thread already running");
}
- polishAndSync();
-
- // Kill non-visual animation timer if it is running
- if (m_animation_timer) {
- killTimer(m_animation_timer);
- m_animation_timer = 0;
- }
+ w->thread->postEvent(new WMExposeEvent(w->window));
+ polishAndSync(w);
+ startOrStopAnimationTimer();
}
/*!
@@ -906,43 +929,47 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
It also starts up the non-vsync animation tick if no more windows
are showing.
*/
-void QSGThreadedRenderLoop::handleObscurity(QQuickWindow *window)
+void QSGThreadedRenderLoop::handleObscurity(Window *w)
{
RLDEBUG1("GUI: handleObscurity");
- if (m_thread->isRunning())
- m_thread->postEvent(new WMWindowEvent(window, WM_Obscure));
+ if (w->thread->isRunning())
+ w->thread->postEvent(new WMWindowEvent(w->window, WM_Obscure));
- if (!anyoneShowing() && m_animation_driver->isRunning() && m_animation_timer == 0) {
- m_animation_timer = startTimer(qsgrl_animation_interval());
- }
+ startOrStopAnimationTimer();
}
+void QSGThreadedRenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ Window *w = windowFor(m_windows, window);
+ if (w)
+ maybeUpdate(w);
+}
+
/*!
Called whenever the QML scene has changed. Will post an event to
ourselves that a sync is needed.
*/
-void QSGThreadedRenderLoop::maybeUpdate(QQuickWindow *window)
+void QSGThreadedRenderLoop::maybeUpdate(Window *w)
{
- Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || m_thread->guiIsLocked,
+ Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || m_locked,
"QQuickItem::update()",
"Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
RLDEBUG("GUI: maybeUpdate...");
- Window *w = windowFor(m_windows, window);
- if (!w || !m_thread->isRunning()) {
+ if (!w || !w->thread->isRunning()) {
return;
}
// Call this function from the Gui thread later as startTimer cannot be
// called from the render thread.
- if (QThread::currentThread() == m_thread) {
+ if (QThread::currentThread() == w->thread) {
RLDEBUG("GUI: - on render thread, will update later..");
- m_sync_triggered_update = true;
+ w->updateDuringSync = true;
return;
}
- maybePostPolishRequest();
+ maybePostPolishRequest(w);
}
/*!
@@ -952,15 +979,19 @@ void QSGThreadedRenderLoop::maybeUpdate(QQuickWindow *window)
*/
void QSGThreadedRenderLoop::update(QQuickWindow *window)
{
- if (QThread::currentThread() == m_thread) {
- RLDEBUG("Gui: update called on render thread");
- m_thread->requestRepaint();
+ Window *w = windowFor(m_windows, window);
+ if (!w)
+ return;
+
+ if (w->thread == QThread::currentThread()) {
+ RLDEBUG(" Render: QQuickWindow::update called on render thread");
+ w->thread->requestRepaint();
return;
}
- RLDEBUG("Gui: update called");
- m_thread->postEvent(new QEvent(WM_RequestRepaint));
- maybeUpdate(window);
+ RLDEBUG("GUI: update called");
+ w->thread->postEvent(new QEvent(WM_RequestRepaint));
+ maybeUpdate(w);
}
@@ -973,26 +1004,32 @@ void QSGThreadedRenderLoop::releaseResources(QQuickWindow *window, bool inDestru
{
RLDEBUG1("GUI: releaseResources requested...");
- m_thread->mutex.lock();
- if (m_thread->isRunning() && !m_thread->shouldExit) {
+ Window *w = windowFor(m_windows, window);
+ if (!w)
+ return;
+
+ w->thread->mutex.lock();
+ if (w->thread->isRunning() && w->thread->active) {
RLDEBUG1("GUI: - posting release request to render thread");
- m_thread->postEvent(new WMTryReleaseEvent(window, inDestructor));
- m_thread->waitCondition.wait(&m_thread->mutex);
+ w->thread->postEvent(new WMTryReleaseEvent(window, inDestructor));
+ w->thread->waitCondition.wait(&w->thread->mutex);
}
- m_thread->mutex.unlock();
+ w->thread->mutex.unlock();
}
-void QSGThreadedRenderLoop::polishAndSync()
+void QSGThreadedRenderLoop::polishAndSync(Window *w)
{
- if (!anyoneShowing()) {
- killTimer(m_update_timer);
- m_update_timer = 0;
+ RLDEBUG("GUI: polishAndSync()");
+
+ if (!w->window->isExposed() || !w->window->isVisible()) {
+ RLDEBUG("GUI: - not exposed, aborting...");
+ killTimer(w->timerId);
+ w->timerId = 0;
return;
}
- RLDEBUG("GUI: polishAndSync()");
#ifndef QSG_NO_RENDER_TIMING
QElapsedTimer timer;
@@ -1004,32 +1041,29 @@ void QSGThreadedRenderLoop::polishAndSync()
timer.start();
#endif
- // Polish as the last thing we do before we allow the sync to take place
- for (int i=0; i<m_windows.size(); ++i) {
- const Window &w = m_windows.at(i);
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window);
- d->polishItems();
- }
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(w->window);
+ d->polishItems();
+
#ifndef QSG_NO_RENDER_TIMING
if (profileFrames)
polishTime = timer.nsecsElapsed();
#endif
- m_sync_triggered_update = false;
+ w->updateDuringSync = false;
RLDEBUG("GUI: - lock for sync...");
- m_thread->mutex.lock();
- m_thread->guiIsLocked = true;
- m_thread->postEvent(new QEvent(WM_RequestSync));
+ w->thread->mutex.lock();
+ m_locked = true;
+ w->thread->postEvent(new QEvent(WM_RequestSync));
RLDEBUG("GUI: - wait for sync...");
#ifndef QSG_NO_RENDER_TIMING
if (profileFrames)
waitTime = timer.nsecsElapsed();
#endif
- m_thread->waitCondition.wait(&m_thread->mutex);
- m_thread->guiIsLocked = false;
- m_thread->mutex.unlock();
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ m_locked = false;
+ w->thread->mutex.unlock();
RLDEBUG("GUI: - unlocked after sync...");
#ifndef QSG_NO_RENDER_TIMING
@@ -1037,19 +1071,21 @@ void QSGThreadedRenderLoop::polishAndSync()
syncTime = timer.nsecsElapsed();
#endif
- killTimer(m_update_timer);
- m_update_timer = 0;
+ killTimer(w->timerId);
+ w->timerId = 0;
- if (m_animation_driver->isRunning()) {
+ if (m_windows.size() == 1 && m_animation_driver->isRunning()) {
RLDEBUG("GUI: - animations advancing");
m_animation_driver->advance();
RLDEBUG("GUI: - animations done");
// We need to trigger another sync to keep animations running...
- maybePostPolishRequest();
- } else if (m_sync_triggered_update) {
- maybePostPolishRequest();
+ maybePostPolishRequest(w);
+ emit timeToIncubate();
+ } else if (w->updateDuringSync) {
+ maybePostPolishRequest(w);
}
+
#ifndef QSG_NO_RENDER_TIMING
if (qsg_render_timing)
qDebug(" - on GUI: polish=%d, lock=%d, block/sync=%d -- animations=%d",
@@ -1073,15 +1109,26 @@ bool QSGThreadedRenderLoop::event(QEvent *e)
{
switch ((int) e->type()) {
- case QEvent::Timer:
- if (static_cast<QTimerEvent *>(e)->timerId() == m_animation_timer) {
+ case QEvent::Timer: {
+ QTimerEvent *te = static_cast<QTimerEvent *>(e);
+ if (te->timerId() == m_animation_timer) {
RLDEBUG("GUI: QEvent::Timer -> non-visual animation");
m_animation_driver->advance();
- } else if (static_cast<QTimerEvent *>(e)->timerId() == m_update_timer) {
+ emit timeToIncubate();
+ } else {
RLDEBUG("GUI: QEvent::Timer -> Polish & Sync");
- polishAndSync();
+ Window *w = 0;
+ for (int i=0; i<m_windows.size(); ++i) {
+ if (m_windows.at(i).timerId == te->timerId()) {
+ w = const_cast<Window *>(&m_windows.at(i));
+ break;
+ }
+ }
+ if (w)
+ polishAndSync(w);
}
return true;
+ }
default:
break;
@@ -1105,7 +1152,11 @@ bool QSGThreadedRenderLoop::event(QEvent *e)
QImage QSGThreadedRenderLoop::grab(QQuickWindow *window)
{
RLDEBUG("GUI: grab");
- if (!m_thread->isRunning())
+
+ Window *w = windowFor(m_windows, window);
+ Q_ASSERT(w);
+
+ if (!w->thread->isRunning())
return QImage();
if (!window->handle())
@@ -1116,12 +1167,12 @@ QImage QSGThreadedRenderLoop::grab(QQuickWindow *window)
d->polishItems();
QImage result;
- m_thread->mutex.lock();
+ w->thread->mutex.lock();
RLDEBUG1("GUI: - locking, posting grab event");
- m_thread->postEvent(new WMGrabEvent(window, &result));
- m_thread->waitCondition.wait(&m_thread->mutex);
+ w->thread->postEvent(new WMGrabEvent(window, &result));
+ w->thread->waitCondition.wait(&w->thread->mutex);
RLDEBUG1("GUI: - locking, grab done, unlocking");
- m_thread->mutex.unlock();
+ w->thread->mutex.unlock();
RLDEBUG1("Gui: - grab complete");
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
index b943739a0d..5943d0bd08 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h
+++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
@@ -64,14 +64,12 @@ public:
void windowDestroyed(QQuickWindow *window);
void exposureChanged(QQuickWindow *window);
- void handleExposure(QQuickWindow *window);
- void handleObscurity(QQuickWindow *window);
-
QImage grab(QQuickWindow *);
void update(QQuickWindow *window);
void maybeUpdate(QQuickWindow *window);
QSGContext *sceneGraphContext() const;
+ QSGRenderContext *createRenderContext(QSGContext *) const;
QAnimationDriver *animationDriver() const;
@@ -86,6 +84,13 @@ public Q_SLOTS:
void animationStopped();
private:
+ struct Window {
+ QQuickWindow *window;
+ QSGRenderThread *thread;
+ int timerId;
+ uint updateDuringSync : 1;
+ };
+
friend class QSGRenderThread;
void releaseResources(QQuickWindow *window, bool inDestructor);
@@ -94,25 +99,24 @@ private:
bool anyoneShowing() const;
void initialize();
- void maybePostPolishRequest();
-
+ void startOrStopAnimationTimer();
+ void maybePostPolishRequest(Window *w);
void waitForReleaseComplete();
+ void polishAndSync(Window *w);
+ void maybeUpdate(Window *window);
- void polishAndSync();
+ void handleExposure(Window *w);
+ void handleObscurity(Window *w);
- struct Window {
- QQuickWindow *window;
- };
- QSGRenderThread *m_thread;
+ QSGContext *sg;
QAnimationDriver *m_animation_driver;
QList<Window> m_windows;
int m_animation_timer;
- int m_update_timer;
int m_exhaust_delay;
- bool m_sync_triggered_update;
+ bool m_locked;
};
diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
index 95584d0f5d..f1d09c3785 100644
--- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp
+++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
@@ -85,6 +85,8 @@ QSGWindowsRenderLoop::QSGWindowsRenderLoop()
qsg_debug_timer.start();
#endif
+ m_rc = new QSGRenderContext(m_sg);
+
m_animationDriver = m_sg->createAnimationDriver(m_sg);
m_animationDriver->install();
@@ -102,6 +104,12 @@ QSGWindowsRenderLoop::QSGWindowsRenderLoop()
#endif
}
+QSGWindowsRenderLoop::~QSGWindowsRenderLoop()
+{
+ delete m_rc;
+ delete m_sg;
+}
+
bool QSGWindowsRenderLoop::interleaveIncubation() const
{
return m_animationDriver->isRunning() && anyoneShowing();
@@ -176,7 +184,7 @@ void QSGWindowsRenderLoop::show(QQuickWindow *window)
m_gl->makeCurrent(window);
RLDEBUG(" - initializing SG");
QSG_RENDER_TIMING_SAMPLE(time_current);
- m_sg->initialize(m_gl);
+ QQuickWindowPrivate::get(window)->context->initialize(m_gl);
#ifndef QSG_NO_RENDER_TIMING
if (qsg_render_timing) {
@@ -230,7 +238,7 @@ void QSGWindowsRenderLoop::hide(QQuickWindow *window)
// potentially clean up.
if (m_windows.size() == 0) {
if (!cd->persistentSceneGraph) {
- m_sg->invalidate();
+ QQuickWindowPrivate::get(window)->context->invalidate();
if (!cd->persistentGLContext) {
delete m_gl;
m_gl = 0;
@@ -246,7 +254,7 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window)
// If this is the last tracked window, clean up SG and GL.
if (m_windows.size() == 0) {
- m_sg->invalidate();
+ QQuickWindowPrivate::get(window)->context->invalidate();
delete m_gl;
m_gl = 0;
}
diff --git a/src/quick/scenegraph/qsgwindowsrenderloop_p.h b/src/quick/scenegraph/qsgwindowsrenderloop_p.h
index cb01e1e04b..ff5529646b 100644
--- a/src/quick/scenegraph/qsgwindowsrenderloop_p.h
+++ b/src/quick/scenegraph/qsgwindowsrenderloop_p.h
@@ -51,11 +51,14 @@
QT_BEGIN_NAMESPACE
+class QSGRenderContext;
+
class QSGWindowsRenderLoop : public QSGRenderLoop
{
Q_OBJECT
public:
explicit QSGWindowsRenderLoop();
+ ~QSGWindowsRenderLoop();
void show(QQuickWindow *window);
void hide(QQuickWindow *window);
@@ -71,6 +74,7 @@ public:
QAnimationDriver *animationDriver() const { return m_animationDriver; }
QSGContext *sceneGraphContext() const { return m_sg; }
+ QSGRenderContext *createRenderContext(QSGContext *) const { return m_rc; }
void releaseResources(QQuickWindow *) { }
@@ -102,6 +106,7 @@ private:
QOpenGLContext *m_gl;
QSGContext *m_sg;
+ QSGRenderContext *m_rc;
QAnimationDriver *m_animationDriver;
diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri
index 3b8221264b..2bcef43843 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -67,7 +67,6 @@ HEADERS += \
$$PWD/qsgdefaultglyphnode_p_p.h \
$$PWD/qsgdefaultimagenode_p.h \
$$PWD/qsgdefaultrectanglenode_p.h \
- $$PWD/qsgflashnode_p.h \
$$PWD/qsgshareddistancefieldglyphcache_p.h \
$$PWD/qsgrenderloop_p.h \
$$PWD/qsgthreadedrenderloop_p.h \
@@ -84,7 +83,6 @@ SOURCES += \
$$PWD/qsgdistancefieldglyphnode_p.cpp \
$$PWD/qsgdefaultimagenode.cpp \
$$PWD/qsgdefaultrectanglenode.cpp \
- $$PWD/qsgflashnode.cpp \
$$PWD/qsgshareddistancefieldglyphcache.cpp \
$$PWD/qsgrenderloop.cpp \
$$PWD/qsgthreadedrenderloop.cpp \
diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp
index 12ab48e193..b180bc43bd 100644
--- a/src/quick/scenegraph/util/qsgatlastexture.cpp
+++ b/src/quick/scenegraph/util/qsgatlastexture.cpp
@@ -47,11 +47,14 @@
#include <QtGui/QOpenGLContext>
#include <QtGui/QGuiApplication>
#include <QtGui/QScreen>
+#include <QtGui/QSurface>
#include <private/qsgtexture_p.h>
#include <private/qqmlprofilerservice_p.h>
+QT_BEGIN_NAMESPACE
+
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
@@ -89,11 +92,15 @@ static int qsg_envInt(const char *name, int defaultValue)
Manager::Manager()
: m_atlas(0)
{
- QSize screenSize = QGuiApplication::primaryScreen()->geometry().size();
+ QOpenGLContext *gl = QOpenGLContext::currentContext();
+ Q_ASSERT(gl);
+ QSurface *surface = gl->surface();
+ QSize surfaceSize = surface->size();
int max;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
- int w = qMin(max, qsg_envInt("QSG_ATLAS_WIDTH", qsg_powerOfTwo(screenSize.width())));
- int h = qMin(max, qsg_envInt("QSG_ATLAS_HEIGHT", qsg_powerOfTwo(screenSize.height())));
+
+ int w = qMin(max, qsg_envInt("QSG_ATLAS_WIDTH", qMax(512, qsg_powerOfTwo(surfaceSize.width()))));
+ int h = qMin(max, qsg_envInt("QSG_ATLAS_HEIGHT", qMax(512, qsg_powerOfTwo(surfaceSize.height()))));
m_atlas_size_limit = qsg_envInt("QSG_ATLAS_SIZE_LIMIT", qMax(w, h) / 2);
m_atlas_size = QSize(w, h);
@@ -102,13 +109,16 @@ Manager::Manager()
Manager::~Manager()
{
- invalidate();
+ Q_ASSERT(m_atlas == 0);
}
void Manager::invalidate()
{
- delete m_atlas;
- m_atlas = 0;
+ if (m_atlas) {
+ m_atlas->invalidate();
+ m_atlas->deleteLater();
+ m_atlas = 0;
+ }
}
QSGTexture *Manager::create(const QImage &image)
@@ -118,10 +128,7 @@ QSGTexture *Manager::create(const QImage &image)
if (!m_atlas)
m_atlas = new Atlas(m_atlas_size);
t = m_atlas->create(image);
- if (t)
- return t;
}
-
return t;
}
@@ -153,13 +160,21 @@ Atlas::Atlas(const QSize &size)
Atlas::~Atlas()
{
- if (m_texture_id)
- glDeleteTextures(1, &m_texture_id);
+ Q_ASSERT(!m_texture_id);
}
+void Atlas::invalidate()
+{
+ Q_ASSERT(QOpenGLContext::currentContext());
+ if (m_texture_id) {
+ glDeleteTextures(1, &m_texture_id);
+ m_texture_id = 0;
+ }
+}
Texture *Atlas::create(const QImage &image)
{
+ // No need to lock, as manager already locked it.
QRect rect = m_allocator.allocate(QSize(image.width() + 2, image.height() + 2));
if (rect.width() > 0 && rect.height() > 0) {
Texture *t = new Texture(this, rect, image);
@@ -386,7 +401,6 @@ void Atlas::remove(Texture *t)
{
QRect atlasRect = t->atlasSubRect();
m_allocator.deallocate(atlasRect);
-
m_pending_uploads.removeOne(t);
}
@@ -431,3 +445,5 @@ QSGTexture *Texture::removedFromAtlas() const
}
}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgatlastexture_p.h b/src/quick/scenegraph/util/qsgatlastexture_p.h
index f8edd96f47..ade7b8f00e 100644
--- a/src/quick/scenegraph/util/qsgatlastexture_p.h
+++ b/src/quick/scenegraph/util/qsgatlastexture_p.h
@@ -50,6 +50,8 @@
#include <QtQuick/private/qsgtexture_p.h>
#include <QtQuick/private/qsgareaallocator_p.h>
+QT_BEGIN_NAMESPACE
+
namespace QSGAtlasTexture
{
@@ -69,19 +71,18 @@ public:
private:
Atlas *m_atlas;
- Atlas *m_secondary_atlas;
QSize m_atlas_size;
int m_atlas_size_limit;
};
-class Atlas
+class Atlas : public QObject
{
public:
Atlas(const QSize &size);
~Atlas();
- void initialize();
+ void invalidate();
int textureId() const;
bool bind(QSGTexture::Filtering filteing);
@@ -95,7 +96,6 @@ public:
QSize size() const { return m_size; }
private:
-
QSGAreaAllocator m_allocator;
GLuint m_texture_id;
QSize m_size;
@@ -153,4 +153,6 @@ private:
}
+QT_END_NAMESPACE
+
#endif
diff --git a/src/quick/scenegraph/util/qsgpainternode.cpp b/src/quick/scenegraph/util/qsgpainternode.cpp
index d6bec550d3..797fc4d145 100644
--- a/src/quick/scenegraph/util/qsgpainternode.cpp
+++ b/src/quick/scenegraph/util/qsgpainternode.cpp
@@ -111,7 +111,7 @@ QSGPainterNode::QSGPainterNode(QQuickPaintedItem *item)
, m_dirtyRenderTarget(false)
, m_dirtyTexture(false)
{
- m_context = static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphContext();
+ m_context = static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphRenderContext();
setMaterial(&m_materialO);
setOpaqueMaterial(&m_material);
@@ -260,7 +260,7 @@ void QSGPainterNode::updateRenderTarget()
if (m_actualRenderTarget == QQuickPaintedItem::FramebufferObject ||
m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject) {
- const QOpenGLContext *ctx = m_context->glContext();
+ const QOpenGLContext *ctx = m_context->openglContext();
if (m_fbo && !m_dirtyGeometry && (!ctx->format().samples() || !m_multisamplingSupported))
return;
@@ -323,7 +323,7 @@ void QSGPainterNode::updateFBOSize()
fboWidth = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qt_next_power_of_two(m_size.width()));
fboHeight = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qt_next_power_of_two(m_size.height()));
} else {
- QSize minimumFBOSize = m_context->minimumFBOSize();
+ QSize minimumFBOSize = m_context->sceneGraphContext()->minimumFBOSize();
fboWidth = qMax(minimumFBOSize.width(), m_size.width());
fboHeight = qMax(minimumFBOSize.height(), m_size.height());
}
diff --git a/src/quick/scenegraph/util/qsgpainternode_p.h b/src/quick/scenegraph/util/qsgpainternode_p.h
index bc1556672c..df0943d38e 100644
--- a/src/quick/scenegraph/util/qsgpainternode_p.h
+++ b/src/quick/scenegraph/util/qsgpainternode_p.h
@@ -113,7 +113,7 @@ private:
void updateRenderTarget();
void updateFBOSize();
- QSGContext *m_context;
+ QSGRenderContext *m_context;
QQuickPaintedItem::RenderTarget m_preferredRenderTarget;
QQuickPaintedItem::RenderTarget m_actualRenderTarget;
diff --git a/tests/auto/quick/nodes/tst_nodestest.cpp b/tests/auto/quick/nodes/tst_nodestest.cpp
index 3ba7771538..9910729807 100644
--- a/tests/auto/quick/nodes/tst_nodestest.cpp
+++ b/tests/auto/quick/nodes/tst_nodestest.cpp
@@ -98,7 +98,7 @@ class DummyRenderer : public QSGBatchRenderer::Renderer
{
public:
DummyRenderer(QSGRootNode *root)
- : QSGBatchRenderer::Renderer(QSGContext::createDefaultContext())
+ : QSGBatchRenderer::Renderer(new QSGRenderContext(0))
, changedNode(0)
, changedState(0)
, renderCount(0)
diff --git a/tests/auto/quick/nokeywords/tst_nokeywords.cpp b/tests/auto/quick/nokeywords/tst_nokeywords.cpp
index 7146b1483a..bc8821ae60 100644
--- a/tests/auto/quick/nokeywords/tst_nokeywords.cpp
+++ b/tests/auto/quick/nokeywords/tst_nokeywords.cpp
@@ -68,7 +68,6 @@
#include <QtQuick/private/qsgdepthstencilbuffer_p.h>
#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
#include <QtQuick/private/qsgdistancefieldutil_p.h>
-#include <QtQuick/private/qsgflashnode_p.h>
#include <QtQuick/private/qsggeometry_p.h>
#include <QtQuick/private/qsgnode_p.h>
#include <QtQuick/private/qsgnodeupdater_p.h>
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index 28dacb1f81..984881c8da 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -1093,7 +1093,11 @@ void tst_qquickwindow::noUpdateWhenNothingChanges()
QQuickRectangle rect(window.contentItem());
window.showNormal();
- QTRY_VERIFY(window.isExposed());
+ QTest::qWaitForWindowExposed(&window);
+ // Many platforms are broken in the sense that that they follow up
+ // the initial expose with a second expose or more. Let these go
+ // through before we let the test continue.
+ QTest::qWait(100);
if (window.openglContext()->thread() == QGuiApplication::instance()->thread()) {
QSKIP("Only threaded renderloop implements this feature");
@@ -1102,7 +1106,8 @@ void tst_qquickwindow::noUpdateWhenNothingChanges()
QSignalSpy spy(&window, SIGNAL(frameSwapped()));
rect.update();
- QTest::qWait(500);
+ // Wait a while and verify that no more frameSwapped come our way.
+ QTest::qWait(100);
QCOMPARE(spy.size(), 0);
}
@@ -1348,11 +1353,6 @@ void tst_qquickwindow::hideThenDelete_data()
void tst_qquickwindow::hideThenDelete()
{
- if (QGuiApplication::platformName() == QStringLiteral("xcb")) {
- QSKIP("For some obscure reason this test fails in CI only");
- return;
- }
-
QFETCH(bool, persistentSG);
QFETCH(bool, persistentGL);
diff --git a/tests/auto/quick/scenegraph/data/logo-big.jpg b/tests/auto/quick/scenegraph/data/logo-big.jpg
new file mode 100644
index 0000000000..1be71d9945
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/logo-big.jpg
Binary files differ
diff --git a/tests/auto/quick/scenegraph/data/logo-small.jpg b/tests/auto/quick/scenegraph/data/logo-small.jpg
new file mode 100644
index 0000000000..f11a5dfc6b
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/logo-small.jpg
Binary files differ
diff --git a/src/quick/scenegraph/qsgflashnode_p.h b/tests/auto/quick/scenegraph/data/manyWindows_dftext.qml
index 96496158ae..69d235d021 100644
--- a/src/quick/scenegraph/qsgflashnode_p.h
+++ b/tests/auto/quick/scenegraph/data/manyWindows_dftext.qml
@@ -3,7 +3,7 @@
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
-** This file is part of the QtQuick module of the Qt Toolkit.
+** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
@@ -39,25 +39,12 @@
**
****************************************************************************/
-#ifndef QSGFLASHNODE_H
-#define QSGFLASHNODE_H
+import QtQuick 2.2
-#include <QtQuick/QSGSimpleRectNode>
-
-QT_BEGIN_NAMESPACE
-
-class QSGFlashNode : public QSGSimpleRectNode
+Text
{
-public:
- QSGFlashNode();
-
- void preprocess();
-
-private:
- int m_counter;
-};
-
-QT_END_NAMESPACE
-
-#endif // QSGFLASHNODE_H
-
+ width: 100
+ height: 100
+ text: "abcdefghijklmnopqrstuwABCDEFGHIJKLMNOPPQRSTUWXYZ1234567890!@#$%^&*()_"
+ wrapMode: Text.WrapAnywhere
+}
diff --git a/src/quick/scenegraph/qsgflashnode.cpp b/tests/auto/quick/scenegraph/data/manyWindows_image.qml
index 7fbe673e15..1b356110ed 100644
--- a/src/quick/scenegraph/qsgflashnode.cpp
+++ b/tests/auto/quick/scenegraph/data/manyWindows_image.qml
@@ -3,7 +3,7 @@
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
-** This file is part of the QtQuick module of the Qt Toolkit.
+** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
@@ -39,24 +39,14 @@
**
****************************************************************************/
-#include "qsgflashnode_p.h"
+import QtQuick 2.2
-QT_BEGIN_NAMESPACE
-
-QSGFlashNode::QSGFlashNode()
- : m_counter(1)
-{
- setFlag(UsePreprocess);
- setColor(QColor(rand()%56 + 200, rand()%56 + 200, rand()%156 + 100)); // A random, mostly yellow, color
-}
-
-void QSGFlashNode::preprocess()
-{
- if (m_counter) {
- --m_counter;
- } else {
- delete this;
+Image {
+ width: 100
+ height: 100
+ source: "logo-big.jpg"
+ Image {
+ anchors.centerIn: parent
+ source: "logo-small.jpg"
}
}
-
-QT_END_NAMESPACE
diff --git a/tests/auto/quick/scenegraph/data/manyWindows_ntext.qml b/tests/auto/quick/scenegraph/data/manyWindows_ntext.qml
new file mode 100644
index 0000000000..ebd06e360b
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/manyWindows_ntext.qml
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+
+Text
+{
+ width: 100
+ height: 100
+ text: "abcdefghijklmnopqrstuwABCDEFGHIJKLMNOPPQRSTUWXYZ1234567890!@#$%^&*()_"
+ wrapMode: Text.WrapAnywhere
+ renderType: Text.NativeRendering
+}
diff --git a/tests/auto/quick/scenegraph/data/manyWindows_rects.qml b/tests/auto/quick/scenegraph/data/manyWindows_rects.qml
new file mode 100644
index 0000000000..984968985a
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/manyWindows_rects.qml
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+
+Rectangle {
+ width: 100
+ height: 100
+
+ gradient: Gradient {
+ GradientStop { position: 0; color: "black" }
+ GradientStop { position: 1; color: "red" }
+ }
+
+ Rectangle {
+ width: 40
+ height: 40
+ rotation: 30
+ color: "#00ff00";
+ antialiasing: true
+ x: parent.width / 3 - width / 2
+ y: parent.height / 3 - height/ 2
+ }
+ Rectangle {
+ width: 40
+ height: 40
+ rotation: 30
+ color: "blue"
+ x: parent.width * 2 / 3 - width / 2
+ y: parent.height * 2 / 3 - height/ 2
+ }
+}
diff --git a/tests/auto/quick/scenegraph/scenegraph.pro b/tests/auto/quick/scenegraph/scenegraph.pro
new file mode 100644
index 0000000000..7e7a8eb162
--- /dev/null
+++ b/tests/auto/quick/scenegraph/scenegraph.pro
@@ -0,0 +1,11 @@
+CONFIG += testcase
+TARGET = tst_scenegraph
+SOURCES += tst_scenegraph.cpp
+
+include (../../shared/util.pri)
+
+macx:CONFIG -= app_bundle
+
+QT += core-private gui-private qml-private quick-private testlib
+
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
new file mode 100644
index 0000000000..cfa491ce97
--- /dev/null
+++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
@@ -0,0 +1,209 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qtest.h>
+
+#include <QtQuick>
+#include <private/qquickanimator_p.h>
+
+
+
+#include <QtQml>
+
+class tst_SceneGraph : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void manyWindows_data();
+ void manyWindows();
+
+};
+
+template <typename T> class ScopedList : public QList<T> {
+public:
+ ~ScopedList() { qDeleteAll(*this); }
+};
+
+QQuickView *createView(const QString &file, QWindow *parent = 0, int x = -1, int y = -1, int w = -1, int h = -1)
+{
+ QQuickView *view = new QQuickView(parent);
+ view->setSource(QUrl::fromLocalFile("data/" + file));
+ if (x >= 0 && y >= 0) view->setPosition(x, y);
+ if (w >= 0 && h >= 0) view->resize(w, h);
+ view->show();
+ return view;
+}
+
+QImage showAndGrab(const QString &file, int w, int h)
+{
+ QQuickView view;
+ view.setSource(QUrl::fromLocalFile(QStringLiteral("data/") + file));
+ if (w >= 0 && h >= 0)
+ view.resize(w, h);
+ view.create();
+ return view.grabWindow();
+}
+
+// Assumes the images are opaque white...
+bool containsSomethingOtherThanWhite(const QImage &image)
+{
+ Q_ASSERT(image.format() == QImage::Format_ARGB32_Premultiplied
+ || image.format() == QImage::Format_RGB32);
+ int w = image.width();
+ int h = image.height();
+ for (int y=0; y<h; ++y) {
+ const uint *pixels = (const uint *) image.constScanLine(y);
+ for (int x=0; x<w; ++x)
+ if (pixels[x] != 0xffffffff)
+ return true;
+ }
+ return false;
+}
+
+// To compare images, we need a special function with a bit of error
+// as glyphs has an invisible, but measurable variance depending
+// on where in the glyph cache they end up.
+bool compareImages(const QImage &ia, const QImage &ib)
+{
+ if (ia.size() != ib.size())
+ qDebug() << "images are of different size" << ia.size() << ib.size();
+ Q_ASSERT(ia.size() == ib.size());
+ Q_ASSERT(ia.format() == ib.format());
+
+ int w = ia.width();
+ int h = ia.height();
+ const int tolerance = 1;
+ for (int y=0; y<h; ++y) {
+ const uint *as= (const uint *) ia.constScanLine(y);
+ const uint *bs= (const uint *) ib.constScanLine(y);
+ for (int x=0; x<w; ++x) {
+ uint a = as[x];
+ uint b = bs[x];
+
+ // No tolerance for error in the alpha.
+ if ((a & 0xff000000) != (b & 0xff000000))
+ return false;
+ if (qAbs(qRed(a) - qRed(b)) > tolerance)
+ return false;
+ if (qAbs(qRed(a) - qRed(b)) > tolerance)
+ return false;
+ if (qAbs(qRed(a) - qRed(b)) > tolerance)
+ return false;
+ }
+ }
+ return true;
+}
+
+void tst_SceneGraph::manyWindows_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<bool>("toplevel");
+
+ QTest::newRow("image,toplevel") << QStringLiteral("manyWindows_image.qml") << true;
+ QTest::newRow("image,subwindow") << QStringLiteral("manyWindows_image.qml") << false;
+ QTest::newRow("dftext,toplevel") << QStringLiteral("manyWindows_dftext.qml") << true;
+ QTest::newRow("dftext,subwindow") << QStringLiteral("manyWindows_dftext.qml") << false;
+ QTest::newRow("ntext,toplevel") << QStringLiteral("manyWindows_ntext.qml") << true;
+ QTest::newRow("ntext,subwindow") << QStringLiteral("manyWindows_ntext.qml") << false;
+ QTest::newRow("rects,toplevel") << QStringLiteral("manyWindows_rects.qml") << true;
+ QTest::newRow("rects,subwindow") << QStringLiteral("manyWindows_rects.qml") << false;
+}
+
+void tst_SceneGraph::manyWindows()
+{
+ QFETCH(QString, file);
+ QFETCH(bool, toplevel);
+
+ QScopedPointer<QWindow> parent;
+ if (!toplevel) {
+ parent.reset(new QWindow());
+ parent->resize(200, 200);
+ parent->show();
+ }
+
+ ScopedList <QQuickView *> views;
+
+ const int COUNT = 4;
+
+ QImage baseLine;
+ for (int i=0; i<COUNT; ++i) {
+ views << createView(file, parent.data(), (i % 2) * 100, (i / 2) * 100, 100, 100);
+ }
+ for (int i=0; i<COUNT; ++i) {
+ QQuickView *view = views.at(i);
+ QTest::qWaitForWindowExposed(view);
+ QImage content = view->grabWindow();
+ if (i == 0) {
+ baseLine = content;
+ QVERIFY(containsSomethingOtherThanWhite(baseLine));
+ } else {
+ QVERIFY(compareImages(content, baseLine));
+ }
+ }
+
+ // Wipe and recreate one (scope pointer delets it...)
+ delete views.takeLast();
+ QQuickView *last = createView(file, parent.data(), 100, 100, 100, 100);
+ QTest::qWaitForWindowExposed(last);
+ views << last;
+ QVERIFY(compareImages(baseLine, last->grabWindow()));
+
+ // Wipe and recreate all
+ qDeleteAll(views);
+ views.clear();
+
+ for (int i=0; i<COUNT; ++i) {
+ views << createView(file, parent.data(), (i % 2) * 100, (i / 2) * 100, 100, 100);
+ }
+ for (int i=0; i<COUNT; ++i) {
+ QQuickView *view = views.at(i);
+ QTest::qWaitForWindowExposed(view);
+ QImage content = view->grabWindow();
+ QVERIFY(compareImages(content, baseLine));
+ }
+}
+
+
+#include "tst_scenegraph.moc"
+
+QTEST_MAIN(tst_SceneGraph)
+