Introduce RenderLoop
At the moment, our frame scheduling infrastructure is still heavily
based on Xinerama-style rendering. Specifically, we assume that painting
is driven by a single timer, etc.
This change introduces a new type - RenderLoop. Its main purpose is to
drive compositing on a specific output, or in case of X11, on the
overlay window.
With RenderLoop, compositing is synchronized to vblank events. It
exposes the last and the next estimated presentation timestamp. The
expected presentation timestamp can be used by effects to ensure that
animations are synchronized with the upcoming vblank event.
On Wayland, every outputs has its own render loop. On X11, per screen
rendering is not possible, therefore the platform exposes the render
loop for the overlay window. Ideally, the Scene has to expose the
RenderLoop, but as the first step towards better compositing scheduling
it's good as is for the time being.
The RenderLoop tries to minimize the latency by delaying compositing as
close as possible to the next vblank event. One tricky thing about it is
that if compositing is too close to the next vblank event, animations
may become a little bit choppy. However, increasing the latency reduces
the choppiness.
Given that, there is no any "silver bullet" solution for the choppiness
issue, a new option has been added in the Compositing KCM to specify the
amount of latency. By default, it's "Medium," but if a user is not
satisfied with the upstream default, they can tweak it.
2020-11-19 08:52:29 +00:00
|
|
|
/*
|
|
|
|
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
|
|
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "kwinglobals.h"
|
|
|
|
|
|
|
|
#include <QObject>
|
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
|
|
|
class RenderLoopPrivate;
|
2021-02-20 15:04:18 +00:00
|
|
|
class SurfaceItem;
|
Introduce RenderLoop
At the moment, our frame scheduling infrastructure is still heavily
based on Xinerama-style rendering. Specifically, we assume that painting
is driven by a single timer, etc.
This change introduces a new type - RenderLoop. Its main purpose is to
drive compositing on a specific output, or in case of X11, on the
overlay window.
With RenderLoop, compositing is synchronized to vblank events. It
exposes the last and the next estimated presentation timestamp. The
expected presentation timestamp can be used by effects to ensure that
animations are synchronized with the upcoming vblank event.
On Wayland, every outputs has its own render loop. On X11, per screen
rendering is not possible, therefore the platform exposes the render
loop for the overlay window. Ideally, the Scene has to expose the
RenderLoop, but as the first step towards better compositing scheduling
it's good as is for the time being.
The RenderLoop tries to minimize the latency by delaying compositing as
close as possible to the next vblank event. One tricky thing about it is
that if compositing is too close to the next vblank event, animations
may become a little bit choppy. However, increasing the latency reduces
the choppiness.
Given that, there is no any "silver bullet" solution for the choppiness
issue, a new option has been added in the Compositing KCM to specify the
amount of latency. By default, it's "Medium," but if a user is not
satisfied with the upstream default, they can tweak it.
2020-11-19 08:52:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The RenderLoop class represents the compositing scheduler on a particular output.
|
|
|
|
*
|
|
|
|
* The RenderLoop class drives the compositing. The frameRequested() signal is emitted
|
|
|
|
* when the loop wants a new frame to be rendered. The frameCompleted() signal is
|
|
|
|
* emitted when a previously rendered frame has been presented on the screen. In case
|
|
|
|
* you want the compositor to repaint the scene, call the scheduleRepaint() function.
|
|
|
|
*/
|
|
|
|
class KWIN_EXPORT RenderLoop : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
|
|
|
explicit RenderLoop(QObject *parent = nullptr);
|
|
|
|
~RenderLoop() override;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pauses the render loop. While the render loop is inhibited, scheduleRepaint()
|
|
|
|
* requests are queued.
|
|
|
|
*
|
|
|
|
* Once the render loop is uninhibited, the pending schedule requests are going to
|
|
|
|
* be re-applied.
|
|
|
|
*/
|
|
|
|
void inhibit();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Uninhibits the render loop.
|
|
|
|
*/
|
|
|
|
void uninhibit();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function must be called before the Compositor starts rendering the next
|
|
|
|
* frame.
|
|
|
|
*/
|
|
|
|
void beginFrame();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function must be called after the Compositor has finished rendering the
|
|
|
|
* next frame.
|
|
|
|
*/
|
|
|
|
void endFrame();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the refresh rate at which the output is being updated, in millihertz.
|
|
|
|
*/
|
|
|
|
int refreshRate() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the refresh rate of this RenderLoop to @a refreshRate, in millihertz.
|
|
|
|
*/
|
|
|
|
void setRefreshRate(int refreshRate);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Schedules a compositing cycle at the next available moment.
|
|
|
|
*/
|
|
|
|
void scheduleRepaint();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the timestamp of the last frame that has been presented on the screen.
|
|
|
|
* The returned timestamp is sourced from the monotonic clock.
|
|
|
|
*/
|
|
|
|
std::chrono::nanoseconds lastPresentationTimestamp() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If a repaint has been scheduled, this function returns the expected time when
|
|
|
|
* the next frame will be presented on the screen. The returned timestamp is sourced
|
|
|
|
* from the monotonic clock.
|
|
|
|
*/
|
|
|
|
std::chrono::nanoseconds nextPresentationTimestamp() const;
|
|
|
|
|
2021-02-20 15:04:18 +00:00
|
|
|
/**
|
|
|
|
* Sets the surface that currently gets scanned out,
|
|
|
|
* so that this RenderLoop can adjust its timing behavior to that surface
|
|
|
|
*/
|
|
|
|
void setFullscreenSurface(SurfaceItem *surface);
|
|
|
|
|
|
|
|
enum class VrrPolicy : uint32_t {
|
|
|
|
Never = 0,
|
|
|
|
Always = 1,
|
|
|
|
Automatic = 2,
|
|
|
|
};
|
|
|
|
Q_ENUM(VrrPolicy);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* the current policy regarding the use of variable refresh rate
|
|
|
|
*/
|
|
|
|
VrrPolicy vrrPolicy() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the policy regarding the use of variable refresh rate with RenderLoop
|
|
|
|
*/
|
|
|
|
void setVrrPolicy(VrrPolicy vrrPolicy);
|
|
|
|
|
Introduce RenderLoop
At the moment, our frame scheduling infrastructure is still heavily
based on Xinerama-style rendering. Specifically, we assume that painting
is driven by a single timer, etc.
This change introduces a new type - RenderLoop. Its main purpose is to
drive compositing on a specific output, or in case of X11, on the
overlay window.
With RenderLoop, compositing is synchronized to vblank events. It
exposes the last and the next estimated presentation timestamp. The
expected presentation timestamp can be used by effects to ensure that
animations are synchronized with the upcoming vblank event.
On Wayland, every outputs has its own render loop. On X11, per screen
rendering is not possible, therefore the platform exposes the render
loop for the overlay window. Ideally, the Scene has to expose the
RenderLoop, but as the first step towards better compositing scheduling
it's good as is for the time being.
The RenderLoop tries to minimize the latency by delaying compositing as
close as possible to the next vblank event. One tricky thing about it is
that if compositing is too close to the next vblank event, animations
may become a little bit choppy. However, increasing the latency reduces
the choppiness.
Given that, there is no any "silver bullet" solution for the choppiness
issue, a new option has been added in the Compositing KCM to specify the
amount of latency. By default, it's "Medium," but if a user is not
satisfied with the upstream default, they can tweak it.
2020-11-19 08:52:29 +00:00
|
|
|
Q_SIGNALS:
|
|
|
|
/**
|
|
|
|
* This signal is emitted when the refresh rate of this RenderLoop has changed.
|
|
|
|
*/
|
|
|
|
void refreshRateChanged();
|
|
|
|
/**
|
|
|
|
* This signal is emitted when a frame has been actually presented on the screen.
|
|
|
|
* @a timestamp indicates the time when it took place.
|
|
|
|
*/
|
|
|
|
void framePresented(RenderLoop *loop, std::chrono::nanoseconds timestamp);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This signal is emitted when the render loop wants a new frame to be composited.
|
|
|
|
*
|
|
|
|
* The Compositor should make a connection to this signal using Qt::DirectConnection.
|
|
|
|
*/
|
|
|
|
void frameRequested(RenderLoop *loop);
|
|
|
|
|
|
|
|
private:
|
|
|
|
QScopedPointer<RenderLoopPrivate> d;
|
|
|
|
friend class RenderLoopPrivate;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace KWin
|