core/renderloop: improve frame scheduling heuristics with VRR

Instead of checking for fullscreen windows and deciding whether or not to schedule repaints
based on that, check if the active window is refreshing fast enough to be reasonable for vrr.
For automatic mode, vrr is also enabled with the active window instead of the direct scanout
candidate.

BUG: 478680
BUG: 477199
wilder/Plasma/6.2
Xaver Hugl 2 years ago
parent e1373ae2e1
commit 7f9cbbaa98
  1. 15
      src/compositor.cpp
  2. 1
      src/core/output.h
  3. 16
      src/core/renderloop.cpp
  4. 7
      src/core/renderloop.h
  5. 3
      src/core/renderloop_p.h
  6. 21
      src/scene/surfaceitem.cpp
  7. 7
      src/scene/surfaceitem.h

@ -25,6 +25,7 @@
#include "utils/common.h"
#include "wayland/surface.h"
#include "wayland_server.h"
#include "window.h"
#include "workspace.h"
#include <KLocalizedString>
@ -166,18 +167,20 @@ void Compositor::composite(RenderLoop *renderLoop)
primaryLayer->resetRepaints();
prePaintPass(superLayer, &surfaceDamage);
SurfaceItem *scanoutCandidate = superLayer->delegate()->scanoutCandidate();
renderLoop->setFullscreenSurface(scanoutCandidate);
frame->setContentType(scanoutCandidate ? scanoutCandidate->contentType() : ContentType::None);
const bool vrr = (output->capabilities() & Output::Capability::Vrr) && (output->vrrPolicy() == VrrPolicy::Always || (output->vrrPolicy() == VrrPolicy::Automatic && scanoutCandidate));
const bool tearing = (output->capabilities() & Output::Capability::Tearing) && options->allowTearing() && scanoutCandidate && scanoutCandidate->presentationHint() == PresentationModeHint::Async;
Window *const activeWindow = workspace()->activeWindow();
SurfaceItem *const activeFullscreenItem = activeWindow && activeWindow->isFullScreen() ? activeWindow->surfaceItem() : nullptr;
frame->setContentType(activeWindow && activeFullscreenItem ? activeFullscreenItem->contentType() : ContentType::None);
const bool vrr = (output->capabilities() & Output::Capability::Vrr) && (output->vrrPolicy() == VrrPolicy::Always || (output->vrrPolicy() == VrrPolicy::Automatic && activeFullscreenItem));
const bool tearing = (output->capabilities() & Output::Capability::Tearing) && options->allowTearing() && activeFullscreenItem && activeFullscreenItem->presentationHint() == PresentationModeHint::Async;
if (vrr) {
frame->setPresentationMode(tearing ? PresentationMode::AdaptiveAsync : PresentationMode::AdaptiveSync);
} else {
frame->setPresentationMode(tearing ? PresentationMode::Async : PresentationMode::VSync);
}
bool directScanout = false;
if (scanoutCandidate) {
if (const auto scanoutCandidate = superLayer->delegate()->scanoutCandidate()) {
const auto sublayers = superLayer->sublayers();
const bool scanoutPossible = std::none_of(sublayers.begin(), sublayers.end(), [](RenderLayer *sublayer) {
return sublayer->isVisible();

@ -265,6 +265,7 @@ public:
/**
* Returns the RenderLoop for this output. If the platform does not support per screen
* rendering, all outputs will share the same render loop.
* FIXME: remove this and decouple RenderLoop from Output
*/
virtual RenderLoop *renderLoop() const = 0;

@ -9,6 +9,8 @@
#include "renderloop_p.h"
#include "scene/surfaceitem.h"
#include "utils/common.h"
#include "window.h"
#include "workspace.h"
namespace KWin
{
@ -193,9 +195,16 @@ void RenderLoop::setRefreshRate(int refreshRate)
void RenderLoop::scheduleRepaint(Item *item)
{
if (d->pendingRepaint || (d->fullscreenItem != nullptr && item != nullptr && item != d->fullscreenItem)) {
if (d->pendingRepaint) {
return;
}
const bool vrr = d->presentationMode == PresentationMode::AdaptiveSync || d->presentationMode == PresentationMode::AdaptiveAsync;
if (vrr && workspace()->activeWindow()) {
Window *const activeWindow = workspace()->activeWindow();
if (activeWindow->surfaceItem() && item != activeWindow->surfaceItem() && activeWindow->surfaceItem()->refreshRateEstimation() >= 30) {
return;
}
}
if (!d->pendingFrameCount && !d->inhibitCount) {
d->scheduleRepaint();
} else {
@ -213,11 +222,6 @@ std::chrono::nanoseconds RenderLoop::nextPresentationTimestamp() const
return d->nextPresentationTimestamp;
}
void RenderLoop::setFullscreenSurface(Item *surfaceItem)
{
d->fullscreenItem = surfaceItem;
}
void RenderLoop::setPresentationMode(PresentationMode mode)
{
d->presentationMode = mode;

@ -14,6 +14,7 @@ namespace KWin
{
class RenderLoopPrivate;
class SurfaceItem;
class Item;
/**
@ -86,12 +87,6 @@ public:
*/
std::chrono::nanoseconds nextPresentationTimestamp() const;
/**
* Sets the surface that currently gets scanned out,
* so that this RenderLoop can adjust its timing behavior to that surface
*/
void setFullscreenSurface(Item *surface);
void setPresentationMode(PresentationMode mode);
Q_SIGNALS:

@ -16,6 +16,8 @@
namespace KWin
{
class SurfaceItem;
class KWIN_EXPORT RenderLoopPrivate
{
public:
@ -43,7 +45,6 @@ public:
int inhibitCount = 0;
bool pendingReschedule = false;
bool pendingRepaint = false;
Item *fullscreenItem = nullptr;
PresentationMode presentationMode = PresentationMode::VSync;
};

@ -81,6 +81,16 @@ static QRegion expandRegion(const QRegion &region, const QMargins &padding)
void SurfaceItem::addDamage(const QRegion &region)
{
if (m_lastDamage) {
const auto diff = std::chrono::steady_clock::now() - *m_lastDamage;
m_lastDamageTimeDiffs.push_back(diff);
if (m_lastDamageTimeDiffs.size() > 100) {
m_lastDamageTimeDiffs.pop_front();
}
const auto average = std::accumulate(m_lastDamageTimeDiffs.begin(), m_lastDamageTimeDiffs.end(), std::chrono::nanoseconds::zero()) / m_lastDamageTimeDiffs.size();
m_refreshRate = std::chrono::nanoseconds(1'000'000'000) / average;
}
m_lastDamage = std::chrono::steady_clock::now();
m_damage += region;
const QRectF sourceBox = m_bufferTransform.map(m_bufferSourceBox, m_bufferSize);
@ -231,6 +241,17 @@ void SurfaceItem::freeze()
{
}
double SurfaceItem::refreshRateEstimation() const
{
if (m_lastDamage) {
const auto diff = std::chrono::steady_clock::now() - *m_lastDamage;
const double refreshRate = std::chrono::nanoseconds(1'000'000'000) / diff;
return std::min(m_refreshRate, refreshRate);
} else {
return m_refreshRate;
}
}
SurfaceTexture::~SurfaceTexture()
{
}

@ -10,6 +10,8 @@
#include "core/output.h"
#include "scene/item.h"
#include <deque>
namespace KWin
{
@ -56,6 +58,8 @@ public:
virtual void freeze();
double refreshRateEstimation() const;
Q_SIGNALS:
void damaged();
@ -75,6 +79,9 @@ protected:
QMatrix4x4 m_surfaceToBufferMatrix;
QMatrix4x4 m_bufferToSurfaceMatrix;
int m_referencePixmapCounter = 0;
std::deque<std::chrono::nanoseconds> m_lastDamageTimeDiffs;
std::optional<std::chrono::steady_clock::time_point> m_lastDamage;
double m_refreshRate = 0;
};
class KWIN_EXPORT SurfaceTexture

Loading…
Cancel
Save