8#include <private/qsgrenderer_p.h>
9#include <private/qquickwindow_p.h>
10#include <private/qquickitem_p.h>
11#include <private/qquickprofiler_p.h>
12#include <private/qquickanimatorcontroller_p.h>
13#include <private/qquickprofiler_p.h>
14#include <private/qqmldebugserviceinterfaces_p.h>
15#include <private/qqmldebugconnector_p.h>
17#include <qpa/qplatformbackingstore.h>
19#include <QtCore/QQueue>
20#include <QtCore/QElapsedTimer>
21#include <QtCore/QThread>
22#include <QtCore/QMutex>
23#include <QtCore/QWaitCondition>
24#include <QtGui/QGuiApplication>
25#include <QtGui/QBackingStore>
26#include <QtQuick/QQuickWindow>
28#include <qtquick_tracepoints_p.h>
53 ,
dpr(
c->effectiveDevicePixelRatio())
112 bool waiting =
false;
118 return refreshRate < 1 ? 16 : int(1000 / refreshRate);
141 void sync(
bool inExpose);
188 switch ((
int)
e->type()) {
196 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_Obscure - window removed");
217 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_RequestSync - triggered from expose");
221 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_RequestSync - repaint regardless");
228 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_TryRelease");
234 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_TryRelease - invalidating rc");
252 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_TryRelease - not releasing because window is still active");
261 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_Grab");
275 if (softwareRenderer)
283 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_Grab - waking gui to handle result");
290 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_PostJob");
297 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_PostJob - job done");
335 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - run()");
340 if (QQmlDebugConnector::service<QQmlProfilerService>())
353 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - done drawing, sleep");
360 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - run() exiting");
371 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - sync");
374 Q_ASSERT_X(
renderLoop->lockedForSync,
"QSGSoftwareRenderThread::sync()",
"sync triggered with gui not locked");
378 bool hadRenderer = wd->
renderer !=
nullptr;
389 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - created renderer");
402 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - sync complete, waking gui");
417 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - syncAndRender()");
430 sync(exposeRequested);
434 QQuickProfiler::SceneGraphRenderLoopSync);
437 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - no changes, render aborted");
444 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - rendering started");
453 bool canRender = wd->
renderer !=
nullptr;
457 if (softwareRenderer)
463 QQuickProfiler::SceneGraphRenderLoopRender);
466 if (softwareRenderer)
473 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - blocking for %d ms", blockTime);
482 QQuickProfiler::SceneGraphRenderLoopSync, 1);
484 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - window not ready, skipping render");
487 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - rendering done");
491 if (exposeRequested) {
492 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - wake gui after initial expose");
499 QQuickProfiler::SceneGraphRenderLoopSwap);
502QSGSoftwareThreadedRenderLoop::WindowData *QSGSoftwareThreadedRenderLoop::windowFor(
QQuickWindow *
window)
504 for (
const auto &
t :
std::as_const(m_windows)) {
506 return const_cast<WindowData *
>(&
t);
514 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"software threaded render loop constructor");
524 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"software threaded render loop destructor");
538 handleObscurity(windowFor(
window));
553 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"window destroyed" <<
window;
555 WindowData *
w = windowFor(
window);
560 handleResourceRelease(
w,
true);
569 for (
int i = 0;
i < m_windows.
size(); ++
i) {
579 startOrStopAnimationTimer();
584 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"exposure changed" <<
window;
586 if (
window->isExposed()) {
589 WindowData *
w = windowFor(
window);
599 WindowData *
w = windowFor(
window);
602 const bool tempExpose = !
w;
609 if (!
w->thread->isRunning())
619 w->thread->mutex.lock();
620 lockedForSync =
true;
622 w->thread->waitCondition.wait(&
w->thread->mutex);
623 lockedForSync =
false;
624 w->thread->mutex.unlock();
626 result.setDevicePixelRatio(
window->effectiveDevicePixelRatio());
636 WindowData *
w = windowFor(
window);
641 w->thread->requestRepaint();
647 w->forceRenderPass =
true;
653 WindowData *
w = windowFor(
window);
660 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"handleUpdateRequest" <<
window;
662 WindowData *
w = windowFor(
window);
664 polishAndSync(
w,
false);
684 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"releaseResources" <<
window;
686 WindowData *
w = windowFor(
window);
688 handleResourceRelease(
w,
false);
693 WindowData *
w = windowFor(
window);
694 if (
w &&
w->thread &&
w->thread->exposedWindow)
707 bool somethingVisible =
false;
708 for (
const WindowData &
w : m_windows) {
709 if (
w.window->isVisible() &&
w.window->isExposed()) {
710 somethingVisible =
true;
714 return somethingVisible && m_anim->
isRunning();
726 if (te->
timerId() == animationTimer) {
738 startOrStopAnimationTimer();
740 for (
const WindowData &
w : std::as_const(m_windows))
741 w.window->requestUpdate();
746 startOrStopAnimationTimer();
749void QSGSoftwareThreadedRenderLoop::startOrStopAnimationTimer()
751 int exposedWindowCount = 0;
752 const WindowData *exposed =
nullptr;
754 for (
int i = 0;
i < m_windows.
size(); ++
i) {
755 const WindowData &
w(m_windows[
i]);
756 if (
w.window->isVisible() &&
w.window->isExposed()) {
757 ++exposedWindowCount;
762 if (animationTimer && (exposedWindowCount == 1 || !m_anim->
isRunning())) {
767 exposed->window->requestUpdate();
768 }
else if (!animationTimer && exposedWindowCount != 1 && m_anim->
isRunning()) {
775 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"handleExposure" <<
window;
777 WindowData *
w = windowFor(
window);
779 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"adding window to list");
784 win.updateDuringSync =
false;
785 win.forceRenderPass =
true;
787 w = &m_windows.
last();
792 w->thread->exposedWindow =
window;
794 if (
w->window->size().isEmpty()
795 || (
w->window->isTopLevel() && !
w->window->geometry().intersects(
w->window->screen()->availableGeometry()))) {
797 qWarning().noquote().nospace() <<
"QSGSotwareThreadedRenderLoop: expose event received for window "
798 <<
w->window <<
" with invalid geometry: " <<
w->window->geometry()
799 <<
" on " <<
w->window->screen();
803 if (!
w->window->handle())
807 if (!
w->thread->isRunning()) {
808 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"starting render thread");
815 w->thread->rc->moveToThread(
w->thread);
816 w->thread->moveToThread(
w->thread);
819 w->thread->active =
true;
822 if (!
w->thread->isRunning())
823 qFatal(
"Render thread failed to start, aborting application.");
826 polishAndSync(
w,
true);
828 startOrStopAnimationTimer();
831void QSGSoftwareThreadedRenderLoop::handleObscurity(QSGSoftwareThreadedRenderLoop::WindowData *
w)
833 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"handleObscurity" <<
w->window;
835 if (
w->thread->isRunning()) {
836 w->thread->mutex.lock();
838 w->thread->waitCondition.wait(&
w->thread->mutex);
839 w->thread->mutex.unlock();
842 startOrStopAnimationTimer();
845void QSGSoftwareThreadedRenderLoop::scheduleUpdate(QSGSoftwareThreadedRenderLoop::WindowData *
w)
850 if (!
w || !
w->thread->isRunning())
855 qWarning() <<
"Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
859 if (current ==
w->thread) {
860 w->updateDuringSync =
true;
864 w->window->requestUpdate();
867void QSGSoftwareThreadedRenderLoop::handleResourceRelease(QSGSoftwareThreadedRenderLoop::WindowData *
w,
bool destroying)
869 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"handleResourceRelease" << (destroying ?
"destroying" :
"hide/releaseResources") <<
w->window;
871 w->thread->mutex.lock();
872 if (
w->thread->isRunning() &&
w->thread->active) {
880 w->thread->waitCondition.wait(&
w->thread->mutex);
888 if (!
w->thread->active)
891 w->thread->mutex.unlock();
894void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop::WindowData *
w,
bool inExpose)
896 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"polishAndSync" << (inExpose ?
"(in expose)" :
"(normal)") <<
w->window;
899 if (!
w->thread || !
w->thread->exposedWindow) {
900 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - not exposed, abort");
908 if (!
w || !
w->thread || !
w->thread->exposedWindow) {
909 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - removed after touch event flushing, abort");
915 Q_TRACE(QSG_polishItems_entry);
923 QQuickProfiler::SceneGraphPolishAndSyncPolish);
926 w->updateDuringSync =
false;
930 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - lock for sync");
931 w->thread->mutex.lock();
932 lockedForSync =
true;
934 w->forceRenderPass =
false;
936 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - wait for sync");
940 QQuickProfiler::SceneGraphPolishAndSyncWait);
942 w->thread->waitCondition.wait(&
w->thread->mutex);
943 lockedForSync =
false;
944 w->thread->mutex.unlock();
945 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - unlock after sync");
949 QQuickProfiler::SceneGraphPolishAndSyncSync);
952 if (!animationTimer && m_anim->
isRunning()) {
953 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - advancing animations");
956 w->window->requestUpdate();
958 }
else if (
w->updateDuringSync) {
959 w->window->requestUpdate();
964 QQuickProfiler::SceneGraphPolishAndSyncAnimations);
969#include "qsgsoftwarethreadedrenderloop.moc"
970#include "moc_qsgsoftwarethreadedrenderloop_p.cpp"
void install()
Installs this animation driver.
void started()
This signal is emitted by the animation framework to notify the driver that continuous animation has ...
virtual void advance()
Advances the animation.
void stopped()
This signal is emitted by the animation framework to notify the driver that continuous animation has ...
The QBackingStore class provides a drawing area for QWindow.
QPlatformBackingStore * handle() const
Returns a pointer to the QPlatformBackingStore implementation.
void flush(const QRegion ®ion, QWindow *window=nullptr, const QPoint &offset=QPoint())
Flushes the given region from the specified window onto the screen.
void resize(const QSize &size)
Sets the size of the window surface to size.
QSize size() const
Returns the current size of the window surface.
static void processEvents(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
Processes some pending events for the calling thread according to the specified flags.
static QCoreApplication * instance() noexcept
Returns a pointer to the application's QCoreApplication (or QGuiApplication/QApplication) instance.
static void sendPostedEvents(QObject *receiver=nullptr, int event_type=0)
Immediately dispatches all events which have been previously queued with QCoreApplication::postEvent(...
qint64 elapsed() const noexcept
Returns the number of milliseconds since this QElapsedTimer was last started.
qint64 restart() noexcept
Restarts the timer and returns the number of milliseconds elapsed since the previous start.
void start() noexcept
Starts this timer.
Type
This enum type defines the valid event types in Qt.
QScreen * primaryScreen
the primary (or default) screen of the application.
qsizetype size() const noexcept
bool isEmpty() const noexcept
void removeAt(qsizetype i)
const_reference at(qsizetype i) const noexcept
void append(parameter_type t)
void unlock() noexcept
Unlocks the mutex.
void lock() noexcept
Locks the mutex.
int startTimer(int interval, Qt::TimerType timerType=Qt::CoarseTimer)
This is an overloaded function that will start a timer of type timerType and a timeout of interval mi...
void moveToThread(QThread *thread)
Changes the thread affinity for this object and its children.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
virtual bool event(QEvent *event)
This virtual function receives events to an object and should return true if the event e was recogniz...
QThread * thread() const
Returns the thread in which the object lives.
void killTimer(int id)
Kills the timer with timer identifier, id.
void enqueue(const QEvent * &t)
Adds value t to the tail of the queue.
QEvent * dequeue()
Removes the head item in the queue and returns it.
void flushFrameSynchronousEvents(QQuickWindow *win)
static QQuickWindowPrivate * get(QQuickWindow *c)
QSGRenderContext * context
void cleanupNodesOnShutdown()
QQuickDeliveryAgentPrivate * deliveryAgentPrivate() const
QScopedPointer< QQuickAnimatorController > animationController
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
virtual void run()=0
Implement this pure virtual function in your subclass.
void sceneGraphChanged()
This signal is emitted on the first modification of a node in the tree after the last scene render.
The QSGContext holds the scene graph entry points for one QML engine.
virtual QAnimationDriver * createAnimationDriver(QObject *parent)
Creates a new animation driver.
virtual void initialize(const InitParams *params)
QSGContext * sceneGraphContext() const
@ SupportsGrabWithoutExpose
QSGRenderContext * createRenderContext() override
QEvent * takeEvent(bool wait)
QSGSoftwareGrabEvent(QQuickWindow *c, QImage *result)
QSGSoftwareJobEvent(QQuickWindow *c, QRunnable *postedJob)
void invalidate() override
QElapsedTimer renderThrottleTimer
QSGSoftwareThreadedRenderLoop * renderLoop
bool event(QEvent *e) override
This virtual function receives events to an object and should return true if the event e was recogniz...
bool syncResultedInChanges
~QSGSoftwareRenderThread()
void onSceneGraphChanged()
QBackingStore * backingStore
QWaitCondition waitCondition
void processEventsAndWaitForMore()
QQuickWindow * exposedWindow
QSGSoftwareEventQueue eventQueue
QAnimationDriver * rtAnim
QSGSoftwareRenderContext * rc
void postEvent(QEvent *e)
QSGSoftwareRenderThread(QSGSoftwareThreadedRenderLoop *rl, QSGRenderContext *renderContext)
void setBackingStore(QBackingStore *backingStore)
QSGSoftwareSyncEvent(QQuickWindow *c, bool inExpose, bool force)
void exposureChanged(QQuickWindow *window) override
QAnimationDriver * animationDriver() const override
void resize(QQuickWindow *window) override
void handleUpdateRequest(QQuickWindow *window) override
void update(QQuickWindow *window) override
void windowDestroyed(QQuickWindow *window) override
bool event(QEvent *e) override
This virtual function receives events to an object and should return true if the event e was recogniz...
int flags() const override
~QSGSoftwareThreadedRenderLoop()
friend class QSGSoftwareRenderThread
QImage grab(QQuickWindow *window) override
void maybeUpdate(QQuickWindow *window) override
void releaseResources(QQuickWindow *window) override
QSGContext * sceneGraphContext() const override
QSurface::SurfaceType windowSurfaceType() const override
void onAnimationStarted()
void hide(QQuickWindow *window) override
QSGRenderContext * createRenderContext(QSGContext *) const override
void postJob(QQuickWindow *window, QRunnable *job) override
bool interleaveIncubation() const override
void onAnimationStopped()
void show(QQuickWindow *window) override
QSGSoftwareThreadedRenderLoop()
QSGSoftwareTryReleaseEvent(QQuickWindow *win, bool destroy)
QSGSoftwareWindowEvent(QQuickWindow *c, QEvent::Type type)
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
qreal refreshRate
the approximate vertical refresh rate of the screen in Hz
SurfaceType
The SurfaceType enum describes what type of surface this is.
bool event(QEvent *event) override
This virtual function receives events to an object and should return true if the event e was recogniz...
static QThread * currentThread()
static void yieldCurrentThread()
static void msleep(unsigned long)
int timerId() const
Returns the unique timer identifier, which is the same identifier as returned from QObject::startTime...
Combined button and popup list for selecting options.
#define qCDebug(category,...)
GLfloat GLfloat GLfloat w
[0]
GLenum GLuint GLintptr GLsizeiptr size
[1]
#define Q_QUICK_SG_PROFILE_END(Type, position)
#define Q_QUICK_SG_PROFILE_SKIP(Type, position, Skip)
#define Q_QUICK_SG_PROFILE_RECORD(Type, position)
#define Q_QUICK_SG_PROFILE_START(Type)
#define Q_ASSERT_X(cond, x, msg)
static int qsgrl_animation_interval()
#define Q_TRACE_SCOPE(x,...)
static void registerAnimationCallback()