1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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;
};
}
|