/* SPDX-License-Identifier: MIT */ #pragma once #include #include #include namespace drm { /// /// A DRM synchronization object is a container which can hold one DRM fence. /// /// Their primary use-case is to implement Vulkan fences & semaphores, however they can /// be used across ordinary processes as well. /// /// A sync object is reference counted, therefore destroying a sync object does not /// invalidate all exported opaque file descriptors. /// /// Ordinary use cases for sync objects include: /// - Creating two sync objects in separate processes, exporting a sync object fd into /// the other process. Then signaling/polling the shared sync object. /// - Exporting a Vulkan fence/semaphore as a sync file and importing it into a sync object. /// It is then possible to wait for a signal from Vulkan inside of the DRM subsystem. /// /// Timeline synchronization objects extend the single DRM fence to a list of fences, where /// fences are identified via 64-bit unsigned integer. /// /// https://www.kernel.org/doc/html/v6.19/gpu/drm-mm.html#drm-sync-objects /// class SyncObject { public: /// /// Create a new DRM sync object /// /// By default, the sync object will not have a DRM fence emplaced. /// /// @param fd DRM node file descriptor /// @param signal Emplace a signaled fence on creation /// @throws drm::ioctl::Exception on failure /// SyncObject(int fd, bool signal = false); /// /// Import a DRM sync object from a file descriptor /// /// @param fd DRM node file descriptor /// @param syncobj_fd Sync object file descriptor /// @param close Close the file descriptor after import (regardless of success) /// @throws drm::ioctl::Exception on failure /// SyncObject(int fd, int syncobj_fd, bool close = true); /// /// Export a reference to the sync object /// /// @throws drm::ioctl::Exception on failure /// @returns Exported file descriptor. /// [[nodiscard]] int exportFd() const; /// /// Import a sync file (DRM fence) into the sync object /// /// @param syncfile_fd File descriptor to import /// @param point Timeline point to export for timeline sync objects /// @param close Close the file descriptor after import (regardless of success) /// @throws drm::ioctl::Exception on failure /// void importSyncFile(int syncfile_fd, uint64_t point = 0, bool close = true) const; /// /// Export a sync file from the DRM fence within the sync object /// /// Any subsequent modifications to the sync object are not applied to /// the exported sync file. /// /// @param point Timeline point to export for timeline sync objects /// @throws drm::ioctl::Exception on failure /// @returns Exported file descriptor /// [[nodiscard]] int exportSyncFile(uint64_t point = 0) const; /// /// Register an eventfd to be signaled by a sync object /// /// @param eventfd_fd File descriptor to signal /// @param point Timeline point to signal for timeline sync objects /// @param waitAvailable Only wait for a fence to be available, as opposed to signaled. /// @throws drm::ioctl::Exception on failure /// void registerEventFd(int eventfd_fd, uint64_t point = 0, bool waitAvailable = false) const; /// /// Copy a DRM fence into another sync object /// /// @param dstObject Destination sync object handle /// @param srcPoint Timeline point to copy from /// @param dstPoint Timeline point to copy into /// @throws drm::ioctl::Exception on failure /// void transfer(uint32_t dstObject, uint64_t srcPoint = 0, uint64_t dstPoint = 0) const; /// /// Emplace signaled fences into a list of sync objects /// /// When not using timeline sync objects, pass an empty list of timeline points. /// /// @param fd DRM node file descriptor /// @param objects List of sync object handles /// @param points List of timeline points to signal for each sync object /// @throws drm::ioctl::Exception on failure /// @throws std::invalid_argument invalid points size /// static void signal( int fd, const std::vector& objects, const std::vector& points = {} ); /// /// Remove the DRM fence from a list of sync objects /// /// @param fd DRM node file descriptor /// @param objects List of sync object handles /// @throws drm::ioctl::Exception on failure /// static void reset(int fd, const std::vector& objects); /// /// Wait for a list of sync objects to be signaled /// /// When not using timeline sync objects, pass an empty list of timeline points. /// /// If waitEmpty or waitAvailable is not set, any empty sync object will result in /// an error. /// /// If waitAvailable is set, the function will return as soon as a fence is emplaced into /// all sync objects, as opposed to waiting for the fence to be signaled. This option /// should not be used together with waitEmpty. /// /// @param fd DRM node file descriptor /// @param objects List of sync object handles /// @param points List of timeline points to wait on for each sync object /// @param timeout Absolute timeout in nanoseconds, or zero for polling /// @param waitAll Wait for all sync objects, as opposed to a single one /// @param waitEmpty Wait for sync objects, which do not yet have a fence emplaced /// @param waitAvailable Only wait for a fence to be available, as opposed to signaled. /// @param deadlineHint Set a CLOCK_MONOTONIC deadline hint in nanoseconds on all fences /// @throws drm::ioctl::Exception on failure /// @throws std::invalid_argument invalid points size /// @return Handle which was signaled first, when waitAll is false. /// [[nodiscard]] static uint32_t wait( int fd, const std::vector& objects, const std::vector& points = {}, int64_t timeout = 0, bool waitAll = false, bool waitEmpty = false, bool waitAvailable = false, std::optional deadlineHint = std::nullopt ); /// /// Query the timeline points of a list of sync objects /// /// This should only be used with timeline sync objects. /// /// @param fd DRM node file descriptor /// @param objects List of sync object handles /// @param lastSubmitted Query the last submitted instead of signaled point. /// @throws drm::ioctl::Exception on failure /// @returns List of timeline points /// [[nodiscard]] static std::vector query( int fd, const std::vector& objects, bool lastSubmitted = false ); // Into handle operator uint32_t() const { return this->m_handle; } // Move constructor/operator SyncObject(SyncObject&& other) noexcept : m_fd(other.m_fd), m_handle(other.m_handle) { other.m_fd = -1; // invalidate other } SyncObject& operator=(SyncObject&& other) noexcept { if (this != &other) { this->destruct(); m_handle = other.m_handle; m_fd = other.m_fd; other.m_fd = -1; // invalidate other } return *this; } // Copy constructor/operator SyncObject(const SyncObject& other) : SyncObject(other.m_fd, other.exportFd(), true) {} SyncObject& operator=(const SyncObject& other) { if (this != &other) { const int fd = other.exportFd(); *this = SyncObject(other.m_fd, fd, true); } return *this; } // Destructor ~SyncObject(); private: int m_fd; // indicates object validity (>= 0) uint32_t m_handle; void destruct() noexcept; }; }