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;