diff options
author | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-04-29 14:17:46 +0200 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-04-29 14:17:46 +0200 |
commit | 7224dd977163335d61ed4297341e6161bf1dbcf7 (patch) | |
tree | 1674bfdd872ba45908437c9cbc67ba98f5f3b534 /src/quick/scenegraph | |
parent | c704226b6815df4d989142729bae9fadd14f6f9d (diff) | |
parent | f3973cbd077d2d92250958f8bda57fea30827f00 (diff) |
Merge remote-tracking branch 'origin/stable' into dev
Change-Id: I35f07a1725af104952f822274763dd7a96a9d560
Diffstat (limited to 'src/quick/scenegraph')
-rw-r--r-- | src/quick/scenegraph/qsgcontext.cpp | 4 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgrenderloop.cpp | 52 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgthreadedrenderloop.cpp | 5 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgwindowsrenderloop.cpp | 435 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgwindowsrenderloop_p.h | 114 | ||||
-rw-r--r-- | src/quick/scenegraph/scenegraph.pri | 16 |
6 files changed, 600 insertions, 26 deletions
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index 8d36fce481..5d894e5236 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -354,7 +354,11 @@ QSGDistanceFieldGlyphCache *QSGContext::distanceFieldGlyphCache(const QRawFont & */ QSGGlyphNode *QSGContext::createNativeGlyphNode() { +#if defined(QT_OPENGL_ES) && !defined(QT_OPENGL_ES_2_ANGLE) + return createGlyphNode(); +#else return new QSGDefaultGlyphNode; +#endif } /*! diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 04981b54e7..f71ccea294 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -41,6 +41,7 @@ #include "qsgrenderloop_p.h" #include "qsgthreadedrenderloop_p.h" +#include "qsgwindowsrenderloop_p.h" #include <QtCore/QCoreApplication> #include <QtCore/QTime> @@ -130,15 +131,6 @@ QSGRenderLoop *QSGRenderLoop::instance() s_instance = QSGContext::createWindowManager(); bool bufferQueuing = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::BufferQueueingOpenGL); -#ifdef Q_OS_WIN - bool fancy = false; // QTBUG-28037 -#else - bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL); -#endif - if (qmlNoThreadedRenderer()) - fancy = false; - else if (qmlForceThreadedRenderer()) - fancy = true; // Enable fixed animation steps... QByteArray fixed = qgetenv("QML_FIXED_ANIMATION_STEP"); @@ -151,9 +143,45 @@ QSGRenderLoop *QSGRenderLoop::instance() QUnifiedTimer::instance(true)->setConsistentTiming(true); if (!s_instance) { - s_instance = fancy - ? (QSGRenderLoop*) new QSGThreadedRenderLoop - : (QSGRenderLoop*) new QSGGuiThreadRenderLoop; + + enum RenderLoopType { + BasicRenderLoop, + ThreadedRenderLoop, + WindowsRenderLoop + }; + + RenderLoopType loopType = BasicRenderLoop; + +#ifdef Q_OS_WIN + loopType = WindowsRenderLoop; +#else + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)) + loopType = ThreadedRenderLoop; +#endif + if (qmlNoThreadedRenderer()) + loopType = BasicRenderLoop; + else if (qmlForceThreadedRenderer()) + loopType = ThreadedRenderLoop; + + const QByteArray loopName = qgetenv("QSG_RENDER_LOOP"); + if (loopName == QByteArrayLiteral("windows")) + loopType = WindowsRenderLoop; + else if (loopName == QByteArrayLiteral("basic")) + loopType = BasicRenderLoop; + else if (loopName == QByteArrayLiteral("threaded")) + loopType = ThreadedRenderLoop; + + switch (loopType) { + case ThreadedRenderLoop: + s_instance = new QSGThreadedRenderLoop(); + break; + case WindowsRenderLoop: + s_instance = new QSGWindowsRenderLoop(); + break; + default: + s_instance = new QSGGuiThreadRenderLoop(); + break; + } } } return s_instance; diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 4cca87e990..de1e2517db 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -515,7 +515,6 @@ void QSGRenderThread::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)); @@ -557,8 +556,10 @@ void QSGRenderThread::syncAndRender() syncResultedInChanges = false; bool repaintRequested = pendingUpdate & RepaintRequest; + bool syncRequested = pendingUpdate & SyncRequest; + pendingUpdate = 0; - if (pendingUpdate & SyncRequest) { + if (syncRequested) { RLDEBUG(" Render: - update pending, doing sync"); sync(); } diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp new file mode 100644 index 0000000000..3e21af6ac0 --- /dev/null +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -0,0 +1,435 @@ +/**************************************************************************** +** +** 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 "qsgwindowsrenderloop_p.h" + +#include <QtCore/QCoreApplication> + +#include <QtGui/QScreen> +#include <QtGui/QGuiApplication> + +#include <QtQuick/private/qsgcontext_p.h> +#include <QtQuick/private/qquickwindow_p.h> + +#include <QtQuick/QQuickWindow> + +QT_BEGIN_NAMESPACE + +extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); + +// #define QSG_RENDER_LOOP_DEBUG + +#ifdef QSG_RENDER_LOOP_DEBUG +static QElapsedTimer qsg_debug_timer; +# define RLDEBUG(x) printf("(%6d) %s : %4d - %s\n", (int) qsg_debug_timer.elapsed(), __FILE__, __LINE__, x) +#else +# define RLDEBUG(x) +#endif + +#ifndef QSG_NO_RENDER_TIMING +static bool qsg_render_timing = !qgetenv("QSG_RENDER_TIMING").isEmpty(); +static QElapsedTimer qsg_render_timer; +#define QSG_RENDER_TIMING_SAMPLE(sampleName) int sampleName = 0; if (qsg_render_timing) sampleName = qsg_render_timer.elapsed() +#else +#define QSG_RENDER_TIMING_SAMPLE(sampleName) +#endif + + +QSGWindowsRenderLoop::QSGWindowsRenderLoop() + : m_gl(0) + , m_sg(QSGContext::createDefaultContext()) + , m_updateTimer(0) + , m_animationTimer(0) +{ +#ifdef QSG_RENDER_LOOP_DEBUG + qsg_debug_timer.start(); +#endif + + m_animationDriver = m_sg->createAnimationDriver(m_sg); + m_animationDriver->install(); + + connect(m_animationDriver, SIGNAL(started()), this, SLOT(started())); + connect(m_animationDriver, SIGNAL(stopped()), this, SLOT(stopped())); + + m_vsyncDelta = 1000 / QGuiApplication::primaryScreen()->refreshRate(); + if (m_vsyncDelta <= 0) + m_vsyncDelta = 16; + + RLDEBUG("Windows Render Loop created"); + +#ifndef QSG_NO_RENDER_TIMIMG + qsg_render_timer.start(); +#endif +} + +QSGWindowsRenderLoop::WindowData *QSGWindowsRenderLoop::windowData(QQuickWindow *window) +{ + for (int i=0; i<m_windows.size(); ++i) { + WindowData &wd = m_windows[i]; + if (wd.window == window) + return &wd; + } + return 0; +} + +void QSGWindowsRenderLoop::maybePostUpdateTimer() +{ + if (!m_updateTimer) { + RLDEBUG(" - posting event"); + m_updateTimer = startTimer(m_vsyncDelta / 3); + } +} + +/* + * If no windows are showing, start ticking animations using a timer, + * otherwise, start rendering + */ +void QSGWindowsRenderLoop::started() +{ + RLDEBUG("Animations started..."); + if (!anyoneShowing()) { + if (m_animationTimer == 0) { + RLDEBUG(" - starting non-visual animation timer"); + m_animationTimer = startTimer(m_vsyncDelta); + } + } else { + maybePostUpdateTimer(); + } +} + +void QSGWindowsRenderLoop::stopped() +{ + RLDEBUG("Animations stopped..."); + if (m_animationTimer) { + RLDEBUG(" - stopping non-visual animation timer"); + killTimer(m_animationTimer); + m_animationTimer = 0; + } +} + +void QSGWindowsRenderLoop::show(QQuickWindow *window) +{ + RLDEBUG("show"); + if (windowData(window) != 0) + return; + + // This happens before the platform window is shown, but after + // it is created. Creating the GL context takes a lot of time + // (hundreds of milliseconds) and will prevent us from rendering + // the first frame in time for the initial show on screen. + // By preparing the GL context here, it is feasible (if the app + // is quick enough) to have a perfect first frame. + if (!m_gl) { + QSG_RENDER_TIMING_SAMPLE(time_start); + + RLDEBUG(" - creating GL context"); + m_gl = new QOpenGLContext(); + m_gl->setFormat(window->requestedFormat()); + m_gl->create(); + QSG_RENDER_TIMING_SAMPLE(time_created); + RLDEBUG(" - making current"); + m_gl->makeCurrent(window); + RLDEBUG(" - initializing SG"); + QSG_RENDER_TIMING_SAMPLE(time_current); + m_sg->initialize(m_gl); + +#ifndef QSG_NO_RENDER_TIMING + if (qsg_render_timing) { + qDebug("WindowsRenderLoop: GL=%d ms, makeCurrent=%d ms, SG=%d ms", + int(time_created - time_start), + int(time_current - time_created), + int(qsg_render_timer.elapsed() - time_current)); + } +#endif + + } + + WindowData data; + data.window = window; + data.pendingUpdate = false; + m_windows << data; + + RLDEBUG(" - done with show"); +} + +void QSGWindowsRenderLoop::hide(QQuickWindow *window) +{ + RLDEBUG("hide"); + + for (int i=0; i<m_windows.size(); ++i) { + if (m_windows.at(i).window == window) { + m_windows.removeAt(i); + break; + } + } + + // The expose event is queued while hide is sent synchronously, so + // the value might not be updated yet. (plus that the windows plugin + // sends exposed=true when it goes to hidden, so it is doubly broken) + // The check is made here, after the removal from m_windows, so + // anyoneShowing will report the right value. + if (window->isExposed()) + handleObscurity(); + + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + cd->cleanupNodesOnShutdown(); + + // If this is the last tracked window, check for persistent SG and GL and + // potentially clean up. + if (m_windows.size() == 0) { + if (!cd->persistentSceneGraph) { + m_sg->invalidate(); + if (!cd->persistentGLContext) { + delete m_gl; + m_gl = 0; + } + } + } +} + +void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window) +{ + RLDEBUG("windowDestroyed"); + hide(window); + + // If this is the last tracked window, clean up SG and GL. + if (m_windows.size() == 0) { + m_sg->invalidate(); + delete m_gl; + m_gl = 0; + } +} + +bool QSGWindowsRenderLoop::anyoneShowing() const +{ + foreach (const WindowData &wd, m_windows) + if (wd.window->isExposed() && wd.window->size().isValid()) + return true; + return false; +} + +void QSGWindowsRenderLoop::exposureChanged(QQuickWindow *window) +{ + + if (windowData(window) == 0) + return; + + if (window->isExposed()) { + + // Stop non-visual animation timer as we now have a window rendering + if (m_animationTimer && anyoneShowing()) { + RLDEBUG(" - stopping non-visual animation timer"); + killTimer(m_animationTimer); + m_animationTimer = 0; + } + + RLDEBUG("exposureChanged - exposed"); + WindowData *wd = windowData(window); + wd->pendingUpdate = true; + + // If we have a pending timer and we get an expose, we need to stop it. + // Otherwise we get two frames and two animation ticks in the same time-interval. + if (m_updateTimer) { + RLDEBUG(" - killing pending update timer"); + killTimer(m_updateTimer); + m_updateTimer = 0; + } + render(); + } else { + handleObscurity(); + } +} + +void QSGWindowsRenderLoop::handleObscurity() +{ + RLDEBUG("handleObscurity"); + // Potentially start the non-visual animation timer if nobody is rendering + if (m_animationDriver->isRunning() && !anyoneShowing() && !m_animationTimer) { + RLDEBUG(" - starting non-visual animation timer"); + m_animationTimer = startTimer(m_vsyncDelta); + } +} + +QImage QSGWindowsRenderLoop::grab(QQuickWindow *window) +{ + RLDEBUG("grab"); + if (!m_gl) + return QImage(); + + m_gl->makeCurrent(window); + + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + d->polishItems(); + d->syncSceneGraph(); + d->renderSceneGraph(window->size()); + + QImage image = qt_gl_read_framebuffer(window->size(), false, false); + return image; +} + +void QSGWindowsRenderLoop::update(QQuickWindow *window) +{ + RLDEBUG("update"); + maybeUpdate(window); +} + +void QSGWindowsRenderLoop::maybeUpdate(QQuickWindow *window) +{ + RLDEBUG("maybeUpdate"); + + WindowData *wd = windowData(window); + if (!wd || !anyoneShowing()) + return; + + wd->pendingUpdate = true; + maybePostUpdateTimer(); +} + +bool QSGWindowsRenderLoop::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::Timer: { + QTimerEvent *te = static_cast<QTimerEvent *>(event); + if (te->timerId() == m_animationTimer) { + RLDEBUG("event : animation tick while nothing is showing"); + m_animationDriver->advance(); + } else if (te->timerId() == m_updateTimer) { + RLDEBUG("event : update"); + killTimer(m_updateTimer); + m_updateTimer = 0; + render(); + } + return true; } + default: + break; + } + + return QObject::event(event); +} + +/* + * Go through all windows we control and render them in turn. + * Then tick animations if active. + */ +void QSGWindowsRenderLoop::render() +{ + RLDEBUG("render"); + foreach (const WindowData &wd, m_windows) { + if (wd.pendingUpdate) { + const_cast<WindowData &>(wd).pendingUpdate = false; + renderWindow(wd.window); + } + } + + if (m_animationDriver->isRunning()) { + RLDEBUG("advancing animations"); + QSG_RENDER_TIMING_SAMPLE(time_start); + m_animationDriver->advance(); + RLDEBUG("animations advanced"); + +#ifndef QSG_NO_RENDER_TIMING + if (qsg_render_timing) { + qDebug("WindowsRenderLoop: animations=%d ms", + int(qsg_render_timer.elapsed() - time_start)); + } +#endif + + // It is not given that animations triggered another maybeUpdate() + // and thus another render pass, so to keep things running, + // make sure there is another frame pending. + maybePostUpdateTimer(); + } +} + +/* + * Render the contents of this window. First polish, then sync, render + * then finally swap. + * + * Note: This render function does not implement aborting + * the render call when sync step results in no scene graph changes, + * like the threaded renderer does. + */ +void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window) +{ + RLDEBUG("renderWindow"); + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + + if (!d->isRenderable()) + return; + + if (!m_gl->makeCurrent(window)) + return; + + QSG_RENDER_TIMING_SAMPLE(time_start); + + RLDEBUG(" - polishing"); + d->polishItems(); + QSG_RENDER_TIMING_SAMPLE(time_polished); + + RLDEBUG(" - syncing"); + d->syncSceneGraph(); + QSG_RENDER_TIMING_SAMPLE(time_synced); + + RLDEBUG(" - rendering"); + d->renderSceneGraph(window->size()); + QSG_RENDER_TIMING_SAMPLE(time_rendered); + + RLDEBUG(" - swapping"); + m_gl->swapBuffers(window); + QSG_RENDER_TIMING_SAMPLE(time_swapped); + + RLDEBUG(" - frameDone"); + d->fireFrameSwapped(); + +#ifndef QSG_NO_RENDER_TIMING + if (qsg_render_timing) { + qDebug("WindowsRenderLoop(t=%d): window=%p, polish=%d ms, sync=%d ms, render=%d ms, swap=%d ms", + int(qsg_render_timer.elapsed()), + window, + int(time_polished - time_start), + int(time_synced - time_polished), + int(time_rendered - time_synced), + int(time_swapped - time_rendered)); + } +#endif +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgwindowsrenderloop_p.h b/src/quick/scenegraph/qsgwindowsrenderloop_p.h new file mode 100644 index 0000000000..dc3a409cd5 --- /dev/null +++ b/src/quick/scenegraph/qsgwindowsrenderloop_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** 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 QSGWINDOWSRENDERLOOP_P_H +#define QSGWINDOWSRENDERLOOP_P_H + +#include <QtCore/QObject> +#include <QtCore/QElapsedTimer> + +#include <QtGui/QOpenGLContext> + +#include "qsgrenderloop_p.h" + +QT_BEGIN_NAMESPACE + +class QSGWindowsRenderLoop : public QObject, public QSGRenderLoop +{ + Q_OBJECT +public: + explicit QSGWindowsRenderLoop(); + + void show(QQuickWindow *window); + void hide(QQuickWindow *window); + + void windowDestroyed(QQuickWindow *window); + + void exposureChanged(QQuickWindow *window); + QImage grab(QQuickWindow *window); + + void update(QQuickWindow *window); + void maybeUpdate(QQuickWindow *window); + + QAnimationDriver *animationDriver() const { return m_animationDriver; } + + QSGContext *sceneGraphContext() const { return m_sg; } + + void releaseResources(QQuickWindow *) { } + + void render(); + void renderWindow(QQuickWindow *window); + + void resize(QQuickWindow *, const QSize &) { } + + bool event(QEvent *event); + +public slots: + void started(); + void stopped(); + +private: + struct WindowData { + QQuickWindow *window; + bool pendingUpdate; + }; + + void handleObscurity(); + void maybePostUpdateTimer(); + bool anyoneShowing() const; + WindowData *windowData(QQuickWindow *window); + + QList<WindowData> m_windows; + + QOpenGLContext *m_gl; + QSGContext *m_sg; + + QAnimationDriver *m_animationDriver; + + int m_updateTimer; + int m_animationTimer; + + int m_vsyncDelta; +}; + +QT_END_NAMESPACE + +#endif // QSGWINDOWSRENDERLOOP_P_H diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index ebf886dace..34432ffd9c 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -52,7 +52,6 @@ SOURCES += \ $$PWD/util/qsgpainternode.cpp \ $$PWD/util/qsgdistancefieldutil.cpp - # QML / Adaptations API HEADERS += \ $$PWD/qsgadaptationlayer_p.h \ @@ -68,8 +67,8 @@ HEADERS += \ $$PWD/qsgflashnode_p.h \ $$PWD/qsgshareddistancefieldglyphcache_p.h \ $$PWD/qsgrenderloop_p.h \ - $$PWD/qsgthreadedrenderloop_p.h - + $$PWD/qsgthreadedrenderloop_p.h \ + $$PWD/qsgwindowsrenderloop_p.h SOURCES += \ $$PWD/qsgadaptationlayer.cpp \ @@ -85,12 +84,5 @@ SOURCES += \ $$PWD/qsgflashnode.cpp \ $$PWD/qsgshareddistancefieldglyphcache.cpp \ $$PWD/qsgrenderloop.cpp \ - $$PWD/qsgthreadedrenderloop.cpp - - - - - - - - + $$PWD/qsgthreadedrenderloop.cpp \ + $$PWD/qsgwindowsrenderloop.cpp |