KWin
Loading...
Searching...
No Matches
drm_output.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "drm_output.h"
10#include "drm_backend.h"
11#include "drm_connector.h"
12#include "drm_crtc.h"
13#include "drm_gpu.h"
14#include "drm_pipeline.h"
15
17#include "core/iccprofile.h"
19#include "core/renderbackend.h"
20#include "core/renderloop.h"
21#include "core/renderloop_p.h"
22#include "drm_layer.h"
23#include "drm_logging.h"
24// Qt
25#include <QCryptographicHash>
26#include <QMatrix4x4>
27#include <QPainter>
28// c++
29#include <cerrno>
30// drm
31#include <drm_fourcc.h>
32#include <libdrm/drm_mode.h>
33#include <xf86drm.h>
34
35namespace KWin
36{
37
38static const bool s_allowColorspaceIntel = qEnvironmentVariableIntValue("KWIN_DRM_ALLOW_INTEL_COLORSPACE") == 1;
39
40DrmOutput::DrmOutput(const std::shared_ptr<DrmConnector> &conn)
41 : DrmAbstractOutput(conn->gpu())
42 , m_pipeline(conn->pipeline())
43 , m_connector(conn)
44{
45 m_pipeline->setOutput(this);
46 m_renderLoop->setRefreshRate(m_pipeline->mode()->refreshRate());
47
49 State initialState;
50
51 if (conn->overscan.isValid() || conn->underscan.isValid()) {
53 initialState.overscan = conn->overscan.isValid() ? conn->overscan.value() : conn->underscanVBorder.value();
54 }
55 if (conn->vrrCapable.isValid() && conn->vrrCapable.value()) {
57 }
58 if (gpu()->asyncPageflipSupported()) {
60 }
61 if (conn->broadcastRGB.isValid()) {
63 initialState.rgbRange = DrmConnector::broadcastRgbToRgbRange(conn->broadcastRGB.enumValue());
64 }
65 if (m_connector->hdrMetadata.isValid() && m_connector->edid()->supportsPQ()) {
67 }
68 if (m_connector->colorspace.isValid() && m_connector->colorspace.hasEnum(DrmConnector::Colorspace::BT2020_RGB) && m_connector->edid()->supportsBT2020()) {
69 if (!m_gpu->isI915() || s_allowColorspaceIntel) {
71 }
72 }
73 if (conn->isInternal()) {
74 // TODO only set this if an orientation sensor is available?
76 }
77
78 const Edid *edid = conn->edid();
80 .name = conn->connectorName(),
82 .model = conn->modelName(),
83 .serialNumber = edid->serialNumber(),
84 .eisaId = edid->eisaId(),
85 .physicalSize = conn->physicalSize(),
86 .edid = *edid,
87 .subPixel = conn->subpixel(),
88 .capabilities = capabilities,
89 .panelOrientation = conn->panelOrientation.isValid() ? DrmConnector::toKWinTransform(conn->panelOrientation.enumValue()) : OutputTransform::Normal,
90 .internal = conn->isInternal(),
91 .nonDesktop = conn->isNonDesktop(),
92 .mstPath = conn->mstPath(),
93 .maxPeakBrightness = edid->desiredMaxLuminance(),
94 .maxAverageBrightness = edid->desiredMaxFrameAverageLuminance(),
95 .minBrightness = edid->desiredMinLuminance(),
96 });
97
98 initialState.modes = getModes();
99 initialState.currentMode = m_pipeline->mode();
100 if (!initialState.currentMode) {
101 initialState.currentMode = initialState.modes.constFirst();
102 }
103
104 setState(initialState);
105
106 m_turnOffTimer.setSingleShot(true);
107 m_turnOffTimer.setInterval(dimAnimationTime());
108 connect(&m_turnOffTimer, &QTimer::timeout, this, [this] {
109 setDrmDpmsMode(DpmsMode::Off);
110 });
111}
112
114{
115 m_pipeline->setOutput(nullptr);
116}
117
118bool DrmOutput::addLeaseObjects(QList<uint32_t> &objectList)
119{
120 if (!m_pipeline->crtc()) {
121 qCWarning(KWIN_DRM) << "Can't lease connector: No suitable crtc available";
122 return false;
123 }
124 qCDebug(KWIN_DRM) << "adding connector" << m_pipeline->connector()->id() << "to lease";
125 objectList << m_pipeline->connector()->id();
126 objectList << m_pipeline->crtc()->id();
127 if (m_pipeline->crtc()->primaryPlane()) {
128 objectList << m_pipeline->crtc()->primaryPlane()->id();
129 }
130 return true;
131}
132
134{
135 m_lease = lease;
136}
137
139{
140 qCDebug(KWIN_DRM) << "ended lease for connector" << m_pipeline->connector()->id();
141 m_lease = nullptr;
142}
143
145{
146 return m_lease;
147}
148
150{
151 return m_pipeline->updateCursor();
152}
153
154QList<std::shared_ptr<OutputMode>> DrmOutput::getModes() const
155{
156 const auto drmModes = m_pipeline->connector()->modes();
157
158 QList<std::shared_ptr<OutputMode>> ret;
159 ret.reserve(drmModes.count());
160 for (const auto &drmMode : drmModes) {
161 ret.append(drmMode);
162 }
163 return ret;
164}
165
166void DrmOutput::setDpmsMode(DpmsMode mode)
167{
168 if (mode == DpmsMode::Off) {
169 if (!m_turnOffTimer.isActive()) {
170 Q_EMIT aboutToTurnOff(std::chrono::milliseconds(m_turnOffTimer.interval()));
171 m_turnOffTimer.start();
172 }
173 } else {
174 if (m_turnOffTimer.isActive() || (mode != dpmsMode() && setDrmDpmsMode(mode))) {
175 Q_EMIT wakeUp();
176 }
177 m_turnOffTimer.stop();
178 }
179}
180
181bool DrmOutput::setDrmDpmsMode(DpmsMode mode)
182{
183 if (!isEnabled()) {
184 return false;
185 }
186 bool active = mode == DpmsMode::On;
187 bool isActive = dpmsMode() == DpmsMode::On;
188 if (active == isActive) {
189 updateDpmsMode(mode);
190 return true;
191 }
192 if (!active) {
193 gpu()->waitIdle();
194 }
195 m_pipeline->setActive(active);
197 m_pipeline->applyPendingChanges();
198 updateDpmsMode(mode);
199 if (active) {
200 m_renderLoop->uninhibit();
201 m_renderLoop->scheduleRepaint();
202 doSetChannelFactors(m_channelFactors);
203 } else {
204 m_renderLoop->inhibit();
205 }
206 return true;
207 } else {
208 qCWarning(KWIN_DRM) << "Setting dpms mode failed!";
209 m_pipeline->revertPendingChanges();
210 return false;
211 }
212}
213
214DrmPlane::Transformations outputToPlaneTransform(OutputTransform transform)
215{
216 using PlaneTrans = DrmPlane::Transformation;
217
218 switch (transform.kind()) {
220 return PlaneTrans::Rotate0;
222 return PlaneTrans::ReflectX | PlaneTrans::Rotate0;
224 return PlaneTrans::Rotate90;
226 return PlaneTrans::ReflectX | PlaneTrans::Rotate90;
228 return PlaneTrans::Rotate180;
230 return PlaneTrans::ReflectX | PlaneTrans::Rotate180;
232 return PlaneTrans::Rotate270;
234 return PlaneTrans::ReflectX | PlaneTrans::Rotate270;
235 default:
236 Q_UNREACHABLE();
237 }
238}
239
241{
242 State next = m_state;
243 next.modes = getModes();
244
245 if (m_pipeline->crtc()) {
246 const auto currentMode = m_pipeline->connector()->findMode(m_pipeline->crtc()->queryCurrentMode());
247 if (currentMode != m_pipeline->mode()) {
248 // DrmConnector::findCurrentMode might fail
249 m_pipeline->setMode(currentMode ? currentMode : m_pipeline->connector()->modes().constFirst());
251 m_pipeline->applyPendingChanges();
252 m_renderLoop->setRefreshRate(m_pipeline->mode()->refreshRate());
253 } else {
254 qCWarning(KWIN_DRM) << "Setting changed mode failed!";
255 m_pipeline->revertPendingChanges();
256 }
257 }
258 }
259
260 next.currentMode = m_pipeline->mode();
261 if (!next.currentMode) {
262 next.currentMode = next.modes.constFirst();
263 }
264
265 setState(next);
266}
267
269{
270 State next = m_state;
271 next.dpmsMode = dpmsMode;
272 setState(next);
273}
274
275bool DrmOutput::present(const std::shared_ptr<OutputFrame> &frame)
276{
277 m_frame = frame;
278 const bool needsModeset = gpu()->needsModeset();
279 bool success;
280 if (needsModeset) {
283 success = m_pipeline->maybeModeset();
284 } else {
285 m_pipeline->setPresentationMode(frame->presentationMode());
286 DrmPipeline::Error err = m_pipeline->present();
287 if (err != DrmPipeline::Error::None && frame->presentationMode() != PresentationMode::VSync) {
288 // retry with a more basic presentation mode
290 err = m_pipeline->present();
291 }
292 success = err == DrmPipeline::Error::None;
294 QTimer::singleShot(0, m_gpu->platform(), &DrmBackend::updateOutputs);
295 }
296 }
297 m_renderLoop->setPresentationMode(m_pipeline->presentationMode());
298 if (success) {
299 Q_EMIT outputChange(m_pipeline->primaryLayer()->currentDamage());
300 return true;
301 } else if (!needsModeset) {
302 qCWarning(KWIN_DRM) << "Presentation failed!" << strerror(errno);
303 m_frame->failed();
304 }
305 return false;
306}
307
309{
310 return m_connector.get();
311}
312
314{
315 return m_pipeline;
316}
317
318bool DrmOutput::queueChanges(const std::shared_ptr<OutputChangeSet> &props)
319{
320 const auto mode = props->mode.value_or(currentMode()).lock();
321 if (!mode) {
322 return false;
323 }
324 const bool bt2020 = props->wideColorGamut.value_or(m_state.wideColorGamut);
325 const bool hdr = props->highDynamicRange.value_or(m_state.highDynamicRange);
326 m_pipeline->setMode(std::static_pointer_cast<DrmConnectorMode>(mode));
327 m_pipeline->setOverscan(props->overscan.value_or(m_pipeline->overscan()));
328 m_pipeline->setRgbRange(props->rgbRange.value_or(m_pipeline->rgbRange()));
329 m_pipeline->setRenderOrientation(outputToPlaneTransform(props->transform.value_or(transform())));
330 m_pipeline->setEnable(props->enabled.value_or(m_pipeline->enabled()));
331 m_pipeline->setColorDescription(createColorDescription(props));
332 if (bt2020 || hdr) {
333 // ICC profiles don't support HDR (yet)
334 m_pipeline->setIccProfile(nullptr);
335 } else {
336 m_pipeline->setIccProfile(props->iccProfile.value_or(m_state.iccProfile));
337 }
338 if (bt2020 || hdr || m_pipeline->iccProfile()) {
339 // remove unused gamma ramp and ctm, if present
340 m_pipeline->setGammaRamp(nullptr);
341 m_pipeline->setCTM(QMatrix3x3{});
342 }
343 return true;
344}
345
346ColorDescription DrmOutput::createColorDescription(const std::shared_ptr<OutputChangeSet> &props) const
347{
348 if (props->highDynamicRange.value_or(m_state.highDynamicRange) && m_connector->edid()) {
349 const auto colorimetry = props->wideColorGamut.value_or(m_state.wideColorGamut) ? NamedColorimetry::BT2020 : NamedColorimetry::BT709;
350 const auto nativeColorimetry = m_information.edid.colorimetry().value_or(Colorimetry::fromName(NamedColorimetry::BT709));
351 const auto sdrBrightness = props->sdrBrightness.value_or(m_state.sdrBrightness);
353 props->minBrightnessOverride.value_or(m_state.minBrightnessOverride).value_or(m_connector->edid()->desiredMinLuminance()),
354 props->maxAverageBrightnessOverride.value_or(m_state.maxAverageBrightnessOverride).value_or(m_connector->edid()->desiredMaxFrameAverageLuminance().value_or(sdrBrightness)),
355 props->maxPeakBrightnessOverride.value_or(m_state.maxPeakBrightnessOverride).value_or(m_connector->edid()->desiredMaxLuminance().value_or(1000)),
356 Colorimetry::fromName(NamedColorimetry::BT709).interpolateGamutTo(nativeColorimetry, props->sdrGamutWideness.value_or(m_state.sdrGamutWideness)));
357 } else if (const auto profile = props->iccProfile.value_or(m_state.iccProfile)) {
358 return ColorDescription(profile->colorimetry(), NamedTransferFunction::gamma22, 200, 0, 200, 200);
359 } else {
361 }
362}
363
364void DrmOutput::applyQueuedChanges(const std::shared_ptr<OutputChangeSet> &props)
365{
366 if (!m_connector->isConnected()) {
367 return;
368 }
369 Q_EMIT aboutToChange(props.get());
370 m_pipeline->applyPendingChanges();
371
372 State next = m_state;
373 next.enabled = props->enabled.value_or(m_state.enabled) && m_pipeline->crtc();
374 next.position = props->pos.value_or(m_state.position);
375 next.scale = props->scale.value_or(m_state.scale);
376 next.transform = props->transform.value_or(m_state.transform);
377 next.manualTransform = props->manualTransform.value_or(m_state.manualTransform);
378 next.currentMode = m_pipeline->mode();
379 next.overscan = m_pipeline->overscan();
380 next.rgbRange = m_pipeline->rgbRange();
381 next.highDynamicRange = props->highDynamicRange.value_or(m_state.highDynamicRange);
382 next.sdrBrightness = props->sdrBrightness.value_or(m_state.sdrBrightness);
383 next.wideColorGamut = props->wideColorGamut.value_or(m_state.wideColorGamut);
384 next.autoRotatePolicy = props->autoRotationPolicy.value_or(m_state.autoRotatePolicy);
385 next.maxPeakBrightnessOverride = props->maxPeakBrightnessOverride.value_or(m_state.maxPeakBrightnessOverride);
386 next.maxAverageBrightnessOverride = props->maxAverageBrightnessOverride.value_or(m_state.maxAverageBrightnessOverride);
387 next.minBrightnessOverride = props->minBrightnessOverride.value_or(m_state.minBrightnessOverride);
388 next.sdrGamutWideness = props->sdrGamutWideness.value_or(m_state.sdrGamutWideness);
389 next.iccProfilePath = props->iccProfilePath.value_or(m_state.iccProfilePath);
390 next.iccProfile = props->iccProfile.value_or(m_state.iccProfile);
391 next.colorDescription = m_pipeline->colorDescription();
392 next.vrrPolicy = props->vrrPolicy.value_or(m_state.vrrPolicy);
393 setState(next);
394
395 if (!isEnabled() && m_pipeline->needsModeset()) {
397 }
398
399 m_renderLoop->setRefreshRate(refreshRate());
400 m_renderLoop->scheduleRepaint();
401
402 if (!next.wideColorGamut && !next.highDynamicRange && !m_pipeline->iccProfile()) {
403 // re-set the CTM and/or gamma lut
404 doSetChannelFactors(m_channelFactors);
405 }
406
407 Q_EMIT changed();
408}
409
411{
412 m_pipeline->revertPendingChanges();
413}
414
416{
417 return m_pipeline->primaryLayer();
418}
419
421{
422 return m_pipeline->cursorLayer();
423}
424
425bool DrmOutput::setChannelFactors(const QVector3D &rgb)
426{
427 return m_channelFactors == rgb || doSetChannelFactors(rgb);
428}
429
430bool DrmOutput::doSetChannelFactors(const QVector3D &rgb)
431{
432 m_renderLoop->scheduleRepaint();
433 m_channelFactors = rgb;
435 // the shader "fallback" is always active
436 return true;
437 }
438 if (!m_pipeline->activePending()) {
439 return false;
440 }
442 if (m_pipeline->hasCTM()) {
443 QMatrix3x3 ctm;
444 ctm(0, 0) = inGamma22.x();
445 ctm(1, 1) = inGamma22.y();
446 ctm(2, 2) = inGamma22.z();
447 m_pipeline->setCTM(ctm);
448 m_pipeline->setGammaRamp(nullptr);
450 m_pipeline->applyPendingChanges();
451 m_channelFactorsNeedShaderFallback = false;
452 return true;
453 } else {
454 m_pipeline->setCTM(QMatrix3x3());
455 m_pipeline->applyPendingChanges();
456 }
457 }
458 if (m_pipeline->hasGammaRamp()) {
460 if (lut) {
461 m_pipeline->setGammaRamp(std::move(lut));
463 m_pipeline->applyPendingChanges();
464 m_channelFactorsNeedShaderFallback = false;
465 return true;
466 } else {
467 m_pipeline->setGammaRamp(nullptr);
468 m_pipeline->applyPendingChanges();
469 }
470 }
471 }
472 m_channelFactorsNeedShaderFallback = m_channelFactors != QVector3D{1, 1, 1};
473 return true;
474}
475
477{
478 return m_channelFactors;
479}
480
482{
483 static bool forceColorManagement = qEnvironmentVariableIntValue("KWIN_DRM_FORCE_COLOR_MANAGEMENT") != 0;
484 return forceColorManagement || m_state.wideColorGamut || m_state.highDynamicRange || m_state.iccProfile || m_channelFactorsNeedShaderFallback;
485}
486}
487
488#include "moc_drm_output.cpp"
static const ColorDescription sRGB
Definition colorspace.h:132
static QVector3D nitsToEncoded(const QVector3D &rgb, NamedTransferFunction tf, double sdrBrightness)
static std::unique_ptr< ColorTransformation > createScalingTransform(const QVector3D &scale)
Colorimetry interpolateGamutTo(const Colorimetry &one, double factor) const
static const Colorimetry & fromName(NamedColorimetry name)
std::shared_ptr< OutputFrame > m_frame
std::unique_ptr< RenderLoop > m_renderLoop
static Output::RgbRange broadcastRgbToRgbRange(BroadcastRgbOptions rgbRange)
std::shared_ptr< DrmConnectorMode > findMode(const drmModeModeInfo &modeInfo) const
static OutputTransform toKWinTransform(PanelOrientation orientation)
QList< std::shared_ptr< DrmConnectorMode > > modes() const
DrmPlane * primaryPlane() const
Definition drm_crtc.cpp:87
drmModeModeInfo queryCurrentMode()
Definition drm_crtc.cpp:55
void waitIdle()
Definition drm_gpu.cpp:493
bool needsModeset() const
Definition drm_gpu.cpp:743
bool maybeModeset()
Definition drm_gpu.cpp:750
DrmBackend * platform() const
Definition drm_gpu.cpp:585
DrmPipeline::Error testPendingConfiguration()
Definition drm_gpu.cpp:431
bool isI915() const
Definition drm_gpu.cpp:693
uint32_t id() const
bool setChannelFactors(const QVector3D &rgb) override
bool queueChanges(const std::shared_ptr< OutputChangeSet > &properties)
DrmOutputLayer * primaryLayer() const override
DrmOutput(const std::shared_ptr< DrmConnector > &connector)
bool present(const std::shared_ptr< OutputFrame > &frame) override
bool addLeaseObjects(QList< uint32_t > &objectList)
DrmPipeline * pipeline() const
bool updateCursorLayer() override
void updateDpmsMode(DpmsMode dpmsMode)
void revertQueuedChanges()
bool needsColormanagement() const
QVector3D channelFactors() const
DrmLease * lease() const
DrmOutputLayer * cursorLayer() const override
~DrmOutput() override
DrmConnector * connector() const
void leased(DrmLease *lease)
void applyQueuedChanges(const std::shared_ptr< OutputChangeSet > &properties)
virtual QRegion currentDamage() const
Definition drm_layer.cpp:20
Output::RgbRange rgbRange() const
DrmPipelineLayer * cursorLayer() const
DrmConnector * connector() const
bool hasGammaRamp() const
uint32_t overscan() const
void setOutput(DrmOutput *output)
void setContentType(DrmConnector::DrmContentType type)
PresentationMode presentationMode() const
void setMode(const std::shared_ptr< DrmConnectorMode > &mode)
bool needsModeset() const
bool hasCTM() const
const std::shared_ptr< IccProfile > & iccProfile() const
void setRgbRange(Output::RgbRange range)
const ColorDescription & colorDescription() const
void setEnable(bool enable)
void setCTM(const QMatrix3x3 &ctm)
void setActive(bool active)
bool enabled() const
static Error commitPipelines(const QList< DrmPipeline * > &pipelines, CommitMode mode, const QList< DrmObject * > &unusedObjects={})
void setColorDescription(const ColorDescription &description)
DrmCrtc * crtc() const
void setRenderOrientation(DrmPlane::Transformations orientation)
void setPresentationMode(PresentationMode mode)
std::shared_ptr< DrmConnectorMode > mode() const
void setIccProfile(const std::shared_ptr< IccProfile > &profile)
DrmPipelineLayer * primaryLayer() const
void setOverscan(uint32_t overscan)
bool activePending() const
void setGammaRamp(const std::shared_ptr< ColorTransformation > &transformation)
QString manufacturerString() const
Definition edid.cpp:210
double desiredMinLuminance() const
Definition edid.cpp:247
QByteArray eisaId() const
Definition edid.cpp:185
std::optional< double > desiredMaxFrameAverageLuminance() const
Definition edid.cpp:252
QByteArray serialNumber() const
Definition edid.cpp:195
std::optional< Colorimetry > colorimetry() const
Definition edid.cpp:242
std::optional< double > desiredMaxLuminance() const
Definition edid.cpp:257
bool isValid() const
Definition edid.cpp:175
std::shared_ptr< OutputMode > currentMode() const
Definition output.cpp:500
QString manufacturer
Definition output.h:137
void outputChange(const QRegion &damagedRegion)
uint32_t refreshRate() const
Definition output.cpp:475
void setInformation(const Information &information)
Definition output.cpp:556
void setState(const State &state)
Definition output.cpp:562
OutputTransform transform() const
Definition output.cpp:369
static std::chrono::milliseconds dimAnimationTime()
Definition output.cpp:419
void aboutToChange(OutputChangeSet *changeSet)
uint32_t sdrBrightness() const
Definition output.cpp:702
const Edid & edid() const
Definition output.cpp:490
State m_state
Definition output.h:477
void changed()
void aboutToTurnOff(std::chrono::milliseconds time)
Information m_information
Definition output.h:478
Capabilities capabilities() const
Definition output.cpp:450
bool isEnabled() const
Definition output.cpp:536
DpmsMode dpmsMode() const
Definition output.cpp:647
Kind kind() const
Definition output.cpp:64
DrmPlane::Transformations outputToPlaneTransform(OutputTransform transform)
uint32_t sdrBrightness
Definition output.h:462
std::shared_ptr< OutputMode > currentMode
Definition output.h:454
QList< std::shared_ptr< OutputMode > > modes
Definition output.h:453
double sdrGamutWideness
Definition output.h:470
uint32_t overscan
Definition output.h:458
QString iccProfilePath
Definition output.h:464
ColorDescription colorDescription
Definition output.h:466
VrrPolicy vrrPolicy
Definition output.h:471
OutputTransform manualTransform
Definition output.h:452
std::shared_ptr< IccProfile > iccProfile
Definition output.h:465
std::optional< double > maxAverageBrightnessOverride
Definition output.h:468
DpmsMode dpmsMode
Definition output.h:455
OutputTransform transform
Definition output.h:451
std::optional< double > minBrightnessOverride
Definition output.h:469
RgbRange rgbRange
Definition output.h:459
AutoRotationPolicy autoRotatePolicy
Definition output.h:463
std::optional< double > maxPeakBrightnessOverride
Definition output.h:467