Skip to content

Commit b53ef91

Browse files
ikalcoaphelei
authored andcommitted
core: wait for dmabuf readiness (hyprwm#9806)
* add doOnReadable to event loop manager * move syncTimeline addWaiter to doOnReadable * wait on dmabuf buffers to be readable * don't over synchronize in scanout, also give present feedback on same buffer commit
1 parent 6dce34a commit b53ef91

File tree

8 files changed

+147
-116
lines changed

8 files changed

+147
-116
lines changed

src/helpers/Monitor.cpp

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,10 @@ bool CMonitor::attemptDirectScanout() {
13771377
auto PBUFFER = PSURFACE->current.buffer.buffer;
13781378

13791379
if (PBUFFER == output->state->state().buffer) {
1380+
timespec now;
1381+
clock_gettime(CLOCK_MONOTONIC, &now);
1382+
PSURFACE->presentFeedback(&now, self.lock());
1383+
13801384
if (scanoutNeedsCursorUpdate) {
13811385
if (!state.test()) {
13821386
Debug::log(TRACE, "attemptDirectScanout: failed basic test");
@@ -1423,32 +1427,10 @@ bool CMonitor::attemptDirectScanout() {
14231427
output->state->addDamage(PSURFACE->current.accumulateBufferDamage());
14241428
output->state->resetExplicitFences();
14251429

1426-
auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); });
1427-
1428-
auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings(output);
1429-
1430-
bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->current.buffer && PSURFACE->current.acquire && explicitOptions.explicitKMSEnabled;
1431-
if (DOEXPLICIT) {
1432-
// wait for surface's explicit fence if present
1433-
inFence = PSURFACE->current.acquire.exportAsFD();
1434-
if (inFence.isValid()) {
1435-
Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", inFence.get());
1436-
output->state->setExplicitInFence(inFence.get());
1437-
} else {
1438-
Debug::log(TRACE, "attemptDirectScanout: failed to acquire an sync file fd for aq IN_FENCE");
1439-
DOEXPLICIT = false;
1440-
}
1441-
}
1430+
// no need to do explicit sync here as surface current can only ever be ready to read
14421431

14431432
bool ok = output->commit();
14441433

1445-
if (!ok && DOEXPLICIT) {
1446-
Debug::log(TRACE, "attemptDirectScanout: EXPLICIT SYNC FAILED: commit() returned false. Resetting fences and retrying, might result in glitches.");
1447-
output->state->resetExplicitFences();
1448-
1449-
ok = output->commit();
1450-
}
1451-
14521434
if (!ok) {
14531435
Debug::log(TRACE, "attemptDirectScanout: failed to scanout surface");
14541436
lastScanout.reset();

src/helpers/sync/SyncTimeline.cpp

Lines changed: 2 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,6 @@ SP<CSyncTimeline> CSyncTimeline::create(int drmFD_, CFileDescriptor&& drmSyncobj
3434
}
3535

3636
CSyncTimeline::~CSyncTimeline() {
37-
for (auto& w : waiters) {
38-
if (w->source) {
39-
wl_event_source_remove(w->source);
40-
w->source = nullptr;
41-
}
42-
}
43-
4437
if (handle == 0)
4538
return;
4639

@@ -64,34 +57,8 @@ std::optional<bool> CSyncTimeline::check(uint64_t point, uint32_t flags) {
6457
return ret == 0;
6558
}
6659

67-
static int handleWaiterFD(int fd, uint32_t mask, void* data) {
68-
auto waiter = (CSyncTimeline::SWaiter*)data;
69-
70-
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
71-
Debug::log(ERR, "handleWaiterFD: eventfd error");
72-
return 0;
73-
}
74-
75-
if (mask & WL_EVENT_READABLE) {
76-
uint64_t value = 0;
77-
if (read(fd, &value, sizeof(value)) <= 0)
78-
Debug::log(ERR, "handleWaiterFD: failed to read from eventfd");
79-
}
80-
81-
wl_event_source_remove(waiter->source);
82-
waiter->source = nullptr;
83-
84-
if (waiter->fn)
85-
waiter->fn();
86-
87-
if (waiter->timeline)
88-
waiter->timeline->removeWaiter(waiter);
89-
90-
return 0;
91-
}
92-
9360
bool CSyncTimeline::addWaiter(const std::function<void()>& waiter, uint64_t point, uint32_t flags) {
94-
CFileDescriptor eventFd = CFileDescriptor{eventfd(0, EFD_CLOEXEC)};
61+
auto eventFd = CFileDescriptor(eventfd(0, EFD_CLOEXEC));
9562

9663
if (!eventFd.isValid()) {
9764
Debug::log(ERR, "CSyncTimeline::addWaiter: failed to acquire an eventfd");
@@ -103,46 +70,11 @@ bool CSyncTimeline::addWaiter(const std::function<void()>& waiter, uint64_t poin
10370
return false;
10471
}
10572

106-
if (eventFd.isReadable()) {
107-
waiter();
108-
return true;
109-
}
110-
111-
auto w = makeShared<SWaiter>();
112-
w->fn = waiter;
113-
w->timeline = self;
114-
w->eventFd = std::move(eventFd);
115-
116-
w->source = wl_event_loop_add_fd(g_pEventLoopManager->m_sWayland.loop, w->eventFd.get(), WL_EVENT_READABLE, ::handleWaiterFD, w.get());
117-
if (!w->source) {
118-
Debug::log(ERR, "CSyncTimeline::addWaiter: wl_event_loop_add_fd failed");
119-
return false;
120-
}
121-
122-
waiters.emplace_back(w);
73+
g_pEventLoopManager->doOnReadable(std::move(eventFd), waiter);
12374

12475
return true;
12576
}
12677

127-
void CSyncTimeline::removeWaiter(SWaiter* w) {
128-
if (w->source) {
129-
wl_event_source_remove(w->source);
130-
w->source = nullptr;
131-
}
132-
std::erase_if(waiters, [w](const auto& e) { return e.get() == w; });
133-
}
134-
135-
void CSyncTimeline::removeAllWaiters() {
136-
for (auto& w : waiters) {
137-
if (w->source) {
138-
wl_event_source_remove(w->source);
139-
w->source = nullptr;
140-
}
141-
}
142-
143-
waiters.clear();
144-
}
145-
14678
CFileDescriptor CSyncTimeline::exportAsSyncFileFD(uint64_t src) {
14779
int sync = -1;
14880

src/helpers/sync/SyncTimeline.hpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,12 @@ class CSyncTimeline {
2020
static SP<CSyncTimeline> create(int drmFD_, Hyprutils::OS::CFileDescriptor&& drmSyncobjFD);
2121
~CSyncTimeline();
2222

23-
struct SWaiter {
24-
std::function<void()> fn;
25-
wl_event_source* source = nullptr;
26-
WP<CSyncTimeline> timeline;
27-
Hyprutils::OS::CFileDescriptor eventFd;
28-
};
29-
3023
// check if the timeline point has been signaled
3124
// flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE
3225
// std::nullopt on fail
3326
std::optional<bool> check(uint64_t point, uint32_t flags);
3427

3528
bool addWaiter(const std::function<void()>& waiter, uint64_t point, uint32_t flags);
36-
void removeWaiter(SWaiter*);
37-
void removeAllWaiters();
3829
Hyprutils::OS::CFileDescriptor exportAsSyncFileFD(uint64_t src);
3930
bool importFromSyncFileFD(uint64_t dst, Hyprutils::OS::CFileDescriptor& fd);
4031
bool transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_t toPoint);
@@ -47,6 +38,4 @@ class CSyncTimeline {
4738

4839
private:
4940
CSyncTimeline() = default;
50-
51-
std::vector<SP<SWaiter>> waiters;
5241
};

src/managers/eventLoop/EventLoopManager.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ CEventLoopManager::~CEventLoopManager() {
2626
wl_event_source_remove(eventSourceData.eventSource);
2727
}
2828

29+
for (auto const& w : m_vReadableWaiters) {
30+
if (w->source != nullptr)
31+
wl_event_source_remove(w->source);
32+
}
33+
2934
if (m_sWayland.eventSource)
3035
wl_event_source_remove(m_sWayland.eventSource);
3136
if (m_sIdle.eventSource)
@@ -50,6 +55,33 @@ static int configWatcherWrite(int fd, uint32_t mask, void* data) {
5055
return 0;
5156
}
5257

58+
static int handleWaiterFD(int fd, uint32_t mask, void* data) {
59+
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
60+
Debug::log(ERR, "handleWaiterFD: readable waiter error");
61+
return 0;
62+
}
63+
64+
if (mask & WL_EVENT_READABLE)
65+
g_pEventLoopManager->onFdReadable((CEventLoopManager::SReadableWaiter*)data);
66+
67+
return 0;
68+
}
69+
70+
void CEventLoopManager::onFdReadable(SReadableWaiter* waiter) {
71+
auto it = std::ranges::find_if(m_vReadableWaiters, [waiter](const UP<SReadableWaiter>& w) { return waiter == w.get() && w->fd == waiter->fd && w->source == waiter->source; });
72+
73+
if (waiter->source) {
74+
wl_event_source_remove(waiter->source);
75+
waiter->source = nullptr;
76+
}
77+
78+
if (waiter->fn)
79+
waiter->fn();
80+
81+
if (it != m_vReadableWaiters.end())
82+
m_vReadableWaiters.erase(it);
83+
}
84+
5385
void CEventLoopManager::enterLoop() {
5486
m_sWayland.eventSource = wl_event_loop_add_fd(m_sWayland.loop, m_sTimers.timerfd.get(), WL_EVENT_READABLE, timerWrite, nullptr);
5587

@@ -143,6 +175,16 @@ void CEventLoopManager::doLater(const std::function<void()>& fn) {
143175
&m_sIdle);
144176
}
145177

178+
void CEventLoopManager::doOnReadable(CFileDescriptor fd, const std::function<void()>& fn) {
179+
if (!fd.isValid() || fd.isReadable()) {
180+
fn();
181+
return;
182+
}
183+
184+
auto& waiter = m_vReadableWaiters.emplace_back(makeUnique<SReadableWaiter>(nullptr, std::move(fd), fn));
185+
waiter->source = wl_event_loop_add_fd(g_pEventLoopManager->m_sWayland.loop, waiter->fd.get(), WL_EVENT_READABLE, ::handleWaiterFD, waiter.get());
186+
}
187+
146188
void CEventLoopManager::syncPollFDs() {
147189
auto aqPollFDs = g_pCompositor->m_pAqBackend->getPollFDs();
148190

src/managers/eventLoop/EventLoopManager.hpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ class CEventLoopManager {
3838
std::vector<std::function<void()>> fns;
3939
};
4040

41+
struct SReadableWaiter {
42+
wl_event_source* source;
43+
Hyprutils::OS::CFileDescriptor fd;
44+
std::function<void()> fn;
45+
};
46+
47+
// schedule function to when fd is readable (WL_EVENT_READABLE / POLLIN),
48+
// takes ownership of fd
49+
void doOnReadable(Hyprutils::OS::CFileDescriptor fd, const std::function<void()>& fn);
50+
void onFdReadable(SReadableWaiter* waiter);
51+
4152
private:
4253
// Manages the event sources after AQ pollFDs change.
4354
void syncPollFDs();
@@ -58,16 +69,16 @@ class CEventLoopManager {
5869
Hyprutils::OS::CFileDescriptor timerfd;
5970
} m_sTimers;
6071

61-
SIdleData m_sIdle;
62-
std::map<int, SEventSourceData> aqEventSources;
72+
SIdleData m_sIdle;
73+
std::map<int, SEventSourceData> aqEventSources;
74+
std::vector<UP<SReadableWaiter>> m_vReadableWaiters;
6375

6476
struct {
6577
CHyprSignalListener pollFDsChanged;
6678
} m_sListeners;
6779

6880
wl_event_source* m_configWatcherInotifySource = nullptr;
6981

70-
friend class CSyncTimeline;
7182
friend class CAsyncDialogBox;
7283
};
7384

src/protocols/core/Compositor.cpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
#include "../../helpers/sync/SyncReleaser.hpp"
1111
#include "../PresentationTime.hpp"
1212
#include "../DRMSyncobj.hpp"
13+
#include "../types/DMABuffer.hpp"
1314
#include "../../render/Renderer.hpp"
1415
#include "config/ConfigValue.hpp"
16+
#include "../../managers/eventLoop/EventLoopManager.hpp"
1517
#include "protocols/types/SurfaceRole.hpp"
1618
#include "render/Texture.hpp"
1719
#include <cstring>
@@ -123,16 +125,15 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
123125
return;
124126
}
125127

126-
if ((!pending.updated.buffer) || // no new buffer attached
127-
(!pending.buffer && !pending.texture) || // null buffer attached
128-
(!pending.updated.acquire && pending.buffer->isSynchronous()) // synchronous buffers (ex. shm) can be read immediately
128+
if ((!pending.updated.buffer) || // no new buffer attached
129+
(!pending.buffer && !pending.texture) // null buffer attached
129130
) {
130131
commitState(pending);
131132
pending.reset();
132133
return;
133134
}
134135

135-
// save state while we wait for buffer to become ready
136+
// save state while we wait for buffer to become ready to read
136137
const auto& state = pendingStates.emplace(makeUnique<SSurfaceState>(pending));
137138
pending.reset();
138139

@@ -152,13 +153,19 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
152153
if (state->updated.acquire) {
153154
// wait on acquire point for this surface, from explicit sync protocol
154155
state->acquire.addWaiter(whenReadable);
155-
} else if (state->buffer->dmabuf().success) {
156-
// https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#implicit-fence-poll-support
157-
// TODO: wait for the dma-buf fd's to become readable
156+
} else if (state->buffer->isSynchronous()) {
157+
// synchronous (shm) buffers can be read immediately
158158
whenReadable();
159+
} else if (state->buffer->type() == Aquamarine::BUFFER_TYPE_DMABUF && state->buffer->dmabuf().success) {
160+
// async buffer and is dmabuf, then we can wait on implicit fences
161+
auto syncFd = dynamic_cast<CDMABuffer*>(state->buffer.buffer.get())->exportSyncFile();
162+
163+
if (syncFd.isValid())
164+
g_pEventLoopManager->doOnReadable(std::move(syncFd), whenReadable);
165+
else
166+
whenReadable();
159167
} else {
160-
// huh??? only buffers with acquire or dmabuf should get through here...
161-
Debug::log(ERR, "BUG THIS: wl_surface.commit: non-acquire non-dmabuf buffers needs wait...");
168+
Debug::log(ERR, "BUG THIS: wl_surface.commit: no acquire, non-dmabuf, async buffer, needs wait... this shouldn't happen");
162169
whenReadable();
163170
}
164171
});

0 commit comments

Comments
 (0)