summaryrefslogtreecommitdiffstats
path: root/src/monitor-lib/frametimer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/monitor-lib/frametimer.cpp')
-rw-r--r--src/monitor-lib/frametimer.cpp290
1 files changed, 284 insertions, 6 deletions
diff --git a/src/monitor-lib/frametimer.cpp b/src/monitor-lib/frametimer.cpp
index ef483736..534c3033 100644
--- a/src/monitor-lib/frametimer.cpp
+++ b/src/monitor-lib/frametimer.cpp
@@ -41,12 +41,75 @@
#include "frametimer.h"
+#include <QQuickWindow>
+#include <qqmlinfo.h>
+
+#include "waylandwindow.h"
+#include "inprocesswindow.h"
+
+/*!
+ \qmltype FrameTimer
+ \inqmlmodule QtApplicationManager
+ \ingroup common-instantiatable
+ \brief Provides frame-rate information about a given window.
+
+ FrameTimer is used to get frame-rate information for a given window. The window can be either
+ a toplevel Window (from the QtQuick.Window module) or a WindowObject
+ (from the QtApplicationManager.SystemUI module).
+
+ The following snippet shows how to use FrameTimer to display the frame-rate of a Window:
+
+ \qml
+ import QtQuick 2.11
+ import QtApplicationManager 1.0
+
+ Window {
+ id: toplevelWindow
+ ...
+ FrameTimer {
+ id: frameTimer
+ running: topLevelWindow.visible
+ window: toplevelWindow
+ }
+ Text {
+ text: "FPS: " + Number(frameTimer.averageFps).toLocaleString(Qt.locale("en_US"), 'f', 1)
+ }
+ }
+ \endqml
+
+ You can also use this component as a MonitorModel data source if you want to plot its
+ previous values over time:
+
+ \qml
+ import QtQuick 2.11
+ import QtApplicationManager 1.0
+
+ Window {
+ id: toplevelWindow
+ ...
+ MonitorModel {
+ running: true
+ FrameTimer {
+ window: toplevelWindow
+ }
+ }
+ }
+ \endqml
+
+ Please note that when using FrameTimer as a MonitorModel data source there's no need to set it
+ to \l{FrameTimer::running}{running} as MonitorModel will already call update() as needed.
+*/
+
QT_BEGIN_NAMESPACE_AM
const qreal FrameTimer::MicrosInSec = qreal(1000 * 1000);
-FrameTimer::FrameTimer()
-{ }
+FrameTimer::FrameTimer(QObject *parent)
+ : QObject(parent)
+{
+ m_updateTimer.setInterval(1000);
+ connect(&m_updateTimer, &QTimer::timeout, this, &FrameTimer::update);
+}
void FrameTimer::newFrame()
{
@@ -67,24 +130,239 @@ void FrameTimer::reset()
m_min = std::numeric_limits<int>::max();
}
+/*!
+ \qmlproperty real FrameTimer::averageFps
+ \readonly
+
+ The average frame rate of the given \l window, in frames per second, since update()
+ was last called (either manually or automatically in case \l{FrameTimer::running}{running} is set to true).
+
+ \sa window running update()
+*/
qreal FrameTimer::averageFps() const
{
- return m_sum ? MicrosInSec * m_count / m_sum : qreal(0);
+ return m_averageFps;
}
+/*!
+ \qmlproperty real FrameTimer::minimumFps
+ \readonly
+
+ The minimum frame rate of the given \l window, in frames per second, since update()
+ was last called (either manually or automatically in case \l{FrameTimer::running}{running} is set to true).
+
+ \sa window running update()
+*/
qreal FrameTimer::minimumFps() const
{
- return m_max ? MicrosInSec / m_max : qreal(0);
+ return m_minimumFps;
}
+/*!
+ \qmlproperty real FrameTimer::maximumFps
+ \readonly
+
+ The maximum frame rate of the given \l window, in frames per second, since update()
+ was last called (either manually or automatically in case \l{FrameTimer::running}{running} is set to true).
+
+ \sa window running update()
+*/
qreal FrameTimer::maximumFps() const
{
- return m_min ? MicrosInSec / m_min : qreal(0);
+ return m_maximumFps;
}
+/*!
+ \qmlproperty real FrameTimer::jitterFps
+ \readonly
+
+ The frame rate jitter of the given \l window, in frames per second, since update()
+ was last called (either manually or automatically in case \l{FrameTimer::running}{running} is set to true).
+
+ \sa window running update()
+*/
qreal FrameTimer::jitterFps() const
{
- return m_count ? m_jitter / m_count : qreal(0);
+ return m_jitterFps;
+}
+
+/*!
+ \qmlproperty Object FrameTimer::window
+
+ The window to be monitored, from which frame-rate information will be gathered.
+ It can be either a toplevel Window (from the QtQuick.Window module) or a WindowObject
+ (from the QtApplicationManager.SystemUI module).
+
+ \sa WindowObject
+*/
+QObject *FrameTimer::window() const
+{
+ return m_window;
+}
+
+void FrameTimer::setWindow(QObject *value)
+{
+ if (m_window == value)
+ return;
+
+#if defined(AM_MULTI_PROCESS)
+ disconnectFromWaylandSurface();
+#endif
+
+ if (m_window)
+ disconnect(m_window, nullptr, this, nullptr);
+
+ m_window = value;
+
+ if (m_window) {
+ if (!connectToQuickWindow() && !connectToAppManWindow())
+ qmlWarning(this) << "The given window is neither a QQuickWindow nor a WindowObject.";
+ }
+
+ emit windowChanged();
+}
+
+bool FrameTimer::connectToQuickWindow()
+{
+ QQuickWindow *quickWindow = qobject_cast<QQuickWindow*>(m_window);
+ if (!quickWindow)
+ return false;
+
+ connect(quickWindow, &QQuickWindow::frameSwapped, this, &FrameTimer::newFrame, Qt::UniqueConnection);
+ return true;
+}
+
+bool FrameTimer::connectToAppManWindow()
+{
+ Window *appManWindow = qobject_cast<Window*>(m_window);
+ if (!appManWindow)
+ return false;
+
+ if (qobject_cast<InProcessWindow*>(appManWindow)) {
+ qmlWarning(this) << "It makes no sense to measure the FPS of a WindowObject in single-process mode."
+ " FrameTimer won't operate with the given window.";
+ return true;
+ }
+
+#if defined(AM_MULTI_PROCESS)
+ WaylandWindow *waylandWindow = qobject_cast<WaylandWindow*>(m_window);
+ Q_ASSERT(waylandWindow);
+
+ connect(waylandWindow, &WaylandWindow::waylandSurfaceChanged,
+ this, &FrameTimer::connectToWaylandSurface, Qt::UniqueConnection);
+
+ connectToWaylandSurface();
+#endif
+
+ return true;
+}
+
+#if defined(AM_MULTI_PROCESS)
+void FrameTimer::connectToWaylandSurface()
+{
+ WaylandWindow *waylandWindow = qobject_cast<WaylandWindow*>(m_window);
+ Q_ASSERT(waylandWindow);
+
+ disconnectFromWaylandSurface();
+
+ m_waylandSurface = waylandWindow->waylandSurface();
+ if (m_waylandSurface)
+ connect(m_waylandSurface, &QWaylandQuickSurface::redraw, this, &FrameTimer::newFrame, Qt::UniqueConnection);
+}
+
+void FrameTimer::disconnectFromWaylandSurface()
+{
+ if (!m_waylandSurface)
+ return;
+
+ disconnect(m_waylandSurface, nullptr, this, nullptr);
+
+ m_waylandSurface = nullptr;
+}
+#endif
+
+/*!
+ \qmlproperty list<string> FrameTimer::roleNames
+ \readonly
+
+ Names of the roles provided by FrameTimer when used as a MonitorModel data source.
+
+ \sa MonitorModel
+*/
+QStringList FrameTimer::roleNames() const
+{
+ return { qSL("averageFps"), qSL("minimumFps"), qSL("maximumFps"), qSL("jitterFps") };
+}
+
+/*!
+ \qmlmethod FrameTimer::update
+
+ Updates the properties averageFps, minimumFps, maximumFps and jitterFps. Then resets internal
+ counters so that new numbers can be taken for the new time period starting from the moment
+ this method is called.
+
+ Note that you normally don't have to call this method directly, as FrameTimer does it automatically
+ every \l interval milliseconds while \l{FrameTimer::running}{running} is set to true.
+
+ \sa running
+*/
+void FrameTimer::update()
+{
+ m_averageFps = m_sum ? MicrosInSec * m_count / m_sum : qreal(0);
+ m_minimumFps = m_max ? MicrosInSec / m_max : qreal(0);
+ m_maximumFps = m_min ? MicrosInSec / m_min : qreal(0);
+ m_jitterFps = m_count ? m_jitter / m_count : qreal(0);
+
+ // Start counting again for the next sampling period but keep m_timer running because
+ // we still need the diff between the last rendered frame and the upcoming one.
+ reset();
+
+ emit updated();
+}
+
+/*!
+ \qmlproperty bool FrameTimer::running
+
+ If \c true, update() will get called automatically every \l interval milliseconds.
+
+ When using FrameTimer as a MonitorModel data source, this property should be kept as \c false.
+
+ \sa update() interval
+*/
+bool FrameTimer::running() const
+{
+ return m_updateTimer.isActive();
+}
+
+void FrameTimer::setRunning(bool value)
+{
+ if (value && !m_updateTimer.isActive()) {
+ m_updateTimer.start();
+ emit runningChanged();
+ } else if (!value && m_updateTimer.isActive()) {
+ m_updateTimer.stop();
+ emit runningChanged();
+ }
+}
+
+/*!
+ \qmlproperty int FrameTimer::interval
+
+ The interval, in milliseconds, between update() calls while \l{FrameTimer::running}{running} is \c true.
+
+ \sa update() running
+*/
+int FrameTimer::interval() const
+{
+ return m_updateTimer.interval();
+}
+
+void FrameTimer::setInterval(int value)
+{
+ if (value != m_updateTimer.interval()) {
+ m_updateTimer.setInterval(value);
+ emit intervalChanged();
+ }
}
QT_END_NAMESPACE_AM