diff --git a/src/compositor.cpp b/src/compositor.cpp index 6f17399cd5..83b72622a5 100644 --- a/src/compositor.cpp +++ b/src/compositor.cpp @@ -25,6 +25,7 @@ #include "utils/common.h" #include "wayland/surface.h" #include "wayland_server.h" +#include "window.h" #include "workspace.h" #include @@ -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(); diff --git a/src/core/output.h b/src/core/output.h index c84643e96b..2f027378ce 100644 --- a/src/core/output.h +++ b/src/core/output.h @@ -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; diff --git a/src/core/renderloop.cpp b/src/core/renderloop.cpp index 28e0e1115c..4dcd359b47 100644 --- a/src/core/renderloop.cpp +++ b/src/core/renderloop.cpp @@ -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; diff --git a/src/core/renderloop.h b/src/core/renderloop.h index 21992a5381..caa9cb8249 100644 --- a/src/core/renderloop.h +++ b/src/core/renderloop.h @@ -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: diff --git a/src/core/renderloop_p.h b/src/core/renderloop_p.h index cec1bfb0d4..c4779501b2 100644 --- a/src/core/renderloop_p.h +++ b/src/core/renderloop_p.h @@ -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; }; diff --git a/src/scene/surfaceitem.cpp b/src/scene/surfaceitem.cpp index bbd1d50e09..856a06b029 100644 --- a/src/scene/surfaceitem.cpp +++ b/src/scene/surfaceitem.cpp @@ -81,6 +81,16 @@ static QRegion expandRegion(const QRegion ®ion, const QMargins &padding) void SurfaceItem::addDamage(const QRegion ®ion) { + 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() { } diff --git a/src/scene/surfaceitem.h b/src/scene/surfaceitem.h index ea89e8db5a..59519c8825 100644 --- a/src/scene/surfaceitem.h +++ b/src/scene/surfaceitem.h @@ -10,6 +10,8 @@ #include "core/output.h" #include "scene/item.h" +#include + 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 m_lastDamageTimeDiffs; + std::optional m_lastDamage; + double m_refreshRate = 0; }; class KWIN_EXPORT SurfaceTexture