summaryrefslogtreecommitdiff
path: root/src/syncobject.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/syncobject.hpp')
-rw-r--r--src/syncobject.hpp178
1 files changed, 178 insertions, 0 deletions
diff --git a/src/syncobject.hpp b/src/syncobject.hpp
new file mode 100644
index 0000000..cf30078
--- /dev/null
+++ b/src/syncobject.hpp
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: MIT */
+
+#pragma once
+
+#include <cstdint>
+#include <optional>
+#include <vector>
+
+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 close Close the file descriptor after import (regardless of success)
+ /// @throws drm::ioctl::Exception on failure
+ ///
+ 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.
+ ///
+ /// @throws drm::ioctl::Exception on failure
+ /// @returns Exported file descriptor
+ ///
+ [[nodiscard]] int exportSyncFile() const;
+
+ ///
+ /// Emplace signaled fences into 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 signal(int fd, const std::vector<uint32_t>& objects);
+
+ ///
+ /// 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<uint32_t>& objects);
+
+ /// How to handle empty sync objects when waiting
+ enum class EmptyFlags {
+ /// Return -EINVAL if any sync object is empty
+ Throw,
+ /// Block until a fen
+ };
+
+ ///
+ /// Wait for a list of sync objects to be signaled
+ ///
+ /// 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 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
+ /// @return Handle which was signaled first, when waitAll is false.
+ ///
+ [[nodiscard]]
+ static uint32_t wait(
+ int fd,
+ const std::vector<uint32_t>& objects,
+ int64_t timeout = 0,
+ bool waitAll = false,
+ bool waitEmpty = false,
+ bool waitAvailable = false,
+ std::optional<uint64_t> deadlineHint = std::nullopt
+ );
+
+ // 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;
+ };
+}