diff options
| author | PancakeTAS <pancake@mgnet.work> | 2026-05-18 15:51:33 +0200 |
|---|---|---|
| committer | PancakeTAS <pancake@mgnet.work> | 2026-05-18 15:51:33 +0200 |
| commit | 9d2e04c95285825a196205163eebd945a362fb5e (patch) | |
| tree | 59fdd2cfad0d3d548e54b54740806d7604218b89 /include | |
| parent | Extend sync objects with timeline support (diff) | |
Split public/private API into respective locations
Diffstat (limited to '')
| -rw-r--r-- | include/drm++/helper.hpp | 35 | ||||
| -rw-r--r-- | include/drm++/ioctl.hpp | 65 | ||||
| -rw-r--r-- | include/drm++/syncobject.hpp | 248 |
3 files changed, 348 insertions, 0 deletions
diff --git a/include/drm++/helper.hpp b/include/drm++/helper.hpp new file mode 100644 index 0000000..3b6e90d --- /dev/null +++ b/include/drm++/helper.hpp @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT */ + +#pragma once + +#include <cstdint> +#include <functional> + +namespace drm { + +using u8 = uint8_t; +using u16 = uint16_t; +using u32 = uint32_t; +using u64 = uint64_t; +using s8 = int8_t; +using s16 = int16_t; +using s32 = int32_t; +using s64 = int64_t; + +template<typename T> +using ref = std::reference_wrapper<T>; + +} + +/* Helpful macros */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-macros" + +#define GETTER(name) [[nodiscard]] auto name() const { return m_##name; } +#define DEFAULT_OPERATORS(classname) \ + classname(const classname&) = default; \ + classname& operator=(const classname&) = default; \ + classname(classname&&) = default; \ + classname& operator=(classname&&) = default; + +#pragma clang diagnostic pop diff --git a/include/drm++/ioctl.hpp b/include/drm++/ioctl.hpp new file mode 100644 index 0000000..1851df6 --- /dev/null +++ b/include/drm++/ioctl.hpp @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: MIT */ + +#pragma once + +#include "drm++/helper.hpp" + +#include <exception> +#include <string> + +#include <sys/ioctl.h> + +namespace drm::ioctl { + +/// Type of ioctl() error +enum class Error : u8 { + BadFileDescriptor, + InvalidArgument, + NotSupported, + Other //!< check errno field +}; + +/// Exception class wrapping around ioctl() +class Exception : public std::exception { +public: + /// Construct an exception from errno + explicit Exception(int fd, unsigned long op); + + GETTER(fd) + GETTER(op) + GETTER(code) + GETTER(syserrno) + + /// Convert the exception into human-readable form. + std::string readable(); + + DEFAULT_OPERATORS(Exception) + ~Exception() override; +private: + int m_fd; + unsigned long m_op; + Error m_code; + int m_syserrno; +}; + +/// Perform an ioctl() call. +/// @throws drm::ioctl::Exception on failure +template<typename T> +T perform(int fd, unsigned long op) { + T data{}; + + if (::ioctl(fd, op, &data) < 0) + throw Exception(fd, op); + + return data; +} + +/// Perform an ioctl() call. +/// @throws drm::ioctl::Exception on failure +template<typename T> +void perform(int fd, unsigned long op, T& data) { + if (::ioctl(fd, op, &data) < 0) + throw Exception(fd, op); +} + +} diff --git a/include/drm++/syncobject.hpp b/include/drm++/syncobject.hpp new file mode 100644 index 0000000..9751559 --- /dev/null +++ b/include/drm++/syncobject.hpp @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: MIT */ + +#pragma once + +#include "drm++/helper.hpp" + +#include <optional> +#include <vector> + +namespace drm::syncobj { + +/// Wait modes for waiting on binary sync objects +enum class WaitMode : u8 { + NoWaitEmpty, //!< Return -EINVAL when waiting on an empty sync object + WaitEmpty, //!< Wait for empty sync objects to be filled and signaled + WaitAvailable //!< Wait for empty sync objects to be filled, but do not wait for signaling +}; + +class TimelineSyncObject; + +/// A (binary) synchronization object is a reference-counted container which can hold a DRM fence +/// @throws drm::ioctl::Exception on failure +class SyncObject { +public: + /// Create a new sync object + SyncObject(int fd, bool signaled = false); + + /// Import an existing sync object + /// @param close Close syncobj_fd after import (regardless of success) + SyncObject(int fd, int syncobj_fd, bool close = true); + + /// Export a new reference to sync object, increasing the reference counter + [[nodiscard]] int exportFd() const; + + /// Import a sync file (DRM fence) into the sync object + /// @param close Close syncfile_fd after import (regardless of success) + void importSyncFile(int syncfile_fd, 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 + [[nodiscard]] int exportSyncFile() const; + + /// Transfer a DRM fence into another sync object + void transfer(const SyncObject& dest) const; + void transfer(const TimelineSyncObject& dest, u64 destPoint) const; + + /// Signal the sync object by emplacing a signaled fence into it + void signal() const; + + /// Reset the sync object by removing the fence from it + void reset() const; + + /// Wait for the sync object to be signaled + /// @param timeout Absolute timeout in nanoseconds, or zero for polling + /// @param deadlineHint Set a CLOCK_MONOTONIC deadline hint in nanoseconds on the fence + void wait( + s64 timeout, + WaitMode waitMode = WaitMode::NoWaitEmpty, + std::optional<u64> deadlineHint = std::nullopt + ) const; + + /// Register an eventfd to the sync object + /// @param waitAvailable Trigger when a fence is emplaced, not when it is signaled + void registerEventFd(int eventfd_fd, bool waitAvailable = false) const; + + // Private access + GETTER(fd) + GETTER(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(); + this->m_handle = other.m_handle; + this->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) + u32 m_handle; + + void destruct() noexcept; +}; + +/// A timeline synchronization object can hold multiple DRM fences, identified via a +/// monotonically increasing 64-bit unsigned integer "point" +/// @throws drm::ioctl::Exception on failure +class TimelineSyncObject { +public: + /// Create a new sync object + TimelineSyncObject(int fd); + + /// Import an existing sync object + /// @param close Close syncobj_fd after import (regardless of success) + TimelineSyncObject(int fd, int syncobj_fd, bool close = true); + + /// Export a new reference to sync object, increasing the reference counter + [[nodiscard]] int exportFd() const; + + /// Import a sync file (DRM fence) into the sync object + /// @param close Close syncfile_fd after import (regardless of success) + void importSyncFile(int syncfile_fd, u64 point, 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 + [[nodiscard]] int exportSyncFile(u64 point) const; + + /// Transfer a DRM fence into another sync object + void transfer(const TimelineSyncObject& dest, u64 srcPoint, u64 destPoint) const; + void transfer(const SyncObject& dest, u64 srcPoint) const; + + /// Signal the sync object by emplacing a signaled fence into it + void signal(u64 point) const; + + /// Wait for the sync object to be signaled + /// @param timeout Absolute timeout in nanoseconds, or zero for polling + /// @param waitAvailable Only wait for a fence to become available, do not wait for signaling + /// @param deadlineHint Set a CLOCK_MONOTONIC deadline hint in nanoseconds on the fence + void wait( + s64 timeout, + u64 point, + bool waitAvailable, + std::optional<u64> deadlineHint = std::nullopt + ) const; + + /// Query the current timeline point + /// @param lastSubmitted If true, query the last submitted point instead + [[nodiscard]] u64 query(bool lastSubmitted = false) const; + + /// Register an eventfd to the sync object + /// @param waitAvailable Trigger when a fence is emplaced, not when it is signaled + void registerEventFd(int eventfd_fd, u64 point, bool waitAvailable = false) const; + + // Private access + GETTER(fd) + GETTER(handle) + + // Move constructor/operator + TimelineSyncObject(TimelineSyncObject&& other) noexcept + : m_fd(other.m_fd), m_handle(other.m_handle) { + other.m_fd = -1; // invalidate other + } + + TimelineSyncObject& operator=(TimelineSyncObject&& other) noexcept { + if (this != &other) { + this->destruct(); + this->m_handle = other.m_handle; + this->m_fd = other.m_fd; + other.m_fd = -1; // invalidate other + } + + return *this; + } + + // Copy constructor/operator + TimelineSyncObject(const TimelineSyncObject& other) + : TimelineSyncObject(other.m_fd, other.exportFd(), true) {} + + TimelineSyncObject& operator=(const TimelineSyncObject& other) { + if (this != &other) { + const int fd{other.exportFd()}; + *this = TimelineSyncObject(other.m_fd, fd, true); + } + + return *this; + } + + // Destructor + ~TimelineSyncObject(); +private: + int m_fd; // indicates object validity (>= 0) + u32 m_handle; + + void destruct() noexcept; +}; + +/// Signal several binary sync objects at once +void signal(const std::vector<ref<SyncObject>>& objs); + +/// Reset several binary sync objects at once +void reset(const std::vector<ref<SyncObject>>& objs); + +// Wait on several binary sync objects at once +/// @param timeout Absolute timeout in nanoseconds, or zero for polling +/// @param waitAll Wait until all sync objects are signaled +/// @param deadlineHint Set a CLOCK_MONOTONIC deadline hint in nanoseconds on the fence +/// @return The first signaled sync object when waitAll is false +/// @throws std::invalid_argument if objs is empty +SyncObject& wait( + const std::vector<ref<SyncObject>>& objs, + s64 timeout, + bool waitAll = false, + WaitMode waitMode = WaitMode::NoWaitEmpty, + std::optional<u64> deadlineHint = std::nullopt +); + +/// Signal several timeline sync objects at once +/// @throws std::invalid_argument if objs.size() != points.size() +void signal( + const std::vector<ref<TimelineSyncObject>>& objs, + const std::vector<u64>& points +); + +/// Wait on several timeline sync objects at once +/// @param timeout Absolute timeout in nanoseconds, or zero for polling +/// @param waitAvailable Only wait for a fence to become available, do not wait for signaling +/// @param deadlineHint Set a CLOCK_MONOTONIC deadline hint in nanoseconds on the fence +/// @throws std::invalid_argument if objs.size() != points.size() +void wait( + const std::vector<ref<TimelineSyncObject>>& objs, + const std::vector<u64>& points, + s64 timeout, + bool waitAll = false, + bool waitAvailable = false, + std::optional<u64> deadlineHint = std::nullopt +); + +/// Query several timeline sync objects at once +/// @param lastSubmitted If true, query the last submitted point instead +std::vector<u64> query( + const std::vector<ref<TimelineSyncObject>>& objs, + bool lastSubmitted = false +); + +} |
