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:
parent
b8a70e62d5
commit
ad5f8c5c59
8 changed files with 172 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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">
|
||||
|
|
16
options.cpp
16
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)
|
||||
|
|
18
options.h
18
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;
|
||||
|
|
56
renderjournal.cpp
Normal file
56
renderjournal.cpp
Normal 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
57
renderjournal.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue