core: wait for dmabuf readiness (#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
This commit is contained in:
Ikalco 2025-04-15 18:02:31 -05:00 committed by GitHub
parent ffd6cf65e4
commit 0e521788bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 147 additions and 116 deletions

View file

@ -10,8 +10,10 @@
#include "../../helpers/sync/SyncReleaser.hpp"
#include "../PresentationTime.hpp"
#include "../DRMSyncobj.hpp"
#include "../types/DMABuffer.hpp"
#include "../../render/Renderer.hpp"
#include "config/ConfigValue.hpp"
#include "../../managers/eventLoop/EventLoopManager.hpp"
#include "protocols/types/SurfaceRole.hpp"
#include "render/Texture.hpp"
#include <cstring>
@ -123,16 +125,15 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
return;
}
if ((!pending.updated.buffer) || // no new buffer attached
(!pending.buffer && !pending.texture) || // null buffer attached
(!pending.updated.acquire && pending.buffer->isSynchronous()) // synchronous buffers (ex. shm) can be read immediately
if ((!pending.updated.buffer) || // no new buffer attached
(!pending.buffer && !pending.texture) // null buffer attached
) {
commitState(pending);
pending.reset();
return;
}
// save state while we wait for buffer to become ready
// save state while we wait for buffer to become ready to read
const auto& state = pendingStates.emplace(makeUnique<SSurfaceState>(pending));
pending.reset();
@ -152,13 +153,19 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
if (state->updated.acquire) {
// wait on acquire point for this surface, from explicit sync protocol
state->acquire.addWaiter(whenReadable);
} else if (state->buffer->dmabuf().success) {
// https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#implicit-fence-poll-support
// TODO: wait for the dma-buf fd's to become readable
} else if (state->buffer->isSynchronous()) {
// synchronous (shm) buffers can be read immediately
whenReadable();
} else if (state->buffer->type() == Aquamarine::BUFFER_TYPE_DMABUF && state->buffer->dmabuf().success) {
// async buffer and is dmabuf, then we can wait on implicit fences
auto syncFd = dynamic_cast<CDMABuffer*>(state->buffer.buffer.get())->exportSyncFile();
if (syncFd.isValid())
g_pEventLoopManager->doOnReadable(std::move(syncFd), whenReadable);
else
whenReadable();
} else {
// huh??? only buffers with acquire or dmabuf should get through here...
Debug::log(ERR, "BUG THIS: wl_surface.commit: non-acquire non-dmabuf buffers needs wait...");
Debug::log(ERR, "BUG THIS: wl_surface.commit: no acquire, non-dmabuf, async buffer, needs wait... this shouldn't happen");
whenReadable();
}
});

View file

@ -4,6 +4,14 @@
#include "../../render/Renderer.hpp"
#include "../../helpers/Format.hpp"
#if defined(__linux__)
#include <linux/dma-buf.h>
#include <linux/sync_file.h>
#include <sys/ioctl.h>
#endif
using namespace Hyprutils::OS;
CDMABuffer::CDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs const& attrs_) : attrs(attrs_) {
g_pHyprRenderer->makeEGLCurrent();
@ -84,3 +92,61 @@ void CDMABuffer::closeFDs() {
}
attrs.planes = 0;
}
static int doIoctl(int fd, unsigned long request, void* arg) {
int ret;
do {
ret = ioctl(fd, request, arg);
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
return ret;
}
// https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#c.dma_buf_export_sync_file
// returns a sync file that will be signalled when dmabuf is ready to be read
CFileDescriptor CDMABuffer::exportSyncFile() {
if (!good())
return {};
#if !defined(__linux__)
return {};
#else
std::vector<CFileDescriptor> syncFds(attrs.fds.size());
for (const auto& fd : attrs.fds) {
if (fd == -1)
continue;
dma_buf_export_sync_file request{
.flags = DMA_BUF_SYNC_READ,
.fd = -1,
};
if (doIoctl(fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &request) == 0)
syncFds.emplace_back(request.fd);
}
if (syncFds.empty())
return {};
CFileDescriptor syncFd;
for (auto& fd : syncFds) {
if (!syncFd.isValid()) {
syncFd = std::move(fd);
continue;
}
struct sync_merge_data data{
.name = "merged release fence",
.fd2 = fd.get(),
.fence = -1,
};
if (doIoctl(syncFd.get(), SYNC_IOC_MERGE, &data) == 0)
syncFd = CFileDescriptor(data.fence);
else
syncFd = {};
}
return syncFd;
#endif
}

View file

@ -1,6 +1,7 @@
#pragma once
#include "Buffer.hpp"
#include <hyprutils/os/FileDescriptor.hpp>
class CDMABuffer : public IHLBuffer {
public:
@ -16,6 +17,7 @@ class CDMABuffer : public IHLBuffer {
virtual void endDataPtr();
bool good();
void closeFDs();
Hyprutils::OS::CFileDescriptor exportSyncFile();
bool success = false;