diff options
Diffstat (limited to 'src/quick/scenegraph/qsgwindowsrenderloop.cpp')
-rw-r--r-- | src/quick/scenegraph/qsgwindowsrenderloop.cpp | 552 |
1 files changed, 0 insertions, 552 deletions
diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp deleted file mode 100644 index 03c9183126..0000000000 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ /dev/null @@ -1,552 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgwindowsrenderloop_p.h" -#include <QtCore/QCoreApplication> -#include <QtCore/QLibraryInfo> -#include <QtCore/QThread> - -#include <QtGui/QScreen> -#include <QtGui/QGuiApplication> -#include <QtGui/QOffscreenSurface> - -#include <QtQuick/private/qsgcontext_p.h> -#include <QtQuick/private/qquickwindow_p.h> -#include <QtQuick/private/qquickitem_p.h> -#include <QtQuick/private/qsgrenderer_p.h> -#include <QtQuick/private/qsgdefaultrendercontext_p.h> - -#include <QtQuick/QQuickWindow> - -#include <private/qquickprofiler_p.h> -#include <private/qquickanimatorcontroller_p.h> - -#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl) -#include <private/qquickopenglshadereffectnode_p.h> -#endif - -#include <qtquick_tracepoints_p.h> - -QT_BEGIN_NAMESPACE - -// Single-threaded render loop with a custom animation driver. Like a -// combination of basic+threaded but still working on the main thread. Only -// compatible with direct OpenGL, no RHI support here. - -extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); - -#define RLDEBUG(x) qCDebug(QSG_LOG_RENDERLOOP, x) - -static QElapsedTimer qsg_render_timer; -#define QSG_LOG_TIME_SAMPLE(sampleName) \ - qint64 sampleName = 0; \ - if (QSG_LOG_TIME_RENDERLOOP().isDebugEnabled()) \ - sampleName = qsg_render_timer.nsecsElapsed(); \ - -#define QSG_RENDER_TIMING_SAMPLE(frameType, sampleName, position) \ - QSG_LOG_TIME_SAMPLE(sampleName) \ - Q_QUICK_SG_PROFILE_RECORD(frameType, position); - - -QSGWindowsRenderLoop::QSGWindowsRenderLoop() - : m_gl(nullptr) - , m_sg(QSGContext::createDefaultContext()) - , m_updateTimer(0) - , m_animationTimer(0) -{ - m_rc = static_cast<QSGDefaultRenderContext *>(m_sg->createRenderContext()); - - m_vsyncDelta = 1000 / QGuiApplication::primaryScreen()->refreshRate(); - if (m_vsyncDelta <= 0) - m_vsyncDelta = 16; - - RLDEBUG("Windows Render Loop created"); - - m_animationDriver = m_sg->createAnimationDriver(m_sg); - connect(m_animationDriver, SIGNAL(started()), this, SLOT(started())); - connect(m_animationDriver, SIGNAL(stopped()), this, SLOT(stopped())); - m_animationDriver->install(); - - qsg_render_timer.start(); -} - -QSGWindowsRenderLoop::~QSGWindowsRenderLoop() -{ - delete m_rc; - delete m_sg; -} - -bool QSGWindowsRenderLoop::interleaveIncubation() const -{ - return m_animationDriver->isRunning() && anyoneShowing(); -} - -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 nullptr; -} - -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) != nullptr) - 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) { - RLDEBUG(" - creating GL context"); - m_gl = new QOpenGLContext(); - m_gl->setFormat(window->requestedFormat()); - m_gl->setScreen(window->screen()); - if (qt_gl_global_share_context()) - m_gl->setShareContext(qt_gl_global_share_context()); - bool created = m_gl->create(); - if (!created) { - delete m_gl; - m_gl = nullptr; - handleContextCreationFailure(window); - return; - } - - QQuickWindowPrivate::get(window)->fireOpenGLContextCreated(m_gl); - - RLDEBUG(" - making current"); - bool current = m_gl->makeCurrent(window); - RLDEBUG(" - initializing SG"); - if (current) { - QSGDefaultRenderContext::InitParams rcParams; - rcParams.sampleCount = qMax(1, m_gl->format().samples()); - rcParams.openGLContext = m_gl; - rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio(); - rcParams.maybeSurface = window; - m_rc->initialize(&rcParams); - } - } - - WindowData data; - data.window = window; - data.pendingUpdate = false; - m_windows << data; - - RLDEBUG(" - done with show"); -} - -void QSGWindowsRenderLoop::hide(QQuickWindow *window) -{ - RLDEBUG("hide"); - // 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(); - if (!m_gl) - return; - QQuickWindowPrivate::get(window)->fireAboutToStop(); -} - -void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window) -{ - RLDEBUG("windowDestroyed"); - for (int i=0; i<m_windows.size(); ++i) { - if (m_windows.at(i).window == window) { - m_windows.removeAt(i); - break; - } - } - - hide(window); - - QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); - - bool current = false; - QScopedPointer<QOffscreenSurface> offscreenSurface; - if (m_gl) { - QSurface *surface = window; - // There may be no platform window if the window got closed. - if (!window->handle()) { - offscreenSurface.reset(new QOffscreenSurface); - offscreenSurface->setFormat(m_gl->format()); - offscreenSurface->create(); - surface = offscreenSurface.data(); - } - current = m_gl->makeCurrent(surface); - } - if (Q_UNLIKELY(!current)) - RLDEBUG("cleanup without an OpenGL context"); - -#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl) - if (current) - QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); -#endif - - d->cleanupNodesOnShutdown(); - if (m_windows.size() == 0) { - d->context->invalidate(); - delete m_gl; - m_gl = nullptr; - } else if (m_gl && current) { - m_gl->doneCurrent(); - } - - d->animationController.reset(); -} - -bool QSGWindowsRenderLoop::anyoneShowing() const -{ - for (const WindowData &wd : qAsConst(m_windows)) - if (wd.window->isVisible() && wd.window->isExposed() && wd.window->size().isValid()) - return true; - return false; -} - -void QSGWindowsRenderLoop::exposureChanged(QQuickWindow *window) -{ - - if (windowData(window) == nullptr) - return; - - if (window->isExposed() && window->isVisible()) { - - // 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()); - - bool alpha = window->format().alphaBufferSize() > 0 && window->color().alpha() != 255; - QImage image = qt_gl_read_framebuffer(window->size() * window->effectiveDevicePixelRatio(), alpha, alpha); - image.setDevicePixelRatio(window->effectiveDevicePixelRatio()); - 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(); -} - -QSGRenderContext *QSGWindowsRenderLoop::createRenderContext(QSGContext *) const -{ - return m_rc; -} - -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"); - Q_TRACE(QSG_render_entry); - bool rendered = false; - for (const WindowData &wd : qAsConst(m_windows)) { - if (wd.pendingUpdate) { - const_cast<WindowData &>(wd).pendingUpdate = false; - renderWindow(wd.window); - rendered = true; - } - } - - if (!rendered) { - RLDEBUG("no changes, sleep"); - QThread::msleep(m_vsyncDelta); - } - - Q_TRACE(QSG_render_exit); - - if (m_animationDriver->isRunning()) { - RLDEBUG("advancing animations"); - QSG_LOG_TIME_SAMPLE(time_start); - Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphWindowsAnimations); - Q_TRACE(QSG_animations_entry); - m_animationDriver->advance(); - RLDEBUG("animations advanced"); - - qCDebug(QSG_LOG_TIME_RENDERLOOP, - "animations ticked in %dms", - int((qsg_render_timer.nsecsElapsed() - time_start)/1000000)); - - Q_TRACE(QSG_animations_exit); - Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphWindowsAnimations, 1); - - // 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(); - - emit timeToIncubate(); - } -} - -/* - * 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)) { - // Check for context loss. - if (!m_gl->isValid()) { - d->cleanupNodesOnShutdown(); - m_rc->invalidate(); - if (m_gl->create() && m_gl->makeCurrent(window)) { - QSGDefaultRenderContext::InitParams rcParams; - rcParams.sampleCount = qMax(1, m_gl->format().samples()); - rcParams.openGLContext = m_gl; - rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio(); - rcParams.maybeSurface = window; - m_rc->initialize(&rcParams); - } else { - return; - } - } - } - - bool lastDirtyWindow = true; - for (int i=0; i<m_windows.size(); ++i) { - if ( m_windows[i].pendingUpdate) { - lastDirtyWindow = false; - break; - } - } - - d->flushFrameSynchronousEvents(); - // Event delivery or processing has caused the window to stop rendering. - if (!windowData(window)) - return; - - Q_TRACE_SCOPE(QSG_renderWindow); - - QSG_LOG_TIME_SAMPLE(time_start); - Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame); - Q_TRACE(QSG_polishItems_entry); - - RLDEBUG(" - polishing"); - d->polishItems(); - QSG_LOG_TIME_SAMPLE(time_polished); - Q_TRACE(QSG_polishItems_exit); - Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame, - QQuickProfiler::SceneGraphRenderLoopFrame, - QQuickProfiler::SceneGraphPolishPolish); - Q_TRACE(QSG_sync_entry); - - emit window->afterAnimating(); - - RLDEBUG(" - syncing"); - d->syncSceneGraph(); - if (lastDirtyWindow) - m_rc->endSync(); - Q_TRACE(QSG_sync_exit); - QSG_RENDER_TIMING_SAMPLE(QQuickProfiler::SceneGraphRenderLoopFrame, time_synced, - QQuickProfiler::SceneGraphRenderLoopSync); - Q_TRACE(QSG_render_entry); - - RLDEBUG(" - rendering"); - d->renderSceneGraph(window->size()); - Q_TRACE(QSG_render_exit); - QSG_RENDER_TIMING_SAMPLE(QQuickProfiler::SceneGraphRenderLoopFrame, time_rendered, - QQuickProfiler::SceneGraphRenderLoopRender); - Q_TRACE(QSG_swap_entry); - - RLDEBUG(" - swapping"); - if (!d->customRenderStage || !d->customRenderStage->swap()) - m_gl->swapBuffers(window); - Q_TRACE(QSG_swap_exit); - QSG_RENDER_TIMING_SAMPLE(QQuickProfiler::SceneGraphRenderLoopFrame, time_swapped, - QQuickProfiler::SceneGraphRenderLoopSwap); - - RLDEBUG(" - frameDone"); - d->fireFrameSwapped(); - - qCDebug(QSG_LOG_TIME_RENDERLOOP()).nospace() - << "Frame rendered with 'windows' renderloop in: " << (time_swapped - time_start) / 1000000 << "ms" - << ", polish=" << (time_polished - time_start) / 1000000 - << ", sync=" << (time_synced - time_polished) / 1000000 - << ", render=" << (time_rendered - time_synced) / 1000000 - << ", swap=" << (time_swapped - time_rendered) / 1000000 - << " - " << window; - - Q_QUICK_SG_PROFILE_REPORT(QQuickProfiler::SceneGraphRenderLoopFrame, - QQuickProfiler::SceneGraphRenderLoopSwap); -} - -void QSGWindowsRenderLoop::releaseResources(QQuickWindow *w) -{ - // No full invalidation of the rendercontext, just clear some caches. - RLDEBUG("releaseResources"); - QQuickWindowPrivate *d = QQuickWindowPrivate::get(w); - if (d->renderer) - d->renderer->releaseCachedResources(); -} - -QT_END_NAMESPACE - -#include "moc_qsgwindowsrenderloop_p.cpp" |