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 }