summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/drm++/helper.hpp35
-rw-r--r--include/drm++/ioctl.hpp65
-rw-r--r--include/drm++/syncobject.hpp248
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
+);
+
+}