diff options
25 files changed, 1543 insertions, 1374 deletions
diff --git a/src/quick/designer/designersupport.cpp b/src/quick/designer/designersupport.cpp index 924dfe9551..915c87e51e 100644 --- a/src/quick/designer/designersupport.cpp +++ b/src/quick/designer/designersupport.cpp @@ -47,7 +47,7 @@ #include <QtQml/private/qabstractanimationjob_p.h> #include <private/qqmlengine_p.h> #include <private/qquickview_p.h> -#include <private/qquickwindowmanager_p.h> +#include <private/qsgrenderloop_p.h> #include <QtQuick/private/qquickstategroup_p.h> #include <QtGui/QImage> #include <private/qqmlvme_p.h> @@ -428,7 +428,7 @@ void DesignerSupport::updateDirtyNode(QQuickItem *item) void DesignerSupport::activateDesignerWindowManager() { - QQuickWindowManager::setInstance(new DesignerWindowManager); + QSGRenderLoop::setInstance(new DesignerWindowManager); } void DesignerSupport::activateDesignerMode() diff --git a/src/quick/designer/designerwindowmanager_p.h b/src/quick/designer/designerwindowmanager_p.h index 0e95b06197..878d236314 100644 --- a/src/quick/designer/designerwindowmanager_p.h +++ b/src/quick/designer/designerwindowmanager_p.h @@ -55,7 +55,7 @@ #include <QtCore/QScopedPointer> -#include <private/qquickwindowmanager_p.h> +#include <private/qsgrenderloop_p.h> #include <private/qtquickglobal_p.h> #include <QtQuick/private/qsgcontext_p.h> @@ -69,7 +69,7 @@ class QSGContext; class QAnimationDriver; class QOpenGLContext; -class DesignerWindowManager : public QObject, public QQuickWindowManager +class DesignerWindowManager : public QObject, public QSGRenderLoop { Q_OBJECT public: @@ -88,7 +88,7 @@ public: void maybeUpdate(QQuickWindow *window); void update(QQuickWindow *window); // identical for this implementation. - void releaseResources() { } + void releaseResources(QQuickWindow *) { } QAnimationDriver *animationDriver() const { return 0; } diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index a46cd6ab70..c857a69f75 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -62,7 +62,6 @@ #include <private/qv8domerrors_p.h> #include <QtCore/qnumeric.h> #include <private/qquickwindow_p.h> -#include <private/qquickwindowmanager_p.h> #if defined(Q_OS_QNX) || defined(Q_OS_LINUX_ANDROID) #include <ctype.h> @@ -3348,7 +3347,6 @@ QQuickContext2D::QQuickContext2D(QObject *parent) : QQuickCanvasContext(parent) , m_buffer(new QQuickContext2DCommandBuffer) , m_v8engine(0) - , m_windowManager(0) , m_surface(0) , m_glContext(0) , m_thread(0) @@ -3380,7 +3378,6 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args m_renderTarget = canvasItem->renderTarget(); QQuickWindow *window = canvasItem->window(); - m_windowManager = QQuickWindowPrivate::get(window)->windowManager; m_renderStrategy = canvasItem->renderStrategy(); switch (m_renderTarget) { diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h index 2124c731b2..8d96ab579b 100644 --- a/src/quick/items/context2d/qquickcontext2d_p.h +++ b/src/quick/items/context2d/qquickcontext2d_p.h @@ -70,7 +70,6 @@ class QQuickContext2DCommandBuffer; class QQuickContext2DTexture; class QQuickPixmap; class QSGTexture; -class QQuickWindowManager; class QSurface; class QOpenGLContext; @@ -240,7 +239,6 @@ public: v8::Local<v8::Value> m_strokeStyle; v8::Handle<v8::Value> m_v8path; QV8Engine *m_v8engine; - QQuickWindowManager *m_windowManager; QSurface *m_surface; QOpenGLContext *m_glContext; v8::Persistent<v8::Object> m_v8value; diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 13abf7b958..5272a3d5f7 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -74,9 +74,7 @@ HEADERS += \ $$PWD/qquickitemview_p_p.h \ $$PWD/qquickitemviewtransition_p.h \ $$PWD/qquickscreen_p.h \ - $$PWD/qquickwindowmodule_p.h \ - $$PWD/qquickwindowmanager_p.h \ - $$PWD/qquickthreadedwindowmanager_p.h + $$PWD/qquickwindowmodule_p.h SOURCES += \ $$PWD/qquickevents.cpp \ @@ -128,9 +126,7 @@ SOURCES += \ $$PWD/qquickitemview.cpp \ $$PWD/qquickitemviewtransition.cpp \ $$PWD/qquickwindowmodule.cpp \ - $$PWD/qquickscreen.cpp \ - $$PWD/qquickwindowmanager.cpp \ - $$PWD/qquickthreadedwindowmanager.cpp + $$PWD/qquickscreen.cpp SOURCES += \ $$PWD/qquickshadereffect.cpp \ diff --git a/src/quick/items/qquickthreadedwindowmanager.cpp b/src/quick/items/qquickthreadedwindowmanager.cpp deleted file mode 100644 index 6c7b9c0448..0000000000 --- a/src/quick/items/qquickthreadedwindowmanager.cpp +++ /dev/null @@ -1,910 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module 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 "qquickwindowmanager_p.h" -#include "qquickthreadedwindowmanager_p.h" - -#include <QtCore/QTime> -#include <QtCore/QDebug> - -#include <QtGui/QOpenGLContext> -#include <QtGui/private/qguiapplication_p.h> -#include <qpa/qplatformintegration.h> - -#include <QtQml/private/qqmlglobal_p.h> - -#include <QtQuick/QQuickWindow> -#include <QtQuick/private/qquickwindow_p.h> - -QT_BEGIN_NAMESPACE - -//#define THREAD_DEBUG -extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); - -const QEvent::Type QEvent_Sync = QEvent::Type(QEvent::User); -const QEvent::Type QEvent_DeferredUpdate = QEvent::Type(QEvent::User + 1); - -#define QQUICK_RENDER_TIMING -#ifdef QQUICK_RENDER_TIMING -DEFINE_BOOL_CONFIG_OPTION(qquick_render_timing, QML_RENDER_TIMING) -static QTime threadTimer; -static int syncTime; -static int renderTime; -static int swapTime; -#endif - - -/* - 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::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. - */ - - -void QQuickRenderThreadSingleContextWindowManager::initialize() -{ - Q_ASSERT(m_rendered_windows.size()); - - QQuickWindow *win = 0; - for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin(); - it != m_rendered_windows.constEnd() && !win; ++it) { - if (QQuickWindowPrivate::get(it.key())->isRenderable()) - win = it.key(); - } - if (!win) - return; - - gl = new QOpenGLContext(); - // Pick up the surface format from one of them - gl->setFormat(win->requestedFormat()); - gl->create(); - if (!gl->makeCurrent(win)) - qWarning("QQuickWindow: makeCurrent() failed..."); - - Q_ASSERT(!sg->isReady()); - sg->initialize(gl); -} - - -/*! - This function is called when the window is created to register the window with - the window manager. - - Called on GUI Thread. - */ - -void QQuickRenderThreadSingleContextWindowManager::show(QQuickWindow *window) -{ -#ifdef THREAD_DEBUG - printf("GUI: Window added to windowing system, %p, %dx%d\n", window, window->width(), window->height()); -#endif - - WindowTracker tracker; - tracker.window = window; - tracker.isVisible = false; - tracker.toBeRemoved = false; - m_tracked_windows << tracker; - - connect(window, SIGNAL(widthChanged(int)), this, SLOT(windowVisibilityChanged()), Qt::DirectConnection); - connect(window, SIGNAL(heightChanged(int)), this, SLOT(windowVisibilityChanged()), Qt::DirectConnection); - - windowVisibilityChanged(); -} - - -void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickWindow *window) -{ -#ifdef THREAD_DEBUG - printf(" RenderThread: adding window: %p\n", window); -#endif - - WindowData *data = new WindowData; - data->sizeWasChanged = false; - data->windowSize = window->size(); - data->isVisible = window->isVisible(); - data->isRenderable = QQuickWindowPrivate::get(window)->isRenderable(); - m_rendered_windows[window] = data; - - isExternalUpdatePending = true; -} - - -/*! - 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()) { - QQuickWindow *window = m_added_windows.takeLast(); - handleAddedWindow(window); - } -} - - -/*! - Called on the GUI Thread, from the window' destructor - */ - -void QQuickRenderThreadSingleContextWindowManager::windowDestroyed(QQuickWindow *window) -{ -#ifdef THREAD_DEBUG - printf("GUI: Window destroyed: %p\n", window); -#endif - - hide(window); -} - - -/*! - Called on GUI Thread - */ - -void QQuickRenderThreadSingleContextWindowManager::hide(QQuickWindow *window) -{ -#ifdef THREAD_DEBUG - printf("GUI: Window hidden: %p\n", window); -#endif - - int position = -1; - for (int i=0; i<m_tracked_windows.size(); ++i) { - if (m_tracked_windows.at(i).window == window) { - m_tracked_windows[i].toBeRemoved = true; - position = i; - break; - } - } - - if (position >= 0) { - disconnect(window, SIGNAL(widthChanged(int)), this, SLOT(windowVisibilityChanged())); - disconnect(window, SIGNAL(heightChanged(int)), this, SLOT(windowVisibilityChanged())); - windowVisibilityChanged(); - m_tracked_windows.removeAt(position); - } - -#ifdef THREAD_DEBUG - printf("GUI: Window removal completed... %p\n", window); -#endif -} - -/*! - Called on Render Thread - */ -void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows(bool clearGLContext) -{ -#ifdef THREAD_DEBUG - printf(" RenderThread: about to remove %d\n", m_removed_windows.size()); -#endif - - bool removedAnything = false; - while (m_removed_windows.size()) { - QQuickWindow *window = m_removed_windows.takeLast(); -#ifdef THREAD_DEBUG - printf(" RenderThread: removing %p\n", window); -#endif - - QQuickWindowPrivate::get(window)->cleanupNodesOnShutdown(); - delete m_rendered_windows.take(window); - 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 && clearGLContext) - gl->doneCurrent(); -} - - - -/*! - Called on GUI Thread - */ - -void QQuickRenderThreadSingleContextWindowManager::windowVisibilityChanged() -{ - bool anyoneShowing = false; - QList<QQuickWindow *> toAdd, toRemove; - - // Not optimal, but also not frequently used... - for (int i=0; i<m_tracked_windows.size(); ++i) { - WindowTracker &t = const_cast<WindowTracker &>(m_tracked_windows.at(i)); - QQuickWindow *win = t.window; - - Q_ASSERT(win->isVisible() || QQuickWindowPrivate::get(win)->renderWithoutShowing || t.toBeRemoved); - bool windowVisible = win->width() > 0 && win->height() > 0; - anyoneShowing |= (windowVisible && !t.toBeRemoved); - - if ((!windowVisible && t.isVisible) || t.toBeRemoved) { - toRemove << win; - } else if (windowVisible && !t.isVisible) { - toAdd << win; - } - t.isVisible = windowVisible; - } - - 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 - - lock(); - Q_ASSERT(!gl); - initialize(); - // Wake GUI as it is waiting for the GL context to have appeared, as - // an indication that the render thread is now running. - wake(); - unlock(); - - if (!gl) - return; - - while (!shouldExit) { - lock(); - -#ifdef THREAD_DEBUG - printf(" RenderThread: *** NEW FRAME ***\n"); -#endif - - isExternalUpdatePending = false; - handleAddedWindows(); - - if (!isGuiLocked) { - isPostingSyncEvent = true; - -#ifdef THREAD_DEBUG - printf(" RenderThread: acquired sync lock...\n"); -#endif - QCoreApplication::postEvent(this, new QEvent(QEvent_Sync)); - -#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_RENDER_TIMING - if (qquick_render_timing()) - threadTimer.start(); -#endif - inSync = true; - for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin(); - it != m_rendered_windows.constEnd(); ++it) { - QQuickWindow *window = it.key(); - -#ifdef THREAD_DEBUG - printf(" RenderThread: Syncing window: %p\n", window); -#endif - - WindowData *windowData = it.value(); - QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window); - - windowData->isRenderable = windowPrivate->isRenderable(); - - if (windowData->isRenderable) { - gl->makeCurrent(window); - - if (windowData->viewportSize != windowData->windowSize) { -#ifdef THREAD_DEBUG - printf(" RenderThread: --- window has changed size...\n"); -#endif - windowData->viewportSize = windowData->windowSize; - windowData->sizeWasChanged = true; - glViewport(0, 0, windowData->viewportSize.width(), windowData->viewportSize.height()); - } - - windowPrivate->syncSceneGraph(); - } - } - inSync = false; - - // Wake GUI after sync to let it continue animating and event processing. - wake(); - unlock(); -#ifdef THREAD_DEBUG - printf(" RenderThread: sync done\n"); -#endif -#ifdef QQUICK_RENDER_TIMING - if (qquick_render_timing()) - syncTime = threadTimer.elapsed(); -#endif - - for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin(); - it != m_rendered_windows.constEnd(); ++it) { - QQuickWindow *window = it.key(); - WindowData *windowData = it.value(); - QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window); - - if (!windowData->isRenderable) - continue; - -#ifdef THREAD_DEBUG - printf(" RenderThread: Rendering window %p\n", window); -#endif - - Q_ASSERT(windowData->windowSize.width() > 0 && windowData->windowSize.height() > 0); - -#ifdef THREAD_DEBUG - printf(" RenderThread: --- rendering at size %dx%d\n", - windowData->viewportSize.width(), windowData->viewportSize.height() - ); -#endif - - // We only need to re-makeCurrent when we have multiple surfaces. - if (m_rendered_windows.size() > 1) - gl->makeCurrent(window); - - windowPrivate->renderSceneGraph(windowData->viewportSize); -#ifdef QQUICK_RENDER_TIMING - if (qquick_render_timing()) - renderTime = threadTimer.elapsed() - syncTime; -#endif - - // The content of the target buffer is undefined after swap() so grab needs - // to happen before swap(); - if (window == windowToGrab) { -#ifdef THREAD_DEBUG - printf(" RenderThread: --- grabbing...\n"); -#endif - grabContent = qt_gl_read_framebuffer(windowData->windowSize, false, false); - windowToGrab = 0; - } - -#ifdef THREAD_DEBUG - printf(" RenderThread: --- wait for swap...\n"); -#endif - - if (windowData->isVisible && window->isExposed()) - gl->swapBuffers(window); - - windowPrivate->fireFrameSwapped(); -#ifdef THREAD_DEBUG - printf(" RenderThread: --- swap complete...\n"); -#endif - - } - -#ifdef QQUICK_RENDER_TIMING - if (qquick_render_timing()) { - static QTime lastFrameTime = QTime::currentTime(); - swapTime = threadTimer.elapsed() - renderTime - syncTime; - qDebug() << "- Breakdown of frame time; sync:" << syncTime - << "ms render:" << renderTime << "ms swap:" << swapTime - << "ms total:" << swapTime + renderTime + syncTime - << "ms time since last frame:" << (lastFrameTime.msecsTo(QTime::currentTime())) - << "ms"; - lastFrameTime = QTime::currentTime(); - } -#endif - - lock(); - - handleRemovedWindows(); - - // Update sizes... - for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin(); - it != m_rendered_windows.constEnd(); ++it) { - WindowData *windowData = it.value(); - if (windowData->sizeWasChanged) { - windowData->renderedSize = windowData->viewportSize; - windowData->sizeWasChanged = false; - } - } - - - // 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 && !windowToGrab) { -#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(false); - - 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_Sync) { - - // If all windowes 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 - - animDriver->advance(); - -#ifdef THREAD_DEBUG - printf("GUI: Animations advanced...\n"); -#endif - } - - return true; - } else if (e->type() == QEvent_DeferredUpdate) { - handleDeferredUpdate(); - - } else if (e->type() == QEvent::Timer) { -#ifdef THREAD_DEBUG - printf("GUI: Animations advanced via timer...\n"); -#endif - animDriver->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(); - - for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin(); - it != m_rendered_windows.constEnd(); ++it) { - QQuickWindowPrivate::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: acquired 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::exposureChanged(QQuickWindow *window) -{ - Q_UNUSED(window); -#ifdef THREAD_DEBUG - printf("GUI: exposure changed: %p\n", window); -#endif - - if (window->isExposed()) - maybeUpdate(window); - -#ifdef THREAD_DEBUG - printf("GUI: exposure changed done: %p\n", window); -#endif -} - - - -void QQuickRenderThreadSingleContextWindowManager::resize(QQuickWindow *window, const QSize &size) -{ -#ifdef THREAD_DEBUG - printf("GUI: Resize Event: %p = %dx%d\n", window, size.width(), size.height()); -#endif - - // If the rendering thread is not running we do not need to do anything. - // Also if the window is being resized to an invalid size, it will be removed - // by the windowVisibilityChanged slot as result of width/heightcChanged() - if (!isRunning() || size.width() <= 0 || size.height() <= 0) - return; - - lockInGui(); - exhaustSyncEvent(); - - WindowData *windowData = m_rendered_windows.value(window); - if (windowData) { - windowData->windowSize = size; - while (isRunning() && windowData->renderedSize != size && size.width() > 0 && size.height() > 0) { - if (isRenderBlocked) - wake(); - wait(); - } - } - unlockInGui(); - -#ifdef THREAD_DEBUG - printf("GUI: Resize done: %p\n", window); -#endif -} - - - -void QQuickRenderThreadSingleContextWindowManager::startRendering() -{ -#ifdef THREAD_DEBUG - printf("GUI: Starting Render Thread\n"); -#endif - hasExited = false; - shouldExit = false; - isGuiLocked = 0; - isPostingSyncEvent = false; - syncAlreadyHappened = false; - inSync = false; - - lockInGui(); - animationRunning = animDriver->isRunning(); - start(); // Start the render thread... - wait(); - unlockInGui(); - - // 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 (animDriver->isRunning()) - animationTimer = startTimer(1000/60); -} - - - -QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickWindow *window) -{ - if (!isRunning()) - return QImage(); - - if (QThread::currentThread() != qApp->thread()) { - qWarning("QQuickWindow::grabFrameBuffer: can only be called from the GUI thread"); - return QImage(); - } else if (window->size().width() <= 0 || window->size().height() <= 0 ) { - qWarning("QQuickWindow::grabFrameBuffer: Can't grab a Window with size %dx%d", window->size().width(), window->size().height()); - return QImage(); - } - -#ifdef THREAD_DEBUG - printf("GUI: doing a pixelwise grab..\n"); -#endif - - lockInGui(); - exhaustSyncEvent(); - - windowToGrab = window; - while (isRunning() && windowToGrab) { - if (isRenderBlocked) - wake(); - wait(); - } - - QImage grabbed = grabContent; - grabContent = QImage(); - - unlockInGui(); - - return grabbed; -} - - -void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate() -{ -#ifdef THREAD_DEBUG - printf("GUI: handling update to ourselves...\n"); -#endif - - isDeferredUpdatePosted = false; - - lockInGui(); - isExternalUpdatePending = true; - if (isRenderBlocked) - wake(); - unlockInGui(); -} - -void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickWindow *) -{ - Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync, - "QQuickWindow::update", - "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()"); - - if (inSync) { - isExternalUpdatePending = true; - - } else if (!isDeferredUpdatePosted) { -#ifdef THREAD_DEBUG - printf("GUI: posting update to ourselves...\n"); -#endif - isDeferredUpdatePosted = true; - QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate)); - } - -} - -QT_END_NAMESPACE diff --git a/src/quick/items/qquickthreadedwindowmanager_p.h b/src/quick/items/qquickthreadedwindowmanager_p.h deleted file mode 100644 index 76325e2d4f..0000000000 --- a/src/quick/items/qquickthreadedwindowmanager_p.h +++ /dev/null @@ -1,181 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module 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$ -** -****************************************************************************/ - -#ifndef QQUICKTHREADEDWINDOWMANAGER_P_H -#define QQUICKTHREADEDWINDOWMANAGER_P_H - -#include "qquickwindowmanager_p.h" - -#include <QtCore/QCoreApplication> -#include <QtCore/QMutex> -#include <QtCore/QWaitCondition> -#include <QtCore/private/qabstractanimation_p.h> - -#include <QtGui/QOpenGLContext> -#include <QtQuick/private/qsgcontext_p.h> -#include <private/qtquickglobal_p.h> - -QT_BEGIN_NAMESPACE - -class QQuickRenderThreadSingleContextWindowManager : public QThread, public QQuickWindowManager -{ - Q_OBJECT -public: - QQuickRenderThreadSingleContextWindowManager() - : sg(QSGContext::createDefaultContext()) - , gl(0) - , animationTimer(-1) - , isGuiLocked(0) - , animationRunning(false) - , isPostingSyncEvent(false) - , isRenderBlocked(false) - , isExternalUpdatePending(false) - , syncAlreadyHappened(false) - , inSync(false) - , shouldExit(false) - , hasExited(false) - , isDeferredUpdatePosted(false) - , windowToGrab(0) - { - sg->moveToThread(this); - - animDriver = sg->createAnimationDriver(this); - animDriver->install(); - connect(animDriver, SIGNAL(started()), this, SLOT(animationStarted())); - connect(animDriver, SIGNAL(stopped()), this, SLOT(animationStopped())); - } - - QSGContext *sceneGraphContext() const { return sg; } - - void releaseResources() { } - - void show(QQuickWindow *window); - void hide(QQuickWindow *window); - - void windowDestroyed(QQuickWindow *window); - - void exposureChanged(QQuickWindow *window); - QImage grab(QQuickWindow *window); - void resize(QQuickWindow *window, const QSize &size); - void handleDeferredUpdate(); - void maybeUpdate(QQuickWindow *window); - void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation - - void startRendering(); - void stopRendering(); - - void exhaustSyncEvent(); - void sync(bool guiAlreadyLocked); - - void initialize(); - - 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(); - - QAnimationDriver *animationDriver() const { return animDriver; } - -public slots: - void animationStarted(); - void animationStopped(); - void windowVisibilityChanged(); - -private: - void handleAddedWindows(); - void handleAddedWindow(QQuickWindow *window); - void handleRemovedWindows(bool clearGLContext = true); - - QSGContext *sg; - QOpenGLContext *gl; - QAnimationDriver *animDriver; - int animationTimer; - - QMutex mutex; - QWaitCondition condition; - - int isGuiLocked; - uint animationRunning: 1; - uint isPostingSyncEvent : 1; - uint isRenderBlocked : 1; - uint isExternalUpdatePending : 1; - uint syncAlreadyHappened : 1; - uint inSync : 1; - uint shouldExit : 1; - uint hasExited : 1; - uint isDeferredUpdatePosted : 1; - - QQuickWindow *windowToGrab; - QImage grabContent; - - struct WindowData { - QSize renderedSize; - QSize windowSize; - QSize viewportSize; - - uint sizeWasChanged : 1; - uint isVisible : 1; - uint isRenderable : 1; - }; - - QHash<QQuickWindow *, WindowData *> m_rendered_windows; - - struct WindowTracker { - QQuickWindow *window; - uint isVisible : 1; - uint toBeRemoved : 1; - }; - - QList<WindowTracker> m_tracked_windows; - - QList<QQuickWindow *> m_removed_windows; - QList<QQuickWindow *> m_added_windows; -}; - -QT_END_NAMESPACE - -#endif // QQUICKTHREADEDWINDOWMANAGER_P_H diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 1b76dd7390..bf82423ca9 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -50,7 +50,7 @@ #include <QtQuick/private/qsgtexture_p.h> #include <QtQuick/private/qsgflashnode_p.h> -#include <private/qquickwindowmanager_p.h> +#include <private/qsgrenderloop_p.h> #include <private/qguiapplication_p.h> #include <QtGui/QInputMethod> @@ -366,8 +366,8 @@ QQuickWindowPrivate::QQuickWindowPrivate() , windowManager(0) , clearColor(Qt::white) , clearBeforeRendering(true) - , persistentGLContext(false) - , persistentSceneGraph(false) + , persistentGLContext(true) + , persistentSceneGraph(true) , lastWheelEventAccepted(false) , renderTarget(0) , renderTargetId(0) @@ -392,7 +392,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c) contentItemPrivate->windowRefCount = 1; contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope; - windowManager = QQuickWindowManager::instance(); + windowManager = QSGRenderLoop::instance(); context = windowManager->sceneGraphContext(); q->setSurfaceType(QWindow::OpenGLSurface); q->setFormat(context->defaultSurfaceFormat()); @@ -923,6 +923,8 @@ QQuickWindow::QQuickWindow(QWindow *parent) d->init(this); } + + /*! \internal */ @@ -966,20 +968,32 @@ QQuickWindow::~QQuickWindow() void QQuickWindow::releaseResources() { Q_D(QQuickWindow); - d->windowManager->releaseResources(); + d->windowManager->releaseResources(this); QQuickPixmap::purgeCache(); } /*! - Sets whether the OpenGL context can be released as a part of a call to - releaseResources() to \a persistent. + Sets whether the OpenGL context can be released to \a + persistent. The default value is true. + + The OpenGL context can be released to free up graphics resources + when the window is obscured, hidden or not rendering. When this + happens is implementation specific. - The OpenGL context might still be released when the user makes an explicit - call to hide(). + The QOpenGLContext::aboutToBeDestroyed() signal is emitted from + the QQuickWindow::openglContext() when the OpenGL context is about + to be released. The QQuickWindow::sceneGraphInitialized() signal + is emitted when a new OpenGL context is created for this + window. Make a Qt::DirectConnection to these signals to be + notified. - \sa setPersistentSceneGraph() + The OpenGL context is still released when the last QQuickWindow is + deleted. + + \sa setPersistentSceneGraph(), + QOpenGLContext::aboutToBeDestroyed(), sceneGraphInitialized() */ void QQuickWindow::setPersistentOpenGLContext(bool persistent) @@ -989,9 +1003,13 @@ void QQuickWindow::setPersistentOpenGLContext(bool persistent) } + /*! - Returns whether the OpenGL context can be released as a part of a call to - releaseResources(). + Returns whether the OpenGL context can be released during the + lifetime of the QQuickWindow. + + \note This is a hint. When and how this happens is implementation + specific. */ bool QQuickWindow::isPersistentOpenGLContext() const @@ -1003,13 +1021,24 @@ bool QQuickWindow::isPersistentOpenGLContext() const /*! - Sets whether the scene graph nodes and resources can be released as a - part of a call to releaseResources() to \a persistent. + Sets whether the scene graph nodes and resources can be released + to \a persistent. The default value is true. + + The scene graph nodes and resources can be released to free up + graphics resources when the window is obscured, hidden or not + rendering. When this happens is implementation specific. - The scene graph nodes and resources might still be released when the user - makes an explicit call to hide(). + The QQuickWindow::sceneGraphInvalidated() signal is emitted when + cleanup occurs. The QQuickWindow::sceneGraphInitialized() signal + is emitted when a new scene graph is recreated for this + window. Make a Qt::DirectConnection to these signals to be + notified. - \sa setPersistentOpenGLContext() + The scene graph nodes and resources are still released when the + last QQuickWindow is deleted. + + \sa setPersistentOpenGLContext(), + sceneGraphInvalidated(), sceneGraphInitialized() */ void QQuickWindow::setPersistentSceneGraph(bool persistent) @@ -1021,8 +1050,11 @@ void QQuickWindow::setPersistentSceneGraph(bool persistent) /*! - Returns whether the scene graph nodes and resources can be released as a part - of a call to releaseResources(). + Returns whether the scene graph nodes and resources can be + released during the lifetime of this QQuickWindow. + + \note This is a hint. When and how this happens is implementation + specific. */ bool QQuickWindow::isPersistentSceneGraph() const diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index da2ae8284d..f272f30f8e 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -73,7 +73,7 @@ QT_BEGIN_NAMESPACE //Make it easy to identify and customize the root item if needed -class QQuickWindowManager; +class QSGRenderLoop; class QQuickRootItem : public QQuickItem { @@ -200,7 +200,7 @@ public: QSGContext *context; QSGRenderer *renderer; - QQuickWindowManager *windowManager; + QSGRenderLoop *windowManager; QColor clearColor; diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index 1ae3ada3d8..cdc83d1dee 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -69,7 +69,7 @@ class QQuickWindow; class QSGTexture; class QSGMaterial; class QSGMaterialShader; -class QQuickWindowManager; +class QSGRenderLoop; class QOpenGLContext; class QOpenGLFramebufferObject; @@ -127,7 +127,7 @@ public: virtual QAnimationDriver *createAnimationDriver(QObject *parent); static QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image); - static QQuickWindowManager *createWindowManager(); + static QSGRenderLoop *createWindowManager(); public slots: diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp index 76b4e63816..545762aa72 100644 --- a/src/quick/scenegraph/qsgcontextplugin.cpp +++ b/src/quick/scenegraph/qsgcontextplugin.cpp @@ -157,7 +157,7 @@ QQuickTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &im specific window manager. */ -QQuickWindowManager *QSGContext::createWindowManager() +QSGRenderLoop *QSGContext::createWindowManager() { QSGAdaptionPluginData *plugin = contextFactory(); if (plugin->factory) diff --git a/src/quick/scenegraph/qsgcontextplugin_p.h b/src/quick/scenegraph/qsgcontextplugin_p.h index 4e5be6edce..036bc69c66 100644 --- a/src/quick/scenegraph/qsgcontextplugin_p.h +++ b/src/quick/scenegraph/qsgcontextplugin_p.h @@ -53,14 +53,14 @@ QT_BEGIN_NAMESPACE class QSGContext; -class QQuickWindowManager; +class QSGRenderLoop; struct Q_QUICK_PRIVATE_EXPORT QSGContextFactoryInterface : public QFactoryInterface { virtual QSGContext *create(const QString &key) const = 0; virtual QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image) = 0; - virtual QQuickWindowManager *createWindowManager() = 0; + virtual QSGRenderLoop *createWindowManager() = 0; }; #define QSGContextFactoryInterface_iid \ @@ -79,7 +79,7 @@ public: virtual QSGContext *create(const QString &key) const = 0; virtual QQuickTextureFactory *createTextureFactoryFromImage(const QImage &) { return 0; } - virtual QQuickWindowManager *createWindowManager() { return 0; } + virtual QSGRenderLoop *createWindowManager() { return 0; } }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickwindowmanager.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 69aa63c278..9117b2cffa 100644 --- a/src/quick/items/qquickwindowmanager.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#include "qquickwindowmanager_p.h" -#include "qquickthreadedwindowmanager_p.h" +#include "qsgrenderloop_p.h" +#include "qsgthreadedrenderloop_p.h" #include <QtCore/QCoreApplication> #include <QtCore/QTime> @@ -74,17 +74,17 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_ DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP); DEFINE_BOOL_CONFIG_OPTION(qmlForceThreadedRenderer, QML_FORCE_THREADED_RENDERER); // Might trigger graphics driver threading bugs, use at own risk -QQuickWindowManager *QQuickWindowManager::s_instance = 0; +QSGRenderLoop *QSGRenderLoop::s_instance = 0; -QQuickWindowManager::~QQuickWindowManager() +QSGRenderLoop::~QSGRenderLoop() { } -class QQuickTrivialWindowManager : public QObject, public QQuickWindowManager +class QSGGuiThreadRenderLoop : public QObject, public QSGRenderLoop { Q_OBJECT public: - QQuickTrivialWindowManager(); + QSGGuiThreadRenderLoop(); void show(QQuickWindow *window); void hide(QQuickWindow *window); @@ -100,7 +100,7 @@ public: void maybeUpdate(QQuickWindow *window); void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation. - void releaseResources() { } + void releaseResources(QQuickWindow *) { } QAnimationDriver *animationDriver() const { return 0; } @@ -124,7 +124,7 @@ public: }; -QQuickWindowManager *QQuickWindowManager::instance() +QSGRenderLoop *QSGRenderLoop::instance() { if (!s_instance) { @@ -153,20 +153,20 @@ QQuickWindowManager *QQuickWindowManager::instance() if (!s_instance) { s_instance = fancy - ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager - : (QQuickWindowManager*) new QQuickTrivialWindowManager; + ? (QSGRenderLoop*) new QSGThreadedRenderLoop + : (QSGRenderLoop*) new QSGGuiThreadRenderLoop; } } return s_instance; } -void QQuickWindowManager::setInstance(QQuickWindowManager *instance) +void QSGRenderLoop::setInstance(QSGRenderLoop *instance) { Q_ASSERT(!s_instance); s_instance = instance; } -QQuickTrivialWindowManager::QQuickTrivialWindowManager() +QSGGuiThreadRenderLoop::QSGGuiThreadRenderLoop() : gl(0) , eventPending(false) { @@ -174,7 +174,7 @@ QQuickTrivialWindowManager::QQuickTrivialWindowManager() } -void QQuickTrivialWindowManager::show(QQuickWindow *window) +void QSGGuiThreadRenderLoop::show(QQuickWindow *window) { WindowData data; data.updatePending = false; @@ -184,7 +184,7 @@ void QQuickTrivialWindowManager::show(QQuickWindow *window) maybeUpdate(window); } -void QQuickTrivialWindowManager::hide(QQuickWindow *window) +void QSGGuiThreadRenderLoop::hide(QQuickWindow *window) { if (!m_windows.contains(window)) return; @@ -200,12 +200,12 @@ void QQuickTrivialWindowManager::hide(QQuickWindow *window) } } -void QQuickTrivialWindowManager::windowDestroyed(QQuickWindow *window) +void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) { hide(window); } -void QQuickTrivialWindowManager::renderWindow(QQuickWindow *window) +void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) { bool renderWithoutShowing = QQuickWindowPrivate::get(window)->renderWithoutShowing; if ((!window->isExposed() && !renderWithoutShowing) || !m_windows.contains(window)) @@ -294,13 +294,13 @@ void QQuickTrivialWindowManager::renderWindow(QQuickWindow *window) maybeUpdate(window); } -void QQuickTrivialWindowManager::exposureChanged(QQuickWindow *window) +void QSGGuiThreadRenderLoop::exposureChanged(QQuickWindow *window) { if (window->isExposed()) maybeUpdate(window); } -QImage QQuickTrivialWindowManager::grab(QQuickWindow *window) +QImage QSGGuiThreadRenderLoop::grab(QQuickWindow *window) { if (!m_windows.contains(window)) return QImage(); @@ -316,13 +316,13 @@ QImage QQuickTrivialWindowManager::grab(QQuickWindow *window) -void QQuickTrivialWindowManager::resize(QQuickWindow *, const QSize &) +void QSGGuiThreadRenderLoop::resize(QQuickWindow *, const QSize &) { } -void QQuickTrivialWindowManager::maybeUpdate(QQuickWindow *window) +void QSGGuiThreadRenderLoop::maybeUpdate(QQuickWindow *window) { if (!m_windows.contains(window)) return; @@ -337,13 +337,13 @@ void QQuickTrivialWindowManager::maybeUpdate(QQuickWindow *window) -QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const +QSGContext *QSGGuiThreadRenderLoop::sceneGraphContext() const { return sg; } -bool QQuickTrivialWindowManager::event(QEvent *e) +bool QSGGuiThreadRenderLoop::event(QEvent *e) { if (e->type() == QEvent::User) { eventPending = false; @@ -358,6 +358,6 @@ bool QQuickTrivialWindowManager::event(QEvent *e) return QObject::event(e); } -#include "qquickwindowmanager.moc" +#include "qsgrenderloop.moc" QT_END_NAMESPACE diff --git a/src/quick/items/qquickwindowmanager_p.h b/src/quick/scenegraph/qsgrenderloop_p.h index 94142a9d90..fc705f48e8 100644 --- a/src/quick/items/qquickwindowmanager_p.h +++ b/src/quick/scenegraph/qsgrenderloop_p.h @@ -39,22 +39,24 @@ ** ****************************************************************************/ -#ifndef QQUICKWINDOWMANAGER_P_H -#define QQUICKWINDOWMANAGER_P_H +#ifndef QSGRenderLoop_P_H +#define QSGRenderLoop_P_H #include <QtGui/QImage> #include <private/qtquickglobal_p.h> +QT_BEGIN_HEADER + QT_BEGIN_NAMESPACE class QQuickWindow; class QSGContext; class QAnimationDriver; -class Q_QUICK_PRIVATE_EXPORT QQuickWindowManager +class Q_QUICK_PRIVATE_EXPORT QSGRenderLoop { public: - virtual ~QQuickWindowManager(); + virtual ~QSGRenderLoop(); virtual void show(QQuickWindow *window) = 0; virtual void hide(QQuickWindow *window) = 0; @@ -72,16 +74,18 @@ public: virtual QSGContext *sceneGraphContext() const = 0; - virtual void releaseResources() = 0; + virtual void releaseResources(QQuickWindow *window) = 0; // ### make this less of a singleton - static QQuickWindowManager *instance(); - static void setInstance(QQuickWindowManager *instance); + static QSGRenderLoop *instance(); + static void setInstance(QSGRenderLoop *instance); private: - static QQuickWindowManager *s_instance; + static QSGRenderLoop *s_instance; }; QT_END_NAMESPACE -#endif // QQUICKWINDOWMANAGER_P_H +QT_END_HEADER + +#endif // QSGRenderLoop_P_H diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp new file mode 100644 index 0000000000..5ec6de2f04 --- /dev/null +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -0,0 +1,1044 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module 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 <QtCore/QMutex> +#include <QtCore/QWaitCondition> +#include <QtCore/QAnimationDriver> + +#include <QtGui/QGuiApplication> +#include <QtGui/QScreen> + +#include <QtQuick/QQuickWindow> +#include <private/qquickwindow_p.h> + +#include <QtQuick/private/qsgrenderer_p.h> + +#include "qsgthreadedrenderloop_p.h" + +/* + Overall design: + + There are two classes here. QSGThreadedRenderLoop and + QSGRenderThread. All communication between the two is based on + event passing and we have a number of custom events. + + In this implementation, the render thread is never blocked and the + GUI thread will initiate a polishAndSync which will block and wait + for the render thread to pick it up and release the block only + after the render thread is done syncing. The reason for this + is: + + 1. Clear blocking paradigm. We only have one real "block" point + (polishAndSync()) and all blocking is initiated by GUI and picked + up by Render at specific times based on events. This makes the + execution deterministic. + + 2. Render does not have to interact with GUI. This is done so that + the render thread can run its own animation system which stays + alive even when the GUI thread is blocked doing i/o, object + instantiation, QPainter-painting or any other non-trivial task. + + --- + + 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 + obscured, we might end up cleaning up the SG and GL context (if all + windows have disabled persistency). Especially for multiprocess, + low-end systems, this should be quite important. + + */ + +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 + +#ifdef QSG_RENDER_LOOP_DEBUG_FULL +#define QSG_RENDER_LOOP_DEBUG_BASIC +#endif + +#if defined (QSG_RENDER_LOOP_DEBUG_FULL) +# define RLDEBUG1(x) qDebug("%s : %4d - %s", __FILE__, __LINE__, x); +# define RLDEBUG(x) qDebug("%s : %4d - %s", __FILE__, __LINE__, x); +#elif defined (QSG_RENDER_LOOP_DEBUG_BASIC) +# define RLDEBUG1(x) qDebug("%s : %4d - %s", __FILE__, __LINE__, x); +# define RLDEBUG(x) +#else +# define RLDEBUG1(x) +# define RLDEBUG(x) +#endif + + +static int get_env_int(const char *name, int defaultValue) +{ + QByteArray content = qgetenv(name); + + bool ok = false; + int value = content.toInt(&ok); + return ok ? value : defaultValue; +} + + +static inline int qsgrl_animation_interval() { + qreal refreshRate = QGuiApplication::primaryScreen()->refreshRate(); + // To work around that some platforms wrongfully return 0 or something + // bogus for refreshrate + if (refreshRate < 1) + return 16; + return int(1000 / refreshRate); +} + + +#define QQUICK_WINDOW_TIMING +#ifdef QQUICK_WINDOW_TIMING +static bool qquick_window_timing = !qgetenv("QML_WINDOW_TIMING").isEmpty(); +static QTime threadTimer; +static int syncTime; +static int renderTime; +static int sinceLastTime; +#endif + +extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); + +// RL: Render Loop +// RT: Render Thread + +// Passed from the RL to the RT when a window is rendeirng on screen +// and should be added to the render loop. +const QEvent::Type WM_Expose = QEvent::Type(QEvent::User + 1); + +// Passed from the RL to the RT when a window is removed obscured and +// should be removed from the render loop. +const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 2); + +// Passed from the RL to itself to initiate a polishAndSync() call. +const QEvent::Type WM_LockAndSync = QEvent::Type(QEvent::User + 3); + +// Passed from the RL to RT when GUI has been locked, waiting for sync +// (updatePaintNode()) +const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 4); + +// Passed by the RT to itself to trigger another render pass. This is +// typically a result of QQuickWindow::update(). +const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 5); + +// Passed by the RL to the RT when a window has changed size. +const QEvent::Type WM_Resize = QEvent::Type(QEvent::User + 6); + +// Passed by the RL to the RT to free up maybe release SG and GL contexts +// if no windows are rendering. +const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 7); + +// Passed by the RL to the RL when maybeUpdate is called on the RT to +// just replay the maybeUpdate later. This typically happens when +// updatePaintNode() results in a call to QQuickItem::update(). +const QEvent::Type WM_UpdateLater = QEvent::Type(QEvent::User + 8); + +// Passed by the RL to the RT when a QQuickWindow::grabWindow() is +// called. +const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 9); + +// Passed by the RT to the RL to trigger animations to be advanced. +const QEvent::Type WM_AdvanceAnimations = QEvent::Type(QEvent::User + 10); + +template <typename T> T *windowFor(const QList<T> list, QQuickWindow *window) +{ + for (int i=0; i<list.size(); ++i) { + const T &t = list.at(i); + if (t.window == window) + return const_cast<T *>(&t); + } + return 0; +} + + +class WMWindowEvent : public QEvent +{ +public: + WMWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { } + QQuickWindow *window; +}; + +class WMTryReleaseEvent : public WMWindowEvent +{ +public: + WMTryReleaseEvent(QQuickWindow *win, bool destroy) + : WMWindowEvent(win, WM_TryRelease) + , inDestructor(destroy) + {} + + bool inDestructor; +}; + +class WMResizeEvent : public WMWindowEvent +{ +public: + WMResizeEvent(QQuickWindow *c, const QSize &s) : WMWindowEvent(c, WM_Resize), size(s) { } + QSize size; +}; + + +class WMExposeEvent : public WMWindowEvent +{ +public: + WMExposeEvent(QQuickWindow *c) : WMWindowEvent(c, WM_Expose), size(c->size()) { } + QSize size; +}; + + +class WMGrabEvent : public WMWindowEvent +{ +public: + WMGrabEvent(QQuickWindow *c, QImage *result) : WMWindowEvent(c, WM_Grab), image(result) {} + QImage *image; +}; + + +class QSGRenderThread : public QThread +{ + Q_OBJECT +public: + + QSGRenderThread(QSGThreadedRenderLoop *w) + : wm(w) + , gl(0) + , sg(QSGContext::createDefaultContext()) + , pendingUpdate(0) + , sleeping(false) + , animationRunning(false) + , guiIsLocked(false) + , shouldExit(false) + , allowMainThreadProcessing(true) + , animationRequestsPending(0) + { + sg->moveToThread(this); + } + + + void invalidateOpenGL(QQuickWindow *window, bool inDestructor); + void initializeOpenGL(); + + bool event(QEvent *); + void run(); + + void syncAndRender(); + void sync(); + + void requestRepaint() + { + if (sleeping) + exit(); + if (m_windows.size() > 0) + pendingUpdate |= RepaintRequest; + } + +public slots: + void animationStarted() { + RLDEBUG(" Render: animationStarted()"); + animationRunning = true; + if (sleeping) + exit(); + } + + void animationStopped() { + RLDEBUG(" Render: animationStopped()"); + animationRunning = false; + } + +public: + enum UpdateRequest { + SyncRequest = 0x01, + RepaintRequest = 0x02 + }; + + QSGThreadedRenderLoop *wm; + QOpenGLContext *gl; + QSGContext *sg; + + QEventLoop eventLoop; + + uint pendingUpdate : 2; + uint sleeping : 1; + uint animationRunning : 1; + + volatile bool guiIsLocked; + volatile bool shouldExit; + + volatile bool allowMainThreadProcessing; + volatile int animationRequestsPending; + + QMutex mutex; + QWaitCondition waitCondition; + + QElapsedTimer m_timer; + + struct Window { + QQuickWindow *window; + QSize size; + }; + QList<Window> m_windows; +}; + +bool QSGRenderThread::event(QEvent *e) +{ + switch ((int) e->type()) { + + case WM_Expose: { + RLDEBUG1(" Render: WM_Expose"); + WMExposeEvent *se = static_cast<WMExposeEvent *>(e); + + if (windowFor(m_windows, se->window)) { + RLDEBUG1(" Render: - window already added..."); + return true; + } + + Window window; + window.window = se->window; + window.size = se->size; + m_windows << window; + 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); + break; + } + } + + if (sleeping && m_windows.size()) + exit(); + + return true; } + + case WM_RequestSync: + RLDEBUG(" Render: WM_RequestSync"); + if (sleeping) + exit(); + if (m_windows.size() > 0) + pendingUpdate |= SyncRequest; + return true; + + case WM_Resize: { + RLDEBUG(" Render: WM_Resize"); + WMResizeEvent *re = static_cast<WMResizeEvent *>(e); + Window *w = windowFor(m_windows, re->window); + w->size = re->size; + // No need to wake up here as we will get a sync shortly.. (see QSGThreadedRenderLoop::resize()); + return true; } + + case WM_TryRelease: + RLDEBUG1(" Render: WM_TryRelease"); + mutex.lock(); + if (m_windows.size() == 0) { + WMTryReleaseEvent *wme = static_cast<WMTryReleaseEvent *>(e); + RLDEBUG1(" Render: - setting exit flag and invalidating GL"); + invalidateOpenGL(wme->window, wme->inDestructor); + shouldExit = !gl; + if (sleeping) + exit(); + } else { + RLDEBUG1(" Render: - not releasing anything because we have active windows..."); + } + waitCondition.wakeOne(); + mutex.unlock(); + return true; + + case WM_Grab: { + RLDEBUG1(" Render: WM_Grab"); + WMGrabEvent *ce = static_cast<WMGrabEvent *>(e); + Window *w = windowFor(m_windows, ce->window); + mutex.lock(); + if (w) { + gl->makeCurrent(ce->window); + + RLDEBUG1(" Render: - syncing scene graph"); + QQuickWindowPrivate *d = QQuickWindowPrivate::get(w->window); + d->syncSceneGraph(); + + RLDEBUG1(" Render: - rendering scene graph"); + QQuickWindowPrivate::get(ce->window)->renderSceneGraph(w->size); + + RLDEBUG1(" Render: - grabbing result..."); + *ce->image = qt_gl_read_framebuffer(w->size, false, false); + } + RLDEBUG1(" Render: - waking gui to handle grab result"); + waitCondition.wakeOne(); + mutex.unlock(); + return true; + } + + default: + break; + } + return QThread::event(e); +} + +void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor) +{ + RLDEBUG1(" Render: invalidateOpenGL()"); + + if (!gl) + return; + + if (!window) { + qWarning("QSGThreadedRenderLoop:QSGRenderThread: no window to make current..."); + return; + } + + + 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(); + } + } + + gl->makeCurrent(window); + + // The canvas nodes must be cleanded up regardless if we are in the destructor.. + if (!persistentSG || inDestructor) { + QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window); + dd->cleanupNodesOnShutdown(); + } + + // We're not doing any cleanup in this case... + if (persistentSG) { + RLDEBUG1(" Render: - persistent SG, avoiding cleanup"); + return; + } + + sg->invalidate(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + gl->doneCurrent(); + RLDEBUG1(" Render: - invalidated scenegraph.."); + + if (!persistentGL) { + delete gl; + gl = 0; + RLDEBUG1(" Render: - invalidated OpenGL"); + } else { + RLDEBUG1(" Render: - persistent GL, avoiding cleanup"); + } +} + +void QSGRenderThread::initializeOpenGL() +{ + RLDEBUG1(" Render: initializeOpenGL()"); + QWindow *win = m_windows.at(0).window; + bool temp = false; + + // Workaround for broken expose logic... We should not get an + // expose when the size of a window is invalid, but we sometimes do. + // On Mac this leads to harmless, yet annoying, console warnings + if (m_windows.at(0).size.isEmpty()) { + temp = true; + win = new QWindow(); + win->setFormat(m_windows.at(0).window->requestedFormat()); + win->setSurfaceType(QWindow::OpenGLSurface); + win->setGeometry(0, 0, 64, 64); + win->create(); + } + + gl = new QOpenGLContext(); + // Pick up the surface format from one of them + gl->setFormat(win->requestedFormat()); + gl->create(); + if (!gl->makeCurrent(win)) + qWarning("QQuickWindow: makeCurrent() failed..."); + sg->initialize(gl); + + if (temp) { + delete win; + } +} + +/*! + Enters the mutex lock to make sure GUI is blocking and performs + sync, then wakes GUI. + */ +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..."); + pendingUpdate = 0; + + 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); + d->syncSceneGraph(); + } + + RLDEBUG(" Render: - unlocking after sync"); + + waitCondition.wakeOne(); + mutex.unlock(); +} + + +void QSGRenderThread::syncAndRender() +{ +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing) + sinceLastTime = threadTimer.restart(); +#endif + RLDEBUG(" Render: syncAndRender()"); + + // This animate request will get there after the sync + if (animationRunning && animationRequestsPending < 2) { + RLDEBUG(" Render: - posting animate to gui.."); + ++animationRequestsPending; + QCoreApplication::postEvent(wm, new QEvent(WM_AdvanceAnimations)); + + } + + if (pendingUpdate & SyncRequest) { + RLDEBUG(" Render: - update pending, doing sync"); + sync(); + } + +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing) + syncTime = threadTimer.elapsed(); +#endif + + 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); +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing && i == 0) + renderTime = threadTimer.elapsed(); +#endif + gl->swapBuffers(w.window); + d->fireFrameSwapped(); + } + RLDEBUG(" Render: - rendering done"); + +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing) + qDebug("window Time: sinceLast=%d, sync=%d, first render=%d, after final swap=%d", + sinceLastTime, + syncTime, + renderTime - syncTime, + threadTimer.elapsed() - renderTime); +#endif +} + +void QSGRenderThread::run() +{ + RLDEBUG1(" Render: run()"); + while (!shouldExit) { + + if (m_windows.size() > 0) { + if (!gl) + initializeOpenGL(); + if (!sg->isReady()) + sg->initialize(gl); + syncAndRender(); + } + + QCoreApplication::processEvents(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + + if (!shouldExit + && ((!animationRunning && pendingUpdate == 0) || m_windows.size() == 0)) { + RLDEBUG(" Render: enter event loop (going to sleep)"); + sleeping = true; + exec(); + sleeping = false; + } + + } + + Q_ASSERT_X(!gl, "QSGRenderThread::run()", "The OpenGL context should be cleaned up before exiting the render thread..."); + + RLDEBUG1(" Render: run() completed..."); +} + +QSGThreadedRenderLoop::QSGThreadedRenderLoop() + : m_animation_timer(0) + , m_update_timer(0) +{ + m_thread = new QSGRenderThread(this); + m_thread->moveToThread(m_thread); + + m_animation_driver = m_thread->sg->createAnimationDriver(this); + + m_exhaust_delay = get_env_int("QML_EXHAUST_DELAY", 5); + + connect(m_animation_driver, SIGNAL(started()), m_thread, SLOT(animationStarted())); + connect(m_animation_driver, SIGNAL(stopped()), m_thread, SLOT(animationStopped())); + connect(m_animation_driver, SIGNAL(started()), this, SLOT(animationStarted())); + connect(m_animation_driver, SIGNAL(stopped()), this, SLOT(animationStopped())); + + m_animation_driver->install(); + RLDEBUG1("GUI: QSGThreadedRenderLoop() created"); +} + +QAnimationDriver *QSGThreadedRenderLoop::animationDriver() const +{ + return m_animation_driver; +} + +QSGContext *QSGThreadedRenderLoop::sceneGraphContext() const +{ + return m_thread->sg; +} + +bool QSGThreadedRenderLoop::anyoneShowing() +{ + for (int i=0; i<m_windows.size(); ++i) { + QQuickWindow *c = m_windows.at(i).window; + if (c->isVisible() && c->isExposed()) + return true; + } + return false; +} + +void QSGThreadedRenderLoop::animationStarted() +{ + RLDEBUG("GUI: animationStarted()"); + if (!anyoneShowing() && m_animation_timer == 0) + m_animation_timer = startTimer(qsgrl_animation_interval()); +} + +void QSGThreadedRenderLoop::animationStopped() +{ + RLDEBUG("GUI: animationStopped()"); + if (!anyoneShowing()) { + killTimer(m_animation_timer); + m_animation_timer = 0; + } +} + + + +/* + Adds this window to the list of tracked windowes in this window + manager. show() does not trigger rendering to start, that happens + in expose. + */ + +void QSGThreadedRenderLoop::show(QQuickWindow *window) +{ + RLDEBUG1("GUI: show()"); + + Window win; + win.window = window; + win.pendingUpdate = false; + m_windows << win; +} + + + +/* + Removes this window from the list of tracked windowes in this + window manager. hide() will trigger obscure, which in turn will + stop rendering. + */ + +void QSGThreadedRenderLoop::hide(QQuickWindow *window) +{ + RLDEBUG1("GUI: hide()"); + + if (window->isExposed()) + handleObscurity(window); + + releaseResources(window); + + for (int i=0; i<m_windows.size(); ++i) { + if (m_windows.at(i).window == window) { + m_windows.removeAt(i); + break; + } + } +} + + +/*! + If the window is first hide it, then perform a complete cleanup + with releaseResources which will take down the GL context and + exit the rendering thread. + */ +void QSGThreadedRenderLoop::windowDestroyed(QQuickWindow *window) +{ + RLDEBUG1("GUI: windowDestroyed()"); + + if (window->isVisible()) + hide(window); + releaseResources(window, true); + + RLDEBUG1("GUI: - done with windowDestroyed()"); +} + + +void QSGThreadedRenderLoop::exposureChanged(QQuickWindow *window) +{ + RLDEBUG1("GUI: exposureChanged()"); + if (windowFor(m_windows, window) == 0) + return; + + if (window->isExposed()) { + handleExposure(window); + } else { + handleObscurity(window); + } +} + + +/*! + Will post an event to the render thread that this window should + start to render. + */ +void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window) +{ + RLDEBUG1("GUI: handleExposure"); + + // Because we are going to bind a GL context to it, make sure it + // is created. + if (!window->handle()) + window->create(); + + QCoreApplication::postEvent(m_thread, new WMExposeEvent(window)); + + // Start render thread if it is not running + if (!m_thread->isRunning()) { + m_thread->shouldExit = false; + m_thread->animationRunning = m_animation_driver->isRunning(); + + RLDEBUG1("GUI: - starting render thread..."); + m_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; + } + +} + +/*! + This function posts an event to the render thread to remove the window + from the list of windowses to render. + + It also starts up the non-vsync animation tick if no more windows + are showing. + */ +void QSGThreadedRenderLoop::handleObscurity(QQuickWindow *window) +{ + RLDEBUG1("GUI: handleObscurity"); + if (m_thread->isRunning()) + QCoreApplication::postEvent(m_thread, new WMWindowEvent(window, WM_Obscure)); + + if (!anyoneShowing() && m_animation_driver->isRunning() && m_animation_timer == 0) { + m_animation_timer = startTimer(qsgrl_animation_interval()); + } +} + + +/*! + Called whenever the QML scene has changed. Will post an event to + ourselves that a sync is needed. + */ +void QSGThreadedRenderLoop::maybeUpdate(QQuickWindow *window) +{ + Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || m_thread->guiIsLocked, + "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 || w->pendingUpdate || !m_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) { + RLDEBUG("GUI: - on render thread, posting update later"); + QCoreApplication::postEvent(this, new WMWindowEvent(window, WM_UpdateLater)); + return; + } + + + w->pendingUpdate = true; + + if (m_update_timer > 0) { + return; + } + + RLDEBUG("GUI: - posting update"); + m_update_timer = startTimer(m_animation_driver->isRunning() ? m_exhaust_delay : 0, Qt::PreciseTimer); +} + +/*! + Called when the QQuickWindow should be explicitly repainted. This function + can also be called on the render thread when the GUI thread is blocked to + keep render thread animations alive. + */ +void QSGThreadedRenderLoop::update(QQuickWindow *window) +{ + if (QThread::currentThread() == m_thread) { + RLDEBUG("Gui: update called on render thread"); + m_thread->requestRepaint(); + return; + } + + RLDEBUG("Gui: update called"); + maybeUpdate(window); +} + + + +/*! + * Release resources will post an event to the render thread to + * free up the SG and GL resources and exists the render thread. + */ +void QSGThreadedRenderLoop::releaseResources(QQuickWindow *window, bool inDestructor) +{ + RLDEBUG1("GUI: releaseResources requested..."); + + m_thread->mutex.lock(); + if (m_thread->isRunning() && !m_thread->shouldExit) { + RLDEBUG1("GUI: - posting release request to render thread"); + QCoreApplication::postEvent(m_thread, new WMTryReleaseEvent(window, inDestructor)); + m_thread->waitCondition.wait(&m_thread->mutex); + } + m_thread->mutex.unlock(); +} + + + +void QSGThreadedRenderLoop::polishAndSync() +{ + if (!anyoneShowing()) + return; + +#ifdef QQUICK_WINDOW_TIMING + QElapsedTimer timer; + int polishTime; + int waitTime; + if (qquick_window_timing) + timer.start(); +#endif + RLDEBUG("GUI: polishAndSync()"); + // 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(); + } +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing) + polishTime = timer.elapsed(); +#endif + + RLDEBUG("GUI: - clearing update flags..."); + for (int i=0; i<m_windows.size(); ++i) { + m_windows[i].pendingUpdate = false; + } + + RLDEBUG("GUI: - lock for sync..."); + m_thread->mutex.lock(); + m_thread->guiIsLocked = true; + QEvent *event = new QEvent(WM_RequestSync); + + QCoreApplication::postEvent(m_thread, event); + RLDEBUG("GUI: - wait for sync..."); +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing) + waitTime = timer.elapsed(); +#endif + m_thread->waitCondition.wait(&m_thread->mutex); + m_thread->guiIsLocked = false; + m_thread->mutex.unlock(); + RLDEBUG("GUI: - unlocked after sync..."); + +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing) + qDebug(" - polish=%d, wait=%d, sync=%d", polishTime, waitTime - polishTime, int(timer.elapsed() - waitTime)); +#endif +} + +bool QSGThreadedRenderLoop::event(QEvent *e) +{ + switch ((int) e->type()) { + + case QEvent::Timer: + if (static_cast<QTimerEvent *>(e)->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) { + RLDEBUG("Gui: QEvent::Timer -> polishAndSync()"); + killTimer(m_update_timer); + m_update_timer = 0; + polishAndSync(); + } + return true; + + case WM_UpdateLater: { + QQuickWindow *window = static_cast<WMWindowEvent *>(e)->window; + // The window might have gone away... + if (windowFor(m_windows, window)) + maybeUpdate(window); + return true; } + + case WM_AdvanceAnimations: + --m_thread->animationRequestsPending; + RLDEBUG("GUI: WM_AdvanceAnimations"); + if (m_animation_driver->isRunning()) { +#ifdef QQUICK_CANVAS_TIMING + QElapsedTimer timer; + timer.start(); +#endif + m_animation_driver->advance(); + RLDEBUG("GUI: - animations advanced.."); +#ifdef QQUICK_CANVAS_TIMING + if (qquick_canvas_timing) + qDebug(" - animation: %d", (int) timer.elapsed()); +#endif + } + return true; + + default: + break; + } + + return QObject::event(e); +} + + + +/* + Locks down GUI and performs a grab the scene graph, then returns the result. + + Since the QML scene could have changed since the last time it was rendered, + we need to polish and sync the scene graph. This might seem superfluous, but + - QML changes could have triggered deleteLater() which could have removed + textures or other objects from the scene graph, causing render to crash. + - Autotests rely on grab(), setProperty(), grab(), compare behavior. + */ + +QImage QSGThreadedRenderLoop::grab(QQuickWindow *window) +{ + RLDEBUG("GUI: grab"); + if (!m_thread->isRunning()) + return QImage(); + + if (!window->handle()) + window->create(); + + RLDEBUG1("GUI: - polishing items..."); + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + d->polishItems(); + + QImage result; + m_thread->mutex.lock(); + RLDEBUG1("GUI: - locking, posting grab event"); + QCoreApplication::postEvent(m_thread, new WMGrabEvent(window, &result)); + m_thread->waitCondition.wait(&m_thread->mutex); + RLDEBUG1("GUI: - locking, grab done, unlocking"); + m_thread->mutex.unlock(); + + RLDEBUG1("Gui: - grab complete"); + + return result; +} + +/* + Notify the render thread that the window is now a new size. Then + locks GUI until render has adapted. + */ + +void QSGThreadedRenderLoop::resize(QQuickWindow *w, const QSize &size) +{ + RLDEBUG1("GUI: resize"); + + if (!m_thread->isRunning() || !m_windows.size() || !w->isExposed() || windowFor(m_windows, w) == 0) { + return; + } + + if (size.width() == 0 || size.height() == 0) + return; + + RLDEBUG("GUI: - posting resize event..."); + WMResizeEvent *e = new WMResizeEvent(w, size); + QCoreApplication::postEvent(m_thread, e); + + polishAndSync(); +} + +#include "qsgthreadedrenderloop.moc" + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h new file mode 100644 index 0000000000..fd3ab657d3 --- /dev/null +++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module 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$ +** +****************************************************************************/ + +#ifndef QSGTHREADEDRENDERLOOP_P_H +#define QSGTHREADEDRENDERLOOP_P_H + +#include <QtCore/QThread> +#include <QtGui/QOpenGLContext> +#include <private/qsgcontext_p.h> + +#include "qsgrenderloop_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGRenderThread; + +class QSGThreadedRenderLoop : public QObject, public QSGRenderLoop +{ + Q_OBJECT +public: + QSGThreadedRenderLoop(); + + void show(QQuickWindow *window); + void hide(QQuickWindow *window); + + void windowDestroyed(QQuickWindow *window); + void exposureChanged(QQuickWindow *window); + + void handleExposure(QQuickWindow *window); + void handleObscurity(QQuickWindow *window); + + QImage grab(QQuickWindow *); + + void resize(QQuickWindow *, const QSize &); + + void update(QQuickWindow *window); + void maybeUpdate(QQuickWindow *window); + QSGContext *sceneGraphContext() const; + + QAnimationDriver *animationDriver() const; + + void releaseResources(QQuickWindow *window) { releaseResources(window, false); } + + bool event(QEvent *); + + void wakeup(); + +public slots: + void animationStarted(); + void animationStopped(); + +private: + friend class QSGRenderThread; + + void releaseResources(QQuickWindow *window, bool inDestructor); + bool checkAndResetForceUpdate(QQuickWindow *window); + + bool anyoneShowing(); + void initialize(); + + void waitForReleaseComplete(); + + void polishAndSync(); + + struct Window { + QQuickWindow *window; + uint pendingUpdate : 1; + }; + + QSGRenderThread *m_thread; + QAnimationDriver *m_animation_driver; + QList<Window> m_windows; + + int m_animation_timer; + int m_update_timer; + int m_exhaust_delay; +}; + + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGTHREADEDRENDERLOOP_P_H diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index a58e9b46ad..8c87e23ac9 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -65,7 +65,10 @@ HEADERS += \ $$PWD/qsgdefaultimagenode_p.h \ $$PWD/qsgdefaultrectanglenode_p.h \ $$PWD/qsgflashnode_p.h \ - $$PWD/qsgshareddistancefieldglyphcache_p.h + $$PWD/qsgshareddistancefieldglyphcache_p.h \ + $$PWD/qsgrenderloop_p.h \ + $$PWD/qsgthreadedrenderloop_p.h + SOURCES += \ $$PWD/qsgadaptationlayer.cpp \ @@ -79,7 +82,10 @@ SOURCES += \ $$PWD/qsgdefaultimagenode.cpp \ $$PWD/qsgdefaultrectanglenode.cpp \ $$PWD/qsgflashnode.cpp \ - $$PWD/qsgshareddistancefieldglyphcache.cpp + $$PWD/qsgshareddistancefieldglyphcache.cpp \ + $$PWD/qsgrenderloop.cpp \ + $$PWD/qsgthreadedrenderloop.cpp + diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp index 7055fb6e03..9c5fb88af2 100644 --- a/tests/auto/quick/examples/tst_examples.cpp +++ b/tests/auto/quick/examples/tst_examples.cpp @@ -82,11 +82,9 @@ private: QStringList findQmlFiles(const QDir &); QQmlEngine engine; - - QQuickWindow *window; }; -tst_examples::tst_examples() : window(0) +tst_examples::tst_examples() { // Add files to exclude here excludedFiles << "examples/quick/canvas/tiger/tiger.qml"; // QTBUG-26528 @@ -94,7 +92,7 @@ tst_examples::tst_examples() : window(0) // Add directories you want excluded here (don't add examples/, because they install to examples/qtdeclarative/) excludedDirs << "shared"; //Not an example - excludedDirs << "qtquick/text/fonts"; // QTBUG-21415 + excludedDirs << "quick/text/fonts"; // QTBUG-29004 excludedDirs << "snippets/qml/path"; //No root QQuickItem excludedDirs << "tutorials/gettingStartedQml"; //C++ example, but no cpp files in root dir @@ -120,7 +118,6 @@ tst_examples::tst_examples() : window(0) tst_examples::~tst_examples() { - delete window; } void tst_examples::init() @@ -260,6 +257,9 @@ void tst_examples::sgexamples_data() void tst_examples::sgexamples() { QFETCH(QString, file); + QQuickWindow window; + window.setPersistentOpenGLContext(true); + window.setPersistentSceneGraph(true); QQmlComponent component(&engine, QUrl::fromLocalFile(file)); if (component.status() == QQmlComponent::Error) @@ -272,13 +272,11 @@ void tst_examples::sgexamples() component.completeCreate(); QVERIFY(root); - if (!window) { - window = new QQuickWindow(); - window->resize(240, 320); - window->show(); - QVERIFY(QTest::qWaitForWindowExposed(window)); - } - root->setParentItem(window->contentItem()); + window.resize(240, 320); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + root->setParentItem(window.contentItem()); component.completeCreate(); qApp->processEvents(); @@ -303,6 +301,8 @@ void tst_examples::sgsnippets_data() void tst_examples::sgsnippets() { + QQuickWindow window; + QFETCH(QString, file); QQmlComponent component(&engine, QUrl::fromLocalFile(file)); @@ -316,13 +316,11 @@ void tst_examples::sgsnippets() component.completeCreate(); QVERIFY(root); - if (!window) { - window = new QQuickWindow(); - window->resize(240, 320); - window->show(); - QVERIFY(QTest::qWaitForWindowExposed(window)); - } - root->setParentItem(window->contentItem()); + window.resize(240, 320); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + root->setParentItem(window.contentItem()); component.completeCreate(); qApp->processEvents(); diff --git a/tests/auto/quick/qquickflickable/qquickflickable.pro b/tests/auto/quick/qquickflickable/qquickflickable.pro index f61a601130..67d5fc12f0 100644 --- a/tests/auto/quick/qquickflickable/qquickflickable.pro +++ b/tests/auto/quick/qquickflickable/qquickflickable.pro @@ -9,7 +9,6 @@ include (../shared/util.pri) TESTDATA = data/* -CONFIG += parallel_test QT += core-private gui-private v8-private qml-private quick-private testlib DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 CONFIG+=insignificant_test diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 662e86018c..5226434c14 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -208,10 +208,12 @@ void tst_qquickflickable::boundsBehavior() void tst_qquickflickable::rebound() { - QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("rebound.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); @@ -227,7 +229,7 @@ void tst_qquickflickable::rebound() QSignalSpy hMoveSpy(flickable, SIGNAL(movingHorizontallyChanged())); // flick and test the transition is run - flick(window, QPoint(20,20), QPoint(120,120), 200); + flick(window.data(), QPoint(20,20), QPoint(120,120), 200); QTRY_COMPARE(window->rootObject()->property("transitionsStarted").toInt(), 2); QCOMPARE(hMoveSpy.count(), 1); @@ -257,14 +259,13 @@ void tst_qquickflickable::rebound() #ifdef Q_OS_MAC QSKIP("QTBUG-26696 - sometimes fails on Mac"); - delete window; return; #endif // flick and trigger the transition multiple times // (moving signals are emitted as soon as the first transition starts) - flick(window, QPoint(20,20), QPoint(120,120), 200); // both x and y will bounce back - flick(window, QPoint(20,120), QPoint(120,20), 200); // only x will bounce back + flick(window.data(), QPoint(20,20), QPoint(120,120), 200); // both x and y will bounce back + flick(window.data(), QPoint(20,120), QPoint(120,20), 200); // only x will bounce back QVERIFY(flickable->isMoving()); QVERIFY(window->rootObject()->property("transitionsStarted").toInt() >= 1); @@ -293,7 +294,7 @@ void tst_qquickflickable::rebound() // (i.e. moving but transition->running = false) window->rootObject()->setProperty("transitionEnabled", false); - flick(window, QPoint(20,20), QPoint(120,120), 200); + flick(window.data(), QPoint(20,20), QPoint(120,120), 200); QCOMPARE(window->rootObject()->property("transitionsStarted").toInt(), 0); QCOMPARE(hMoveSpy.count(), 1); QCOMPARE(vMoveSpy.count(), 1); @@ -306,8 +307,6 @@ void tst_qquickflickable::rebound() QCOMPARE(movementStartedSpy.count(), 1); QCOMPARE(movementEndedSpy.count(), 1); QCOMPARE(window->rootObject()->property("transitionsStarted").toInt(), 0); - - delete window; } void tst_qquickflickable::maximumFlickVelocity() @@ -353,7 +352,9 @@ void tst_qquickflickable::pressDelay() QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("pressDelay.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); @@ -392,7 +393,9 @@ void tst_qquickflickable::nestedPressDelay() QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("nestedPressDelay.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *outer = qobject_cast<QQuickFlickable*>(window->rootObject()); @@ -501,7 +504,8 @@ void tst_qquickflickable::returnToBounds() { QFETCH(bool, setRebound); - QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> window(new QQuickView); + window->rootContext()->setContextProperty("setRebound", setRebound); window->setSource(testFileUrl("resize.qml")); QVERIFY(window->rootObject() != 0); @@ -532,8 +536,6 @@ void tst_qquickflickable::returnToBounds() QVERIFY(!rebound->running()); QCOMPARE(reboundSpy.count(), setRebound ? 2 : 0); - - delete window; } void tst_qquickflickable::returnToBounds_data() @@ -546,10 +548,12 @@ void tst_qquickflickable::returnToBounds_data() void tst_qquickflickable::wheel() { - QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("wheel.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flick = window->rootObject()->findChild<QQuickFlickable*>("flick"); @@ -559,7 +563,7 @@ void tst_qquickflickable::wheel() QPoint pos(200, 200); QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(0,-120), -120, Qt::Vertical, Qt::NoButton, Qt::NoModifier); event.setAccepted(false); - QGuiApplication::sendEvent(window, &event); + QGuiApplication::sendEvent(window.data(), &event); } QTRY_VERIFY(flick->contentY() > 0); @@ -573,13 +577,11 @@ void tst_qquickflickable::wheel() QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(-120,0), -120, Qt::Horizontal, Qt::NoButton, Qt::NoModifier); event.setAccepted(false); - QGuiApplication::sendEvent(window, &event); + QGuiApplication::sendEvent(window.data(), &event); } QTRY_VERIFY(flick->contentX() > 0); QVERIFY(flick->contentY() == 0); - - delete window; } void tst_qquickflickable::movingAndFlicking_data() @@ -614,11 +616,12 @@ void tst_qquickflickable::movingAndFlicking() const QPoint flickFrom(50, 200); // centre - QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("flickable03.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); - QTest::qWaitForWindowActive(window); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); @@ -637,9 +640,9 @@ void tst_qquickflickable::movingAndFlicking() QSignalSpy flickEndSpy(flickable, SIGNAL(flickEnded())); // do a flick that keeps the view within the bounds - flick(window, flickFrom, flickToWithoutSnapBack, 200); + flick(window.data(), flickFrom, flickToWithoutSnapBack, 200); - QVERIFY(flickable->isMoving()); + QTRY_VERIFY(flickable->isMoving()); QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); QCOMPARE(flickable->isMovingVertically(), verticalEnabled); QVERIFY(flickable->isFlicking()); @@ -693,9 +696,9 @@ void tst_qquickflickable::movingAndFlicking() flickable->setContentX(0); flickable->setContentY(0); QTRY_VERIFY(!flickable->isMoving()); - flick(window, flickFrom, flickToWithSnapBack, 200); + flick(window.data(), flickFrom, flickToWithSnapBack, 10); - QVERIFY(flickable->isMoving()); + QTRY_VERIFY(flickable->isMoving()); QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); QCOMPARE(flickable->isMovingVertically(), verticalEnabled); QVERIFY(flickable->isFlicking()); @@ -737,8 +740,6 @@ void tst_qquickflickable::movingAndFlicking() QCOMPARE(flickable->contentX(), 0.0); QCOMPARE(flickable->contentY(), 0.0); - - delete window; } @@ -774,11 +775,12 @@ void tst_qquickflickable::movingAndDragging() const QPoint moveFrom(50, 200); // centre - QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("flickable03.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); @@ -797,10 +799,10 @@ void tst_qquickflickable::movingAndDragging() QSignalSpy moveEndSpy(flickable, SIGNAL(movementEnded())); // start the drag - QTest::mousePress(window, Qt::LeftButton, 0, moveFrom); - QTest::mouseMove(window, moveFrom + moveByWithoutSnapBack); - QTest::mouseMove(window, moveFrom + moveByWithoutSnapBack*2); - QTest::mouseMove(window, moveFrom + moveByWithoutSnapBack*3); + QTest::mousePress(window.data(), Qt::LeftButton, 0, moveFrom); + QTest::mouseMove(window.data(), moveFrom + moveByWithoutSnapBack); + QTest::mouseMove(window.data(), moveFrom + moveByWithoutSnapBack*2); + QTest::mouseMove(window.data(), moveFrom + moveByWithoutSnapBack*3); QTRY_VERIFY(flickable->isMoving()); QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); @@ -819,7 +821,7 @@ void tst_qquickflickable::movingAndDragging() QCOMPARE(moveStartSpy.count(), 1); QCOMPARE(dragStartSpy.count(), 1); - QTest::mouseRelease(window, Qt::LeftButton, 0, moveFrom + moveByWithoutSnapBack*3); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, moveFrom + moveByWithoutSnapBack*3); QVERIFY(!flickable->isDragging()); QVERIFY(!flickable->isDraggingHorizontally()); @@ -868,10 +870,10 @@ void tst_qquickflickable::movingAndDragging() flickable->setContentX(0); flickable->setContentY(0); QTRY_VERIFY(!flickable->isMoving()); - QTest::mousePress(window, Qt::LeftButton, 0, moveFrom); - QTest::mouseMove(window, moveFrom + moveByWithSnapBack); - QTest::mouseMove(window, moveFrom + moveByWithSnapBack*2); - QTest::mouseMove(window, moveFrom + moveByWithSnapBack*3); + QTest::mousePress(window.data(), Qt::LeftButton, 0, moveFrom); + QTest::mouseMove(window.data(), moveFrom + moveByWithSnapBack); + QTest::mouseMove(window.data(), moveFrom + moveByWithSnapBack*2); + QTest::mouseMove(window.data(), moveFrom + moveByWithSnapBack*3); QVERIFY(flickable->isMoving()); QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); @@ -892,7 +894,7 @@ void tst_qquickflickable::movingAndDragging() QCOMPARE(dragStartSpy.count(), 1); QCOMPARE(dragEndSpy.count(), 0); - QTest::mouseRelease(window, Qt::LeftButton, 0, moveFrom + moveByWithSnapBack*3); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, moveFrom + moveByWithSnapBack*3); // should now start snapping back to bounds (moving but not dragging) QVERIFY(flickable->isMoving()); @@ -935,17 +937,16 @@ void tst_qquickflickable::movingAndDragging() QCOMPARE(flickable->contentX(), 0.0); QCOMPARE(flickable->contentY(), 0.0); - - delete window; } void tst_qquickflickable::flickOnRelease() { - QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("flickable03.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); @@ -958,9 +959,9 @@ void tst_qquickflickable::flickOnRelease() // underlying drivers will hopefully provide a pre-calculated velocity // (based on more data than what the UI gets), thus making this use case // working even with small movements. - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(50, 300)); - QTest::mouseMove(window, QPoint(50, 10), 10); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(50, 10), 10); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(50, 300)); + QTest::mouseMove(window.data(), QPoint(50, 10), 10); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(50, 10), 10); QCOMPARE(vFlickSpy.count(), 1); @@ -972,17 +973,16 @@ void tst_qquickflickable::flickOnRelease() #endif // Stop on a full pixel after user interaction QCOMPARE(flickable->contentY(), (qreal)qRound(flickable->contentY())); - - delete window; } void tst_qquickflickable::pressWhileFlicking() { - QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("flickable03.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); @@ -997,7 +997,7 @@ void tst_qquickflickable::pressWhileFlicking() // flick then press while it is still moving // flicking == false, moving == true; - flick(window, QPoint(20,190), QPoint(20, 50), 200); + flick(window.data(), QPoint(20,190), QPoint(20, 50), 200); QVERIFY(flickable->verticalVelocity() > 0.0); QVERIFY(flickable->isFlicking()); QVERIFY(flickable->isFlickingVertically()); @@ -1012,77 +1012,76 @@ void tst_qquickflickable::pressWhileFlicking() QCOMPARE(hFlickSpy.count(), 0); QCOMPARE(flickSpy.count(), 1); - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(20, 50)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(20, 50)); QTRY_VERIFY(!flickable->isFlicking()); QVERIFY(!flickable->isFlickingVertically()); QVERIFY(flickable->isMoving()); QVERIFY(flickable->isMovingVertically()); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(20,50)); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(20,50)); QVERIFY(!flickable->isFlicking()); QVERIFY(!flickable->isFlickingVertically()); QTRY_VERIFY(!flickable->isMoving()); QVERIFY(!flickable->isMovingVertically()); // Stop on a full pixel after user interaction QCOMPARE(flickable->contentX(), (qreal)qRound(flickable->contentX())); - - delete window; } void tst_qquickflickable::disabled() { - QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("disabled.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flick = window->rootObject()->findChild<QQuickFlickable*>("flickable"); QVERIFY(flick != 0); - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(50, 90)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(50, 90)); - QTest::mouseMove(window, QPoint(50, 80)); - QTest::mouseMove(window, QPoint(50, 70)); - QTest::mouseMove(window, QPoint(50, 60)); + QTest::mouseMove(window.data(), QPoint(50, 80)); + QTest::mouseMove(window.data(), QPoint(50, 70)); + QTest::mouseMove(window.data(), QPoint(50, 60)); QVERIFY(flick->isMoving() == false); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(50, 60)); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(50, 60)); // verify that mouse clicks on other elements still work (QTBUG-20584) - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(50, 10)); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(50, 10)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(50, 10)); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(50, 10)); QTRY_VERIFY(window->rootObject()->property("clicked").toBool() == true); - - delete window; } void tst_qquickflickable::flickVelocity() { - QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("flickable03.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); QVERIFY(flickable != 0); // flick up - flick(window, QPoint(20,190), QPoint(20, 50), 200); + flick(window.data(), QPoint(20,190), QPoint(20, 50), 200); QVERIFY(flickable->verticalVelocity() > 0.0); QTRY_VERIFY(flickable->verticalVelocity() == 0.0); // flick down - flick(window, QPoint(20,10), QPoint(20, 140), 200); - QVERIFY(flickable->verticalVelocity() < 0.0); + flick(window.data(), QPoint(20,10), QPoint(20, 140), 200); + QTRY_VERIFY(flickable->verticalVelocity() < 0.0); QTRY_VERIFY(flickable->verticalVelocity() == 0.0); #ifdef Q_OS_MAC QSKIP("boost doesn't work on OS X"); - delete window; return; #endif @@ -1090,17 +1089,15 @@ void tst_qquickflickable::flickVelocity() QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable); bool boosted = false; for (int i = 0; i < 6; ++i) { - flick(window, QPoint(20,390), QPoint(20, 50), 100); + flick(window.data(), QPoint(20,390), QPoint(20, 50), 100); boosted |= fp->flickBoost > 1.0; } QVERIFY(boosted); // Flick in opposite direction -> boost cancelled. - flick(window, QPoint(20,10), QPoint(20, 340), 200); + flick(window.data(), QPoint(20,10), QPoint(20, 340), 200); QTRY_VERIFY(flickable->verticalVelocity() < 0.0); QVERIFY(fp->flickBoost == 1.0); - - delete window; } void tst_qquickflickable::margins() @@ -1164,20 +1161,22 @@ void tst_qquickflickable::margins() void tst_qquickflickable::cancelOnMouseGrab() { - QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("cancel.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); QVERIFY(flickable != 0); - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(10, 10)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(10, 10)); // drag out of bounds - QTest::mouseMove(window, QPoint(50, 50)); - QTest::mouseMove(window, QPoint(100, 100)); - QTest::mouseMove(window, QPoint(150, 150)); + QTest::mouseMove(window.data(), QPoint(50, 50)); + QTest::mouseMove(window.data(), QPoint(100, 100)); + QTest::mouseMove(window.data(), QPoint(150, 150)); QVERIFY(flickable->contentX() != 0); QVERIFY(flickable->contentY() != 0); @@ -1193,56 +1192,54 @@ void tst_qquickflickable::cancelOnMouseGrab() QTRY_VERIFY(!flickable->isMoving()); QTRY_VERIFY(!flickable->isDragging()); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(50, 10)); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(50, 10)); - delete window; } void tst_qquickflickable::clickAndDragWhenTransformed() { - QQuickView *view = new QQuickView; + QScopedPointer<QQuickView> view(new QQuickView); view->setSource(testFileUrl("transformedFlickable.qml")); view->show(); + QVERIFY(QTest::qWaitForWindowExposed(view.data())); view->requestActivate(); - QVERIFY(QTest::qWaitForWindowExposed(view)); + QVERIFY(QTest::qWaitForWindowActive(view.data())); QVERIFY(view->rootObject() != 0); QQuickFlickable *flickable = view->rootObject()->findChild<QQuickFlickable*>("flickable"); QVERIFY(flickable != 0); // click outside child rect - QTest::mousePress(view, Qt::LeftButton, 0, QPoint(190, 190)); + QTest::mousePress(view.data(), Qt::LeftButton, 0, QPoint(190, 190)); QTest::qWait(10); QCOMPARE(flickable->property("itemPressed").toBool(), false); - QTest::mouseRelease(view, Qt::LeftButton, 0, QPoint(190, 190)); + QTest::mouseRelease(view.data(), Qt::LeftButton, 0, QPoint(190, 190)); // click inside child rect - QTest::mousePress(view, Qt::LeftButton, 0, QPoint(200, 200)); + QTest::mousePress(view.data(), Qt::LeftButton, 0, QPoint(200, 200)); QTest::qWait(10); QCOMPARE(flickable->property("itemPressed").toBool(), true); - QTest::mouseRelease(view, Qt::LeftButton, 0, QPoint(200, 200)); + QTest::mouseRelease(view.data(), Qt::LeftButton, 0, QPoint(200, 200)); const int threshold = qApp->styleHints()->startDragDistance(); // drag outside bounds - QTest::mousePress(view, Qt::LeftButton, 0, QPoint(160, 160)); + QTest::mousePress(view.data(), Qt::LeftButton, 0, QPoint(160, 160)); QTest::qWait(10); - QTest::mouseMove(view, QPoint(160 + threshold * 2, 160)); - QTest::mouseMove(view, QPoint(160 + threshold * 3, 160)); + QTest::mouseMove(view.data(), QPoint(160 + threshold * 2, 160)); + QTest::mouseMove(view.data(), QPoint(160 + threshold * 3, 160)); QCOMPARE(flickable->isDragging(), false); QCOMPARE(flickable->property("itemPressed").toBool(), false); - QTest::mouseRelease(view, Qt::LeftButton, 0, QPoint(180, 160)); + QTest::mouseRelease(view.data(), Qt::LeftButton, 0, QPoint(180, 160)); // drag inside bounds - QTest::mousePress(view, Qt::LeftButton, 0, QPoint(200, 140)); + QTest::mousePress(view.data(), Qt::LeftButton, 0, QPoint(200, 140)); QTest::qWait(10); - QTest::mouseMove(view, QPoint(200 + threshold * 2, 140)); - QTest::mouseMove(view, QPoint(200 + threshold * 3, 140)); + QTest::mouseMove(view.data(), QPoint(200 + threshold * 2, 140)); + QTest::mouseMove(view.data(), QPoint(200 + threshold * 3, 140)); QCOMPARE(flickable->isDragging(), true); QCOMPARE(flickable->property("itemPressed").toBool(), false); - QTest::mouseRelease(view, Qt::LeftButton, 0, QPoint(220, 140)); - - delete view; + QTest::mouseRelease(view.data(), Qt::LeftButton, 0, QPoint(220, 140)); } void tst_qquickflickable::flickTwiceUsingTouches() diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index 802cc0a4c9..93ffbca3e5 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -2871,7 +2871,8 @@ void tst_QQuickGridView::enforceRange_rightToLeft() ctxt->setContextProperty("testTopToBottom", QVariant(true)); window->setSource(testFileUrl("gridview-enforcerange.qml")); - qApp->processEvents(); + window->show(); + QTRY_VERIFY(window->isExposed()); QVERIFY(window->rootObject() != 0); QQuickGridView *gridview = findItem<QQuickGridView>(window->rootObject(), "grid"); diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index a7e0f2feb4..a78625c58e 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -198,7 +198,7 @@ void tst_QQuickPathView::initValues() void tst_QQuickPathView::items() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); QaimModel model; model.addItem("Fred", "12345"); @@ -237,8 +237,6 @@ void tst_QQuickPathView::items() offset.setX(pathview->highlightItem()->width()/2); offset.setY(pathview->highlightItem()->height()/2); QCOMPARE(pathview->highlightItem()->position() + offset, start); - - delete window; } void tst_QQuickPathView::pathview2() @@ -349,7 +347,7 @@ void tst_QQuickPathView::insertModel() QFETCH(qreal, offset); QFETCH(int, currentIndex); - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->show(); QaimModel model; @@ -387,8 +385,6 @@ void tst_QQuickPathView::insertModel() QTRY_COMPARE(pathview->offset(), offset); QCOMPARE(pathview->currentIndex(), currentIndex); - - delete window; } void tst_QQuickPathView::removeModel_data() @@ -443,7 +439,8 @@ void tst_QQuickPathView::removeModel() QFETCH(qreal, offset); QFETCH(int, currentIndex); - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); + window->show(); QaimModel model; @@ -477,8 +474,6 @@ void tst_QQuickPathView::removeModel() QTRY_COMPARE(pathview->offset(), offset); QCOMPARE(pathview->currentIndex(), currentIndex); - - delete window; } @@ -538,7 +533,7 @@ void tst_QQuickPathView::moveModel() QFETCH(qreal, offset); QFETCH(int, currentIndex); - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->show(); QaimModel model; @@ -572,8 +567,6 @@ void tst_QQuickPathView::moveModel() QTRY_COMPARE(pathview->offset(), offset); QCOMPARE(pathview->currentIndex(), currentIndex); - - delete window; } void tst_QQuickPathView::consecutiveModelChanges_data() @@ -639,7 +632,7 @@ void tst_QQuickPathView::consecutiveModelChanges() QFETCH(qreal, offset); QFETCH(int, currentIndex); - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->show(); QaimModel model; @@ -703,7 +696,6 @@ void tst_QQuickPathView::consecutiveModelChanges() QCOMPARE(pathview->currentIndex(), currentIndex); - delete window; } void tst_QQuickPathView::path() @@ -755,7 +747,7 @@ void tst_QQuickPathView::path() void tst_QQuickPathView::dataModel() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->show(); QQmlContext *ctxt = window->rootContext(); @@ -864,13 +856,12 @@ void tst_QQuickPathView::dataModel() model.removeItem(model.count()-1); QCOMPARE(pathview->currentIndex(), model.count()-1); - delete window; delete testObject; } void tst_QQuickPathView::pathMoved() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->show(); QaimModel model; @@ -925,7 +916,6 @@ void tst_QQuickPathView::pathMoved() window->rootObject()->setProperty("delegateScale", 1.2); QTRY_COMPARE(firstItem->position() + offset, start); - delete window; } void tst_QQuickPathView::offset_data() @@ -959,7 +949,7 @@ void tst_QQuickPathView::offset() void tst_QQuickPathView::setCurrentIndex() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->show(); QaimModel model; @@ -1102,12 +1092,11 @@ void tst_QQuickPathView::setCurrentIndex() QCOMPARE(pathview->currentItem(), firstItem); QCOMPARE(firstItem->property("onPath"), QVariant(true)); - delete window; } void tst_QQuickPathView::resetModel() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); QStringList strings; strings << "one" << "two" << "three"; @@ -1142,12 +1131,11 @@ void tst_QQuickPathView::resetModel() QCOMPARE(display->text(), strings.at(i)); } - delete window; } void tst_QQuickPathView::propertyChanges() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); QVERIFY(window); window->setSource(testFileUrl("propertychanges.qml")); @@ -1184,12 +1172,11 @@ void tst_QQuickPathView::propertyChanges() pathView->setMaximumFlickVelocity(1000); QCOMPARE(maximumFlickVelocitySpy.count(), 1); - delete window; } void tst_QQuickPathView::pathChanges() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); QVERIFY(window); window->setSource(testFileUrl("propertychanges.qml")); @@ -1246,12 +1233,11 @@ void tst_QQuickPathView::pathChanges() pathAttribute->setName("scale"); QCOMPARE(nameSpy.count(),1); - delete window; } void tst_QQuickPathView::componentChanges() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); QVERIFY(window); window->setSource(testFileUrl("propertychanges.qml")); @@ -1269,12 +1255,11 @@ void tst_QQuickPathView::componentChanges() pathView->setDelegate(&delegateComponent); QCOMPARE(delegateSpy.count(),1); - delete window; } void tst_QQuickPathView::modelChanges() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); QVERIFY(window); window->setSource(testFileUrl("propertychanges.qml")); @@ -1304,12 +1289,11 @@ void tst_QQuickPathView::modelChanges() QCOMPARE(pathView->currentIndex(), 0); QCOMPARE(currentIndexSpy.count(), 1); - delete window; } void tst_QQuickPathView::pathUpdateOnStartChanged() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); QVERIFY(window); window->setSource(testFileUrl("pathUpdateOnStartChanged.qml")); @@ -1326,17 +1310,16 @@ void tst_QQuickPathView::pathUpdateOnStartChanged() QCOMPARE(item->x(), path->startX() - item->width() / 2.0); QCOMPARE(item->y(), path->startY() - item->height() / 2.0); - delete window; } void tst_QQuickPathView::package() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); QVERIFY(window); window->setSource(testFileUrl("pathview_package.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QQuickPathView *pathView = window->rootObject()->findChild<QQuickPathView*>("photoPathView"); QVERIFY(pathView); @@ -1349,13 +1332,12 @@ void tst_QQuickPathView::package() QVERIFY(item); QVERIFY(item->scale() != 1.0); - delete window; } //QTBUG-13017 void tst_QQuickPathView::emptyModel() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); QStringListModel model; @@ -1370,7 +1352,6 @@ void tst_QQuickPathView::emptyModel() QCOMPARE(pathview->offset(), qreal(0.0)); - delete window; } void tst_QQuickPathView::closed() @@ -1397,7 +1378,7 @@ void tst_QQuickPathView::closed() // QTBUG-14239 void tst_QQuickPathView::pathUpdate() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); QVERIFY(window); window->setSource(testFileUrl("pathUpdate.qml")); @@ -1408,7 +1389,6 @@ void tst_QQuickPathView::pathUpdate() QVERIFY(item); QCOMPARE(item->x(), 150.0); - delete window; } void tst_QQuickPathView::visualDataModel() @@ -1449,12 +1429,12 @@ void tst_QQuickPathView::undefinedPath() void tst_QQuickPathView::mouseDrag() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->setSource(testFileUrl("dragpath.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject()); QVERIFY(pathview != 0); @@ -1468,12 +1448,12 @@ void tst_QQuickPathView::mouseDrag() int current = pathview->currentIndex(); - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(10,100)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(10,100)); QTest::qWait(100); { QMouseEvent mv(QEvent::MouseMove, QPoint(30,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); - QGuiApplication::sendEvent(window, &mv); + QGuiApplication::sendEvent(window.data(), &mv); } // first move beyond threshold does not trigger drag QVERIFY(!pathview->isMoving()); @@ -1487,7 +1467,7 @@ void tst_QQuickPathView::mouseDrag() { QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); - QGuiApplication::sendEvent(window, &mv); + QGuiApplication::sendEvent(window.data(), &mv); } // next move beyond threshold does trigger drag QVERIFY(pathview->isMoving()); @@ -1501,7 +1481,7 @@ void tst_QQuickPathView::mouseDrag() QVERIFY(pathview->currentIndex() != current); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(40,100)); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(40,100)); QVERIFY(!pathview->isDragging()); QCOMPARE(draggingSpy.count(), 2); QCOMPARE(dragStartedSpy.count(), 1); @@ -1510,12 +1490,11 @@ void tst_QQuickPathView::mouseDrag() QTRY_COMPARE(moveEndedSpy.count(), 1); QCOMPARE(moveStartedSpy.count(), 1); - delete window; } void tst_QQuickPathView::treeModel() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->show(); QStandardItemModel model; @@ -1538,18 +1517,17 @@ void tst_QQuickPathView::treeModel() QTRY_VERIFY(item = findItem<QQuickText>(pathview, "wrapper", 0)); QTRY_COMPARE(item->text(), QLatin1String("Row 2 Child Item")); - delete window; } void tst_QQuickPathView::changePreferredHighlight() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->setGeometry(0,0,400,200); window->setSource(testFileUrl("dragpath.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject()); QVERIFY(pathview != 0); @@ -1573,7 +1551,6 @@ void tst_QQuickPathView::changePreferredHighlight() QTRY_COMPARE(firstItem->position() + offset, start); QCOMPARE(pathview->currentIndex(), 0); - delete window; } void tst_QQuickPathView::creationContext() @@ -1594,7 +1571,7 @@ void tst_QQuickPathView::creationContext() // QTBUG-21320 void tst_QQuickPathView::currentOffsetOnInsertion() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->show(); QaimModel model; @@ -1667,12 +1644,11 @@ void tst_QQuickPathView::currentOffsetOnInsertion() // verify that current item (item 1) is still at offset 0.5 QCOMPARE(item->position() + offset, start); - delete window; } void tst_QQuickPathView::asynchronous() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->show(); QQmlIncubationController controller; window->engine()->setIncubationController(&controller); @@ -1723,7 +1699,6 @@ void tst_QQuickPathView::asynchronous() QCOMPARE(curItem->position() + offset, itemPos); } - delete window; } void tst_QQuickPathView::missingPercent() @@ -1738,12 +1713,12 @@ void tst_QQuickPathView::missingPercent() void tst_QQuickPathView::cancelDrag() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->setSource(testFileUrl("dragpath.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject()); QVERIFY(pathview != 0); @@ -1753,10 +1728,10 @@ void tst_QQuickPathView::cancelDrag() QSignalSpy dragEndedSpy(pathview, SIGNAL(dragEnded())); // drag between snap points - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(10,100)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(10,100)); QTest::qWait(100); - QTest::mouseMove(window, QPoint(30, 100)); - QTest::mouseMove(window, QPoint(85, 100)); + QTest::mouseMove(window.data(), QPoint(30, 100)); + QTest::mouseMove(window.data(), QPoint(85, 100)); QTRY_VERIFY(pathview->offset() != qFloor(pathview->offset())); QTRY_VERIFY(pathview->isMoving()); @@ -1777,25 +1752,24 @@ void tst_QQuickPathView::cancelDrag() QCOMPARE(dragStartedSpy.count(), 1); QCOMPARE(dragEndedSpy.count(), 1); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(40,100)); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(40,100)); - delete window; } void tst_QQuickPathView::maximumFlickVelocity() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->setSource(testFileUrl("dragpath.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject()); QVERIFY(pathview != 0); pathview->setMaximumFlickVelocity(700); - flick(window, QPoint(200,10), QPoint(10,10), 180); + flick(window.data(), QPoint(200,10), QPoint(10,10), 180); QVERIFY(pathview->isMoving()); QVERIFY(pathview->isFlicking()); QTRY_VERIFY_WITH_TIMEOUT(!pathview->isMoving(), 50000); @@ -1804,7 +1778,7 @@ void tst_QQuickPathView::maximumFlickVelocity() pathview->setOffset(0.); pathview->setMaximumFlickVelocity(300); - flick(window, QPoint(200,10), QPoint(10,10), 180); + flick(window.data(), QPoint(200,10), QPoint(10,10), 180); QVERIFY(pathview->isMoving()); QVERIFY(pathview->isFlicking()); QTRY_VERIFY_WITH_TIMEOUT(!pathview->isMoving(), 50000); @@ -1813,7 +1787,7 @@ void tst_QQuickPathView::maximumFlickVelocity() pathview->setOffset(0.); pathview->setMaximumFlickVelocity(500); - flick(window, QPoint(200,10), QPoint(10,10), 180); + flick(window.data(), QPoint(200,10), QPoint(10,10), 180); QVERIFY(pathview->isMoving()); QVERIFY(pathview->isFlicking()); QTRY_VERIFY_WITH_TIMEOUT(!pathview->isMoving(), 50000); @@ -1824,14 +1798,13 @@ void tst_QQuickPathView::maximumFlickVelocity() QVERIFY(dist3 > dist2); QVERIFY(dist2 < dist1); - delete window; } void tst_QQuickPathView::snapToItem() { QFETCH(bool, enforceRange); - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->setSource(testFileUrl("panels.qml")); QQuickPathView *pathview = window->rootObject()->findChild<QQuickPathView*>("view"); QVERIFY(pathview != 0); @@ -1843,7 +1816,7 @@ void tst_QQuickPathView::snapToItem() QSignalSpy snapModeSpy(pathview, SIGNAL(snapModeChanged())); - flick(window, QPoint(200,10), QPoint(10,10), 180); + flick(window.data(), QPoint(200,10), QPoint(10,10), 180); QVERIFY(pathview->isMoving()); QTRY_VERIFY(!pathview->isMoving()); @@ -1855,7 +1828,6 @@ void tst_QQuickPathView::snapToItem() else QVERIFY(pathview->currentIndex() == currentIndex); - delete window; } void tst_QQuickPathView::snapToItem_data() @@ -1870,12 +1842,12 @@ void tst_QQuickPathView::snapOneItem() { QFETCH(bool, enforceRange); - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->setSource(testFileUrl("panels.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = window->rootObject()->findChild<QQuickPathView*>("view"); QVERIFY(pathview != 0); @@ -1891,7 +1863,7 @@ void tst_QQuickPathView::snapOneItem() int currentIndex = pathview->currentIndex(); double startOffset = pathview->offset(); - flick(window, QPoint(200,10), QPoint(10,10), 180); + flick(window.data(), QPoint(200,10), QPoint(10,10), 180); QVERIFY(pathview->isMoving()); QTRY_VERIFY(!pathview->isMoving()); @@ -1904,7 +1876,6 @@ void tst_QQuickPathView::snapOneItem() else QVERIFY(pathview->currentIndex() == currentIndex); - delete window; } void tst_QQuickPathView::snapOneItem_data() @@ -1924,12 +1895,12 @@ void tst_QQuickPathView::positionViewAtIndex() QFETCH(QQuickPathView::PositionMode, mode); QFETCH(qreal, offset); - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->setSource(testFileUrl("pathview3.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject()); QVERIFY(pathview != 0); @@ -1945,7 +1916,6 @@ void tst_QQuickPathView::positionViewAtIndex() QCOMPARE(pathview->offset(), offset); - delete window; } void tst_QQuickPathView::positionViewAtIndex_data() @@ -1988,12 +1958,12 @@ void tst_QQuickPathView::indexAt_itemAt() QFETCH(qreal, y); QFETCH(int, index); - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->setSource(testFileUrl("pathview3.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject()); QVERIFY(pathview != 0); @@ -2006,7 +1976,6 @@ void tst_QQuickPathView::indexAt_itemAt() QCOMPARE(pathview->indexAt(x,y), index); QVERIFY(pathview->itemAt(x,y) == item); - delete window; } void tst_QQuickPathView::indexAt_itemAt_data() @@ -2024,7 +1993,7 @@ void tst_QQuickPathView::indexAt_itemAt_data() void tst_QQuickPathView::cacheItemCount() { - QQuickView *window = createView(); + QScopedPointer<QQuickView> window(createView()); window->setSource(testFileUrl("pathview3.qml")); window->show(); @@ -2098,7 +2067,6 @@ void tst_QQuickPathView::cacheItemCount() controller.incubateWhile(&b); } - delete window; } QTEST_MAIN(tst_QQuickPathView) diff --git a/tests/auto/quick/qquickwindow/data/showHideAnimate.qml b/tests/auto/quick/qquickwindow/data/showHideAnimate.qml new file mode 100644 index 0000000000..e83910c5a5 --- /dev/null +++ b/tests/auto/quick/qquickwindow/data/showHideAnimate.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + NumberAnimation on opacity { from: 0; to: 1; duration: 100; loops: Animation.Infinite } +} diff --git a/tests/auto/quick/qquickwindow/qquickwindow.pro b/tests/auto/quick/qquickwindow/qquickwindow.pro index 9ae63a9ef1..59d96429db 100644 --- a/tests/auto/quick/qquickwindow/qquickwindow.pro +++ b/tests/auto/quick/qquickwindow/qquickwindow.pro @@ -13,6 +13,7 @@ TESTDATA = data/* OTHER_FILES += \ data/AnimationsWhileHidden.qml \ - data/Headless.qml + data/Headless.qml \ + data/showHideAnimate.qml DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index f7fc549069..3e907a5204 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -312,6 +312,11 @@ private slots: void ownershipRootItem(); + void hideThenDelete_data(); + void hideThenDelete(); + + void showHideAnimate(); + #ifndef QT_NO_CURSOR void cursor(); #endif @@ -945,6 +950,8 @@ void tst_qquickwindow::headless() QScopedPointer<QObject> cleanup(created); QQuickWindow* window = qobject_cast<QQuickWindow*>(created); + window->setPersistentOpenGLContext(false); + window->setPersistentSceneGraph(false); QVERIFY(window); window->show(); @@ -1212,6 +1219,88 @@ void tst_qquickwindow::cursor() } #endif +void tst_qquickwindow::hideThenDelete_data() +{ + QTest::addColumn<bool>("persistentSG"); + QTest::addColumn<bool>("persistentGL"); + + QTest::newRow("persistent:SG=false,GL=false") << false << false; + QTest::newRow("persistent:SG=true,GL=false") << true << false; + QTest::newRow("persistent:SG=false,GL=true") << false << true; + QTest::newRow("persistent:SG=true,GL=true") << true << true; +} + +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); + + QSignalSpy *openglDestroyed = 0; + QSignalSpy *sgInvalidated = 0; + + { + QQuickWindow window; + window.setColor(Qt::red); + + window.setPersistentSceneGraph(persistentSG); + window.setPersistentOpenGLContext(persistentGL); + + window.resize(400, 300); + window.show(); + + QTest::qWaitForWindowExposed(&window); + + openglDestroyed = new QSignalSpy(window.openglContext(), SIGNAL(aboutToBeDestroyed())); + sgInvalidated = new QSignalSpy(&window, SIGNAL(sceneGraphInvalidated())); + + window.hide(); + + QTRY_VERIFY(!window.isExposed()); + + if (!persistentSG) { + QVERIFY(sgInvalidated->size() > 0); + if (!persistentGL) + QVERIFY(openglDestroyed->size() > 0); + else + QVERIFY(openglDestroyed->size() == 0); + } else { + QVERIFY(sgInvalidated->size() == 0); + QVERIFY(openglDestroyed->size() == 0); + } + } + + QVERIFY(sgInvalidated->size() > 0); + QVERIFY(openglDestroyed->size() > 0); +} + +void tst_qquickwindow::showHideAnimate() +{ + // This test tries to mimick a bug triggered in the qquickanimatedimage test + // A window is shown, then removed again before it is exposed. This left + // traces in the render loop which prevent other animations from running + // later on. + { + QQuickWindow window; + window.resize(400, 300); + window.show(); + } + + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("showHideAnimate.qml")); + QQuickItem* created = qobject_cast<QQuickItem *>(component.create()); + + QVERIFY(created); + + QTRY_VERIFY(created->opacity() > 0.5); + QTRY_VERIFY(created->opacity() < 0.5); +} + QTEST_MAIN(tst_qquickwindow) #include "tst_qquickwindow.moc" |