diff --git a/CMakeLists.txt b/CMakeLists.txt
index f08c1b39e2..399334424a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -506,6 +506,7 @@ set(kwin_SRCS
pluginmanager.cpp
pointer_input.cpp
popup_input_filter.cpp
+ renderjournal.cpp
renderloop.cpp
rootinfo_filter.cpp
rulebooksettings.cpp
diff --git a/kwin.kcfg b/kwin.kcfg
index ea03032d23..4a0effc51c 100644
--- a/kwin.kcfg
+++ b/kwin.kcfg
@@ -255,6 +255,14 @@
LatencyMedium
+
+
+
+
+
+
+ RenderTimeEstimatorMaximum
+
diff --git a/options.cpp b/options.cpp
index 0ed1ad765a..31465c59a5 100644
--- a/options.cpp
+++ b/options.cpp
@@ -54,6 +54,7 @@ Options::Options(QObject *parent)
, m_xwaylandCrashPolicy(Options::defaultXwaylandCrashPolicy())
, m_xwaylandMaxCrashCount(Options::defaultXwaylandMaxCrashCount())
, m_latencyPolicy(Options::defaultLatencyPolicy())
+ , m_renderTimeEstimator(Options::defaultRenderTimeEstimator())
, m_compositingMode(Options::defaultCompositingMode())
, m_useCompositing(Options::defaultUseCompositing())
, m_hiddenPreviews(Options::defaultHiddenPreviews())
@@ -648,6 +649,20 @@ void Options::setLatencyPolicy(LatencyPolicy policy)
emit latencyPolicyChanged();
}
+RenderTimeEstimator Options::renderTimeEstimator() const
+{
+ return m_renderTimeEstimator;
+}
+
+void Options::setRenderTimeEstimator(RenderTimeEstimator estimator)
+{
+ if (m_renderTimeEstimator == estimator) {
+ return;
+ }
+ m_renderTimeEstimator = estimator;
+ emit renderTimeEstimatorChanged();
+}
+
void Options::setGlPlatformInterface(OpenGLPlatformInterface interface)
{
// check environment variable
@@ -796,6 +811,7 @@ void Options::syncFromKcfgc()
setWindowsBlockCompositing(m_settings->windowsBlockCompositing());
setMoveMinimizedWindowsToEndOfTabBoxFocusChain(m_settings->moveMinimizedWindowsToEndOfTabBoxFocusChain());
setLatencyPolicy(m_settings->latencyPolicy());
+ setRenderTimeEstimator(m_settings->renderTimeEstimator());
}
bool Options::loadCompositingConfig (bool force)
diff --git a/options.h b/options.h
index 041848f468..13c4da7ec1 100644
--- a/options.h
+++ b/options.h
@@ -53,6 +53,15 @@ enum LatencyPolicy {
LatencyExtremelyHigh,
};
+/**
+ * This enum type specifies the method for estimating the expected render time.
+ */
+enum RenderTimeEstimator {
+ RenderTimeEstimatorMinimum,
+ RenderTimeEstimatorMaximum,
+ RenderTimeEstimatorAverage,
+};
+
class Settings;
class KWIN_EXPORT Options : public QObject
@@ -60,6 +69,7 @@ class KWIN_EXPORT Options : public QObject
Q_OBJECT
Q_ENUMS(XwaylandCrashPolicy)
Q_ENUMS(LatencyPolicy)
+ Q_ENUMS(RenderTimeEstimator)
Q_PROPERTY(FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy NOTIFY focusPolicyChanged)
Q_PROPERTY(XwaylandCrashPolicy xwaylandCrashPolicy READ xwaylandCrashPolicy WRITE setXwaylandCrashPolicy NOTIFY xwaylandCrashPolicyChanged)
Q_PROPERTY(int xwaylandMaxCrashCount READ xwaylandMaxCrashCount WRITE setXwaylandMaxCrashCount NOTIFY xwaylandMaxCrashCountChanged)
@@ -188,6 +198,7 @@ class KWIN_EXPORT Options : public QObject
Q_PROPERTY(KWin::OpenGLPlatformInterface glPlatformInterface READ glPlatformInterface WRITE setGlPlatformInterface NOTIFY glPlatformInterfaceChanged)
Q_PROPERTY(bool windowsBlockCompositing READ windowsBlockCompositing WRITE setWindowsBlockCompositing NOTIFY windowsBlockCompositingChanged)
Q_PROPERTY(LatencyPolicy latencyPolicy READ latencyPolicy WRITE setLatencyPolicy NOTIFY latencyPolicyChanged)
+ Q_PROPERTY(RenderTimeEstimator renderTimeEstimator READ renderTimeEstimator WRITE setRenderTimeEstimator NOTIFY renderTimeEstimatorChanged)
public:
explicit Options(QObject *parent = nullptr);
@@ -596,6 +607,7 @@ public:
QStringList modifierOnlyDBusShortcut(Qt::KeyboardModifier mod) const;
LatencyPolicy latencyPolicy() const;
+ RenderTimeEstimator renderTimeEstimator() const;
// setters
void setFocusPolicy(FocusPolicy focusPolicy);
@@ -655,6 +667,7 @@ public:
void setWindowsBlockCompositing(bool set);
void setMoveMinimizedWindowsToEndOfTabBoxFocusChain(bool set);
void setLatencyPolicy(LatencyPolicy policy);
+ void setRenderTimeEstimator(RenderTimeEstimator estimator);
// default values
static WindowOperation defaultOperationTitlebarDblClick() {
@@ -756,6 +769,9 @@ public:
static LatencyPolicy defaultLatencyPolicy() {
return LatencyMedium;
}
+ static RenderTimeEstimator defaultRenderTimeEstimator() {
+ return RenderTimeEstimatorMaximum;
+ }
/**
* Performs loading all settings except compositing related.
*/
@@ -828,6 +844,7 @@ Q_SIGNALS:
void animationSpeedChanged();
void latencyPolicyChanged();
void configChanged();
+ void renderTimeEstimatorChanged();
private:
void setElectricBorders(int borders);
@@ -856,6 +873,7 @@ private:
XwaylandCrashPolicy m_xwaylandCrashPolicy;
int m_xwaylandMaxCrashCount;
LatencyPolicy m_latencyPolicy;
+ RenderTimeEstimator m_renderTimeEstimator;
CompositingType m_compositingMode;
bool m_useCompositing;
diff --git a/renderjournal.cpp b/renderjournal.cpp
new file mode 100644
index 0000000000..1189e88e45
--- /dev/null
+++ b/renderjournal.cpp
@@ -0,0 +1,56 @@
+/*
+ SPDX-FileCopyrightText: 2020 Vlad Zahorodnii
+
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#include "renderjournal.h"
+
+namespace KWin
+{
+
+RenderJournal::RenderJournal()
+{
+}
+
+void RenderJournal::beginFrame()
+{
+ m_timer.start();
+}
+
+void RenderJournal::endFrame()
+{
+ std::chrono::nanoseconds duration(m_timer.nsecsElapsed());
+ if (m_log.count() >= m_size) {
+ m_log.dequeue();
+ }
+ m_log.enqueue(duration);
+}
+
+std::chrono::nanoseconds RenderJournal::minimum() const
+{
+ auto it = std::min_element(m_log.constBegin(), m_log.constEnd());
+ return it != m_log.constEnd() ? (*it) : std::chrono::nanoseconds::zero();
+}
+
+std::chrono::nanoseconds RenderJournal::maximum() const
+{
+ auto it = std::max_element(m_log.constBegin(), m_log.constEnd());
+ return it != m_log.constEnd() ? (*it) : std::chrono::nanoseconds::zero();
+}
+
+std::chrono::nanoseconds RenderJournal::average() const
+{
+ if (m_log.isEmpty()) {
+ return std::chrono::nanoseconds::zero();
+ }
+
+ std::chrono::nanoseconds result = std::chrono::nanoseconds::zero();
+ for (const std::chrono::nanoseconds &entry : m_log) {
+ result += entry;
+ }
+
+ return result / m_log.count();
+}
+
+} // namespace KWin
diff --git a/renderjournal.h b/renderjournal.h
new file mode 100644
index 0000000000..dcd73ad347
--- /dev/null
+++ b/renderjournal.h
@@ -0,0 +1,57 @@
+/*
+ SPDX-FileCopyrightText: 2020 Vlad Zahorodnii
+
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#pragma once
+
+#include "kwinglobals.h"
+
+#include
+#include
+
+namespace KWin
+{
+
+/**
+ * The RenderJournal class measures how long it takes to render frames and estimates how
+ * long it will take to render the next frame.
+ */
+class KWIN_EXPORT RenderJournal
+{
+public:
+ RenderJournal();
+
+ /**
+ * This function must be called before starting rendering a new frame.
+ */
+ void beginFrame();
+
+ /**
+ * This function must be called after finishing rendering a frame.
+ */
+ void endFrame();
+
+ /**
+ * Returns the maximum estimated amount of time that it takes to render a single frame.
+ */
+ std::chrono::nanoseconds maximum() const;
+
+ /**
+ * Returns the minimum estimated amount of time that it takes to render a single frame.
+ */
+ std::chrono::nanoseconds minimum() const;
+
+ /**
+ * Returns the average estimated amount of time that it takes to render a single frame.
+ */
+ std::chrono::nanoseconds average() const;
+
+private:
+ QElapsedTimer m_timer;
+ QQueue m_log;
+ int m_size = 15;
+};
+
+} // namespace KWin
diff --git a/renderloop.cpp b/renderloop.cpp
index f6e90d7939..5b836a144f 100644
--- a/renderloop.cpp
+++ b/renderloop.cpp
@@ -65,6 +65,18 @@ void RenderLoopPrivate::scheduleRepaint()
break;
}
+ switch (options->renderTimeEstimator()) {
+ case RenderTimeEstimatorMinimum:
+ renderTime = std::max(renderTime, renderJournal.minimum());
+ break;
+ case RenderTimeEstimatorMaximum:
+ renderTime = std::max(renderTime, renderJournal.maximum());
+ break;
+ case RenderTimeEstimatorAverage:
+ renderTime = std::max(renderTime, renderJournal.average());
+ break;
+ }
+
std::chrono::nanoseconds nextRenderTimestamp = nextPresentationTimestamp - renderTime - safetyMargin;
// If we can't render the frame before the deadline, start compositing immediately.
@@ -162,10 +174,12 @@ void RenderLoop::beginFrame()
{
d->pendingRepaint = false;
d->pendingFrameCount++;
+ d->renderJournal.beginFrame();
}
void RenderLoop::endFrame()
{
+ d->renderJournal.endFrame();
}
int RenderLoop::refreshRate() const
diff --git a/renderloop_p.h b/renderloop_p.h
index ffb0a6f4fd..e26552ebfa 100644
--- a/renderloop_p.h
+++ b/renderloop_p.h
@@ -7,6 +7,7 @@
#pragma once
#include "renderloop.h"
+#include "renderjournal.h"
#include
@@ -32,6 +33,7 @@ public:
std::chrono::nanoseconds lastPresentationTimestamp = std::chrono::nanoseconds::zero();
std::chrono::nanoseconds nextPresentationTimestamp = std::chrono::nanoseconds::zero();
QTimer compositeTimer;
+ RenderJournal renderJournal;
int refreshRate = 60000;
int pendingFrameCount = 0;
int inhibitCount = 0;