diff --git a/src/abstract_wayland_output.cpp b/src/abstract_wayland_output.cpp index dcd9e6ee3e..166d81419a 100644 --- a/src/abstract_wayland_output.cpp +++ b/src/abstract_wayland_output.cpp @@ -179,6 +179,10 @@ void AbstractWaylandOutput::applyChanges(const KWaylandServer::OutputChangeSet * qCDebug(KWIN_CORE) << "Setting overscan:" << changeSet->overscan(); setOverscan(changeSet->overscan()); } + if (changeSet->vrrPolicyChanged()) { + qCDebug(KWIN_CORE) << "Setting VRR Policy:" << changeSet->vrrPolicy(); + setVrrPolicy(static_cast(changeSet->vrrPolicy())); + } overallSizeCheckNeeded |= emitModeChanged; if (overallSizeCheckNeeded) { @@ -368,4 +372,17 @@ void AbstractWaylandOutput::setOverscan(uint32_t overscan) Q_UNUSED(overscan); } +void AbstractWaylandOutput::setVrrPolicy(RenderLoop::VrrPolicy policy) +{ + if (renderLoop()->vrrPolicy() != policy) { + renderLoop()->setVrrPolicy(policy); + emit vrrPolicyChanged(); + } +} + +RenderLoop::VrrPolicy AbstractWaylandOutput::vrrPolicy() const +{ + return renderLoop()->vrrPolicy(); +} + } diff --git a/src/abstract_wayland_output.h b/src/abstract_wayland_output.h index 00365b98b5..82173e4190 100644 --- a/src/abstract_wayland_output.h +++ b/src/abstract_wayland_output.h @@ -11,6 +11,7 @@ #include "abstract_output.h" #include "utils.h" +#include "renderloop.h" #include #include @@ -66,6 +67,7 @@ public: enum class Capability : uint { Dpms = 0x1, Overscan = 0x2, + Vrr = 0x4, }; Q_DECLARE_FLAGS(Capabilities, Capability) @@ -142,6 +144,9 @@ public: bool isBeingRecorded(); + void setVrrPolicy(RenderLoop::VrrPolicy policy); + RenderLoop::VrrPolicy vrrPolicy() const; + Q_SIGNALS: void modeChanged(); void outputChange(const QRegion &damagedRegion); @@ -150,6 +155,7 @@ Q_SIGNALS: void dpmsModeChanged(); void capabilitiesChanged(); void overscanChanged(); + void vrrPolicyChanged(); protected: void initialize(const QString &model, const QString &manufacturer, diff --git a/src/plugins/platforms/drm/drm_object_connector.cpp b/src/plugins/platforms/drm/drm_object_connector.cpp index 6bd17c3297..853d22a6b6 100644 --- a/src/plugins/platforms/drm/drm_object_connector.cpp +++ b/src/plugins/platforms/drm/drm_object_connector.cpp @@ -39,6 +39,7 @@ bool DrmConnector::init() PropertyDefinition(QByteArrayLiteral("DPMS")), PropertyDefinition(QByteArrayLiteral("EDID")), PropertyDefinition(QByteArrayLiteral("overscan")), + PropertyDefinition(QByteArrayLiteral("vrr_capable")), }, DRM_MODE_OBJECT_CONNECTOR)) { return false; } @@ -158,4 +159,12 @@ void DrmConnector::setOverscan(uint32_t overscan) setValue(PropertyIndex::Overscan, overscan); } +bool DrmConnector::vrrCapable() const +{ + if (const auto &prop = m_props[static_cast(PropertyIndex::VrrCapable)]) { + return prop->value(); + } + return false; +} + } diff --git a/src/plugins/platforms/drm/drm_object_connector.h b/src/plugins/platforms/drm/drm_object_connector.h index c199e23759..547ad9a53a 100644 --- a/src/plugins/platforms/drm/drm_object_connector.h +++ b/src/plugins/platforms/drm/drm_object_connector.h @@ -31,6 +31,7 @@ public: Dpms = 2, Edid = 3, Overscan = 4, + VrrCapable = 5, Count }; @@ -66,6 +67,8 @@ public: uint32_t overscan() const; void setOverscan(uint32_t overscan); + bool vrrCapable() const; + private: DrmScopedPointer m_conn; QVector m_encoders; diff --git a/src/plugins/platforms/drm/drm_object_crtc.cpp b/src/plugins/platforms/drm/drm_object_crtc.cpp index 745ff2c5ce..0fe81131f2 100644 --- a/src/plugins/platforms/drm/drm_object_crtc.cpp +++ b/src/plugins/platforms/drm/drm_object_crtc.cpp @@ -33,6 +33,7 @@ bool DrmCrtc::init() return initProps({ PropertyDefinition(QByteArrayLiteral("MODE_ID")), PropertyDefinition(QByteArrayLiteral("ACTIVE")), + PropertyDefinition(QByteArrayLiteral("VRR_ENABLED")), }, DRM_MODE_OBJECT_CRTC); } @@ -81,4 +82,30 @@ bool DrmCrtc::setGammaRamp(const GammaRamp &gamma) return !isError; } +bool DrmCrtc::setVrr(bool enable) +{ + if (const auto &prop = m_props[static_cast(PropertyIndex::VrrEnabled)]) { + if (prop->value() == enable) { + return false; + } + prop->setValue(enable); + if (!gpu()->atomicModeSetting() || gpu()->useEglStreams()) { + if (drmModeObjectSetProperty(gpu()->fd(), id(), DRM_MODE_OBJECT_CRTC, prop->propId(), enable) != 0) { + qCWarning(KWIN_DRM) << "drmModeObjectSetProperty(VRR_ENABLED) failed"; + return false; + } + } + return true; + } + return false; +} + +bool DrmCrtc::isVrrEnabled() const +{ + if (const auto &prop = m_props[static_cast(PropertyIndex::VrrEnabled)]) { + return prop->value(); + } + return false; +} + } diff --git a/src/plugins/platforms/drm/drm_object_crtc.h b/src/plugins/platforms/drm/drm_object_crtc.h index 1ee1ca94d9..ed9dd93e22 100644 --- a/src/plugins/platforms/drm/drm_object_crtc.h +++ b/src/plugins/platforms/drm/drm_object_crtc.h @@ -32,6 +32,7 @@ public: enum class PropertyIndex : uint32_t { ModeId = 0, Active, + VrrEnabled, Count }; @@ -60,6 +61,9 @@ public: } bool setGammaRamp(const GammaRamp &gamma); + bool setVrr(bool enable); + bool isVrrEnabled() const; + private: DrmScopedPointer m_crtc; QSharedPointer m_currentBuffer; diff --git a/src/plugins/platforms/drm/drm_output.cpp b/src/plugins/platforms/drm/drm_output.cpp index 31d0383ab9..d4cc65abda 100644 --- a/src/plugins/platforms/drm/drm_output.cpp +++ b/src/plugins/platforms/drm/drm_output.cpp @@ -16,6 +16,7 @@ #include "logging.h" #include "main.h" #include "renderloop.h" +#include "renderloop_p.h" #include "screens.h" #include "session.h" // Qt @@ -76,13 +77,25 @@ void DrmOutput::teardown() bool DrmOutput::hideCursor() { + if (RenderLoopPrivate::get(m_renderLoop)->presentMode == RenderLoopPrivate::SyncMode::Adaptive + && isCursorVisible()) { + m_renderLoop->scheduleRepaint(); + } return drmModeSetCursor(m_gpu->fd(), m_crtc->id(), 0, 0, 0) == 0; } bool DrmOutput::showCursor(DrmDumbBuffer *c) { const QSize &s = c->size(); - return drmModeSetCursor(m_gpu->fd(), m_crtc->id(), c->handle(), s.width(), s.height()) == 0; + if (drmModeSetCursor(m_gpu->fd(), m_crtc->id(), c->handle(), s.width(), s.height()) == 0) { + if (RenderLoopPrivate::get(m_renderLoop)->presentMode == RenderLoopPrivate::SyncMode::Adaptive + && isCursorVisible()) { + m_renderLoop->scheduleRepaint(); + } + return true; + } else { + return false; + } } bool DrmOutput::showCursor() @@ -97,10 +110,16 @@ bool DrmOutput::showCursor() return ret; } + bool visibleBefore = isCursorVisible(); if (m_hasNewCursor) { m_cursorIndex = (m_cursorIndex + 1) % 2; m_hasNewCursor = false; } + if (RenderLoopPrivate::get(m_renderLoop)->presentMode == RenderLoopPrivate::SyncMode::Adaptive + && !visibleBefore + && isCursorVisible()) { + m_renderLoop->scheduleRepaint(); + } return ret; } @@ -142,7 +161,10 @@ bool DrmOutput::updateCursor() p.setWorldTransform(logicalToNativeMatrix(cursor->rect(), 1, transform()).toTransform()); p.drawImage(QPoint(0, 0), cursorImage); p.end(); - + if (RenderLoopPrivate::get(m_renderLoop)->presentMode == RenderLoopPrivate::SyncMode::Adaptive + && isCursorVisible()) { + m_renderLoop->scheduleRepaint(); + } return true; } @@ -155,7 +177,15 @@ void DrmOutput::moveCursor() QPoint pos = monitorMatrix.map(cursor->pos()); pos -= hotspotMatrix.map(cursor->hotspot()); - drmModeMoveCursor(m_gpu->fd(), m_crtc->id(), pos.x(), pos.y()); + if (pos != m_cursorPos) { + bool visible = isCursorVisible(); + drmModeMoveCursor(m_gpu->fd(), m_crtc->id(), pos.x(), pos.y()); + m_cursorPos = pos; + if (RenderLoopPrivate::get(m_renderLoop)->presentMode == RenderLoopPrivate::SyncMode::Adaptive + && (visible || visible != isCursorVisible())) { + m_renderLoop->scheduleRepaint(); + } + } } namespace { @@ -210,6 +240,10 @@ bool DrmOutput::init(drmModeConnector *connector) setCapabilityInternal(Capability::Overscan); setOverscanInternal(m_conn->overscan()); } + if (m_conn->vrrCapable()) { + setCapabilityInternal(Capability::Vrr); + setVrrPolicy(RenderLoop::VrrPolicy::Automatic); + } initOutputDevice(connector); if (!m_gpu->atomicModeSetting() && !m_crtc->blank(this)) { @@ -564,6 +598,8 @@ bool DrmOutput::present(const QSharedPointer &buffer) if (m_dpmsModePending != DpmsMode::On) { return false; } + RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_renderLoop); + setVrr(renderLoopPrivate->presentMode == RenderLoopPrivate::SyncMode::Adaptive); return m_gpu->atomicModeSetting() ? presentAtomically(buffer) : presentLegacy(buffer); } @@ -747,6 +783,12 @@ bool DrmOutput::doAtomicCommit(AtomicCommitMode mode) flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; } + if (!m_crtc->atomicPopulate(req)) { + qCWarning(KWIN_DRM) << "Failed to populate crtc. Abort atomic commit!"; + errorHandler(); + return false; + } + if (mode == AtomicCommitMode::Real) { if (m_dpmsModePending == DpmsMode::On) { if (!(flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) { @@ -834,11 +876,7 @@ bool DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable) m_crtc->setValue(DrmCrtc::PropertyIndex::ModeId, enable ? m_blobId : 0); m_crtc->setValue(DrmCrtc::PropertyIndex::Active, enable); - bool ret = true; - ret &= m_conn->atomicPopulate(req); - ret &= m_crtc->atomicPopulate(req); - - return ret; + return m_conn->atomicPopulate(req); } int DrmOutput::gammaRampSize() const @@ -859,4 +897,21 @@ void DrmOutput::setOverscan(uint32_t overscan) } } +void DrmOutput::setVrr(bool enable) +{ + if (!m_conn->vrrCapable() || m_crtc->isVrrEnabled() == enable) { + return; + } + if (!m_crtc->setVrr(enable) || (m_gpu->atomicModeSetting() && !doAtomicCommit(AtomicCommitMode::Test))) { + qCWarning(KWIN_DRM) << "Failed to set vrr on" << this; + setVrrPolicy(RenderLoop::VrrPolicy::Never); + m_crtc->setVrr(false); + } +} + +bool DrmOutput::isCursorVisible() +{ + return m_cursor[m_cursorIndex] && QRect(m_cursorPos, m_cursor[m_cursorIndex]->size()).intersects(QRect(0, 0, m_mode.vdisplay, m_mode.hdisplay)); +} + } diff --git a/src/plugins/platforms/drm/drm_output.h b/src/plugins/platforms/drm/drm_output.h index b8cbf4a770..6c6ea98580 100644 --- a/src/plugins/platforms/drm/drm_output.h +++ b/src/plugins/platforms/drm/drm_output.h @@ -129,6 +129,9 @@ private: bool setGammaRamp(const GammaRamp &gamma) override; void setOverscan(uint32_t overscan) override; + void setVrr(bool enable); + bool isCursorVisible(); + DrmBackend *m_backend; DrmGpu *m_gpu; DrmConnector *m_conn = nullptr; @@ -141,6 +144,8 @@ private: uint32_t m_blobId = 0; DrmPlane *m_primaryPlane = nullptr; QVector m_nextPlanesFlipList; + + QPoint m_cursorPos; bool m_pageFlipPending = false; bool m_atomicOffPending = false; bool m_modesetRequested = true; diff --git a/src/plugins/scenes/opengl/scene_opengl.cpp b/src/plugins/scenes/opengl/scene_opengl.cpp index c96e2c21a1..785f3534c3 100644 --- a/src/plugins/scenes/opengl/scene_opengl.cpp +++ b/src/plugins/scenes/opengl/scene_opengl.cpp @@ -644,41 +644,42 @@ void SceneOpenGL::paint(int screenId, const QRegion &damage, const QListbeginFrame(); - bool directScanout = false; - if (m_backend->directScanoutAllowed(screenId)) { - EffectsHandlerImpl *implEffects = static_cast(effects); - if (!implEffects->blocksDirectScanout()) { - for (int i = stacking_order.count() - 1; i >= 0; i--) { - Window *window = stacking_order[i]; - Toplevel *toplevel = window->window(); - if (toplevel->isOnScreen(screenId) && window->isVisible() && toplevel->opacity() > 0) { - AbstractClient *c = dynamic_cast(toplevel); - if (!c || !c->isFullScreen()) { - break; - } - if (!window->surfaceItem()) { - continue; - } - SurfaceItem *topMost = findTopMostSurface(window->surfaceItem()); - auto pixmap = topMost->windowPixmap(); - if (!pixmap) { - break; - } - pixmap->update(); - // the subsurface has to be able to cover the whole window - if (topMost->position() != QPoint(0, 0)) { - break; - } - // and it has to be completely opaque - if (!window->isOpaque() && !topMost->opaque().contains(QRect(0, 0, window->width(), window->height()))) { - break; - } - directScanout = m_backend->scanout(screenId, topMost); - break; - } + SurfaceItem *fullscreenSurface = nullptr; + for (int i = stacking_order.count() - 1; i >=0; i--) { + Window *window = stacking_order[i]; + Toplevel *toplevel = window->window(); + if (toplevel->isOnScreen(screenId) && window->isVisible() && toplevel->opacity() > 0) { + AbstractClient *c = dynamic_cast(toplevel); + if (!c || !c->isFullScreen()) { + break; } + if (!window->surfaceItem()) { + break; + } + SurfaceItem *topMost = findTopMostSurface(window->surfaceItem()); + auto pixmap = topMost->windowPixmap(); + if (!pixmap) { + break; + } + pixmap->update(); + // the subsurface has to be able to cover the whole window + if (topMost->position() != QPoint(0, 0)) { + break; + } + // and it has to be completely opaque + if (!window->isOpaque() && !topMost->opaque().contains(QRect(0, 0, window->width(), window->height()))) { + break; + } + fullscreenSurface = topMost; + break; } } + renderLoop->setFullscreenSurface(fullscreenSurface); + + bool directScanout = false; + if (m_backend->directScanoutAllowed(screenId) && !static_cast(effects)->blocksDirectScanout()) { + directScanout = m_backend->scanout(screenId, fullscreenSurface); + } if (directScanout) { renderLoop->endFrame(); } else { diff --git a/src/renderloop.cpp b/src/renderloop.cpp index 68eb5d9925..0e405712f7 100644 --- a/src/renderloop.cpp +++ b/src/renderloop.cpp @@ -8,6 +8,7 @@ #include "options.h" #include "renderloop_p.h" #include "utils.h" +#include "surfaceitem.h" namespace KWin { @@ -32,12 +33,30 @@ RenderLoopPrivate::RenderLoopPrivate(RenderLoop *q) void RenderLoopPrivate::scheduleRepaint() { - if (compositeTimer.isActive() || kwinApp()->isTerminating()) { + if (kwinApp()->isTerminating()) { + return; + } + if (vrrPolicy == RenderLoop::VrrPolicy::Always || (vrrPolicy == RenderLoop::VrrPolicy::Automatic && hasFullscreenSurface)) { + presentMode = SyncMode::Adaptive; + } else { + presentMode = SyncMode::Fixed; + } + const std::chrono::nanoseconds vblankInterval(1'000'000'000'000ull / refreshRate); + + if (presentMode == SyncMode::Adaptive) { + std::chrono::nanoseconds timeSincePresent = std::chrono::steady_clock::now().time_since_epoch() - lastPresentationTimestamp; + if (timeSincePresent > vblankInterval) { + // client renders slower than refresh rate -> immediately present + compositeTimer.start(0); + return; + } + // client renders faster than refresh rate -> normal frame scheduling + } + if (compositeTimer.isActive()) { return; } const std::chrono::nanoseconds currentTime(std::chrono::steady_clock::now().time_since_epoch()); - const std::chrono::nanoseconds vblankInterval(1'000'000'000'000ull / refreshRate); // Estimate when the next presentation will occur. Note that this is a prediction. nextPresentationTimestamp = lastPresentationTimestamp + vblankInterval; @@ -231,4 +250,19 @@ std::chrono::nanoseconds RenderLoop::nextPresentationTimestamp() const return d->nextPresentationTimestamp; } +void RenderLoop::setFullscreenSurface(SurfaceItem *surfaceItem) +{ + d->hasFullscreenSurface = surfaceItem != nullptr; +} + +RenderLoop::VrrPolicy RenderLoop::vrrPolicy() const +{ + return d->vrrPolicy; +} + +void RenderLoop::setVrrPolicy(VrrPolicy policy) +{ + d->vrrPolicy = policy; +} + } // namespace KWin diff --git a/src/renderloop.h b/src/renderloop.h index d54982e74d..d867048efd 100644 --- a/src/renderloop.h +++ b/src/renderloop.h @@ -14,6 +14,7 @@ namespace KWin { class RenderLoopPrivate; +class SurfaceItem; /** * The RenderLoop class represents the compositing scheduler on a particular output. @@ -85,6 +86,29 @@ 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(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); + Q_SIGNALS: /** * This signal is emitted when the refresh rate of this RenderLoop has changed. diff --git a/src/renderloop_p.h b/src/renderloop_p.h index 54705c387c..ff707a2667 100644 --- a/src/renderloop_p.h +++ b/src/renderloop_p.h @@ -40,6 +40,14 @@ public: int inhibitCount = 0; bool pendingReschedule = false; bool pendingRepaint = false; + RenderLoop::VrrPolicy vrrPolicy = RenderLoop::VrrPolicy::Never; + bool hasFullscreenSurface = false; + + enum class SyncMode { + Fixed, + Adaptive, + }; + SyncMode presentMode = SyncMode::Fixed; }; } // namespace KWin diff --git a/src/screens.cpp b/src/screens.cpp index b1e3dd4d9b..d2c249e3e4 100644 --- a/src/screens.cpp +++ b/src/screens.cpp @@ -17,6 +17,7 @@ #include #include "platform.h" #include "wayland_server.h" +#include "abstract_wayland_output.h" #ifdef KWIN_UNIT_TEST #include #endif @@ -256,6 +257,34 @@ int Screens::physicalDpiY(int screen) const return size(screen).height() / physicalSize(screen).height() * qreal(25.4); } +bool Screens::isVrrCapable(int screen) const +{ +#ifdef KWIN_UNIT_TEST + Q_UNUSED(screen); + return false; +#else + if (auto output = findOutput(screen)) { + if (auto waylandoutput = dynamic_cast(output)) { + return waylandoutput->capabilities() & AbstractWaylandOutput::Capability::Vrr; + } + } +#endif + return true; +} + +RenderLoop::VrrPolicy Screens::vrrPolicy(int screen) const +{ +#ifdef KWIN_UNIT_TEST + Q_UNUSED(screen); + return RenderLoop::VrrPolicy::Never; +#else + if (auto output = findOutput(screen)) { + return output->renderLoop()->vrrPolicy(); + } + return RenderLoop::VrrPolicy::Never; +#endif +} + int Screens::number(const QPoint &pos) const { // TODO: Do something about testScreens and other tests that use MockScreens. diff --git a/src/screens.h b/src/screens.h index 78f167a87b..df03288bf5 100644 --- a/src/screens.h +++ b/src/screens.h @@ -11,6 +11,7 @@ // KWin includes #include +#include // KDE includes #include #include @@ -131,6 +132,15 @@ public: int physicalDpiX(int screen) const; int physicalDpiY(int screen) const; + /** + * @returns @c true if the @p screen is capable of variable refresh rate and if the platform can use it + */ + bool isVrrCapable(int screen) const; + /** + * @returns the vrr policy of the @p screen + */ + RenderLoop::VrrPolicy vrrPolicy(int screen) const; + public Q_SLOTS: void reconfigure(); diff --git a/src/waylandoutputdevice.cpp b/src/waylandoutputdevice.cpp index d589a30dfa..084f6cc958 100644 --- a/src/waylandoutputdevice.cpp +++ b/src/waylandoutputdevice.cpp @@ -26,9 +26,17 @@ static KWaylandServer::OutputDeviceInterface::Capabilities kwinCapabilitiesToOut if (caps & AbstractWaylandOutput::Capability::Overscan) { ret |= KWaylandServer::OutputDeviceInterface::Capability::Overscan; } + if (caps & AbstractWaylandOutput::Capability::Vrr) { + ret |= KWaylandServer::OutputDeviceInterface::Capability::Vrr; + } return ret; } +static KWaylandServer::OutputDeviceInterface::VrrPolicy kwinVrrPolicyToOutputDeviceVrrPolicy(RenderLoop::VrrPolicy policy) +{ + return static_cast(policy); +} + WaylandOutputDevice::WaylandOutputDevice(AbstractWaylandOutput *output, QObject *parent) : QObject(parent) , m_platformOutput(output) @@ -47,6 +55,7 @@ WaylandOutputDevice::WaylandOutputDevice(AbstractWaylandOutput *output, QObject m_outputDevice->setSubPixel(kwinSubPixelToOutputDeviceSubPixel(output->subPixel())); m_outputDevice->setOverscan(output->overscan()); m_outputDevice->setCapabilities(kwinCapabilitiesToOutputDeviceCapabilities(output->capabilities())); + m_outputDevice->setVrrPolicy(kwinVrrPolicyToOutputDeviceVrrPolicy(output->vrrPolicy())); const auto modes = output->modes(); for (const AbstractWaylandOutput::Mode &mode : modes) { @@ -79,6 +88,8 @@ WaylandOutputDevice::WaylandOutputDevice(AbstractWaylandOutput *output, QObject this, &WaylandOutputDevice::handleCapabilitiesChanged); connect(output, &AbstractWaylandOutput::overscanChanged, this, &WaylandOutputDevice::handleOverscanChanged); + connect(output, &AbstractWaylandOutput::vrrPolicyChanged, + this, &WaylandOutputDevice::handleVrrPolicyChanged); } void WaylandOutputDevice::handleGeometryChanged() @@ -120,4 +131,9 @@ void WaylandOutputDevice::handleOverscanChanged() m_outputDevice->setOverscan(m_platformOutput->overscan()); } +void WaylandOutputDevice::handleVrrPolicyChanged() +{ + m_outputDevice->setVrrPolicy(kwinVrrPolicyToOutputDeviceVrrPolicy(m_platformOutput->vrrPolicy())); +} + } // namespace KWin diff --git a/src/waylandoutputdevice.h b/src/waylandoutputdevice.h index 13725e67bb..923d2c751d 100644 --- a/src/waylandoutputdevice.h +++ b/src/waylandoutputdevice.h @@ -28,6 +28,7 @@ private Q_SLOTS: void handleModeChanged(); void handleCapabilitiesChanged(); void handleOverscanChanged(); + void handleVrrPolicyChanged(); private: AbstractWaylandOutput *m_platformOutput; diff --git a/src/workspace.cpp b/src/workspace.cpp index de23055d87..4924072784 100644 --- a/src/workspace.cpp +++ b/src/workspace.cpp @@ -1537,7 +1537,22 @@ QString Workspace::supportInformation() const .arg(geo.width()) .arg(geo.height())); support.append(QStringLiteral("Scale: %1\n").arg(screens()->scale(i))); - support.append(QStringLiteral("Refresh Rate: %1\n\n").arg(screens()->refreshRate(i))); + support.append(QStringLiteral("Refresh Rate: %1\n").arg(screens()->refreshRate(i))); + QString vrr = QStringLiteral("incapable"); + if (screens()->isVrrCapable(i)) { + switch(screens()->vrrPolicy(i)) { + case RenderLoop::VrrPolicy::Never: + vrr = QStringLiteral("never"); + break; + case RenderLoop::VrrPolicy::Always: + vrr = QStringLiteral("always"); + break; + case RenderLoop::VrrPolicy::Automatic: + vrr = QStringLiteral("automatic"); + break; + } + } + support.append(QStringLiteral("Adaptive Sync: %1\n").arg(vrr)); } support.append(QStringLiteral("\nCompositing\n")); support.append(QStringLiteral( "===========\n"));