Wayland: variable refresh rate support
BUG: 405912
This commit is contained in:
parent
2a6fe9748f
commit
faddf0bf5f
17 changed files with 307 additions and 43 deletions
|
@ -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<RenderLoop::VrrPolicy>(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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "abstract_output.h"
|
||||
#include "utils.h"
|
||||
#include "renderloop.h"
|
||||
#include <kwin_export.h>
|
||||
|
||||
#include <QObject>
|
||||
|
@ -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,
|
||||
|
|
|
@ -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<int>(PropertyIndex::VrrCapable)]) {
|
||||
return prop->value();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<drmModeConnector> m_conn;
|
||||
QVector<uint32_t> m_encoders;
|
||||
|
|
|
@ -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<int>(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<int>(PropertyIndex::VrrEnabled)]) {
|
||||
return prop->value();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<drmModeCrtc> m_crtc;
|
||||
QSharedPointer<DrmBuffer> m_currentBuffer;
|
||||
|
|
|
@ -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<DrmBuffer> &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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<DrmPlane*> m_nextPlanesFlipList;
|
||||
|
||||
QPoint m_cursorPos;
|
||||
bool m_pageFlipPending = false;
|
||||
bool m_atomicOffPending = false;
|
||||
bool m_modesetRequested = true;
|
||||
|
|
|
@ -644,41 +644,42 @@ void SceneOpenGL::paint(int screenId, const QRegion &damage, const QList<Topleve
|
|||
} else {
|
||||
renderLoop->beginFrame();
|
||||
|
||||
bool directScanout = false;
|
||||
if (m_backend->directScanoutAllowed(screenId)) {
|
||||
EffectsHandlerImpl *implEffects = static_cast<EffectsHandlerImpl*>(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<AbstractClient*>(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<AbstractClient*>(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<EffectsHandlerImpl*>(effects)->blocksDirectScanout()) {
|
||||
directScanout = m_backend->scanout(screenId, fullscreenSurface);
|
||||
}
|
||||
if (directScanout) {
|
||||
renderLoop->endFrame();
|
||||
} else {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <config-kwin.h>
|
||||
#include "platform.h"
|
||||
#include "wayland_server.h"
|
||||
#include "abstract_wayland_output.h"
|
||||
#ifdef KWIN_UNIT_TEST
|
||||
#include <mock_screens.h>
|
||||
#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<AbstractWaylandOutput*>(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.
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
// KWin includes
|
||||
#include <kwinglobals.h>
|
||||
#include <renderloop.h>
|
||||
// KDE includes
|
||||
#include <KConfig>
|
||||
#include <KSharedConfig>
|
||||
|
@ -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();
|
||||
|
||||
|
|
|
@ -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<KWaylandServer::OutputDeviceInterface::VrrPolicy>(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
|
||||
|
|
|
@ -28,6 +28,7 @@ private Q_SLOTS:
|
|||
void handleModeChanged();
|
||||
void handleCapabilitiesChanged();
|
||||
void handleOverscanChanged();
|
||||
void handleVrrPolicyChanged();
|
||||
|
||||
private:
|
||||
AbstractWaylandOutput *m_platformOutput;
|
||||
|
|
|
@ -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"));
|
||||
|
|
Loading…
Reference in a new issue