/* SPDX-License-Identifier: MIT */ #include "drm++/syncobject.hpp" #include "drm++/helper.hpp" #include "drm++/ioctl.hpp" #include #include #include #include #include #include #include using namespace drm; using namespace drm::syncobj; /* Binary Sync Objects */ SyncObject::SyncObject(int fd, bool signaled) : m_fd(fd) { drm_syncobj_create args{ .flags = signaled ? DRM_SYNCOBJ_CREATE_SIGNALED : 0U }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_CREATE, args); this->m_handle = args.handle; } SyncObject::SyncObject(int fd, int syncobj_fd, bool close) : m_fd(fd) { try { drm_syncobj_handle args{ .fd = syncobj_fd }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, args); this->m_handle = args.handle; } catch (...) { if (close) { ::close(syncobj_fd); } throw; } if (close) { ::close(syncobj_fd); } } int SyncObject::exportFd() const { drm_syncobj_handle args{ .handle = this->m_handle, }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, args); return args.fd; } void SyncObject::importSyncFile(int syncfile_fd, bool close) const { try { drm_syncobj_handle args{ .handle = this->m_handle, .flags = DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE, .fd = syncfile_fd }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, args); } catch (...) { if (close) { ::close(syncfile_fd); } throw; } if (close) { ::close(syncfile_fd); } } int SyncObject::exportSyncFile() const { drm_syncobj_handle args{ .handle = this->m_handle, .flags = DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, args); return args.fd; } void SyncObject::transfer(const SyncObject& dest) const { drm_syncobj_transfer args{ .src_handle = this->m_handle, .dst_handle = dest.m_handle }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_TRANSFER, args); } void SyncObject::transfer(const TimelineSyncObject& dest, u64 destPoint) const { drm_syncobj_transfer args{ .src_handle = this->m_handle, .dst_handle = dest.handle(), .dst_point = destPoint }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_TRANSFER, args); } void SyncObject::signal() const { drm_syncobj_array args{ .handles = reinterpret_cast(&this->m_handle), .count_handles = 1 }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_SIGNAL, args); } void SyncObject::reset() const { drm_syncobj_array args{ .handles = reinterpret_cast(&this->m_handle), .count_handles = 1 }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_RESET, args); } void SyncObject::wait( s64 timeout, WaitMode waitMode, std::optional deadlineHint ) const { drm_syncobj_wait args{ .handles = reinterpret_cast(&this->m_handle), .timeout_nsec = timeout, .count_handles = 1, .flags = (waitMode == WaitMode::WaitEmpty ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT : 0U) | (waitMode == WaitMode::WaitAvailable ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE : 0U) | (deadlineHint.has_value() ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_DEADLINE : 0U), .deadline_nsec = deadlineHint.value_or(0) }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_WAIT, args); } void SyncObject::registerEventFd(int eventfd_fd, bool waitAvailable) const { drm_syncobj_eventfd args{ .handle = this->m_handle, .flags = waitAvailable ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE : 0U, .fd = eventfd_fd }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_EVENTFD, args); } void SyncObject::destruct() noexcept { if (this->m_fd < 0) { return; } drm_syncobj_destroy args{ .handle = this->m_handle }; try { ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_DESTROY, args); } catch(...) { (void) 0; // not much we can do about the leak } this->m_fd = -1; } /* Timeline Sync Objects */ TimelineSyncObject::TimelineSyncObject(int fd) : m_fd(fd) { drm_syncobj_create args{ .flags = 0U }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_CREATE, args); this->m_handle = args.handle; } TimelineSyncObject::TimelineSyncObject(int fd, int syncobj_fd, bool close) : m_fd(fd) { try { drm_syncobj_handle args{ .fd = syncobj_fd }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, args); this->m_handle = args.handle; } catch (...) { if (close) { ::close(syncobj_fd); } throw; } if (close) { ::close(syncobj_fd); } } int TimelineSyncObject::exportFd() const { drm_syncobj_handle args{ .handle = this->m_handle, }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, args); return args.fd; } void TimelineSyncObject::importSyncFile(int syncfile_fd, u64 point, bool close) const { try { drm_syncobj_handle args{ .handle = this->m_handle, .flags = DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE | DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE, .fd = syncfile_fd, .point = point }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, args); } catch (...) { if (close) { ::close(syncfile_fd); } throw; } if (close) { ::close(syncfile_fd); } } int TimelineSyncObject::exportSyncFile(u64 point) const { drm_syncobj_handle args{ .handle = this->m_handle, .flags = DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE | DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE, .point = point }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, args); return args.fd; } void TimelineSyncObject::transfer( const TimelineSyncObject& dest, u64 srcPoint, u64 destPoint ) const { drm_syncobj_transfer args{ .src_handle = this->m_handle, .dst_handle = dest.m_handle, .src_point = srcPoint, .dst_point = destPoint }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_TRANSFER, args); } void TimelineSyncObject::transfer(const SyncObject& dest, u64 srcPoint) const { drm_syncobj_transfer args{ .src_handle = this->m_handle, .dst_handle = dest.handle(), .src_point = srcPoint }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_TRANSFER, args); } void TimelineSyncObject::signal(u64 point) const { drm_syncobj_timeline_array args{ .handles = reinterpret_cast(&this->m_handle), .points = reinterpret_cast(&point), .count_handles = 1 }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, args); } void TimelineSyncObject::wait( s64 timeout, u64 point, bool waitAvailable, std::optional deadlineHint ) const { drm_syncobj_timeline_wait args{ .handles = reinterpret_cast(&this->m_handle), .points = reinterpret_cast(&point), .timeout_nsec = timeout, .count_handles = 1, .flags = (waitAvailable ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE : 0U) | (deadlineHint.has_value() ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_DEADLINE : 0U), .deadline_nsec = deadlineHint.value_or(0) }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_WAIT, args); } u64 TimelineSyncObject::query(bool lastSubmitted) const { u64 point{}; drm_syncobj_timeline_array args{ .handles = reinterpret_cast(&this->m_handle), .points = reinterpret_cast(&point), .count_handles = 1, .flags = lastSubmitted ? DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED : 0U }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_QUERY, args); return point; } void TimelineSyncObject::registerEventFd( int eventfd_fd, u64 point, bool waitAvailable ) const { drm_syncobj_eventfd args{ .handle = this->m_handle, .flags = waitAvailable ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE : 0U, .point = point, .fd = eventfd_fd }; ioctl::perform(this->m_fd, DRM_IOCTL_SYNCOBJ_EVENTFD, args); } /* Bulk Operations */ void syncobj::signal(const std::vector>& objs) { if (objs.empty()) { return; } std::vector handles(objs.size()); for (size_t i = 0; i < objs.size(); ++i) { handles.at(i) = objs.at(i).get().handle(); } drm_syncobj_array args{ .handles = reinterpret_cast(handles.data()), .count_handles = static_cast(handles.size()) }; ioctl::perform(objs.front().get().fd(), DRM_IOCTL_SYNCOBJ_SIGNAL, args); } void syncobj::reset(const std::vector>& objs) { if (objs.empty()) { return; } std::vector handles(objs.size()); for (size_t i = 0; i < objs.size(); ++i) { handles.at(i) = objs.at(i).get().handle(); } drm_syncobj_array args{ .handles = reinterpret_cast(handles.data()), .count_handles = static_cast(handles.size()) }; ioctl::perform(objs.front().get().fd(), DRM_IOCTL_SYNCOBJ_RESET, args); } SyncObject& syncobj::wait( const std::vector>& objs, s64 timeout, bool waitAll, WaitMode waitMode, std::optional deadlineHint ) { if (objs.empty()) { throw std::invalid_argument("must wait on at least one object"); } std::vector handles(objs.size()); for (size_t i = 0; i < objs.size(); ++i) { handles.at(i) = objs.at(i).get().handle(); } drm_syncobj_wait args{ .handles = reinterpret_cast(handles.data()), .timeout_nsec = timeout, .count_handles = static_cast(handles.size()), .flags = (waitAll ? 0U : DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL) | (waitMode == WaitMode::WaitEmpty ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT : 0U) | (waitMode == WaitMode::WaitAvailable ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE : 0U) | (deadlineHint.has_value() ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_DEADLINE : 0U), .deadline_nsec = deadlineHint.value_or(0) }; ioctl::perform(objs.front().get().fd(), DRM_IOCTL_SYNCOBJ_WAIT, args); return objs.at(args.first_signaled).get(); } void syncobj::signal( const std::vector>& objs, const std::vector& points ) { if (objs.empty()) { return; } std::vector handles(objs.size()); for (size_t i = 0; i < objs.size(); ++i) { handles.at(i) = objs.at(i).get().handle(); } drm_syncobj_timeline_array args{ .handles = reinterpret_cast(handles.data()), .points = reinterpret_cast(points.data()), .count_handles = static_cast(handles.size()) }; ioctl::perform(objs.front().get().fd(), DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, args); } void syncobj::wait( const std::vector>& objs, const std::vector& points, s64 timeout, bool waitAll, bool waitAvailable, std::optional deadlineHint ) { if (objs.empty()) { return; } std::vector handles(objs.size()); for (size_t i = 0; i < objs.size(); ++i) { handles.at(i) = objs.at(i).get().handle(); } drm_syncobj_timeline_wait args{ .handles = reinterpret_cast(handles.data()), .points = reinterpret_cast(points.data()), .timeout_nsec = timeout, .count_handles = static_cast(handles.size()), .flags = (waitAll ? 0U : DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL) | (waitAvailable ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE : 0U) | (deadlineHint.has_value() ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_DEADLINE : 0U), .deadline_nsec = deadlineHint.value_or(0) }; ioctl::perform(objs.front().get().fd(), DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, args); } std::vector syncobj::query( const std::vector>& objs, bool lastSubmitted ) { if (objs.empty()) { return {}; } std::vector handles(objs.size()); for (size_t i = 0; i < objs.size(); ++i) { handles.at(i) = objs.at(i).get().handle(); } std::vector points(objs.size()); drm_syncobj_timeline_array args{ .handles = reinterpret_cast(handles.data()), .points = reinterpret_cast(points.data()), .count_handles = static_cast(handles.size()), .flags = lastSubmitted ? DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED : 0U }; ioctl::perform(objs.front().get().fd(), DRM_IOCTL_SYNCOBJ_QUERY, args); return points; }