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.
icc-effect-5.26.4
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
pointer_input.cpp
popup_input_filter.cpp
renderjournal.cpp
renderloop.cpp
rootinfo_filter.cpp
rulebooksettings.cpp

View File

@ -255,6 +255,14 @@
</choices>
<default>LatencyMedium</default>
</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 name="TabBox">
<entry name="ShowDelay" type="Bool">

View File

@ -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)

View File

@ -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;

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;
}
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

View File

@ -7,6 +7,7 @@
#pragma once
#include "renderloop.h"
#include "renderjournal.h"
#include <QTimer>
@ -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;