mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-05-19 08:30:22 -07:00
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:
parent
ffd6cf65e4
commit
0e521788bc
@ -1377,6 +1377,10 @@ bool CMonitor::attemptDirectScanout() {
|
|||||||
auto PBUFFER = PSURFACE->current.buffer.buffer;
|
auto PBUFFER = PSURFACE->current.buffer.buffer;
|
||||||
|
|
||||||
if (PBUFFER == output->state->state().buffer) {
|
if (PBUFFER == output->state->state().buffer) {
|
||||||
|
timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
PSURFACE->presentFeedback(&now, self.lock());
|
||||||
|
|
||||||
if (scanoutNeedsCursorUpdate) {
|
if (scanoutNeedsCursorUpdate) {
|
||||||
if (!state.test()) {
|
if (!state.test()) {
|
||||||
Debug::log(TRACE, "attemptDirectScanout: failed basic test");
|
Debug::log(TRACE, "attemptDirectScanout: failed basic test");
|
||||||
@ -1423,32 +1427,10 @@ bool CMonitor::attemptDirectScanout() {
|
|||||||
output->state->addDamage(PSURFACE->current.accumulateBufferDamage());
|
output->state->addDamage(PSURFACE->current.accumulateBufferDamage());
|
||||||
output->state->resetExplicitFences();
|
output->state->resetExplicitFences();
|
||||||
|
|
||||||
auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); });
|
// no need to do explicit sync here as surface current can only ever be ready to read
|
||||||
|
|
||||||
auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings(output);
|
|
||||||
|
|
||||||
bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->current.buffer && PSURFACE->current.acquire && explicitOptions.explicitKMSEnabled;
|
|
||||||
if (DOEXPLICIT) {
|
|
||||||
// wait for surface's explicit fence if present
|
|
||||||
inFence = PSURFACE->current.acquire.exportAsFD();
|
|
||||||
if (inFence.isValid()) {
|
|
||||||
Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", inFence.get());
|
|
||||||
output->state->setExplicitInFence(inFence.get());
|
|
||||||
} else {
|
|
||||||
Debug::log(TRACE, "attemptDirectScanout: failed to acquire an sync file fd for aq IN_FENCE");
|
|
||||||
DOEXPLICIT = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ok = output->commit();
|
bool ok = output->commit();
|
||||||
|
|
||||||
if (!ok && DOEXPLICIT) {
|
|
||||||
Debug::log(TRACE, "attemptDirectScanout: EXPLICIT SYNC FAILED: commit() returned false. Resetting fences and retrying, might result in glitches.");
|
|
||||||
output->state->resetExplicitFences();
|
|
||||||
|
|
||||||
ok = output->commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
Debug::log(TRACE, "attemptDirectScanout: failed to scanout surface");
|
Debug::log(TRACE, "attemptDirectScanout: failed to scanout surface");
|
||||||
lastScanout.reset();
|
lastScanout.reset();
|
||||||
|
@ -34,13 +34,6 @@ SP<CSyncTimeline> CSyncTimeline::create(int drmFD_, CFileDescriptor&& drmSyncobj
|
|||||||
}
|
}
|
||||||
|
|
||||||
CSyncTimeline::~CSyncTimeline() {
|
CSyncTimeline::~CSyncTimeline() {
|
||||||
for (auto& w : waiters) {
|
|
||||||
if (w->source) {
|
|
||||||
wl_event_source_remove(w->source);
|
|
||||||
w->source = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle == 0)
|
if (handle == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -64,34 +57,8 @@ std::optional<bool> CSyncTimeline::check(uint64_t point, uint32_t flags) {
|
|||||||
return ret == 0;
|
return ret == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handleWaiterFD(int fd, uint32_t mask, void* data) {
|
|
||||||
auto waiter = (CSyncTimeline::SWaiter*)data;
|
|
||||||
|
|
||||||
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
|
|
||||||
Debug::log(ERR, "handleWaiterFD: eventfd error");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mask & WL_EVENT_READABLE) {
|
|
||||||
uint64_t value = 0;
|
|
||||||
if (read(fd, &value, sizeof(value)) <= 0)
|
|
||||||
Debug::log(ERR, "handleWaiterFD: failed to read from eventfd");
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_event_source_remove(waiter->source);
|
|
||||||
waiter->source = nullptr;
|
|
||||||
|
|
||||||
if (waiter->fn)
|
|
||||||
waiter->fn();
|
|
||||||
|
|
||||||
if (waiter->timeline)
|
|
||||||
waiter->timeline->removeWaiter(waiter);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CSyncTimeline::addWaiter(const std::function<void()>& waiter, uint64_t point, uint32_t flags) {
|
bool CSyncTimeline::addWaiter(const std::function<void()>& waiter, uint64_t point, uint32_t flags) {
|
||||||
CFileDescriptor eventFd = CFileDescriptor{eventfd(0, EFD_CLOEXEC)};
|
auto eventFd = CFileDescriptor(eventfd(0, EFD_CLOEXEC));
|
||||||
|
|
||||||
if (!eventFd.isValid()) {
|
if (!eventFd.isValid()) {
|
||||||
Debug::log(ERR, "CSyncTimeline::addWaiter: failed to acquire an eventfd");
|
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
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventFd.isReadable()) {
|
g_pEventLoopManager->doOnReadable(std::move(eventFd), waiter);
|
||||||
waiter();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto w = makeShared<SWaiter>();
|
|
||||||
w->fn = waiter;
|
|
||||||
w->timeline = self;
|
|
||||||
w->eventFd = std::move(eventFd);
|
|
||||||
|
|
||||||
w->source = wl_event_loop_add_fd(g_pEventLoopManager->m_sWayland.loop, w->eventFd.get(), WL_EVENT_READABLE, ::handleWaiterFD, w.get());
|
|
||||||
if (!w->source) {
|
|
||||||
Debug::log(ERR, "CSyncTimeline::addWaiter: wl_event_loop_add_fd failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
waiters.emplace_back(w);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSyncTimeline::removeWaiter(SWaiter* w) {
|
|
||||||
if (w->source) {
|
|
||||||
wl_event_source_remove(w->source);
|
|
||||||
w->source = nullptr;
|
|
||||||
}
|
|
||||||
std::erase_if(waiters, [w](const auto& e) { return e.get() == w; });
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSyncTimeline::removeAllWaiters() {
|
|
||||||
for (auto& w : waiters) {
|
|
||||||
if (w->source) {
|
|
||||||
wl_event_source_remove(w->source);
|
|
||||||
w->source = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
waiters.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
CFileDescriptor CSyncTimeline::exportAsSyncFileFD(uint64_t src) {
|
CFileDescriptor CSyncTimeline::exportAsSyncFileFD(uint64_t src) {
|
||||||
int sync = -1;
|
int sync = -1;
|
||||||
|
|
||||||
|
@ -20,21 +20,12 @@ class CSyncTimeline {
|
|||||||
static SP<CSyncTimeline> create(int drmFD_, Hyprutils::OS::CFileDescriptor&& drmSyncobjFD);
|
static SP<CSyncTimeline> create(int drmFD_, Hyprutils::OS::CFileDescriptor&& drmSyncobjFD);
|
||||||
~CSyncTimeline();
|
~CSyncTimeline();
|
||||||
|
|
||||||
struct SWaiter {
|
|
||||||
std::function<void()> fn;
|
|
||||||
wl_event_source* source = nullptr;
|
|
||||||
WP<CSyncTimeline> timeline;
|
|
||||||
Hyprutils::OS::CFileDescriptor eventFd;
|
|
||||||
};
|
|
||||||
|
|
||||||
// check if the timeline point has been signaled
|
// check if the timeline point has been signaled
|
||||||
// flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE
|
// flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE
|
||||||
// std::nullopt on fail
|
// std::nullopt on fail
|
||||||
std::optional<bool> check(uint64_t point, uint32_t flags);
|
std::optional<bool> check(uint64_t point, uint32_t flags);
|
||||||
|
|
||||||
bool addWaiter(const std::function<void()>& waiter, uint64_t point, uint32_t flags);
|
bool addWaiter(const std::function<void()>& waiter, uint64_t point, uint32_t flags);
|
||||||
void removeWaiter(SWaiter*);
|
|
||||||
void removeAllWaiters();
|
|
||||||
Hyprutils::OS::CFileDescriptor exportAsSyncFileFD(uint64_t src);
|
Hyprutils::OS::CFileDescriptor exportAsSyncFileFD(uint64_t src);
|
||||||
bool importFromSyncFileFD(uint64_t dst, Hyprutils::OS::CFileDescriptor& fd);
|
bool importFromSyncFileFD(uint64_t dst, Hyprutils::OS::CFileDescriptor& fd);
|
||||||
bool transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_t toPoint);
|
bool transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_t toPoint);
|
||||||
@ -47,6 +38,4 @@ class CSyncTimeline {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
CSyncTimeline() = default;
|
CSyncTimeline() = default;
|
||||||
|
|
||||||
std::vector<SP<SWaiter>> waiters;
|
|
||||||
};
|
};
|
||||||
|
@ -26,6 +26,11 @@ CEventLoopManager::~CEventLoopManager() {
|
|||||||
wl_event_source_remove(eventSourceData.eventSource);
|
wl_event_source_remove(eventSourceData.eventSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto const& w : m_vReadableWaiters) {
|
||||||
|
if (w->source != nullptr)
|
||||||
|
wl_event_source_remove(w->source);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_sWayland.eventSource)
|
if (m_sWayland.eventSource)
|
||||||
wl_event_source_remove(m_sWayland.eventSource);
|
wl_event_source_remove(m_sWayland.eventSource);
|
||||||
if (m_sIdle.eventSource)
|
if (m_sIdle.eventSource)
|
||||||
@ -50,6 +55,33 @@ static int configWatcherWrite(int fd, uint32_t mask, void* data) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int handleWaiterFD(int fd, uint32_t mask, void* data) {
|
||||||
|
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
|
||||||
|
Debug::log(ERR, "handleWaiterFD: readable waiter error");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & WL_EVENT_READABLE)
|
||||||
|
g_pEventLoopManager->onFdReadable((CEventLoopManager::SReadableWaiter*)data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEventLoopManager::onFdReadable(SReadableWaiter* waiter) {
|
||||||
|
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; });
|
||||||
|
|
||||||
|
if (waiter->source) {
|
||||||
|
wl_event_source_remove(waiter->source);
|
||||||
|
waiter->source = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waiter->fn)
|
||||||
|
waiter->fn();
|
||||||
|
|
||||||
|
if (it != m_vReadableWaiters.end())
|
||||||
|
m_vReadableWaiters.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
void CEventLoopManager::enterLoop() {
|
void CEventLoopManager::enterLoop() {
|
||||||
m_sWayland.eventSource = wl_event_loop_add_fd(m_sWayland.loop, m_sTimers.timerfd.get(), WL_EVENT_READABLE, timerWrite, nullptr);
|
m_sWayland.eventSource = wl_event_loop_add_fd(m_sWayland.loop, m_sTimers.timerfd.get(), WL_EVENT_READABLE, timerWrite, nullptr);
|
||||||
|
|
||||||
@ -143,6 +175,16 @@ void CEventLoopManager::doLater(const std::function<void()>& fn) {
|
|||||||
&m_sIdle);
|
&m_sIdle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CEventLoopManager::doOnReadable(CFileDescriptor fd, const std::function<void()>& fn) {
|
||||||
|
if (!fd.isValid() || fd.isReadable()) {
|
||||||
|
fn();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& waiter = m_vReadableWaiters.emplace_back(makeUnique<SReadableWaiter>(nullptr, std::move(fd), fn));
|
||||||
|
waiter->source = wl_event_loop_add_fd(g_pEventLoopManager->m_sWayland.loop, waiter->fd.get(), WL_EVENT_READABLE, ::handleWaiterFD, waiter.get());
|
||||||
|
}
|
||||||
|
|
||||||
void CEventLoopManager::syncPollFDs() {
|
void CEventLoopManager::syncPollFDs() {
|
||||||
auto aqPollFDs = g_pCompositor->m_pAqBackend->getPollFDs();
|
auto aqPollFDs = g_pCompositor->m_pAqBackend->getPollFDs();
|
||||||
|
|
||||||
|
@ -38,6 +38,17 @@ class CEventLoopManager {
|
|||||||
std::vector<std::function<void()>> fns;
|
std::vector<std::function<void()>> fns;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SReadableWaiter {
|
||||||
|
wl_event_source* source;
|
||||||
|
Hyprutils::OS::CFileDescriptor fd;
|
||||||
|
std::function<void()> fn;
|
||||||
|
};
|
||||||
|
|
||||||
|
// schedule function to when fd is readable (WL_EVENT_READABLE / POLLIN),
|
||||||
|
// takes ownership of fd
|
||||||
|
void doOnReadable(Hyprutils::OS::CFileDescriptor fd, const std::function<void()>& fn);
|
||||||
|
void onFdReadable(SReadableWaiter* waiter);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Manages the event sources after AQ pollFDs change.
|
// Manages the event sources after AQ pollFDs change.
|
||||||
void syncPollFDs();
|
void syncPollFDs();
|
||||||
@ -58,8 +69,9 @@ class CEventLoopManager {
|
|||||||
Hyprutils::OS::CFileDescriptor timerfd;
|
Hyprutils::OS::CFileDescriptor timerfd;
|
||||||
} m_sTimers;
|
} m_sTimers;
|
||||||
|
|
||||||
SIdleData m_sIdle;
|
SIdleData m_sIdle;
|
||||||
std::map<int, SEventSourceData> aqEventSources;
|
std::map<int, SEventSourceData> aqEventSources;
|
||||||
|
std::vector<UP<SReadableWaiter>> m_vReadableWaiters;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
CHyprSignalListener pollFDsChanged;
|
CHyprSignalListener pollFDsChanged;
|
||||||
@ -67,7 +79,6 @@ class CEventLoopManager {
|
|||||||
|
|
||||||
wl_event_source* m_configWatcherInotifySource = nullptr;
|
wl_event_source* m_configWatcherInotifySource = nullptr;
|
||||||
|
|
||||||
friend class CSyncTimeline;
|
|
||||||
friend class CAsyncDialogBox;
|
friend class CAsyncDialogBox;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,8 +10,10 @@
|
|||||||
#include "../../helpers/sync/SyncReleaser.hpp"
|
#include "../../helpers/sync/SyncReleaser.hpp"
|
||||||
#include "../PresentationTime.hpp"
|
#include "../PresentationTime.hpp"
|
||||||
#include "../DRMSyncobj.hpp"
|
#include "../DRMSyncobj.hpp"
|
||||||
|
#include "../types/DMABuffer.hpp"
|
||||||
#include "../../render/Renderer.hpp"
|
#include "../../render/Renderer.hpp"
|
||||||
#include "config/ConfigValue.hpp"
|
#include "config/ConfigValue.hpp"
|
||||||
|
#include "../../managers/eventLoop/EventLoopManager.hpp"
|
||||||
#include "protocols/types/SurfaceRole.hpp"
|
#include "protocols/types/SurfaceRole.hpp"
|
||||||
#include "render/Texture.hpp"
|
#include "render/Texture.hpp"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -123,16 +125,15 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!pending.updated.buffer) || // no new buffer attached
|
if ((!pending.updated.buffer) || // no new buffer attached
|
||||||
(!pending.buffer && !pending.texture) || // null buffer attached
|
(!pending.buffer && !pending.texture) // null buffer attached
|
||||||
(!pending.updated.acquire && pending.buffer->isSynchronous()) // synchronous buffers (ex. shm) can be read immediately
|
|
||||||
) {
|
) {
|
||||||
commitState(pending);
|
commitState(pending);
|
||||||
pending.reset();
|
pending.reset();
|
||||||
return;
|
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));
|
const auto& state = pendingStates.emplace(makeUnique<SSurfaceState>(pending));
|
||||||
pending.reset();
|
pending.reset();
|
||||||
|
|
||||||
@ -152,13 +153,19 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
|
|||||||
if (state->updated.acquire) {
|
if (state->updated.acquire) {
|
||||||
// wait on acquire point for this surface, from explicit sync protocol
|
// wait on acquire point for this surface, from explicit sync protocol
|
||||||
state->acquire.addWaiter(whenReadable);
|
state->acquire.addWaiter(whenReadable);
|
||||||
} else if (state->buffer->dmabuf().success) {
|
} else if (state->buffer->isSynchronous()) {
|
||||||
// https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#implicit-fence-poll-support
|
// synchronous (shm) buffers can be read immediately
|
||||||
// TODO: wait for the dma-buf fd's to become readable
|
|
||||||
whenReadable();
|
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 {
|
} else {
|
||||||
// huh??? only buffers with acquire or dmabuf should get through here...
|
Debug::log(ERR, "BUG THIS: wl_surface.commit: no acquire, non-dmabuf, async buffer, needs wait... this shouldn't happen");
|
||||||
Debug::log(ERR, "BUG THIS: wl_surface.commit: non-acquire non-dmabuf buffers needs wait...");
|
|
||||||
whenReadable();
|
whenReadable();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -4,6 +4,14 @@
|
|||||||
#include "../../render/Renderer.hpp"
|
#include "../../render/Renderer.hpp"
|
||||||
#include "../../helpers/Format.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_) {
|
CDMABuffer::CDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs const& attrs_) : attrs(attrs_) {
|
||||||
g_pHyprRenderer->makeEGLCurrent();
|
g_pHyprRenderer->makeEGLCurrent();
|
||||||
|
|
||||||
@ -84,3 +92,61 @@ void CDMABuffer::closeFDs() {
|
|||||||
}
|
}
|
||||||
attrs.planes = 0;
|
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
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Buffer.hpp"
|
#include "Buffer.hpp"
|
||||||
|
#include <hyprutils/os/FileDescriptor.hpp>
|
||||||
|
|
||||||
class CDMABuffer : public IHLBuffer {
|
class CDMABuffer : public IHLBuffer {
|
||||||
public:
|
public:
|
||||||
@ -16,6 +17,7 @@ class CDMABuffer : public IHLBuffer {
|
|||||||
virtual void endDataPtr();
|
virtual void endDataPtr();
|
||||||
bool good();
|
bool good();
|
||||||
void closeFDs();
|
void closeFDs();
|
||||||
|
Hyprutils::OS::CFileDescriptor exportSyncFile();
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user