File indexing completed on 2024-05-12 05:32:31

0001 /*
0002     SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "wayland/transaction.h"
0008 #include "utils/filedescriptor.h"
0009 #include "wayland/subcompositor.h"
0010 #include "wayland/surface_p.h"
0011 #include "wayland/transaction_p.h"
0012 
0013 namespace KWin
0014 {
0015 
0016 TransactionDmaBufLocker *TransactionDmaBufLocker::get(GraphicsBuffer *buffer)
0017 {
0018     static QHash<GraphicsBuffer *, TransactionDmaBufLocker *> lockers;
0019     if (auto it = lockers.find(buffer); it != lockers.end()) {
0020         return *it;
0021     }
0022 
0023     const DmaBufAttributes *attributes = buffer->dmabufAttributes();
0024     if (!attributes) {
0025         return nullptr;
0026     }
0027 
0028     auto locker = new TransactionDmaBufLocker(attributes);
0029     lockers[buffer] = locker;
0030     QObject::connect(buffer, &QObject::destroyed, [buffer]() {
0031         delete lockers.take(buffer);
0032     });
0033 
0034     return locker;
0035 }
0036 
0037 TransactionDmaBufLocker::TransactionDmaBufLocker(const DmaBufAttributes *attributes)
0038 {
0039     for (int i = 0; i < attributes->planeCount; ++i) {
0040         auto notifier = new QSocketNotifier(attributes->fd[i].get(), QSocketNotifier::Read);
0041         notifier->setEnabled(false);
0042         connect(notifier, &QSocketNotifier::activated, this, [this, notifier]() {
0043             notifier->setEnabled(false);
0044             m_pending.removeOne(notifier);
0045             if (m_pending.isEmpty()) {
0046                 const auto transactions = m_transactions; // unlock() may destroy this
0047                 m_transactions.clear();
0048                 for (Transaction *transition : transactions) {
0049                     transition->unlock();
0050                 }
0051             }
0052         });
0053         m_notifiers.emplace_back(notifier);
0054     }
0055 }
0056 
0057 void TransactionDmaBufLocker::add(Transaction *transition)
0058 {
0059     if (arm()) {
0060         transition->lock();
0061         m_transactions.append(transition);
0062     }
0063 }
0064 
0065 bool TransactionDmaBufLocker::arm()
0066 {
0067     if (!m_pending.isEmpty()) {
0068         return true;
0069     }
0070     for (const auto &notifier : m_notifiers) {
0071         if (!FileDescriptor::isReadable(notifier->socket())) {
0072             notifier->setEnabled(true);
0073             m_pending.append(notifier.get());
0074         }
0075     }
0076     return !m_pending.isEmpty();
0077 }
0078 
0079 Transaction::Transaction()
0080 {
0081 }
0082 
0083 void Transaction::lock()
0084 {
0085     m_locks++;
0086 }
0087 
0088 void Transaction::unlock()
0089 {
0090     Q_ASSERT(m_locks > 0);
0091     m_locks--;
0092     if (m_locks == 0) {
0093         tryApply();
0094     }
0095 }
0096 
0097 bool Transaction::isReady() const
0098 {
0099     if (m_locks) {
0100         return false;
0101     }
0102 
0103     return std::none_of(m_entries.cbegin(), m_entries.cend(), [](const TransactionEntry &entry) {
0104         return entry.previousTransaction;
0105     });
0106 }
0107 
0108 Transaction *Transaction::next(SurfaceInterface *surface) const
0109 {
0110     for (const TransactionEntry &entry : m_entries) {
0111         if (entry.surface == surface) {
0112             return entry.nextTransaction;
0113         }
0114     }
0115     return nullptr;
0116 }
0117 
0118 void Transaction::add(SurfaceInterface *surface)
0119 {
0120     SurfaceState *pending = SurfaceInterfacePrivate::get(surface)->pending.get();
0121 
0122     for (TransactionEntry &entry : m_entries) {
0123         if (entry.surface == surface) {
0124             if (pending->bufferIsSet) {
0125                 entry.buffer = GraphicsBufferRef(pending->buffer);
0126             }
0127             pending->mergeInto(entry.state.get());
0128             return;
0129         }
0130     }
0131 
0132     auto state = std::make_unique<SurfaceState>();
0133     pending->mergeInto(state.get());
0134 
0135     m_entries.emplace_back(TransactionEntry{
0136         .surface = surface,
0137         .buffer = GraphicsBufferRef(state->buffer),
0138         .state = std::move(state),
0139     });
0140 }
0141 
0142 void Transaction::amend(SurfaceInterface *surface, std::function<void(SurfaceState *)> mutator)
0143 {
0144     for (TransactionEntry &entry : m_entries) {
0145         if (entry.surface == surface) {
0146             mutator(entry.state.get());
0147         }
0148     }
0149 }
0150 
0151 void Transaction::merge(Transaction *other)
0152 {
0153     for (size_t i = 0; i < other->m_entries.size(); ++i) {
0154         m_entries.emplace_back(std::move(other->m_entries[i]));
0155     }
0156     other->m_entries.clear();
0157 }
0158 
0159 static bool isAncestor(SurfaceInterface *surface, SurfaceInterface *ancestor)
0160 {
0161     SurfaceInterface *candidate = surface;
0162     while (candidate) {
0163         SubSurfaceInterface *subsurface = candidate->subSurface();
0164         if (!subsurface) {
0165             return false;
0166         }
0167 
0168         if (subsurface->parentSurface() == ancestor) {
0169             return true;
0170         }
0171 
0172         candidate = subsurface->parentSurface();
0173     }
0174 
0175     return false;
0176 }
0177 
0178 static SurfaceInterface *mainSurface(SurfaceInterface *surface)
0179 {
0180     SubSurfaceInterface *subsurface = surface->subSurface();
0181     if (subsurface) {
0182         return subsurface->mainSurface();
0183     }
0184     return surface;
0185 }
0186 
0187 void Transaction::apply()
0188 {
0189     // Sort surfaces so descendants come first, then their ancestors.
0190     std::sort(m_entries.begin(), m_entries.end(), [](const TransactionEntry &a, const TransactionEntry &b) {
0191         if (!a.surface) {
0192             return false;
0193         }
0194         if (!b.surface) {
0195             return true;
0196         }
0197 
0198         if (isAncestor(a.surface, b.surface)) {
0199             return true;
0200         }
0201         if (isAncestor(b.surface, a.surface)) {
0202             return false;
0203         }
0204         return mainSurface(a.surface) < mainSurface(b.surface);
0205     });
0206 
0207     for (TransactionEntry &entry : m_entries) {
0208         if (entry.surface) {
0209             SurfaceInterfacePrivate::get(entry.surface)->applyState(entry.state.get());
0210         }
0211     }
0212 
0213     for (TransactionEntry &entry : m_entries) {
0214         if (entry.surface) {
0215             if (entry.surface->lastTransaction() == this) {
0216                 entry.surface->setFirstTransaction(nullptr);
0217                 entry.surface->setLastTransaction(nullptr);
0218             } else {
0219                 entry.surface->setFirstTransaction(entry.nextTransaction);
0220             }
0221         }
0222 
0223         if (entry.nextTransaction) {
0224             for (TransactionEntry &otherEntry : entry.nextTransaction->m_entries) {
0225                 if (otherEntry.previousTransaction == this) {
0226                     otherEntry.previousTransaction = nullptr;
0227                     break;
0228                 }
0229             }
0230             entry.nextTransaction->tryApply();
0231         }
0232     }
0233 
0234     delete this;
0235 }
0236 
0237 bool Transaction::tryApply()
0238 {
0239     if (!isReady()) {
0240         return false;
0241     }
0242     apply();
0243     return true;
0244 }
0245 
0246 void Transaction::commit()
0247 {
0248     for (TransactionEntry &entry : m_entries) {
0249         if (entry.state->bufferIsSet && entry.state->buffer) {
0250             // Avoid applying the transaction until all graphics buffers have become idle.
0251             if (auto locker = TransactionDmaBufLocker::get(entry.state->buffer)) {
0252                 locker->add(this);
0253             }
0254         }
0255 
0256         if (entry.surface->firstTransaction()) {
0257             Transaction *lastTransaction = entry.surface->lastTransaction();
0258             for (TransactionEntry &lastEntry : lastTransaction->m_entries) {
0259                 if (lastEntry.surface == entry.surface) {
0260                     lastEntry.nextTransaction = this;
0261                 }
0262             }
0263         } else {
0264             entry.surface->setFirstTransaction(this);
0265         }
0266 
0267         entry.previousTransaction = entry.surface->lastTransaction();
0268         entry.surface->setLastTransaction(this);
0269     }
0270 
0271     if (!tryApply()) {
0272         for (const TransactionEntry &entry : m_entries) {
0273             Q_EMIT entry.surface->stateStashed(entry.state->serial);
0274         }
0275     }
0276 }
0277 
0278 } // namespace KWin
0279 
0280 #include "moc_transaction.cpp"