aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/items/items.pri8
-rw-r--r--src/quick/items/qquickcanvas.cpp810
-rw-r--r--src/quick/items/qquickcanvas.h7
-rw-r--r--src/quick/items/qquickcanvas_p.h142
-rw-r--r--src/quick/items/qquickwindowmanager.cpp1242
-rw-r--r--src/quick/items/qquickwindowmanager_p.h76
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp77
-rw-r--r--src/quick/scenegraph/qsgcontext_p.h21
-rw-r--r--tests/auto/qtquick2/qquickcanvas/data/AnimationsWhileHidden.qml17
-rw-r--r--tests/auto/qtquick2/qquickcanvas/qquickcanvas.pro4
-rw-r--r--tests/auto/qtquick2/qquickcanvas/tst_qquickcanvas.cpp70
-rw-r--r--tools/qmlscene/main.cpp4
12 files changed, 1517 insertions, 961 deletions
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index 308706f0b9..a224db31d6 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -67,7 +67,8 @@ HEADERS += \
$$PWD/qquickmultipointtoucharea_p.h \
$$PWD/qquickitemview_p.h \
$$PWD/qquickitemview_p_p.h \
- $$PWD/qquickwindowmodule_p.h
+ $$PWD/qquickwindowmodule_p.h \
+ $$PWD/qquickwindowmanager_p.h
SOURCES += \
$$PWD/qquickevents.cpp \
@@ -114,7 +115,8 @@ SOURCES += \
$$PWD/qquickdroparea.cpp \
$$PWD/qquickmultipointtoucharea.cpp \
$$PWD/qquickitemview.cpp \
- $$PWD/qquickwindowmodule.cpp
+ $$PWD/qquickwindowmodule.cpp \
+ $$PWD/qquickwindowmanager.cpp
SOURCES += \
$$PWD/qquickshadereffect.cpp \
@@ -129,3 +131,5 @@ HEADERS += \
$$PWD/qquickshadereffectsource_p.h \
include(context2d/context2d.pri)
+
+
diff --git a/src/quick/items/qquickcanvas.cpp b/src/quick/items/qquickcanvas.cpp
index 9c6ab08fc7..5bf27dea5b 100644
--- a/src/quick/items/qquickcanvas.cpp
+++ b/src/quick/items/qquickcanvas.cpp
@@ -45,10 +45,12 @@
#include "qquickitem.h"
#include "qquickitem_p.h"
-#include <QtQuick/private/qsgrenderer_p.h>
-#include <QtQuick/private/qsgtexture_p.h>
+#include <private/qsgrenderer_p.h>
+#include <private/qsgtexture_p.h>
#include <private/qsgflashnode_p.h>
-#include <QtQuick/qsgengine.h>
+#include <qsgengine.h>
+
+#include <private/qquickwindowmanager_p.h>
#include <private/qguiapplication_p.h>
#include <QtGui/QInputPanel>
@@ -66,19 +68,6 @@
QT_BEGIN_NAMESPACE
-#define QQUICK_CANVAS_TIMING
-#ifdef QQUICK_CANVAS_TIMING
-static bool qquick_canvas_timing = !qgetenv("QML_CANVAS_TIMING").isEmpty();
-static QTime threadTimer;
-static int syncTime;
-static int renderTime;
-static int swapTime;
-#endif
-
-DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP)
-DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP)
-
-extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
void QQuickCanvasPrivate::updateFocusItemTransform()
{
@@ -100,7 +89,7 @@ protected:
if (e->type() == QEvent::User) {
Q_ASSERT(m_eventSent);
- bool *amtp = m_canvas->thread->allowMainThreadProcessing();
+ bool *amtp = m_canvas->windowManager->allowMainThreadProcessing();
while (incubatingObjectCount()) {
if (amtp)
incubateWhile(amtp);
@@ -127,83 +116,6 @@ private:
bool m_eventSent;
};
-class QQuickCanvasPlainRenderLoop : public QObject, public QQuickCanvasRenderLoop
-{
-public:
- QQuickCanvasPlainRenderLoop()
- : updatePending(false)
- , animationRunning(false)
- {
- }
-
- virtual void paint() {
- updatePending = false;
- if (animationRunning && animationDriver())
- animationDriver()->advance();
- polishItems();
- syncSceneGraph();
- makeCurrent();
- glViewport(0, 0, size.width(), size.height());
- renderSceneGraph(size);
- swapBuffers();
-
- if (animationRunning)
- maybeUpdate();
- }
-
- virtual QImage grab() {
- return qt_gl_read_framebuffer(size, false, false);
- }
-
- virtual void startRendering() {
- if (!glContext()) {
- createGLContext();
- makeCurrent();
- initializeSceneGraph();
- } else {
- makeCurrent();
- }
- maybeUpdate();
- }
-
- virtual void stopRendering() {
- cleanupNodesOnShutdown();
- }
-
- virtual void maybeUpdate() {
- if (!updatePending) {
- QCoreApplication::postEvent(this, new QEvent(QEvent::User));
- updatePending = true;
- }
- }
-
- virtual void animationStarted() {
- animationRunning = true;
- maybeUpdate();
- }
-
- virtual void animationStopped() {
- animationRunning = false;
- }
-
- virtual bool isRunning() const { return glContext(); } // Event loop is always running...
- virtual void resize(const QSize &s) { size = s; }
- virtual void setWindowSize(const QSize &s) { size = s; }
-
- bool event(QEvent *e) {
- if (e->type() == QEvent::User) {
- paint();
- return true;
- }
- return QObject::event(e);
- }
-
- QSize size;
-
- uint updatePending : 1;
- uint animationRunning : 1;
-};
-
/*
@@ -225,74 +137,11 @@ thus the first item that has focus will get it (assuming the scope doesn't alrea
have a scope focused item), and the other items will have their focus cleared.
*/
-/*
- Threaded Rendering
- ==================
-
- The threaded rendering uses a number of different variables to track potential
- states used to handle resizing, initial paint, grabbing and driving animations
- while ALWAYS keeping the GL context in the rendering thread and keeping the
- overhead of normal one-shot paints and vblank driven animations at a minimum.
-
- Resize, initial show and grab suffer slightly in this model as they are locked
- to the rendering in the rendering thread, but this is a necessary evil for
- the system to work.
-
- Variables that are used:
-
- Private::animationRunning: This is true while the animations are running, and only
- written to inside locks.
-
- RenderThread::isGuiBlocked: This is used to indicate that the GUI thread owns the
- lock. This variable is an integer to allow for recursive calls to lockInGui()
- without using a recursive mutex. See isGuiBlockPending.
-
- RenderThread::isPaintComplete: This variable is cleared when rendering starts and
- set once rendering is complete. It is monitored in the paintEvent(),
- resizeEvent() and grab() functions to force them to wait for rendering to
- complete.
-
- RenderThread::isGuiBlockPending: This variable is set in the render thread just
- before the sync event is sent to the GUI thread. It is used to avoid deadlocks
- in the case where render thread waits while waiting for GUI to pick up the sync
- event and GUI thread gets a resizeEvent, the initial paintEvent or a grab.
- When this happens, we use the
- exhaustSyncEvent() function to do the sync right there and mark the coming
- sync event to be discarded. There can only ever be one sync incoming.
-
- RenderThread::isRenderBlock: This variable is true when animations are not
- running and the render thread has gone to sleep, waiting for more to do.
-
- RenderThread::isExternalUpdatePending: This variable is set to false when
- a new render pass is started and to true in maybeUpdate(). It is an
- indication to the render thread that another render pass needs to take
- place, rather than the render thread going to sleep after completing its swap.
-
- RenderThread::doGrab: This variable is set by the grab() function and
- tells the renderer to do a grab after rendering is complete and before
- swapping happens.
-
- RenderThread::shouldExit: This variable is used to determine if the render
- thread should do a nother pass. It is typically set as a result of show()
- and unset as a result of hide() or during shutdown()
-
- RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
- after shouldExit has been set to true.
- */
// #define FOCUS_DEBUG
// #define MOUSE_DEBUG
// #define TOUCH_DEBUG
// #define DIRTY_DEBUG
-// #define THREAD_DEBUG
-
-// #define FRAME_TIMING
-
-#ifdef FRAME_TIMING
-static QTime frameTimer;
-int sceneGraphRenderTime;
-int readbackTime;
-#endif
QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
: transformNode(0)
@@ -306,47 +155,23 @@ QQuickRootItem::QQuickRootItem()
void QQuickCanvas::exposeEvent(QExposeEvent *)
{
Q_D(QQuickCanvas);
- d->thread->paint();
+ d->windowManager->paint(this);
}
void QQuickCanvas::resizeEvent(QResizeEvent *)
{
Q_D(QQuickCanvas);
- d->thread->resize(size());
-}
-
-void QQuickCanvas::animationStarted()
-{
- d_func()->thread->animationStarted();
-}
-
-void QQuickCanvas::animationStopped()
-{
- d_func()->thread->animationStopped();
+ d->windowManager->resize(this, size());
}
void QQuickCanvas::showEvent(QShowEvent *)
{
- Q_D(QQuickCanvas);
- if (d->vsyncAnimations) {
- if (!d->animationDriver) {
- d->animationDriver = d->context->createAnimationDriver(this);
- connect(d->animationDriver, SIGNAL(started()), this, SLOT(animationStarted()), Qt::DirectConnection);
- connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()), Qt::DirectConnection);
- }
- d->animationDriver->install();
- }
-
- if (!d->thread->isRunning()) {
- d->thread->setWindowSize(size());
- d->thread->startRendering();
- }
+ d_func()->windowManager->show(this);
}
void QQuickCanvas::hideEvent(QHideEvent *)
{
- Q_D(QQuickCanvas);
- d->thread->stopRendering();
+ d_func()->windowManager->hide(this);
}
void QQuickCanvas::focusOutEvent(QFocusEvent *)
@@ -362,64 +187,6 @@ void QQuickCanvas::focusInEvent(QFocusEvent *)
}
-/*!
- Sets weither this canvas should use vsync driven animations.
-
- This option can only be set on one single QQuickCanvas, and that it's
- vsync signal will then be used to drive all animations in the
- process.
-
- This feature is primarily useful for single QQuickCanvas, QML-only
- applications.
-
- \warning Enabling vsync on multiple QQuickCanvas instances has
- undefined behavior.
- */
-void QQuickCanvas::setVSyncAnimations(bool enabled)
-{
- Q_D(QQuickCanvas);
- if (visible()) {
- qWarning("QQuickCanvas::setVSyncAnimations: Cannot be changed when widget is shown");
- return;
- }
- d->vsyncAnimations = enabled;
-}
-
-
-
-/*!
- Returns true if this canvas should use vsync driven animations;
- otherwise returns false.
- */
-bool QQuickCanvas::vsyncAnimations() const
-{
- Q_D(const QQuickCanvas);
- return d->vsyncAnimations;
-}
-
-void QQuickCanvasPrivate::initializeSceneGraph()
-{
- if (!context)
- context = QSGContext::createDefaultContext();
-
- if (context->isReady())
- return;
-
- QOpenGLContext *glctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
- context->initialize(glctx);
-
- Q_Q(QQuickCanvas);
-
- if (!QQuickItemPrivate::get(rootItem)->itemNode()->parent()) {
- context->rootNode()->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode());
- }
-
- engine = new QSGEngine();
- engine->setCanvas(q);
-
- emit q_func()->sceneGraphInitialized();
-}
-
void QQuickCanvasPrivate::polishItems()
{
while (!itemsToPolish.isEmpty()) {
@@ -435,51 +202,46 @@ void QQuickCanvasPrivate::polishItems()
void QQuickCanvasPrivate::syncSceneGraph()
{
+ if (!renderer) {
+ QSGRootNode *rootNode = new QSGRootNode;
+ rootNode->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode());
+ renderer = context->createRenderer();
+ renderer->setRootNode(rootNode);
+ }
+
updateDirtyNodes();
// Copy the current state of clearing from canvas into renderer.
- context->renderer()->setClearColor(clearColor);
+ renderer->setClearColor(clearColor);
QSGRenderer::ClearMode mode = QSGRenderer::ClearStencilBuffer | QSGRenderer::ClearDepthBuffer;
if (clearBeforeRendering)
mode |= QSGRenderer::ClearColorBuffer;
- context->renderer()->setClearMode(mode);
+ renderer->setClearMode(mode);
}
void QQuickCanvasPrivate::renderSceneGraph(const QSize &size)
{
Q_Q(QQuickCanvas);
- context->renderer()->setDeviceRect(QRect(QPoint(0, 0), size));
- context->renderer()->setViewportRect(QRect(QPoint(0, 0), renderTarget ? renderTarget->size() : size));
- context->renderer()->setProjectionMatrixToDeviceRect();
+ renderer->setDeviceRect(QRect(QPoint(0, 0), size));
+ renderer->setViewportRect(QRect(QPoint(0, 0), renderTarget ? renderTarget->size() : size));
+ renderer->setProjectionMatrixToDeviceRect();
emit q->beforeRendering();
- context->renderNextFrame(renderTarget);
+ context->renderNextFrame(renderer, renderTarget);
emit q->afterRendering();
-
-#ifdef FRAME_TIMING
- sceneGraphRenderTime = frameTimer.elapsed();
-#endif
-
-#ifdef FRAME_TIMING
-// int pixel;
-// glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
- readbackTime = frameTimer.elapsed();
-#endif
}
-
QQuickCanvasPrivate::QQuickCanvasPrivate()
: rootItem(0)
, activeFocusItem(0)
, mouseGrabberItem(0)
, dirtyItemList(0)
, context(0)
+ , renderer(0)
+ , windowManager(0)
, clearColor(Qt::white)
- , vsyncAnimations(false)
, clearBeforeRendering(true)
- , thread(0)
- , animationDriver(0)
, renderTarget(0)
, incubationController(0)
{
@@ -491,10 +253,6 @@ QQuickCanvasPrivate::~QQuickCanvasPrivate()
void QQuickCanvasPrivate::init(QQuickCanvas *c)
{
- QUnifiedTimer* ut = QUnifiedTimer::instance(true);
- if (qmlFixedAnimationStep())
- ut->setConsistentTiming(true);
-
q_ptr = c;
Q_Q(QQuickCanvas);
@@ -510,26 +268,18 @@ void QQuickCanvasPrivate::init(QQuickCanvas *c)
//It is important that this call happens after the rootItem has a canvas..
rootItem->setFocus(true);
- bool threaded = !qmlNoThreadedRenderer();
-
- if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)) {
- qWarning("QQuickCanvas: platform does not support threaded rendering!");
- threaded = false;
- }
-
- if (threaded)
- thread = new QQuickCanvasRenderThread();
- else
- thread = new QQuickCanvasPlainRenderLoop();
-
- thread->renderer = q;
- thread->d = this;
-
- context = QSGContext::createDefaultContext();
- thread->moveContextToThread(context);
-
+ windowManager = QQuickWindowManager::instance();
+ context = windowManager->sceneGraphContext();
q->setSurfaceType(QWindow::OpenGLSurface);
q->setFormat(context->defaultSurfaceFormat());
+
+ QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()));
+ QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()));
+ QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()));
+
+ // ### TODO: remove QSGEngine
+ engine = new QSGEngine();
+ engine->setCanvas(q);
}
QDeclarativeListProperty<QObject> QQuickCanvasPrivate::data()
@@ -844,8 +594,6 @@ void QQuickCanvasPrivate::cleanup(QSGNode *n)
\since QtQuick 2.0
\brief The QQuickCanvas class provides the canvas for displaying a graphical QML scene
- \inmodule QtQuick
-
QQuickCanvas provides the graphical scene management needed to interact with and display
a scene of QQuickItems.
@@ -872,8 +620,7 @@ QQuickCanvas::~QQuickCanvas()
{
Q_D(QQuickCanvas);
- if (d->thread->isRunning())
- d->thread->stopRendering();
+ d->windowManager->canvasDestroyed(this);
// ### should we change ~QQuickItem to handle this better?
// manually cleanup for the root item (item destructor only handles these when an item is parented)
@@ -883,8 +630,6 @@ QQuickCanvas::~QQuickCanvas()
delete d->incubationController; d->incubationController = 0;
delete d->rootItem; d->rootItem = 0;
-
- delete d->thread; d->thread = 0;
}
/*!
@@ -1918,9 +1663,20 @@ void QQuickCanvasPrivate::updateDirtyNode(QQuickItem *item)
void QQuickCanvas::maybeUpdate()
{
Q_D(QQuickCanvas);
+ d->windowManager->maybeUpdate(this);
+}
+
+void QQuickCanvas::cleanupSceneGraph()
+{
+ Q_D(QQuickCanvas);
+
+ if (!d->renderer)
+ return;
+
+ delete d->renderer->rootNode();
+ delete d->renderer;
- if (d->thread && d->thread->isRunning())
- d->thread->maybeUpdate();
+ d->renderer = 0;
}
/*!
@@ -2001,7 +1757,7 @@ QOpenGLFramebufferObject *QQuickCanvas::renderTarget() const
QImage QQuickCanvas::grabFrameBuffer()
{
Q_D(QQuickCanvas);
- return d->thread ? d->thread->grab() : QImage();
+ return d->windowManager->grab(this);
}
/*!
@@ -2120,7 +1876,7 @@ bool QQuickCanvas::clearBeforeRendering() const
QSGTexture *QQuickCanvas::createTextureFromImage(const QImage &image) const
{
Q_D(const QQuickCanvas);
- if (d->context)
+ if (d->context && d->context->isReady())
return d->context->createTexture(image);
else
return 0;
@@ -2143,7 +1899,7 @@ QSGTexture *QQuickCanvas::createTextureFromImage(const QImage &image) const
QSGTexture *QQuickCanvas::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
{
Q_D(const QQuickCanvas);
- if (d->context) {
+ if (d->context && d->context->isReady()) {
QSGPlainTexture *texture = new QSGPlainTexture();
texture->setTextureId(id);
texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
@@ -2166,9 +1922,11 @@ QSGTexture *QQuickCanvas::createTextureFromId(uint id, const QSize &size, Create
void QQuickCanvas::setClearColor(const QColor &color)
{
- if (color == d_func()->clearColor)
+ Q_D(QQuickCanvas);
+ if (color == d->clearColor)
return;
- d_func()->clearColor = color;
+
+ d->clearColor = color;
emit clearColorChanged(color);
}
@@ -2185,462 +1943,6 @@ QColor QQuickCanvas::clearColor() const
-void QQuickCanvasRenderLoop::createGLContext()
-{
- gl = new QOpenGLContext();
- gl->setFormat(renderer->requestedFormat());
- gl->create();
-}
-
-void QQuickCanvasRenderThread::run()
-{
-#ifdef THREAD_DEBUG
- qDebug("QML Rendering Thread Started");
-#endif
-
- if (!glContext()) {
- createGLContext();
- makeCurrent();
- initializeSceneGraph();
- } else {
- makeCurrent();
- }
-
- while (!shouldExit) {
- lock();
-
- bool sizeChanged = false;
- isExternalUpdatePending = false;
-
- if (renderedSize != windowSize) {
-#ifdef THREAD_DEBUG
- printf(" RenderThread: window has changed size...\n");
-#endif
- glViewport(0, 0, windowSize.width(), windowSize.height());
- sizeChanged = true;
- }
-
-#ifdef THREAD_DEBUG
- printf(" RenderThread: preparing to sync...\n");
-#endif
-
- if (!isGuiBlocked) {
- isGuiBlockPending = true;
-
-#ifdef THREAD_DEBUG
- printf(" RenderThread: aquired sync lock...\n");
-#endif
- allowMainThreadProcessingFlag = false;
- QCoreApplication::postEvent(this, new QEvent(QEvent::User));
-#ifdef THREAD_DEBUG
- printf(" RenderThread: going to sleep...\n");
-#endif
- wait();
-
- isGuiBlockPending = false;
- }
-
-#ifdef THREAD_DEBUG
- printf(" RenderThread: Doing locked sync\n");
-#endif
-#ifdef QQUICK_CANVAS_TIMING
- if (qquick_canvas_timing)
- threadTimer.start();
-#endif
- inSync = true;
- syncSceneGraph();
- inSync = false;
-
- // Wake GUI after sync to let it continue animating and event processing.
- allowMainThreadProcessingFlag = true;
- wake();
- unlock();
-#ifdef THREAD_DEBUG
- printf(" RenderThread: sync done\n");
-#endif
-#ifdef QQUICK_CANVAS_TIMING
- if (qquick_canvas_timing)
- syncTime = threadTimer.elapsed();
-#endif
-
-#ifdef THREAD_DEBUG
- printf(" RenderThread: rendering... %d x %d\n", windowSize.width(), windowSize.height());
-#endif
-
- renderSceneGraph(windowSize);
-#ifdef QQUICK_CANVAS_TIMING
- if (qquick_canvas_timing)
- renderTime = threadTimer.elapsed() - syncTime;
-#endif
-
- // The content of the target buffer is undefined after swap() so grab needs
- // to happen before swap();
- if (doGrab) {
-#ifdef THREAD_DEBUG
- printf(" RenderThread: doing a grab...\n");
-#endif
- grabContent = qt_gl_read_framebuffer(windowSize, false, false);
- doGrab = false;
- }
-
-#ifdef THREAD_DEBUG
- printf(" RenderThread: wait for swap...\n");
-#endif
-
- swapBuffers();
-#ifdef THREAD_DEBUG
- printf(" RenderThread: swap complete...\n");
-#endif
-#ifdef QQUICK_CANVAS_TIMING
- if (qquick_canvas_timing) {
- swapTime = threadTimer.elapsed() - renderTime;
- qDebug() << "- Breakdown of frame time: sync:" << syncTime
- << "ms render:" << renderTime << "ms swap:" << swapTime
- << "ms total:" << swapTime + renderTime << "ms";
- }
-#endif
-
- lock();
- isPaintCompleted = true;
- if (sizeChanged)
- renderedSize = windowSize;
-
- // Wake the GUI thread now that rendering is complete, to signal that painting
- // is done, resizing is done or grabbing is completed. For grabbing, we're
- // signalling this much later than needed (we could have done it before swap)
- // but we don't want to lock an extra time.
- wake();
-
- if (!animationRunning && !isExternalUpdatePending && !shouldExit && !doGrab) {
-#ifdef THREAD_DEBUG
- printf(" RenderThread: nothing to do, going to sleep...\n");
-#endif
- isRenderBlocked = true;
- wait();
- isRenderBlocked = false;
- }
-
- unlock();
-
- QCoreApplication::processEvents();
-
- // Process any "deleteLater" objects...
- QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
- }
-
-#ifdef THREAD_DEBUG
- printf(" RenderThread: deleting all outstanding nodes\n");
-#endif
- cleanupNodesOnShutdown();
-
-#ifdef THREAD_DEBUG
- printf(" RenderThread: render loop exited... Good Night!\n");
-#endif
-
- doneCurrent();
-
- lock();
- hasExited = true;
-#ifdef THREAD_DEBUG
- printf(" RenderThread: waking GUI for final sleep..\n");
-#endif
- wake();
- unlock();
-
-#ifdef THREAD_DEBUG
- printf(" RenderThread: All done...\n");
-#endif
-}
-
-
-
-bool QQuickCanvasRenderThread::event(QEvent *e)
-{
- Q_ASSERT(QThread::currentThread() == qApp->thread());
-
- if (e->type() == QEvent::User) {
- if (!syncAlreadyHappened)
- sync(false);
-
- syncAlreadyHappened = false;
-
- if (animationRunning && animationDriver()) {
-#ifdef THREAD_DEBUG
- qDebug("GUI: Advancing animations...\n");
-#endif
-
- animationDriver()->advance();
-
-#ifdef THREAD_DEBUG
- qDebug("GUI: Animations advanced...\n");
-#endif
- }
-
- return true;
- }
-
- return QThread::event(e);
-}
-
-
-
-void QQuickCanvasRenderThread::exhaustSyncEvent()
-{
- if (isGuiBlockPending) {
- sync(true);
- syncAlreadyHappened = true;
- }
-}
-
-
-
-void QQuickCanvasRenderThread::sync(bool guiAlreadyLocked)
-{
-#ifdef THREAD_DEBUG
- printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
-#endif
- if (!guiAlreadyLocked)
- lockInGui();
-
- renderThreadAwakened = false;
-
- polishItems();
-
- wake();
- wait();
-
- if (!guiAlreadyLocked)
- unlockInGui();
-}
-
-
-
-
-/*!
- Acquires the mutex for the GUI thread. The function uses the isGuiBlocked
- variable to keep track of how many recursion levels the gui is locked with.
- We only actually acquire the mutex for the first level to avoid deadlocking
- ourselves.
- */
-
-void QQuickCanvasRenderThread::lockInGui()
-{
- // We must avoid recursive locking in the GUI thread, hence we
- // only lock when we are the first one to try to block.
- if (!isGuiBlocked)
- lock();
-
- isGuiBlocked++;
-
-#ifdef THREAD_DEBUG
- printf("GUI: aquired lock... %d\n", isGuiBlocked);
-#endif
-}
-
-
-
-void QQuickCanvasRenderThread::unlockInGui()
-{
-#ifdef THREAD_DEBUG
- printf("GUI: releasing lock... %d\n", isGuiBlocked);
-#endif
- --isGuiBlocked;
- if (!isGuiBlocked)
- unlock();
-}
-
-
-
-
-void QQuickCanvasRenderThread::animationStarted()
-{
-#ifdef THREAD_DEBUG
- printf("GUI: animationStarted()\n");
-#endif
-
- lockInGui();
-
- animationRunning = true;
-
- if (isRenderBlocked)
- wake();
-
- unlockInGui();
-}
-
-
-
-void QQuickCanvasRenderThread::animationStopped()
-{
-#ifdef THREAD_DEBUG
- printf("GUI: animationStopped()...\n");
-#endif
-
- lockInGui();
- animationRunning = false;
- unlockInGui();
-}
-
-
-void QQuickCanvasRenderThread::paint()
-{
-#ifdef THREAD_DEBUG
- printf("GUI: paint called..\n");
-#endif
-
- lockInGui();
- exhaustSyncEvent();
-
- isPaintCompleted = false;
- while (isRunning() && !isPaintCompleted) {
- if (isRenderBlocked)
- wake();
- wait();
- }
- unlockInGui();
-}
-
-
-
-void QQuickCanvasRenderThread::resize(const QSize &size)
-{
-#ifdef THREAD_DEBUG
- printf("GUI: Resize Event: %dx%d\n", size.width(), size.height());
-#endif
-
- if (!isRunning()) {
- windowSize = size;
- return;
- }
-
- lockInGui();
- exhaustSyncEvent();
-
- windowSize = size;
-
- while (isRunning() && renderedSize != windowSize) {
- if (isRenderBlocked)
- wake();
- wait();
- }
- unlockInGui();
-}
-
-
-
-void QQuickCanvasRenderThread::startRendering()
-{
-#ifdef THREAD_DEBUG
- printf("GUI: Starting Render Thread\n");
-#endif
- hasExited = false;
- shouldExit = false;
- isGuiBlocked = 0;
- isGuiBlockPending = false;
- start();
-}
-
-
-
-void QQuickCanvasRenderThread::stopRendering()
-{
-#ifdef THREAD_DEBUG
- printf("GUI: stopping render thread\n");
-#endif
-
- lockInGui();
- exhaustSyncEvent();
- shouldExit = true;
-
- if (isRenderBlocked) {
-#ifdef THREAD_DEBUG
- printf("GUI: waking up render thread\n");
-#endif
- wake();
- }
-
- while (!hasExited) {
-#ifdef THREAD_DEBUG
- printf("GUI: waiting for render thread to have exited..\n");
-#endif
- wait();
- }
-
- unlockInGui();
-
-#ifdef THREAD_DEBUG
- printf("GUI: waiting for render thread to terminate..\n");
-#endif
- // Actually wait for the thread to terminate. Otherwise we can delete it
- // too early and crash.
- QThread::wait();
-
-#ifdef THREAD_DEBUG
- printf("GUI: thread has terminated and we're all good..\n");
-#endif
-
-}
-
-
-
-QImage QQuickCanvasRenderThread::grab()
-{
- if (!isRunning())
- return QImage();
-
- if (QThread::currentThread() != qApp->thread()) {
- qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
- return QImage();
- }
-
-#ifdef THREAD_DEBUG
- printf("GUI: doing a pixelwise grab..\n");
-#endif
-
- lockInGui();
- exhaustSyncEvent();
-
- doGrab = true;
- isPaintCompleted = false;
- while (isRunning() && !isPaintCompleted) {
- if (isRenderBlocked)
- wake();
- wait();
- }
-
- QImage grabbed = grabContent;
- grabContent = QImage();
-
- unlockInGui();
-
- return grabbed;
-}
-
-
-
-void QQuickCanvasRenderThread::maybeUpdate()
-{
- Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
- "QQuickCanvas::update",
- "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
-
- if (inSync) {
- isExternalUpdatePending = true;
-
- } else if (!renderThreadAwakened) {
-#ifdef THREAD_DEBUG
- printf("GUI: doing update...\n");
-#endif
- renderThreadAwakened = true;
- lockInGui();
- isExternalUpdatePending = true;
- if (isRenderBlocked)
- wake();
- unlockInGui();
- }
-}
-
-
#include "moc_qquickcanvas.cpp"
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickcanvas.h b/src/quick/items/qquickcanvas.h
index 9ea73b087b..d38ed97028 100644
--- a/src/quick/items/qquickcanvas.h
+++ b/src/quick/items/qquickcanvas.h
@@ -90,9 +90,6 @@ public:
QSGEngine *sceneGraphEngine() const;
- void setVSyncAnimations(bool enabled);
- bool vsyncAnimations() const;
-
QImage grabFrameBuffer();
void setRenderTarget(QOpenGLFramebufferObject *fbo);
@@ -115,6 +112,7 @@ public:
Q_SIGNALS:
void frameSwapped();
void sceneGraphInitialized();
+ void sceneGraphInvalidated();
void beforeRendering();
void afterRendering();
void clearColorChanged(const QColor &);
@@ -144,8 +142,7 @@ protected:
private Q_SLOTS:
void maybeUpdate();
- void animationStarted();
- void animationStopped();
+ void cleanupSceneGraph();
private:
friend class QQuickItem;
diff --git a/src/quick/items/qquickcanvas_p.h b/src/quick/items/qquickcanvas_p.h
index 633f382954..d9d130c280 100644
--- a/src/quick/items/qquickcanvas_p.h
+++ b/src/quick/items/qquickcanvas_p.h
@@ -74,6 +74,8 @@ QT_BEGIN_NAMESPACE
//Make it easy to identify and customize the root item if needed
+class QQuickWindowManager;
+
class QQuickRootItem : public QQuickItem
{
Q_OBJECT
@@ -145,7 +147,6 @@ public:
void dirtyItem(QQuickItem *);
void cleanup(QSGNode *);
- void initializeSceneGraph();
void polishItems();
void syncSceneGraph();
void renderSceneGraph(const QSize &size);
@@ -166,16 +167,13 @@ public:
QSGEngine *engine;
QSGContext *context;
- QColor clearColor;
+ QSGRenderer *renderer;
- uint vsyncAnimations : 1;
- uint clearBeforeRendering : 1;
+ QQuickWindowManager *windowManager;
- QQuickCanvasRenderLoop *thread;
- QSize widgetSize;
- QSize viewportSize;
+ QColor clearColor;
- QAnimationDriver *animationDriver;
+ uint clearBeforeRendering : 1;
QOpenGLFramebufferObject *renderTarget;
@@ -187,134 +185,6 @@ private:
static void cleanupNodesOnShutdown(QQuickItem *);
};
-class QQuickCanvasRenderLoop
-{
-public:
- QQuickCanvasRenderLoop()
- : d(0)
- , renderer(0)
- , gl(0)
- {
- }
- virtual ~QQuickCanvasRenderLoop()
- {
- delete gl;
- }
-
- friend class QQuickCanvasPrivate;
-
- virtual void paint() = 0;
- virtual void resize(const QSize &size) = 0;
- virtual void startRendering() = 0;
- virtual void stopRendering() = 0;
- virtual QImage grab() = 0;
- virtual void setWindowSize(const QSize &size) = 0;
- virtual void maybeUpdate() = 0;
- virtual bool isRunning() const = 0;
- virtual void animationStarted() = 0;
- virtual void animationStopped() = 0;
- virtual void moveContextToThread(QSGContext *) { }
- virtual bool *allowMainThreadProcessing() { return 0; }
-
-protected:
- void initializeSceneGraph() { d->initializeSceneGraph(); }
- void syncSceneGraph() { d->syncSceneGraph(); }
- void cleanupNodesOnShutdown() { d->cleanupNodesOnShutdown(); }
- void renderSceneGraph(const QSize &size) { d->renderSceneGraph(size); }
- void polishItems() { d->polishItems(); }
- QAnimationDriver *animationDriver() const { return d->animationDriver; }
-
- inline QOpenGLContext *glContext() const { return gl; }
- void createGLContext();
- void makeCurrent() { gl->makeCurrent(renderer); }
- void doneCurrent() { gl->doneCurrent(); }
- void swapBuffers() {
- gl->swapBuffers(renderer);
- emit renderer->frameSwapped();
- }
-
-private:
- QQuickCanvasPrivate *d;
- QQuickCanvas *renderer;
-
- QOpenGLContext *gl;
-};
-
-class QQuickCanvasRenderThread : public QThread, public QQuickCanvasRenderLoop
-{
- Q_OBJECT
-public:
- QQuickCanvasRenderThread()
- : mutex(QMutex::NonRecursive)
- , allowMainThreadProcessingFlag(true)
- , animationRunning(false)
- , isGuiBlocked(0)
- , isPaintCompleted(false)
- , isGuiBlockPending(false)
- , isRenderBlocked(false)
- , isExternalUpdatePending(false)
- , syncAlreadyHappened(false)
- , inSync(false)
- , doGrab(false)
- , shouldExit(false)
- , hasExited(false)
- , renderThreadAwakened(false)
- {}
-
- inline void lock() { mutex.lock(); }
- inline void unlock() { mutex.unlock(); }
- inline void wait() { condition.wait(&mutex); }
- inline void wake() { condition.wakeOne(); }
-
- void lockInGui();
- void unlockInGui();
-
- void paint();
- void resize(const QSize &size);
- void startRendering();
- void stopRendering();
- void exhaustSyncEvent();
- void sync(bool guiAlreadyLocked);
- bool isRunning() const { return QThread::isRunning(); }
- void setWindowSize(const QSize &size) { windowSize = size; }
- void maybeUpdate();
- void moveContextToThread(QSGContext *c) { c->moveToThread(this); }
- bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; }
-
- bool event(QEvent *);
-
- QImage grab();
-
-public slots:
- void animationStarted();
- void animationStopped();
-
-public:
- QMutex mutex;
- QWaitCondition condition;
-
- bool allowMainThreadProcessingFlag;
-
- QSize windowSize;
- QSize renderedSize;
-
- uint animationRunning: 1;
- int isGuiBlocked;
- uint isPaintCompleted : 1;
- uint isGuiBlockPending : 1;
- uint isRenderBlocked : 1;
- uint isExternalUpdatePending : 1;
- uint syncAlreadyHappened : 1;
- uint inSync : 1;
- uint doGrab : 1;
- uint shouldExit : 1;
- uint hasExited : 1;
- uint renderThreadAwakened : 1;
-
- QImage grabContent;
-
- void run();
-};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickCanvasPrivate::FocusOptions)
diff --git a/src/quick/items/qquickwindowmanager.cpp b/src/quick/items/qquickwindowmanager.cpp
new file mode 100644
index 0000000000..c6baf13758
--- /dev/null
+++ b/src/quick/items/qquickwindowmanager.cpp
@@ -0,0 +1,1242 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickwindowmanager_p.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QTime>
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtCore/private/qabstractanimation_p.h>
+
+#include <QtGui/QOpenGLContext>
+#include <QtGui/private/qguiapplication_p.h>
+
+#include <QtDeclarative/private/qdeclarativeglobal_p.h>
+
+#include <QtQuick/QQuickCanvas>
+#include <QtQuick/private/qquickcanvas_p.h>
+#include <QtQuick/private/qsgcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+
+#define QQUICK_CANVAS_TIMING
+#ifdef QQUICK_CANVAS_TIMING
+static bool qquick_canvas_timing = !qgetenv("QML_CANVAS_TIMING").isEmpty();
+static QTime threadTimer;
+static int syncTime;
+static int renderTime;
+static int swapTime;
+#endif
+
+extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
+
+
+
+/*!
+ expectations for this manager to work:
+ - one opengl context to render multiple windows
+ - OpenGL pipeline will not block for vsync in swap
+ - OpenGL pipeline will block based on a full buffer queue.
+ - Multiple screens can share the OpenGL context
+ - Animations are advanced for all windows once per swap
+ */
+
+/*
+ Threaded Rendering
+ ==================
+
+ The threaded rendering uses a number of different variables to track potential
+ states used to handle resizing, initial paint, grabbing and driving animations
+ while ALWAYS keeping the GL context in the rendering thread and keeping the
+ overhead of normal one-shot paints and vblank driven animations at a minimum.
+
+ Resize, initial show and grab suffer slightly in this model as they are locked
+ to the rendering in the rendering thread, but this is a necessary evil for
+ the system to work.
+
+ Variables that are used:
+
+ Private::animationRunning: This is true while the animations are running, and only
+ written to inside locks.
+
+ RenderThread::isGuiLocked: This is used to indicate that the GUI thread owns the
+ lock. This variable is an integer to allow for recursive calls to lockInGui()
+ without using a recursive mutex. See isPostingSyncEvent.
+
+ RenderThread::isPaintComplete: This variable is cleared when rendering starts and
+ set once rendering is complete. It is monitored in the paintEvent(),
+ resizeEvent() and grab() functions to force them to wait for rendering to
+ complete.
+
+ RenderThread::isPostingSyncEvent: This variable is set in the render thread just
+ before the sync event is sent to the GUI thread. It is used to avoid deadlocks
+ in the case where render thread waits while waiting for GUI to pick up the sync
+ event and GUI thread gets a resizeEvent, the initial paintEvent or a grab.
+ When this happens, we use the
+ exhaustSyncEvent() function to do the sync right there and mark the coming
+ sync event to be discarded. There can only ever be one sync incoming.
+
+ RenderThread::isRenderBlock: This variable is true when animations are not
+ running and the render thread has gone to sleep, waiting for more to do.
+
+ RenderThread::isExternalUpdatePending: This variable is set to false when
+ a new render pass is started and to true in maybeUpdate(). It is an
+ indication to the render thread that another render pass needs to take
+ place, rather than the render thread going to sleep after completing its swap.
+
+ RenderThread::doGrab: This variable is set by the grab() function and
+ tells the renderer to do a grab after rendering is complete and before
+ swapping happens.
+
+ RenderThread::shouldExit: This variable is used to determine if the render
+ thread should do a nother pass. It is typically set as a result of show()
+ and unset as a result of hide() or during shutdown()
+
+ RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
+ after shouldExit has been set to true.
+ */
+
+DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP);
+DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
+
+//#define THREAD_DEBUG
+
+class QQuickRenderThreadSingleContextWindowManager : public QThread, public QQuickWindowManager
+{
+ Q_OBJECT
+public:
+ QQuickRenderThreadSingleContextWindowManager()
+ : sg(QSGContext::createDefaultContext())
+ , gl(0)
+ , animationTimer(-1)
+ , isGuiLocked(0)
+ , animationRunning(false)
+ , isPaintCompleted(false)
+ , isPostingSyncEvent(false)
+ , isRenderBlocked(false)
+ , syncAlreadyHappened(false)
+ , inSync(false)
+ , shouldExit(false)
+ , hasExited(false)
+ , renderThreadAwakened(false)
+ , canvasToGrab(0)
+ {
+ sg->moveToThread(this);
+
+ animationDriver = sg->createAnimationDriver(this);
+ animationDriver->install();
+ connect(animationDriver, SIGNAL(started()), this, SLOT(animationStarted()));
+ connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
+ }
+
+ QSGContext *sceneGraphContext() const { return sg; }
+
+ void show(QQuickCanvas *canvas);
+ void hide(QQuickCanvas *canvas);
+
+ void canvasDestroyed(QQuickCanvas *canvas);
+
+ void paint(QQuickCanvas *canvas);
+ QImage grab(QQuickCanvas *canvas);
+ void resize(QQuickCanvas *canvas, const QSize &size);
+ void maybeUpdate(QQuickCanvas *canvas);
+
+ void startRendering();
+ void stopRendering();
+
+ void exhaustSyncEvent();
+ void sync(bool guiAlreadyLocked);
+
+ void initialize();
+
+ bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; }
+
+ bool event(QEvent *);
+
+ inline void lock() { mutex.lock(); }
+ inline void unlock() { mutex.unlock(); }
+ inline void wait() { condition.wait(&mutex); }
+ inline void wake() { condition.wakeOne(); }
+ void lockInGui();
+ void unlockInGui();
+
+ void run();
+
+public slots:
+ void animationStarted();
+ void animationStopped();
+ void canvasVisibilityChanged();
+
+private:
+ void handleAddedWindows();
+ void handleAddedWindow(QQuickCanvas *canvas);
+ void handleRemovedWindows();
+
+ QSGContext *sg;
+ QOpenGLContext *gl;
+ QAnimationDriver *animationDriver;
+ int animationTimer;
+
+ QMutex mutex;
+ QWaitCondition condition;
+
+ bool allowMainThreadProcessingFlag;
+
+ int isGuiLocked;
+ uint animationRunning: 1;
+ uint isPaintCompleted : 1;
+ uint isPostingSyncEvent : 1;
+ uint isRenderBlocked : 1;
+ uint isExternalUpdatePending : 1;
+ uint syncAlreadyHappened : 1;
+ uint inSync : 1;
+ uint shouldExit : 1;
+ uint hasExited : 1;
+ uint renderThreadAwakened : 1;
+ uint isGuiAboutToBeBlockedForSync : 1;
+
+ QQuickCanvas *canvasToGrab;
+ QImage grabContent;
+
+ struct CanvasData {
+ QSize renderedSize;
+ QSize windowSize;
+ QSize viewportSize;
+
+ uint sizeWasChanged : 1;
+ uint isExternalUpdatePending : 1;
+ };
+
+ QHash<QQuickCanvas *, CanvasData *> m_rendered_windows;
+
+ struct CanvasTracker {
+ QQuickCanvas *canvas;
+ uint isVisible : 1;
+ uint toBeRemoved : 1;
+ };
+
+ QList<CanvasTracker> m_tracked_windows;
+
+ QList<QQuickCanvas *> m_removed_windows;
+ QList<QQuickCanvas *> m_added_windows;
+};
+
+
+class QQuickTrivialWindowManager : public QObject, public QQuickWindowManager
+{
+public:
+ QQuickTrivialWindowManager();
+
+ void show(QQuickCanvas *canvas);
+ void hide(QQuickCanvas *canvas);
+
+ void canvasDestroyed(QQuickCanvas *canvas);
+
+ void renderCanvas(QQuickCanvas *canvas);
+ void paint(QQuickCanvas *canvas);
+ QImage grab(QQuickCanvas *canvas);
+ void resize(QQuickCanvas *canvas, const QSize &size);
+
+ void maybeUpdate(QQuickCanvas *canvas);
+
+ bool *allowMainThreadProcessing();
+
+ QSGContext *sceneGraphContext() const;
+
+ bool event(QEvent *);
+
+ struct CanvasData {
+ bool updatePending : 1;
+ bool grabOnly : 1;
+ };
+
+ QHash<QQuickCanvas *, CanvasData> m_windows;
+
+ QOpenGLContext *gl;
+ QSGContext *sg;
+
+ QImage grabContent;
+
+ bool eventPending;
+};
+
+
+QQuickWindowManager *QQuickWindowManager::instance()
+{
+ static QQuickWindowManager *theInstance;
+
+ if (!theInstance) {
+ bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL);
+ if (qmlNoThreadedRenderer())
+ fancy = false;
+ if (qmlFixedAnimationStep())
+ QUnifiedTimer::instance(true)->setConsistentTiming(true);
+ theInstance = fancy
+ ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager
+ : (QQuickWindowManager*) new QQuickTrivialWindowManager;
+ }
+ return theInstance;
+}
+
+
+
+
+
+void QQuickRenderThreadSingleContextWindowManager::initialize()
+{
+ Q_ASSERT(m_rendered_windows.size());
+ QQuickCanvas *win = m_rendered_windows.constBegin().key();
+
+ gl = new QOpenGLContext();
+ // Pick up the surface format from one of them
+ gl->setFormat(win->requestedFormat());
+ gl->create();
+ gl->makeCurrent(win);
+
+ Q_ASSERT(!sg->isReady());
+ sg->initialize(gl);
+}
+
+
+/*!
+ This function is called when the canvas is created to register the canvas with
+ the window manager.
+
+ Called on GUI Thread.
+ */
+
+void QQuickRenderThreadSingleContextWindowManager::show(QQuickCanvas *canvas)
+{
+#ifdef THREAD_DEBUG
+ printf("GUI: Canvas added to windowing system, %p, %dx%d\n", canvas, canvas->width(), canvas->height());
+#endif
+
+ CanvasTracker tracker;
+ tracker.canvas = canvas;
+ tracker.isVisible = false;
+ tracker.toBeRemoved = false;
+ m_tracked_windows << tracker;
+
+ connect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
+ connect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
+
+ canvasVisibilityChanged();
+}
+
+
+void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickCanvas *canvas)
+{
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: adding canvas: %p\n", canvas);
+#endif
+
+ CanvasData *data = new CanvasData;
+ data->isExternalUpdatePending = true;
+ data->sizeWasChanged = false;
+ data->windowSize = canvas->size();
+ m_rendered_windows[canvas] = data;
+}
+
+
+/*!
+ Called on Render Thread
+ */
+void QQuickRenderThreadSingleContextWindowManager::handleAddedWindows()
+{
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: about to add %d\n", m_added_windows.size());
+#endif
+
+ while (m_added_windows.size()) {
+ QQuickCanvas *canvas = m_added_windows.takeLast();
+ handleAddedWindow(canvas);
+ }
+}
+
+
+/*!
+ Called on the GUI Thread, from the canvas' destructor
+ */
+
+void QQuickRenderThreadSingleContextWindowManager::canvasDestroyed(QQuickCanvas *canvas)
+{
+#ifdef THREAD_DEBUG
+ printf("GUI: Canvas destroyed: %p\n", canvas);
+#endif
+
+ hide(canvas);
+}
+
+
+/*!
+ Called on GUI Thread
+ */
+
+void QQuickRenderThreadSingleContextWindowManager::hide(QQuickCanvas *canvas)
+{
+#ifdef THREAD_DEBUG
+ printf("GUI: Canvas hidden: %p\n", canvas);
+#endif
+
+ int position = -1;
+ for (int i=0; i<m_tracked_windows.size(); ++i) {
+ if (m_tracked_windows.at(i).canvas == canvas) {
+ m_tracked_windows[i].toBeRemoved = true;
+ position = i;
+ break;
+ }
+ }
+
+ if (position >= 0) {
+ disconnect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()));
+ disconnect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()));
+ canvasVisibilityChanged();
+ m_tracked_windows.removeAt(position);
+ }
+
+#ifdef THREAD_DEBUG
+ printf("GUI: Canvas removal completed... %p\n", canvas);
+#endif
+}
+
+/*!
+ Called on Render Thread
+ */
+void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows()
+{
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: about to remove %d\n", m_removed_windows.size());
+#endif
+
+ bool removedAnything = false;
+ while (m_removed_windows.size()) {
+ QQuickCanvas *canvas = m_removed_windows.takeLast();
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: removing %p\n", canvas);
+#endif
+
+ QQuickCanvasPrivate::get(canvas)->cleanupNodesOnShutdown();
+ delete m_rendered_windows.take(canvas);
+ removedAnything = true;
+ }
+
+ // If a window is removed because it has been hidden it will take with it
+ // the gl context (at least on Mac) if bound, so disconnect the gl context
+ // from anything
+ if (removedAnything)
+ gl->doneCurrent();
+}
+
+
+
+/*!
+ Called on GUI Thread
+ */
+
+void QQuickRenderThreadSingleContextWindowManager::canvasVisibilityChanged()
+{
+ bool anyoneShowing = false;
+ QList<QQuickCanvas *> toAdd, toRemove;
+
+ // Not optimal, but also not frequently used...
+ for (int i=0; i<m_tracked_windows.size(); ++i) {
+ CanvasTracker &t = const_cast<CanvasTracker &>(m_tracked_windows.at(i));
+ QQuickCanvas *win = t.canvas;
+
+ Q_ASSERT(win->visible() || t.toBeRemoved);
+ bool canvasVisible = win->width() > 0 && win->height() > 0;
+ anyoneShowing |= (canvasVisible && !t.toBeRemoved);
+
+ if ((!canvasVisible && t.isVisible) || t.toBeRemoved) {
+ toRemove << win;
+ } else if (canvasVisible && !t.isVisible) {
+ toAdd << win;
+ }
+ t.isVisible = canvasVisible;
+ }
+
+ if (isRunning()) {
+ if (!anyoneShowing) {
+ stopRendering();
+ } else {
+ lockInGui();
+ exhaustSyncEvent();
+ m_added_windows << toAdd;
+ m_removed_windows << toRemove;
+ while (isRunning() && (m_added_windows.size() || m_removed_windows.size())) {
+ if (isRenderBlocked)
+ wake();
+ wait();
+ }
+ unlockInGui();
+ }
+
+ } else if (anyoneShowing) {
+ Q_ASSERT(toRemove.isEmpty()); // since loop is not running, nothing is showing now
+ for (int i=0; i<toAdd.size(); ++i)
+ handleAddedWindow(toAdd.at(i));
+ startRendering();
+ }
+
+}
+
+
+void QQuickRenderThreadSingleContextWindowManager::run()
+{
+#ifdef THREAD_DEBUG
+ printf("QML Rendering Thread Started\n");
+#endif
+
+ if (!gl)
+ initialize();
+
+ while (!shouldExit) {
+ lock();
+
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: *** NEW FRAME ***\n");
+#endif
+
+ handleAddedWindows();
+
+ if (!isGuiLocked) {
+ isPostingSyncEvent = true;
+
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: aquired sync lock...\n");
+#endif
+ allowMainThreadProcessingFlag = false;
+ QCoreApplication::postEvent(this, new QEvent(QEvent::User));
+
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: going to sleep...\n");
+#endif
+ wake(); // In case the event got through all the way to wait() before this thread got to wait.
+ wait();
+
+
+ isPostingSyncEvent = false;
+ }
+
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: Doing locked sync\n");
+#endif
+#ifdef QQUICK_CANVAS_TIMING
+ if (qquick_canvas_timing)
+ threadTimer.start();
+#endif
+ inSync = true;
+ for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
+ it != m_rendered_windows.constEnd(); ++it) {
+ QQuickCanvas *canvas = it.key();
+
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: Syncing canvas: %p\n", canvas);
+#endif
+
+ CanvasData *canvasData = it.value();
+ QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
+
+ Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
+
+ gl->makeCurrent(canvas);
+
+ if (canvasData->viewportSize != canvasData->windowSize) {
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: --- window has changed size...\n");
+#endif
+ canvasData->viewportSize = canvasData->windowSize;
+ canvasData->sizeWasChanged = true;
+ glViewport(0, 0, canvasData->viewportSize.width(), canvasData->viewportSize.height());
+ }
+
+ canvasData->isExternalUpdatePending = false;
+ canvasPrivate->syncSceneGraph();
+ }
+ inSync = false;
+
+ // Wake GUI after sync to let it continue animating and event processing.
+ allowMainThreadProcessingFlag = true;
+ wake();
+ unlock();
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: sync done\n");
+#endif
+#ifdef QQUICK_CANVAS_TIMING
+ if (qquick_canvas_timing)
+ syncTime = threadTimer.elapsed();
+#endif
+
+ for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
+ it != m_rendered_windows.constEnd(); ++it) {
+ QQuickCanvas *canvas = it.key();
+ CanvasData *canvasData = it.value();
+ QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
+
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: Rendering canvas %p\n", canvas);
+#endif
+
+ Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
+
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: --- rendering at size %dx%d\n",
+ canvasData->viewportSize.width(), canvasData->viewportSize.height()
+ );
+#endif
+
+ // We only need to re-makeCurrent when we have multiple surfaces.
+ if (m_rendered_windows.size() > 1)
+ gl->makeCurrent(canvas);
+
+ canvasPrivate->renderSceneGraph(canvasData->viewportSize);
+#ifdef QQUICK_CANVAS_TIMING
+ if (qquick_canvas_timing)
+ renderTime = threadTimer.elapsed() - syncTime;
+#endif
+
+ // The content of the target buffer is undefined after swap() so grab needs
+ // to happen before swap();
+ if (canvas == canvasToGrab) {
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: --- grabbing...\n");
+#endif
+ grabContent = qt_gl_read_framebuffer(canvasData->windowSize, false, false);
+ canvasToGrab = 0;
+ }
+
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: --- wait for swap...\n");
+#endif
+
+ gl->swapBuffers(canvas);
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: --- swap complete...\n");
+#endif
+
+ }
+
+#ifdef QQUICK_CANVAS_TIMING
+ if (qquick_canvas_timing) {
+ swapTime = threadTimer.elapsed() - renderTime;
+ qDebug() << "- Breakdown of frame time; sync:" << syncTime
+ << "ms render:" << renderTime << "ms swap:" << swapTime
+ << "ms total:" << swapTime + renderTime << "ms";
+ }
+#endif
+
+ lock();
+
+ handleRemovedWindows();
+
+ isPaintCompleted = true;
+
+ bool isExternalUpdatePending = false;
+
+ // Update sizes...
+ for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
+ it != m_rendered_windows.constEnd(); ++it) {
+ CanvasData *canvasData = it.value();
+ if (canvasData->sizeWasChanged) {
+ canvasData->renderedSize = canvasData->viewportSize;
+ canvasData->sizeWasChanged = false;
+ }
+ isExternalUpdatePending |= canvasData->isExternalUpdatePending;
+ }
+
+
+ // Wake the GUI thread now that rendering is complete, to signal that painting
+ // is done, resizing is done or grabbing is completed. For grabbing, we're
+ // signalling this much later than needed (we could have done it before swap)
+ // but we don't want to lock an extra time.
+ wake();
+
+ if (!animationRunning && !isExternalUpdatePending && !shouldExit && !canvasToGrab) {
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: nothing to do, going to sleep...\n");
+#endif
+ isRenderBlocked = true;
+ wait();
+ isRenderBlocked = false;
+ }
+
+ unlock();
+
+ QCoreApplication::processEvents();
+
+ // Process any "deleteLater" objects...
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ }
+
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: deleting all outstanding nodes\n");
+#endif
+
+ m_removed_windows << m_rendered_windows.keys();
+ handleRemovedWindows();
+
+ sg->invalidate();
+
+ gl->doneCurrent();
+ delete gl;
+ gl = 0;
+
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: render loop exited... Good Night!\n");
+#endif
+
+ lock();
+ hasExited = true;
+
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: waking GUI for final sleep..\n");
+#endif
+ wake();
+ unlock();
+
+#ifdef THREAD_DEBUG
+ printf(" RenderThread: All done...\n");
+#endif
+}
+
+bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e)
+{
+ Q_ASSERT(QThread::currentThread() == qApp->thread());
+
+ if (e->type() == QEvent::User) {
+
+ // If all canvases have been hidden, ignore the event
+ if (!isRunning())
+ return true;
+
+ if (!syncAlreadyHappened)
+ sync(false);
+
+ syncAlreadyHappened = false;
+
+ if (animationRunning) {
+#ifdef THREAD_DEBUG
+ printf("GUI: Advancing animations...\n");
+#endif
+
+ animationDriver->advance();
+
+#ifdef THREAD_DEBUG
+ printf("GUI: Animations advanced...\n");
+#endif
+ }
+
+ return true;
+
+ } else if (e->type() == QEvent::Timer) {
+#ifdef THREAD_DEBUG
+ printf("GUI: Animations advanced via timer...\n");
+#endif
+ animationDriver->advance();
+ }
+
+ return QThread::event(e);
+}
+
+
+
+void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent()
+{
+ if (isPostingSyncEvent) {
+ sync(true);
+ syncAlreadyHappened = true;
+ }
+}
+
+
+
+void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked)
+{
+#ifdef THREAD_DEBUG
+ printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
+#endif
+ if (!guiAlreadyLocked)
+ lockInGui();
+
+ renderThreadAwakened = false;
+
+ for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
+ it != m_rendered_windows.constEnd(); ++it) {
+ QQuickCanvasPrivate::get(it.key())->polishItems();
+ }
+
+ wake();
+ wait();
+
+ if (!guiAlreadyLocked)
+ unlockInGui();
+}
+
+
+
+
+/*!
+ Acquires the mutex for the GUI thread. The function uses the isGuiLocked
+ variable to keep track of how many recursion levels the gui is locked with.
+ We only actually acquire the mutex for the first level to avoid deadlocking
+ ourselves.
+ */
+
+void QQuickRenderThreadSingleContextWindowManager::lockInGui()
+{
+ if (++isGuiLocked == 1)
+ lock();
+
+#ifdef THREAD_DEBUG
+ printf("GUI: aquired lock... level=%d\n", isGuiLocked);
+#endif
+}
+
+
+
+void QQuickRenderThreadSingleContextWindowManager::unlockInGui()
+{
+#ifdef THREAD_DEBUG
+ printf("GUI: releasing lock... level=%d\n", isGuiLocked);
+#endif
+
+ if (--isGuiLocked == 0)
+ unlock();
+}
+
+
+
+
+void QQuickRenderThreadSingleContextWindowManager::animationStarted()
+{
+#ifdef THREAD_DEBUG
+ printf("GUI: animationStarted()\n");
+#endif
+
+ if (!isRunning()) {
+ animationTimer = startTimer(1000/60);
+ return;
+ }
+
+ lockInGui();
+
+ animationRunning = true;
+
+ if (isRenderBlocked)
+ wake();
+
+ unlockInGui();
+}
+
+
+
+void QQuickRenderThreadSingleContextWindowManager::animationStopped()
+{
+#ifdef THREAD_DEBUG
+ printf("GUI: animationStopped()...\n");
+#endif
+
+ if (!isRunning()) {
+ killTimer(animationTimer);
+ animationTimer = -1;
+ return;
+ }
+
+ lockInGui();
+ animationRunning = false;
+ unlockInGui();
+}
+
+
+void QQuickRenderThreadSingleContextWindowManager::paint(QQuickCanvas *canvas)
+{
+ Q_UNUSED(canvas);
+#ifdef THREAD_DEBUG
+ printf("GUI: paint called: %p\n", canvas);
+#endif
+
+ return;
+
+
+
+ lockInGui();
+ exhaustSyncEvent();
+
+ isPaintCompleted = false;
+ while (isRunning() && !isPaintCompleted) {
+ if (isRenderBlocked)
+ wake();
+ wait();
+ }
+ unlockInGui();
+
+#ifdef THREAD_DEBUG
+ printf("GUI: paint done: %p\n", canvas);
+#endif
+}
+
+
+
+void QQuickRenderThreadSingleContextWindowManager::resize(QQuickCanvas *canvas, const QSize &size)
+{
+#ifdef THREAD_DEBUG
+ printf("GUI: Resize Event: %p = %dx%d\n", canvas, size.width(), size.height());
+#endif
+
+ // If the rendering thread is not running we do not need to do anything.
+ // Also if the canvas is being resized to an invalid size, it will be removed
+ // by the canvasVisibilityChanged slot as result of width/heightcChanged()
+ if (!isRunning() || size.width() <= 0 || size.height() <= 0)
+ return;
+
+ lockInGui();
+ exhaustSyncEvent();
+
+ CanvasData *canvasData = m_rendered_windows.value(canvas);
+ if (canvasData) {
+ canvasData->windowSize = size;
+ while (isRunning() && canvasData->renderedSize != size && size.width() > 0 && size.height() > 0) {
+ if (isRenderBlocked)
+ wake();
+ wait();
+ }
+ }
+ unlockInGui();
+
+#ifdef THREAD_DEBUG
+ printf("GUI: Resize done: %p\n", canvas);
+#endif
+}
+
+
+
+void QQuickRenderThreadSingleContextWindowManager::startRendering()
+{
+#ifdef THREAD_DEBUG
+ printf("GUI: Starting Render Thread\n");
+#endif
+ hasExited = false;
+ shouldExit = false;
+ isGuiLocked = 0;
+ isPostingSyncEvent = false;
+ syncAlreadyHappened = false;
+ renderThreadAwakened = false;
+ inSync = false;
+
+ start(); // Start the render thread...
+
+ // Animations will now be driven from the rendering thread.
+ if (animationTimer >= 0) {
+ killTimer(animationTimer);
+ animationTimer = -1;
+ }
+
+
+}
+
+
+
+void QQuickRenderThreadSingleContextWindowManager::stopRendering()
+{
+#ifdef THREAD_DEBUG
+ printf("GUI: stopping render thread\n");
+#endif
+
+ lockInGui();
+ exhaustSyncEvent();
+ shouldExit = true;
+
+ if (isRenderBlocked) {
+#ifdef THREAD_DEBUG
+ printf("GUI: waking up render thread\n");
+#endif
+ wake();
+ }
+
+ while (!hasExited) {
+#ifdef THREAD_DEBUG
+ printf("GUI: waiting for render thread to have exited..\n");
+#endif
+ wait();
+ }
+
+ unlockInGui();
+
+#ifdef THREAD_DEBUG
+ printf("GUI: waiting for render thread to terminate..\n");
+#endif
+ // Actually wait for the thread to terminate. Otherwise we can delete it
+ // too early and crash.
+ QThread::wait();
+
+#ifdef THREAD_DEBUG
+ printf("GUI: thread has terminated and we're all good..\n");
+#endif
+
+ // Activate timer to keep animations running
+ if (animationDriver->isRunning())
+ animationTimer = startTimer(1000/60);
+}
+
+
+
+QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickCanvas *canvas)
+{
+ if (!isRunning())
+ return QImage();
+
+ if (QThread::currentThread() != qApp->thread()) {
+ qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
+ return QImage();
+ }
+
+#ifdef THREAD_DEBUG
+ printf("GUI: doing a pixelwise grab..\n");
+#endif
+
+ lockInGui();
+ exhaustSyncEvent();
+
+ canvasToGrab = canvas;
+ isPaintCompleted = false;
+ while (isRunning() && !isPaintCompleted) {
+ if (isRenderBlocked)
+ wake();
+ wait();
+ }
+
+ QImage grabbed = grabContent;
+ grabContent = QImage();
+
+ unlockInGui();
+
+ return grabbed;
+}
+
+
+
+void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickCanvas *canvas)
+{
+ Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
+ "QQuickCanvas::update",
+ "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
+
+ if (inSync) {
+ CanvasData *canvasData = m_rendered_windows.value(canvas);
+ if (canvasData)
+ canvasData->isExternalUpdatePending = true;
+
+ } else if (!renderThreadAwakened) {
+#ifdef THREAD_DEBUG
+ printf("GUI: doing update...\n");
+#endif
+ renderThreadAwakened = true;
+ bool locked = false;
+
+ // If we are getting here from the renderer's sync event, the renderer is about
+ // to go to sleep anyway.
+ if (!isGuiAboutToBeBlockedForSync)
+ lockInGui();
+ CanvasData *canvasData = m_rendered_windows.value(canvas);
+ if (canvasData)
+ canvasData->isExternalUpdatePending = true;
+ if (isRenderBlocked)
+ wake();
+ if (!isGuiAboutToBeBlockedForSync)
+ unlockInGui();
+ }
+}
+
+QQuickTrivialWindowManager::QQuickTrivialWindowManager()
+ : gl(0)
+ , eventPending(false)
+{
+ sg = QSGContext::createDefaultContext();
+}
+
+
+void QQuickTrivialWindowManager::show(QQuickCanvas *canvas)
+{
+ CanvasData data;
+ data.updatePending = false;
+ m_windows[canvas] = data;
+
+ maybeUpdate(canvas);
+}
+
+void QQuickTrivialWindowManager::hide(QQuickCanvas *canvas)
+{
+ if (!m_windows.contains(canvas))
+ return;
+
+ m_windows.remove(canvas);
+ QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
+ cd->cleanupNodesOnShutdown();
+
+ if (m_windows.size() == 0) {
+ sg->invalidate();
+ delete gl;
+ gl = 0;
+ }
+}
+
+void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas)
+{
+ hide(canvas);
+}
+
+void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas)
+{
+ if (!m_windows.contains(canvas))
+ return;
+
+ CanvasData &data = const_cast<CanvasData &>(m_windows[canvas]);
+
+ if (!gl) {
+ gl = new QOpenGLContext();
+ gl->setFormat(canvas->requestedFormat());
+ gl->create();
+ gl->makeCurrent(canvas);
+ sg->initialize(gl);
+ } else {
+ gl->makeCurrent(canvas);
+ }
+
+ bool alsoSwap = data.updatePending;
+ data.updatePending = false;
+
+ QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
+ cd->polishItems();
+ cd->syncSceneGraph();
+ cd->renderSceneGraph(canvas->size());
+
+ if (data.grabOnly) {
+ grabContent = qt_gl_read_framebuffer(canvas->size(), false, false);
+ data.grabOnly = false;
+ }
+
+ if (alsoSwap)
+ gl->swapBuffers(canvas);
+
+ // Might have been set during syncSceneGraph()
+ if (data.updatePending)
+ maybeUpdate(canvas);
+}
+
+void QQuickTrivialWindowManager::paint(QQuickCanvas *canvas)
+{
+ if (!m_windows.contains(canvas))
+ return;
+
+ m_windows[canvas].updatePending = true;
+ renderCanvas(canvas);
+}
+
+QImage QQuickTrivialWindowManager::grab(QQuickCanvas *canvas)
+{
+ if (!m_windows.contains(canvas))
+ return QImage();
+
+ m_windows[canvas].grabOnly = true;
+
+ renderCanvas(canvas);
+
+ QImage grabbed = grabContent;
+ grabContent = QImage();
+ return grabbed;
+}
+
+
+
+void QQuickTrivialWindowManager::resize(QQuickCanvas *, const QSize &)
+{
+}
+
+
+
+void QQuickTrivialWindowManager::maybeUpdate(QQuickCanvas *canvas)
+{
+ if (!m_windows.contains(canvas))
+ return;
+
+ m_windows[canvas].updatePending = true;
+
+ if (!eventPending) {
+ QCoreApplication::postEvent(this, new QEvent(QEvent::User));
+ eventPending = true;
+ }
+}
+
+
+
+bool *QQuickTrivialWindowManager::allowMainThreadProcessing()
+{
+ return 0;
+}
+
+
+
+QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const
+{
+ return sg;
+}
+
+
+bool QQuickTrivialWindowManager::event(QEvent *e)
+{
+ if (e->type() == QEvent::User) {
+ eventPending = false;
+ for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
+ it != m_windows.constEnd(); ++it) {
+ const CanvasData &data = it.value();
+ if (data.updatePending)
+ renderCanvas(it.key());
+ }
+ return true;
+ }
+ return QObject::event(e);
+}
+
+#include "qquickwindowmanager.moc"
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickwindowmanager_p.h b/src/quick/items/qquickwindowmanager_p.h
new file mode 100644
index 0000000000..8a5073effd
--- /dev/null
+++ b/src/quick/items/qquickwindowmanager_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKWINDOWMANAGER_P_H
+#define QQUICKWINDOWMANAGER_P_H
+
+#include <QtGui/QImage>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickCanvas;
+class QSGContext;
+
+class QQuickWindowManager
+{
+public:
+ virtual void show(QQuickCanvas *canvas) = 0;
+ virtual void hide(QQuickCanvas *canvas) = 0;
+
+ virtual void canvasDestroyed(QQuickCanvas *canvas) = 0;
+
+ virtual void paint(QQuickCanvas *canvas) = 0;
+ virtual QImage grab(QQuickCanvas *canvas) = 0;
+ virtual void resize(QQuickCanvas *canvas, const QSize &size) = 0;
+
+ virtual void maybeUpdate(QQuickCanvas *canvas) = 0;
+
+ virtual bool *allowMainThreadProcessing() = 0;
+
+ virtual QSGContext *sceneGraphContext() const = 0;
+
+ // ### make this less of a singleton
+ static QQuickWindowManager *instance();
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKWINDOWMANAGER_P_H
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index 93fea15921..b83e328158 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -39,30 +39,25 @@
**
****************************************************************************/
-#include "qsgcontext_p.h"
-#include <QtQuick/private/qsgrenderer_p.h>
-#include <QtQuick/qsgnode.h>
-
-#include <QtQuick/private/qdeclarativepixmapcache_p.h>
-
-#include <private/qsgdefaultrenderer_p.h>
-
+#include <QtQuick/private/qsgcontext_p.h>
+#include <QtQuick/private/qsgdefaultrenderer_p.h>
#include <QtQuick/private/qsgdistancefieldutil_p.h>
#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h>
-#include <private/qsgdefaultrectanglenode_p.h>
-#include <private/qsgdefaultimagenode_p.h>
-#include <private/qsgdefaultglyphnode_p.h>
-#include <private/qsgdistancefieldglyphnode_p.h>
-
+#include <QtQuick/private/qsgdefaultrectanglenode_p.h>
+#include <QtQuick/private/qsgdefaultimagenode_p.h>
+#include <QtQuick/private/qsgdefaultglyphnode_p.h>
+#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qdeclarativepixmapcache_p.h>
+
#include <QGuiApplication>
#include <QOpenGLContext>
#include <QDeclarativeImageProvider>
+#include <private/qdeclarativeglobal_p.h>
#include <private/qobject_p.h>
#include <qmutex.h>
-#include <private/qdeclarativeglobal_p.h>
DEFINE_BOOL_CONFIG_OPTION(qmlFlashMode, QML_FLASH_MODE)
DEFINE_BOOL_CONFIG_OPTION(qmlTranslucentMode, QML_TRANSLUCENT_MODE)
@@ -141,13 +136,24 @@ QSGContext::QSGContext(QObject *parent) :
QSGContext::~QSGContext()
{
+ invalidate();
+}
+
+
+
+void QSGContext::invalidate()
+{
Q_D(QSGContext);
qDeleteAll(d->textures.values());
d->textures.clear();
- delete d->renderer;
- delete d->rootNode;
qDeleteAll(d->materials.values());
+ d->materials.clear();
delete d->distanceFieldCacheManager;
+ d->distanceFieldCacheManager = 0;
+
+ d->gl = 0;
+
+ emit invalidated();
}
@@ -181,28 +187,6 @@ void QSGContext::textureFactoryDestroyed(QObject *o)
}
-
-/*!
- Returns the renderer. The renderer instance is created through the adaptation layer.
- */
-QSGRenderer *QSGContext::renderer() const
-{
- Q_D(const QSGContext);
- return d->renderer;
-}
-
-
-/*!
- Returns the root node. The root node instance is only created once the scene graph
- context becomes ready.
- */
-QSGRootNode *QSGContext::rootNode() const
-{
- Q_D(const QSGContext);
- return d->rootNode;
-}
-
-
QOpenGLContext *QSGContext::glContext() const
{
Q_D(const QSGContext);
@@ -218,16 +202,9 @@ void QSGContext::initialize(QOpenGLContext *context)
Q_D(QSGContext);
Q_ASSERT(!d->gl);
-
d->gl = context;
- d->renderer = createRenderer();
- d->renderer->setClearColor(Qt::white);
-
- d->rootNode = new QSGRootNode();
- d->renderer->setRootNode(d->rootNode);
-
- emit ready();
+ emit initialized();
}
@@ -242,15 +219,15 @@ bool QSGContext::isReady() const
}
-void QSGContext::renderNextFrame(QOpenGLFramebufferObject *fbo)
+void QSGContext::renderNextFrame(QSGRenderer *renderer, QOpenGLFramebufferObject *fbo)
{
Q_D(QSGContext);
if (fbo) {
QSGBindableFbo bindable(fbo);
- d->renderer->renderScene(bindable);
+ renderer->renderScene(bindable);
} else {
- d->renderer->renderScene();
+ renderer->renderScene();
}
}
@@ -344,7 +321,7 @@ QSGRenderer *QSGContext::createRenderer()
bool QSGContext::canDecodeImageToTexture() const
{
- return true;
+ return false;
}
diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h
index ded9d2727a..0d69a4fb47 100644
--- a/src/quick/scenegraph/qsgcontext_p.h
+++ b/src/quick/scenegraph/qsgcontext_p.h
@@ -56,6 +56,8 @@ QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE
+QT_MODULE(Declarative)
+
class QSGContextPrivate;
class QSGRectangleNode;
class QSGImageNode;
@@ -83,11 +85,7 @@ public:
~QSGContext();
virtual void initialize(QOpenGLContext *context);
-
- QSGRenderer *renderer() const;
-
- void setRootNode(QSGRootNode *node);
- QSGRootNode *rootNode() const;
+ virtual void invalidate();
QOpenGLContext *glContext() const;
@@ -95,7 +93,7 @@ public:
QSGMaterialShader *prepareMaterial(QSGMaterial *material);
- virtual void renderNextFrame(QOpenGLFramebufferObject *fbo = 0);
+ virtual void renderNextFrame(QSGRenderer *renderer, QOpenGLFramebufferObject *fbo = 0);
virtual QSGDistanceFieldGlyphCache *createDistanceFieldGlyphCache(const QRawFont &font);
@@ -106,8 +104,9 @@ public:
virtual bool canDecodeImageToTexture() const;
virtual QSGTexture *decodeImageToTexture(QIODevice *dev,
- QSize *size,
- const QSize &requestSize);
+ QSize *size,
+ const QSize &requestSize);
+
virtual QSGTexture *createTexture(const QImage &image = QImage()) const;
virtual QSize minimumFBOSize() const;
@@ -128,11 +127,13 @@ public:
virtual QAnimationDriver *createAnimationDriver(QObject *parent);
-signals:
- void ready();
public slots:
void textureFactoryDestroyed(QObject *o);
+
+signals:
+ void initialized();
+ void invalidated();
};
QT_END_NAMESPACE
diff --git a/tests/auto/qtquick2/qquickcanvas/data/AnimationsWhileHidden.qml b/tests/auto/qtquick2/qquickcanvas/data/AnimationsWhileHidden.qml
new file mode 100644
index 0000000000..e95b029210
--- /dev/null
+++ b/tests/auto/qtquick2/qquickcanvas/data/AnimationsWhileHidden.qml
@@ -0,0 +1,17 @@
+import QtQuick 2.0
+import QtQuick.Window 2.0 as Window
+
+Window.Window
+{
+ id: win
+ visible: true
+ width: 250
+ height: 250
+
+ SequentialAnimation {
+ PauseAnimation { duration: 500 }
+ PropertyAction { target: win; property: "visible"; value: true }
+ loops: Animation.Infinite
+ running: true
+ }
+}
diff --git a/tests/auto/qtquick2/qquickcanvas/qquickcanvas.pro b/tests/auto/qtquick2/qquickcanvas/qquickcanvas.pro
index c95d474a21..b4a4bc5d9c 100644
--- a/tests/auto/qtquick2/qquickcanvas/qquickcanvas.pro
+++ b/tests/auto/qtquick2/qquickcanvas/qquickcanvas.pro
@@ -6,3 +6,7 @@ macx:CONFIG -= app_bundle
CONFIG += parallel_test
QT += core-private gui-private declarative-private quick-private testlib
+
+OTHER_FILES += \
+ data/AnimationsWhileHidden.qml
+
diff --git a/tests/auto/qtquick2/qquickcanvas/tst_qquickcanvas.cpp b/tests/auto/qtquick2/qquickcanvas/tst_qquickcanvas.cpp
index f894ff3d39..60522b7d44 100644
--- a/tests/auto/qtquick2/qquickcanvas/tst_qquickcanvas.cpp
+++ b/tests/auto/qtquick2/qquickcanvas/tst_qquickcanvas.cpp
@@ -203,6 +203,11 @@ private slots:
void qmlCreation();
void clearColor();
+
+ void grab();
+ void multipleWindows();
+
+ void animationsWhileHidden();
};
tst_qquickcanvas::tst_qquickcanvas()
@@ -221,6 +226,7 @@ void tst_qquickcanvas::cleanupTestCase()
void tst_qquickcanvas::constantUpdates()
{
QQuickCanvas canvas;
+ canvas.resize(250, 250);
ConstantUpdateItem item(canvas.rootItem());
canvas.show();
QTRY_VERIFY(item.iterations > 60);
@@ -520,6 +526,8 @@ void tst_qquickcanvas::mouseFiltering()
QCOMPARE(middleItem->mousePressId, 1);
QCOMPARE(bottomItem->mousePressId, 2);
QCOMPARE(topItem->mousePressId, 3);
+
+ delete canvas;
}
void tst_qquickcanvas::qmlCreation()
@@ -552,6 +560,68 @@ void tst_qquickcanvas::clearColor()
delete canvas;
}
+void tst_qquickcanvas::grab()
+{
+ QQuickCanvas canvas;
+ canvas.setClearColor(Qt::red);
+
+ canvas.resize(250, 250);
+ canvas.show();
+
+ QImage content = canvas.grabFrameBuffer();
+ QCOMPARE(content.width(), canvas.width());
+ QCOMPARE(content.height(), canvas.height());
+ QCOMPARE((uint) content.convertToFormat(QImage::Format_RGB32).pixel(0, 0), (uint) 0xffff0000);
+}
+
+void tst_qquickcanvas::multipleWindows()
+{
+ QList<QQuickCanvas *> windows;
+ for (int i=0; i<6; ++i) {
+ QQuickCanvas *c = new QQuickCanvas();
+ c->setClearColor(Qt::GlobalColor(Qt::red + i));
+ c->resize(300, 200);
+ c->setPos(100 + i * 30, 100 + i * 20);
+ c->show();
+ windows << c;
+ QVERIFY(c->visible());
+ }
+
+ // move them
+ for (int i=0; i<windows.size(); ++i) {
+ QQuickCanvas *c = windows.at(i);
+ c->setPos(c->x() - 10, c->y() - 10);
+ }
+
+ // resize them
+ for (int i=0; i<windows.size(); ++i) {
+ QQuickCanvas *c = windows.at(i);
+ c->resize(200, 150);
+ }
+
+ qDeleteAll(windows);
+}
+
+void tst_qquickcanvas::animationsWhileHidden()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine);
+ component.loadUrl(TESTDATA("AnimationsWhileHidden.qml"));
+ QObject* created = component.create();
+
+ QQuickCanvas* canvas = qobject_cast<QQuickCanvas*>(created);
+ QVERIFY(canvas);
+ QVERIFY(canvas->visible());
+
+ // Now hide the window and verify that it went off screen
+ canvas->hide();
+ QTest::qWait(10);
+ QVERIFY(!canvas->visible());
+
+ // Running animaiton should cause it to become visible again shortly.
+ QTRY_VERIFY(canvas->visible());
+}
+
QTEST_MAIN(tst_qquickcanvas)
#include "tst_qquickcanvas.moc"
diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp
index 6cefc8a5de..a9a03aac6c 100644
--- a/tools/qmlscene/main.cpp
+++ b/tools/qmlscene/main.cpp
@@ -159,7 +159,6 @@ struct Options
bool scenegraphOnGraphicsview;
bool clip;
bool versionDetection;
- bool vsync;
};
#if defined(QMLSCENE_BUNDLE)
@@ -364,8 +363,6 @@ int main(int argc, char ** argv)
options.versionDetection = false;
else if (lowerArgument == QLatin1String("-i") && i + 1 < argc)
imports.append(QString::fromLatin1(argv[++i]));
- else if (lowerArgument == QLatin1String("--no-vsync-animations"))
- options.vsync = false;
else if (lowerArgument == QLatin1String("--help")
|| lowerArgument == QLatin1String("-help")
|| lowerArgument == QLatin1String("--h")
@@ -395,7 +392,6 @@ int main(int argc, char ** argv)
if (options.versionDetection)
checkAndAdaptVersion(options.file);
QQuickView *qxView = new MyQQuickView();
- qxView->setVSyncAnimations(options.vsync);
engine = qxView->engine();
for (int i = 0; i < imports.size(); ++i)
engine->addImportPath(imports.at(i));