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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
/* 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 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<uint32_t>& objects,
const std::vector<uint64_t>& 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<uint32_t>& 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<uint32_t>& objects,
const std::vector<uint64_t>& points = {},
int64_t timeout = 0,
bool waitAll = false,
bool waitEmpty = false,
bool waitAvailable = false,
std::optional<uint64_t> 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<uint64_t> query(
int fd,
const std::vector<uint32_t>& 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;
};
}
|