KWin
Loading...
Searching...
No Matches
transaction.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
10#include "wayland/surface_p.h"
12
13namespace KWin
14{
15
17{
18 static QHash<GraphicsBuffer *, TransactionDmaBufLocker *> lockers;
19 if (auto it = lockers.find(buffer); it != lockers.end()) {
20 return *it;
21 }
22
23 const DmaBufAttributes *attributes = buffer->dmabufAttributes();
24 if (!attributes) {
25 return nullptr;
26 }
27
28 auto locker = new TransactionDmaBufLocker(attributes);
29 lockers[buffer] = locker;
30 QObject::connect(buffer, &QObject::destroyed, [buffer]() {
31 delete lockers.take(buffer);
32 });
33
34 return locker;
35}
36
37TransactionDmaBufLocker::TransactionDmaBufLocker(const DmaBufAttributes *attributes)
38{
39 for (int i = 0; i < attributes->planeCount; ++i) {
40 auto notifier = new QSocketNotifier(attributes->fd[i].get(), QSocketNotifier::Read);
41 notifier->setEnabled(false);
42 connect(notifier, &QSocketNotifier::activated, this, [this, notifier]() {
43 notifier->setEnabled(false);
44 m_pending.removeOne(notifier);
45 if (m_pending.isEmpty()) {
46 const auto transactions = m_transactions; // unlock() may destroy this
47 m_transactions.clear();
48 for (Transaction *transition : transactions) {
49 transition->unlock();
50 }
51 }
52 });
53 m_notifiers.emplace_back(notifier);
54 }
55}
56
57void TransactionDmaBufLocker::add(Transaction *transition)
58{
59 if (arm()) {
60 transition->lock();
61 m_transactions.append(transition);
62 }
63}
64
65bool TransactionDmaBufLocker::arm()
66{
67 if (!m_pending.isEmpty()) {
68 return true;
69 }
70 for (const auto &notifier : m_notifiers) {
71 if (!FileDescriptor::isReadable(notifier->socket())) {
72 notifier->setEnabled(true);
73 m_pending.append(notifier.get());
74 }
75 }
76 return !m_pending.isEmpty();
77}
78
79Transaction::Transaction()
80{
81}
82
83void Transaction::lock()
84{
85 m_locks++;
86}
87
88void Transaction::unlock()
89{
90 Q_ASSERT(m_locks > 0);
91 m_locks--;
92 if (m_locks == 0) {
93 tryApply();
94 }
95}
96
97bool Transaction::isReady() const
98{
99 if (m_locks) {
100 return false;
101 }
102
103 return std::none_of(m_entries.cbegin(), m_entries.cend(), [](const TransactionEntry &entry) {
104 return entry.previousTransaction;
105 });
106}
107
108Transaction *Transaction::next(SurfaceInterface *surface) const
109{
110 for (const TransactionEntry &entry : m_entries) {
111 if (entry.surface == surface) {
112 return entry.nextTransaction;
113 }
114 }
115 return nullptr;
116}
117
118void Transaction::add(SurfaceInterface *surface)
119{
120 SurfaceState *pending = SurfaceInterfacePrivate::get(surface)->pending.get();
121
122 for (TransactionEntry &entry : m_entries) {
123 if (entry.surface == surface) {
124 if (pending->bufferIsSet) {
125 entry.buffer = GraphicsBufferRef(pending->buffer);
126 }
127 pending->mergeInto(entry.state.get());
128 return;
129 }
130 }
131
132 auto state = std::make_unique<SurfaceState>();
133 pending->mergeInto(state.get());
134
135 m_entries.emplace_back(TransactionEntry{
136 .surface = surface,
137 .buffer = GraphicsBufferRef(state->buffer),
138 .state = std::move(state),
139 });
140}
141
142void Transaction::amend(SurfaceInterface *surface, std::function<void(SurfaceState *)> mutator)
143{
144 for (TransactionEntry &entry : m_entries) {
145 if (entry.surface == surface) {
146 mutator(entry.state.get());
147 }
148 }
149}
150
151void Transaction::merge(Transaction *other)
152{
153 for (size_t i = 0; i < other->m_entries.size(); ++i) {
154 m_entries.emplace_back(std::move(other->m_entries[i]));
155 }
156 other->m_entries.clear();
157}
158
159static bool isAncestor(SurfaceInterface *surface, SurfaceInterface *ancestor)
160{
161 SurfaceInterface *candidate = surface;
162 while (candidate) {
163 SubSurfaceInterface *subsurface = candidate->subSurface();
164 if (!subsurface) {
165 return false;
166 }
167
168 if (subsurface->parentSurface() == ancestor) {
169 return true;
170 }
171
172 candidate = subsurface->parentSurface();
173 }
174
175 return false;
176}
177
178static SurfaceInterface *mainSurface(SurfaceInterface *surface)
179{
180 SubSurfaceInterface *subsurface = surface->subSurface();
181 if (subsurface) {
182 return subsurface->mainSurface();
183 }
184 return surface;
185}
186
187void Transaction::apply()
188{
189 // Sort surfaces so descendants come first, then their ancestors.
190 std::sort(m_entries.begin(), m_entries.end(), [](const TransactionEntry &a, const TransactionEntry &b) {
191 if (!a.surface) {
192 return false;
193 }
194 if (!b.surface) {
195 return true;
196 }
197
198 if (isAncestor(a.surface, b.surface)) {
199 return true;
200 }
201 if (isAncestor(b.surface, a.surface)) {
202 return false;
203 }
204 return mainSurface(a.surface) < mainSurface(b.surface);
205 });
206
207 for (TransactionEntry &entry : m_entries) {
208 if (entry.surface) {
209 SurfaceInterfacePrivate::get(entry.surface)->applyState(entry.state.get());
210 }
211 }
212
213 for (TransactionEntry &entry : m_entries) {
214 if (entry.surface) {
215 if (entry.surface->lastTransaction() == this) {
216 entry.surface->setFirstTransaction(nullptr);
217 entry.surface->setLastTransaction(nullptr);
218 } else {
219 entry.surface->setFirstTransaction(entry.nextTransaction);
220 }
221 }
222
223 if (entry.nextTransaction) {
224 for (TransactionEntry &otherEntry : entry.nextTransaction->m_entries) {
225 if (otherEntry.previousTransaction == this) {
226 otherEntry.previousTransaction = nullptr;
227 break;
228 }
229 }
230 entry.nextTransaction->tryApply();
231 }
232 }
233
234 delete this;
235}
236
237bool Transaction::tryApply()
238{
239 if (!isReady()) {
240 return false;
241 }
242 apply();
243 return true;
244}
245
246void Transaction::commit()
247{
248 for (TransactionEntry &entry : m_entries) {
249 if (entry.state->bufferIsSet && entry.state->buffer) {
250 // Avoid applying the transaction until all graphics buffers have become idle.
251 if (auto locker = TransactionDmaBufLocker::get(entry.state->buffer)) {
252 locker->add(this);
253 }
254 }
255
256 if (entry.surface->firstTransaction()) {
257 Transaction *lastTransaction = entry.surface->lastTransaction();
258 for (TransactionEntry &lastEntry : lastTransaction->m_entries) {
259 if (lastEntry.surface == entry.surface) {
260 lastEntry.nextTransaction = this;
261 }
262 }
263 } else {
264 entry.surface->setFirstTransaction(this);
265 }
266
267 entry.previousTransaction = entry.surface->lastTransaction();
268 entry.surface->setLastTransaction(this);
269 }
270
271 if (!tryApply()) {
272 for (const TransactionEntry &entry : m_entries) {
273 Q_EMIT entry.surface->stateStashed(entry.state->serial);
274 }
275 }
276}
277
278} // namespace KWin
279
280#include "moc_transaction.cpp"
virtual const DmaBufAttributes * dmabufAttributes() const
SurfaceInterface * parentSurface() const
SurfaceInterface * mainSurface() const
Resource representing a wl_surface.
Definition surface.h:80
GraphicsBuffer * buffer() const
Definition surface.cpp:809
void setFirstTransaction(Transaction *transaction)
Definition surface.cpp:1154
SubSurfaceInterface * subSurface() const
Definition surface.cpp:845
static TransactionDmaBufLocker * get(GraphicsBuffer *buffer)
std::array< FileDescriptor, 4 > fd
void mergeInto(SurfaceState *target)
Definition surface.cpp:514
QPointer< GraphicsBuffer > buffer
Definition surface_p.h:65
Definition transaction.h:28
QPointer< SurfaceInterface > surface
Definition transaction.h:34