File indexing completed on 2024-11-10 04:57:36
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 ¬ifier : 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"