Introduce RenderJournal

Currently, we estimate the expected render time purely based on the
latency policy.

The problem with doing so is that the real render time might be larger,
this can result in frame drops.

In order to avoid frame drops, we need to take into account previous
render times while estimating the next render time. For now, we just
measure how long it takes to record rendering commands on the CPU.

In the future, we might want consider using OpenGL timer queries for
measuring the real render time, but for now, it's good enough.
This commit is contained in:
Vlad Zahorodnii 2020-11-28 17:12:38 +02:00
parent b8a70e62d5
commit ad5f8c5c59
8 changed files with 172 additions and 0 deletions

View file

@ -506,6 +506,7 @@ set(kwin_SRCS
pluginmanager.cpp pluginmanager.cpp
pointer_input.cpp pointer_input.cpp
popup_input_filter.cpp popup_input_filter.cpp
renderjournal.cpp
renderloop.cpp renderloop.cpp
rootinfo_filter.cpp rootinfo_filter.cpp
rulebooksettings.cpp rulebooksettings.cpp

View file

@ -255,6 +255,14 @@
</choices> </choices>
<default>LatencyMedium</default> <default>LatencyMedium</default>
</entry> </entry>
<entry name="RenderTimeEstimator" type="Enum">
<choices name="KWin::RenderTimeEstimator">
<choice name="RenderTimeEstimatorMinimum" value="Minimum"/>
<choice name="RenderTimeEstimatorMaximum" value="Maximum"/>
<choice name="RenderTimeEstimatorAverage" value="Average"/>
</choices>
<default>RenderTimeEstimatorMaximum</default>
</entry>
</group> </group>
<group name="TabBox"> <group name="TabBox">
<entry name="ShowDelay" type="Bool"> <entry name="ShowDelay" type="Bool">

View file

@ -54,6 +54,7 @@ Options::Options(QObject *parent)
, m_xwaylandCrashPolicy(Options::defaultXwaylandCrashPolicy()) , m_xwaylandCrashPolicy(Options::defaultXwaylandCrashPolicy())
, m_xwaylandMaxCrashCount(Options::defaultXwaylandMaxCrashCount()) , m_xwaylandMaxCrashCount(Options::defaultXwaylandMaxCrashCount())
, m_latencyPolicy(Options::defaultLatencyPolicy()) , m_latencyPolicy(Options::defaultLatencyPolicy())
, m_renderTimeEstimator(Options::defaultRenderTimeEstimator())
, m_compositingMode(Options::defaultCompositingMode()) , m_compositingMode(Options::defaultCompositingMode())
, m_useCompositing(Options::defaultUseCompositing()) , m_useCompositing(Options::defaultUseCompositing())
, m_hiddenPreviews(Options::defaultHiddenPreviews()) , m_hiddenPreviews(Options::defaultHiddenPreviews())
@ -648,6 +649,20 @@ void Options::setLatencyPolicy(LatencyPolicy policy)
emit latencyPolicyChanged(); 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) void Options::setGlPlatformInterface(OpenGLPlatformInterface interface)
{ {
// check environment variable // check environment variable
@ -796,6 +811,7 @@ void Options::syncFromKcfgc()
setWindowsBlockCompositing(m_settings->windowsBlockCompositing()); setWindowsBlockCompositing(m_settings->windowsBlockCompositing());
setMoveMinimizedWindowsToEndOfTabBoxFocusChain(m_settings->moveMinimizedWindowsToEndOfTabBoxFocusChain()); setMoveMinimizedWindowsToEndOfTabBoxFocusChain(m_settings->moveMinimizedWindowsToEndOfTabBoxFocusChain());
setLatencyPolicy(m_settings->latencyPolicy()); setLatencyPolicy(m_settings->latencyPolicy());
setRenderTimeEstimator(m_settings->renderTimeEstimator());
} }
bool Options::loadCompositingConfig (bool force) bool Options::loadCompositingConfig (bool force)

View file

@ -53,6 +53,15 @@ enum LatencyPolicy {
LatencyExtremelyHigh, LatencyExtremelyHigh,
}; };
/**
* This enum type specifies the method for estimating the expected render time.
*/
enum RenderTimeEstimator {
RenderTimeEstimatorMinimum,
RenderTimeEstimatorMaximum,
RenderTimeEstimatorAverage,
};
class Settings; class Settings;
class KWIN_EXPORT Options : public QObject class KWIN_EXPORT Options : public QObject
@ -60,6 +69,7 @@ class KWIN_EXPORT Options : public QObject
Q_OBJECT Q_OBJECT
Q_ENUMS(XwaylandCrashPolicy) Q_ENUMS(XwaylandCrashPolicy)
Q_ENUMS(LatencyPolicy) Q_ENUMS(LatencyPolicy)
Q_ENUMS(RenderTimeEstimator)
Q_PROPERTY(FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy NOTIFY focusPolicyChanged) Q_PROPERTY(FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy NOTIFY focusPolicyChanged)
Q_PROPERTY(XwaylandCrashPolicy xwaylandCrashPolicy READ xwaylandCrashPolicy WRITE setXwaylandCrashPolicy NOTIFY xwaylandCrashPolicyChanged) Q_PROPERTY(XwaylandCrashPolicy xwaylandCrashPolicy READ xwaylandCrashPolicy WRITE setXwaylandCrashPolicy NOTIFY xwaylandCrashPolicyChanged)
Q_PROPERTY(int xwaylandMaxCrashCount READ xwaylandMaxCrashCount WRITE setXwaylandMaxCrashCount NOTIFY xwaylandMaxCrashCountChanged) 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(KWin::OpenGLPlatformInterface glPlatformInterface READ glPlatformInterface WRITE setGlPlatformInterface NOTIFY glPlatformInterfaceChanged)
Q_PROPERTY(bool windowsBlockCompositing READ windowsBlockCompositing WRITE setWindowsBlockCompositing NOTIFY windowsBlockCompositingChanged) Q_PROPERTY(bool windowsBlockCompositing READ windowsBlockCompositing WRITE setWindowsBlockCompositing NOTIFY windowsBlockCompositingChanged)
Q_PROPERTY(LatencyPolicy latencyPolicy READ latencyPolicy WRITE setLatencyPolicy NOTIFY latencyPolicyChanged) Q_PROPERTY(LatencyPolicy latencyPolicy READ latencyPolicy WRITE setLatencyPolicy NOTIFY latencyPolicyChanged)
Q_PROPERTY(RenderTimeEstimator renderTimeEstimator READ renderTimeEstimator WRITE setRenderTimeEstimator NOTIFY renderTimeEstimatorChanged)
public: public:
explicit Options(QObject *parent = nullptr); explicit Options(QObject *parent = nullptr);
@ -596,6 +607,7 @@ public:
QStringList modifierOnlyDBusShortcut(Qt::KeyboardModifier mod) const; QStringList modifierOnlyDBusShortcut(Qt::KeyboardModifier mod) const;
LatencyPolicy latencyPolicy() const; LatencyPolicy latencyPolicy() const;
RenderTimeEstimator renderTimeEstimator() const;
// setters // setters
void setFocusPolicy(FocusPolicy focusPolicy); void setFocusPolicy(FocusPolicy focusPolicy);
@ -655,6 +667,7 @@ public:
void setWindowsBlockCompositing(bool set); void setWindowsBlockCompositing(bool set);
void setMoveMinimizedWindowsToEndOfTabBoxFocusChain(bool set); void setMoveMinimizedWindowsToEndOfTabBoxFocusChain(bool set);
void setLatencyPolicy(LatencyPolicy policy); void setLatencyPolicy(LatencyPolicy policy);
void setRenderTimeEstimator(RenderTimeEstimator estimator);
// default values // default values
static WindowOperation defaultOperationTitlebarDblClick() { static WindowOperation defaultOperationTitlebarDblClick() {
@ -756,6 +769,9 @@ public:
static LatencyPolicy defaultLatencyPolicy() { static LatencyPolicy defaultLatencyPolicy() {
return LatencyMedium; return LatencyMedium;
} }
static RenderTimeEstimator defaultRenderTimeEstimator() {
return RenderTimeEstimatorMaximum;
}
/** /**
* Performs loading all settings except compositing related. * Performs loading all settings except compositing related.
*/ */
@ -828,6 +844,7 @@ Q_SIGNALS:
void animationSpeedChanged(); void animationSpeedChanged();
void latencyPolicyChanged(); void latencyPolicyChanged();
void configChanged(); void configChanged();
void renderTimeEstimatorChanged();
private: private:
void setElectricBorders(int borders); void setElectricBorders(int borders);
@ -856,6 +873,7 @@ private:
XwaylandCrashPolicy m_xwaylandCrashPolicy; XwaylandCrashPolicy m_xwaylandCrashPolicy;
int m_xwaylandMaxCrashCount; int m_xwaylandMaxCrashCount;
LatencyPolicy m_latencyPolicy; LatencyPolicy m_latencyPolicy;
RenderTimeEstimator m_renderTimeEstimator;
CompositingType m_compositingMode; CompositingType m_compositingMode;
bool m_useCompositing; bool m_useCompositing;

56
renderjournal.cpp Normal file
View file

@ -0,0 +1,56 @@
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
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

57
renderjournal.h Normal file
View file

@ -0,0 +1,57 @@
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kwinglobals.h"
#include <QElapsedTimer>
#include <QQueue>
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<std::chrono::nanoseconds> m_log;
int m_size = 15;
};
} // namespace KWin

View file

@ -65,6 +65,18 @@ void RenderLoopPrivate::scheduleRepaint()
break; 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; std::chrono::nanoseconds nextRenderTimestamp = nextPresentationTimestamp - renderTime - safetyMargin;
// If we can't render the frame before the deadline, start compositing immediately. // If we can't render the frame before the deadline, start compositing immediately.
@ -162,10 +174,12 @@ void RenderLoop::beginFrame()
{ {
d->pendingRepaint = false; d->pendingRepaint = false;
d->pendingFrameCount++; d->pendingFrameCount++;
d->renderJournal.beginFrame();
} }
void RenderLoop::endFrame() void RenderLoop::endFrame()
{ {
d->renderJournal.endFrame();
} }
int RenderLoop::refreshRate() const int RenderLoop::refreshRate() const

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include "renderloop.h" #include "renderloop.h"
#include "renderjournal.h"
#include <QTimer> #include <QTimer>
@ -32,6 +33,7 @@ public:
std::chrono::nanoseconds lastPresentationTimestamp = std::chrono::nanoseconds::zero(); std::chrono::nanoseconds lastPresentationTimestamp = std::chrono::nanoseconds::zero();
std::chrono::nanoseconds nextPresentationTimestamp = std::chrono::nanoseconds::zero(); std::chrono::nanoseconds nextPresentationTimestamp = std::chrono::nanoseconds::zero();
QTimer compositeTimer; QTimer compositeTimer;
RenderJournal renderJournal;
int refreshRate = 60000; int refreshRate = 60000;
int pendingFrameCount = 0; int pendingFrameCount = 0;
int inhibitCount = 0; int inhibitCount = 0;