File indexing completed on 2024-11-10 04:56:27

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "drm_commit.h"
0010 #include "drm_blob.h"
0011 #include "drm_buffer.h"
0012 #include "drm_connector.h"
0013 #include "drm_crtc.h"
0014 #include "drm_gpu.h"
0015 #include "drm_object.h"
0016 #include "drm_property.h"
0017 
0018 #include <QApplication>
0019 #include <QThread>
0020 
0021 namespace KWin
0022 {
0023 
0024 DrmCommit::DrmCommit(DrmGpu *gpu)
0025     : m_gpu(gpu)
0026 {
0027 }
0028 
0029 DrmCommit::~DrmCommit()
0030 {
0031     Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
0032 }
0033 
0034 DrmGpu *DrmCommit::gpu() const
0035 {
0036     return m_gpu;
0037 }
0038 
0039 DrmAtomicCommit::DrmAtomicCommit(const QList<DrmPipeline *> &pipelines)
0040     : DrmCommit(pipelines.front()->gpu())
0041     , m_pipelines(pipelines)
0042 {
0043 }
0044 
0045 void DrmAtomicCommit::addProperty(const DrmProperty &prop, uint64_t value)
0046 {
0047     m_properties[prop.drmObject()->id()][prop.propId()] = value;
0048 }
0049 
0050 void DrmAtomicCommit::addBlob(const DrmProperty &prop, const std::shared_ptr<DrmBlob> &blob)
0051 {
0052     addProperty(prop, blob ? blob->blobId() : 0);
0053     m_blobs[&prop] = blob;
0054 }
0055 
0056 void DrmAtomicCommit::addBuffer(DrmPlane *plane, const std::shared_ptr<DrmFramebuffer> &buffer)
0057 {
0058     addProperty(plane->fbId, buffer ? buffer->framebufferId() : 0);
0059     m_buffers[plane] = buffer;
0060     // atomic commits with IN_FENCE_FD fail with NVidia
0061     if (plane->inFenceFd.isValid() && !plane->gpu()->isNVidia()) {
0062         addProperty(plane->inFenceFd, buffer ? buffer->syncFd().get() : -1);
0063     }
0064     m_planes.emplace(plane);
0065 }
0066 
0067 void DrmAtomicCommit::setVrr(DrmCrtc *crtc, bool vrr)
0068 {
0069     addProperty(crtc->vrrEnabled, vrr ? 1 : 0);
0070     m_vrr = vrr;
0071 }
0072 
0073 void DrmAtomicCommit::setPresentationMode(PresentationMode mode)
0074 {
0075     m_mode = mode;
0076 }
0077 
0078 bool DrmAtomicCommit::test()
0079 {
0080     return doCommit(DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_NONBLOCK);
0081 }
0082 
0083 bool DrmAtomicCommit::testAllowModeset()
0084 {
0085     return doCommit(DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET);
0086 }
0087 
0088 bool DrmAtomicCommit::commit()
0089 {
0090     return doCommit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT);
0091 }
0092 
0093 bool DrmAtomicCommit::commitModeset()
0094 {
0095     m_modeset = true;
0096     return doCommit(DRM_MODE_ATOMIC_ALLOW_MODESET);
0097 }
0098 
0099 bool DrmAtomicCommit::doCommit(uint32_t flags)
0100 {
0101     std::vector<uint32_t> objects;
0102     std::vector<uint32_t> propertyCounts;
0103     std::vector<uint32_t> propertyIds;
0104     std::vector<uint64_t> values;
0105     objects.reserve(m_properties.size());
0106     propertyCounts.reserve(m_properties.size());
0107     uint64_t totalPropertiesCount = 0;
0108     for (const auto &[object, properties] : m_properties) {
0109         objects.push_back(object);
0110         propertyCounts.push_back(properties.size());
0111         totalPropertiesCount += properties.size();
0112     }
0113     propertyIds.reserve(totalPropertiesCount);
0114     values.reserve(totalPropertiesCount);
0115     for (const auto &[object, properties] : m_properties) {
0116         for (const auto &[property, value] : properties) {
0117             propertyIds.push_back(property);
0118             values.push_back(value);
0119         }
0120     }
0121     drm_mode_atomic commitData{
0122         .flags = flags,
0123         .count_objs = uint32_t(objects.size()),
0124         .objs_ptr = reinterpret_cast<uint64_t>(objects.data()),
0125         .count_props_ptr = reinterpret_cast<uint64_t>(propertyCounts.data()),
0126         .props_ptr = reinterpret_cast<uint64_t>(propertyIds.data()),
0127         .prop_values_ptr = reinterpret_cast<uint64_t>(values.data()),
0128         .reserved = 0,
0129         .user_data = reinterpret_cast<uint64_t>(this),
0130     };
0131     return drmIoctl(m_gpu->fd(), DRM_IOCTL_MODE_ATOMIC, &commitData) == 0;
0132 }
0133 
0134 void DrmAtomicCommit::pageFlipped(std::chrono::nanoseconds timestamp) const
0135 {
0136     Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
0137     for (const auto &[plane, buffer] : m_buffers) {
0138         plane->setCurrentBuffer(buffer);
0139     }
0140     DrmPipeline::PageflipType type = DrmPipeline::PageflipType::Normal;
0141     if (m_modeset) {
0142         type = DrmPipeline::PageflipType::Modeset;
0143     } else if (m_cursorOnly) {
0144         type = DrmPipeline::PageflipType::CursorOnly;
0145     }
0146     for (const auto pipeline : std::as_const(m_pipelines)) {
0147         pipeline->pageFlipped(timestamp, type, m_mode);
0148     }
0149 }
0150 
0151 bool DrmAtomicCommit::areBuffersReadable() const
0152 {
0153     return std::all_of(m_buffers.begin(), m_buffers.end(), [](const auto &pair) {
0154         const auto &[plane, buffer] = pair;
0155         return !buffer || buffer->isReadable();
0156     });
0157 }
0158 
0159 void DrmAtomicCommit::setDeadline(std::chrono::steady_clock::time_point deadline)
0160 {
0161     for (const auto &[plane, buffer] : m_buffers) {
0162         if (buffer) {
0163             buffer->setDeadline(deadline);
0164         }
0165     }
0166 }
0167 
0168 std::optional<bool> DrmAtomicCommit::isVrr() const
0169 {
0170     return m_vrr;
0171 }
0172 
0173 const std::unordered_set<DrmPlane *> &DrmAtomicCommit::modifiedPlanes() const
0174 {
0175     return m_planes;
0176 }
0177 
0178 void DrmAtomicCommit::merge(DrmAtomicCommit *onTop)
0179 {
0180     for (const auto &[obj, properties] : onTop->m_properties) {
0181         auto &ownProperties = m_properties[obj];
0182         for (const auto &[prop, value] : properties) {
0183             ownProperties[prop] = value;
0184         }
0185     }
0186     for (const auto &[plane, buffer] : onTop->m_buffers) {
0187         m_buffers[plane] = buffer;
0188         m_planes.emplace(plane);
0189     }
0190     for (const auto &[prop, blob] : onTop->m_blobs) {
0191         m_blobs[prop] = blob;
0192     }
0193     if (onTop->m_vrr) {
0194         m_vrr = onTop->m_vrr;
0195     }
0196     m_cursorOnly &= onTop->isCursorOnly();
0197 }
0198 
0199 void DrmAtomicCommit::setCursorOnly(bool cursor)
0200 {
0201     m_cursorOnly = cursor;
0202 }
0203 
0204 bool DrmAtomicCommit::isCursorOnly() const
0205 {
0206     return m_cursorOnly;
0207 }
0208 
0209 DrmLegacyCommit::DrmLegacyCommit(DrmPipeline *pipeline, const std::shared_ptr<DrmFramebuffer> &buffer)
0210     : DrmCommit(pipeline->gpu())
0211     , m_pipeline(pipeline)
0212     , m_buffer(buffer)
0213 {
0214 }
0215 
0216 bool DrmLegacyCommit::doModeset(DrmConnector *connector, DrmConnectorMode *mode)
0217 {
0218     m_modeset = true;
0219     uint32_t connectorId = connector->id();
0220     if (drmModeSetCrtc(gpu()->fd(), m_pipeline->crtc()->id(), m_buffer->framebufferId(), 0, 0, &connectorId, 1, mode->nativeMode()) == 0) {
0221         m_pipeline->crtc()->setCurrent(m_buffer);
0222         return true;
0223     } else {
0224         return false;
0225     }
0226 }
0227 
0228 bool DrmLegacyCommit::doPageflip(PresentationMode mode)
0229 {
0230     m_mode = mode;
0231     uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT;
0232     if (mode == PresentationMode::Async || mode == PresentationMode::AdaptiveAsync) {
0233         flags |= DRM_MODE_PAGE_FLIP_ASYNC;
0234     }
0235     return drmModePageFlip(gpu()->fd(), m_pipeline->crtc()->id(), m_buffer->framebufferId(), flags, this) == 0;
0236 }
0237 
0238 void DrmLegacyCommit::pageFlipped(std::chrono::nanoseconds timestamp) const
0239 {
0240     Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
0241     m_pipeline->crtc()->setCurrent(m_buffer);
0242     m_pipeline->pageFlipped(timestamp, m_modeset ? DrmPipeline::PageflipType::Modeset : DrmPipeline::PageflipType::Normal, m_mode);
0243 }
0244 }