1 // Copyright 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include "VirtioGpuTimelines.h"
15
16 #include <cinttypes>
17 #include <cstdio>
18
19 #include "gfxstream/host/Tracing.h"
20 #include "host-common/GfxstreamFatalError.h"
21
22 using TaskId = VirtioGpuTimelines::TaskId;
23 using Ring = VirtioGpuTimelines::Ring;
24 using FenceId = VirtioGpuTimelines::FenceId;
25 using emugl::ABORT_REASON_OTHER;
26 using emugl::FatalError;
27
create(FenceCompletionCallback callback)28 std::unique_ptr<VirtioGpuTimelines> VirtioGpuTimelines::create(FenceCompletionCallback callback) {
29 return std::unique_ptr<VirtioGpuTimelines>(new VirtioGpuTimelines(std::move(callback)));
30 }
31
VirtioGpuTimelines(FenceCompletionCallback callback)32 VirtioGpuTimelines::VirtioGpuTimelines(FenceCompletionCallback callback)
33 : mFenceCompletionCallback(std::move(callback)), mNextId(0) {
34 gfxstream::host::InitializeTracing();
35 }
36
enqueueTask(const Ring & ring)37 TaskId VirtioGpuTimelines::enqueueTask(const Ring& ring) {
38 std::lock_guard<std::mutex> lock(mTimelinesMutex);
39
40 TaskId id = mNextId++;
41
42 const uint64_t traceId = gfxstream::host::GetUniqueTracingId();
43 GFXSTREAM_TRACE_EVENT_INSTANT(GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY,
44 "Queue timeline task", "Task ID", id,
45 GFXSTREAM_TRACE_FLOW(traceId));
46
47 std::shared_ptr<Task> task(new Task(id, ring, traceId), [this](Task* task) {
48 mTaskIdToTask.erase(task->mId);
49 delete task;
50 });
51 mTaskIdToTask[id] = task;
52
53 Timeline& timeline = GetOrCreateTimelineLocked(ring);
54 timeline.mQueue.emplace_back(std::move(task));
55 return id;
56 }
57
enqueueFence(const Ring & ring,FenceId fenceId)58 void VirtioGpuTimelines::enqueueFence(const Ring& ring, FenceId fenceId) {
59 std::lock_guard<std::mutex> lock(mTimelinesMutex);
60
61 Timeline& timeline = GetOrCreateTimelineLocked(ring);
62 timeline.mQueue.emplace_back(fenceId);
63
64 poll_locked(ring);
65 }
66
notifyTaskCompletion(TaskId taskId)67 void VirtioGpuTimelines::notifyTaskCompletion(TaskId taskId) {
68 std::lock_guard<std::mutex> lock(mTimelinesMutex);
69 auto iTask = mTaskIdToTask.find(taskId);
70 if (iTask == mTaskIdToTask.end()) {
71 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
72 << "Task(id = " << static_cast<uint64_t>(taskId) << ") can't be found";
73 }
74 std::shared_ptr<Task> task = iTask->second.lock();
75 if (task == nullptr) {
76 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
77 << "Task(id = " << static_cast<uint64_t>(taskId) << ") has been destroyed";
78 }
79 if (task->mId != taskId) {
80 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
81 << "Task id mismatch. Expected " << static_cast<uint64_t>(taskId) << " Actual "
82 << static_cast<uint64_t>(task->mId);
83 }
84 if (task->mHasCompleted) {
85 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
86 << "Task(id = " << static_cast<uint64_t>(taskId) << ") has been set to completed.";
87 }
88
89 GFXSTREAM_TRACE_EVENT_INSTANT(GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY,
90 "Notify timeline task completed",
91 GFXSTREAM_TRACE_FLOW(task->mTraceId), "Task ID", task->mId);
92
93 task->mHasCompleted = true;
94
95 poll_locked(task->mRing);
96 }
97
GetOrCreateTimelineLocked(const Ring & ring)98 VirtioGpuTimelines::Timeline& VirtioGpuTimelines::GetOrCreateTimelineLocked(const Ring& ring) {
99 auto [it, inserted] =
100 mTimelineQueues.emplace(std::piecewise_construct, std::make_tuple(ring), std::make_tuple());
101 Timeline& timeline = it->second;
102 if (inserted) {
103 timeline.mTraceTrackId = gfxstream::host::GetUniqueTracingId();
104
105 const std::string timelineName = "Virtio Gpu Timeline " + to_string(ring);
106 GFXSTREAM_TRACE_NAME_TRACK(GFXSTREAM_TRACE_TRACK(timeline.mTraceTrackId), timelineName);
107
108 GFXSTREAM_TRACE_EVENT_INSTANT(GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY,
109 "Create Timeline",
110 GFXSTREAM_TRACE_TRACK(timeline.mTraceTrackId));
111 }
112
113 return timeline;
114 }
115
poll()116 void VirtioGpuTimelines::poll() {
117 std::lock_guard<std::mutex> lock(mTimelinesMutex);
118 for (const auto& [ring, timeline] : mTimelineQueues) {
119 poll_locked(ring);
120 }
121 }
poll_locked(const Ring & ring)122 void VirtioGpuTimelines::poll_locked(const Ring& ring) {
123 auto timelineIt = mTimelineQueues.find(ring);
124 if (timelineIt == mTimelineQueues.end()) {
125 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
126 << "Ring(" << to_string(ring) << ") doesn't exist.";
127 }
128 Timeline& timeline = timelineIt->second;
129
130 auto& timelineQueue = timeline.mQueue;
131 auto i = timelineQueue.begin();
132 for (; i != timelineQueue.end(); i++) {
133 bool shouldStop = std::visit(
134 [&](auto& arg) {
135 using T = std::decay_t<decltype(arg)>;
136 if constexpr (std::is_same_v<T, FenceId>) {
137 auto& fenceId = arg;
138
139 GFXSTREAM_TRACE_EVENT_INSTANT(
140 GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY, "Signal Virtio Gpu Fence",
141 GFXSTREAM_TRACE_TRACK(timeline.mTraceTrackId), "Fence", fenceId);
142
143 mFenceCompletionCallback(ring, fenceId);
144
145 return false;
146 } else if constexpr (std::is_same_v<T, std::shared_ptr<Task>>) {
147 auto& task = arg;
148
149 const bool completed = task->mHasCompleted;
150 if (completed) {
151 GFXSTREAM_TRACE_EVENT_INSTANT(
152 GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY, "Process Task Complete",
153 GFXSTREAM_TRACE_TRACK(timeline.mTraceTrackId),
154 GFXSTREAM_TRACE_FLOW(task->mTraceId), "Task", task->mId);
155 }
156 return !completed;
157 }
158 },
159 *i);
160
161 if (shouldStop) {
162 break;
163 }
164 }
165 timelineQueue.erase(timelineQueue.begin(), i);
166 }
167
168 #ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT
169
SnapshotRing(const VirtioGpuRing & ring)170 std::optional<gfxstream::host::snapshot::VirtioGpuRing> SnapshotRing(const VirtioGpuRing& ring) {
171 gfxstream::host::snapshot::VirtioGpuRing snapshot;
172
173 if (std::holds_alternative<VirtioGpuRingGlobal>(ring)) {
174 snapshot.mutable_global();
175 } else if (std::holds_alternative<VirtioGpuRingContextSpecific>(ring)) {
176 auto& specific = std::get<VirtioGpuRingContextSpecific>(ring);
177
178 auto* specificSnapshot = snapshot.mutable_context_specific();
179 specificSnapshot->set_context_id(specific.mCtxId);
180 specificSnapshot->set_ring_id(specific.mRingIdx);
181 } else {
182 return std::nullopt;
183 }
184
185 return snapshot;
186 }
187
RestoreRing(const gfxstream::host::snapshot::VirtioGpuRing & snapshot)188 std::optional<VirtioGpuRing> RestoreRing(const gfxstream::host::snapshot::VirtioGpuRing& snapshot) {
189 if (snapshot.has_global()) {
190 return VirtioGpuRingGlobal{};
191 } else if (snapshot.has_context_specific()) {
192 const auto& specific = snapshot.context_specific();
193 return VirtioGpuRingContextSpecific{
194 .mCtxId = specific.context_id(),
195 .mRingIdx = static_cast<VirtioGpuRingIdx>(specific.ring_id()),
196 };
197 } else {
198 return std::nullopt;
199 }
200 }
201
202 /*static*/
203 std::optional<gfxstream::host::snapshot::VirtioGpuTimelineItem>
SnapshotTimelineItem(const TimelineItem & timelineItem)204 VirtioGpuTimelines::SnapshotTimelineItem(const TimelineItem& timelineItem) {
205 gfxstream::host::snapshot::VirtioGpuTimelineItem snapshot;
206
207 if (std::holds_alternative<FenceId>(timelineItem)) {
208 auto fenceId = std::get<FenceId>(timelineItem);
209
210 auto* fenceSnapshot = snapshot.mutable_fence();
211 fenceSnapshot->set_id(fenceId);
212 } else if (std::holds_alternative<std::shared_ptr<Task>>(timelineItem)) {
213 const auto& task = std::get<std::shared_ptr<Task>>(timelineItem);
214
215 auto ringOpt = SnapshotRing(task->mRing);
216 if (!ringOpt) {
217 stream_renderer_error("Failed to snapshot timeline item: failed to snapshot ring.");
218 return std::nullopt;
219 }
220
221 auto* taskSnapshot = snapshot.mutable_task();
222 taskSnapshot->set_id(task->mId);
223 taskSnapshot->mutable_ring()->Swap(&*ringOpt);
224 taskSnapshot->set_trace_id(task->mTraceId);
225 taskSnapshot->set_completed(task->mHasCompleted);
226 } else {
227 stream_renderer_error("Failed to snapshot timeline item: unhandled.");
228 return std::nullopt;
229 }
230
231 return snapshot;
232 }
233
234 /*static*/
RestoreTimelineItem(const gfxstream::host::snapshot::VirtioGpuTimelineItem & snapshot)235 std::optional<VirtioGpuTimelines::TimelineItem> VirtioGpuTimelines::RestoreTimelineItem(
236 const gfxstream::host::snapshot::VirtioGpuTimelineItem& snapshot) {
237 if (snapshot.has_fence()) {
238 return snapshot.fence().id();
239 } else if (snapshot.has_task()) {
240 const auto& taskSnapshot = snapshot.task();
241
242 auto ringOpt = RestoreRing(taskSnapshot.ring());
243 if (!ringOpt) {
244 stream_renderer_error("Failed to restore timeline item: failed to restore ring.");
245 return std::nullopt;
246 }
247
248 auto task = std::make_shared<Task>(taskSnapshot.id(), *ringOpt, taskSnapshot.trace_id());
249 task->mHasCompleted = taskSnapshot.completed();
250 return task;
251 }
252
253 stream_renderer_error("Failed to restore timeline item: unhandled.");
254 return std::nullopt;
255 }
256
Snapshot() const257 std::optional<gfxstream::host::snapshot::VirtioGpuTimeline> VirtioGpuTimelines::Timeline::Snapshot()
258 const {
259 gfxstream::host::snapshot::VirtioGpuTimeline timeline;
260 timeline.set_trace_id(mTraceTrackId);
261 for (const auto& timelineItem : mQueue) {
262 auto timelineItemSnapshotOpt = SnapshotTimelineItem(timelineItem);
263 if (!timelineItemSnapshotOpt) {
264 stream_renderer_error("Failed to snapshot timeline item.");
265 return std::nullopt;
266 }
267 timeline.mutable_items()->Add(std::move(*timelineItemSnapshotOpt));
268 }
269 return timeline;
270 }
271
272 /*static*/
Restore(const gfxstream::host::snapshot::VirtioGpuTimeline & snapshot)273 std::optional<VirtioGpuTimelines::Timeline> VirtioGpuTimelines::Timeline::Restore(
274 const gfxstream::host::snapshot::VirtioGpuTimeline& snapshot) {
275 VirtioGpuTimelines::Timeline timeline;
276 timeline.mTraceTrackId = snapshot.trace_id();
277 for (const auto& timelineItemSnapshot : snapshot.items()) {
278 auto timelineItemOpt = RestoreTimelineItem(timelineItemSnapshot);
279 if (!timelineItemOpt) {
280 stream_renderer_error("Failed to snapshot timeline item.");
281 return std::nullopt;
282 }
283 timeline.mQueue.emplace_back(std::move(*timelineItemOpt));
284 }
285 return timeline;
286 }
287
Snapshot() const288 std::optional<gfxstream::host::snapshot::VirtioGpuTimelinesSnapshot> VirtioGpuTimelines::Snapshot()
289 const {
290 std::lock_guard<std::mutex> lock(mTimelinesMutex);
291
292 gfxstream::host::snapshot::VirtioGpuTimelinesSnapshot snapshot;
293 snapshot.set_next_id(mNextId);
294
295 for (const auto& [ring, timeline] : mTimelineQueues) {
296 auto ringSnapshotOpt = SnapshotRing(ring);
297 if (!ringSnapshotOpt) {
298 stream_renderer_error("Failed to snapshot timelines: failed to snapshot ring.");
299 return std::nullopt;
300 }
301
302 auto timelineSnapshotOpt = timeline.Snapshot();
303 if (!timelineSnapshotOpt) {
304 stream_renderer_error("Failed to snapshot timelines: failed to snapshot timeline.");
305 return std::nullopt;
306 }
307
308 auto* kv = snapshot.add_timelines();
309 kv->mutable_ring()->Swap(&*ringSnapshotOpt);
310 kv->mutable_timeline()->Swap(&*timelineSnapshotOpt);
311 }
312
313 return snapshot;
314 }
315
316 /*static*/
Restore(FenceCompletionCallback callback,const gfxstream::host::snapshot::VirtioGpuTimelinesSnapshot & snapshot)317 std::unique_ptr<VirtioGpuTimelines> VirtioGpuTimelines::Restore(
318 FenceCompletionCallback callback,
319 const gfxstream::host::snapshot::VirtioGpuTimelinesSnapshot& snapshot) {
320 std::unique_ptr<VirtioGpuTimelines> timelines(new VirtioGpuTimelines(std::move(callback)));
321
322 std::lock_guard<std::mutex> lock(timelines->mTimelinesMutex);
323
324 timelines->mNextId.store(snapshot.next_id());
325
326 for (const auto& timelineSnapshot : snapshot.timelines()) {
327 if (!timelineSnapshot.has_ring()) {
328 stream_renderer_error("Failed to restore timelines: missing ring.");
329 return nullptr;
330 }
331 auto ringOpt = RestoreRing(timelineSnapshot.ring());
332 if (!ringOpt) {
333 stream_renderer_error("Failed to restore timelines: failed to restore ring.");
334 return nullptr;
335 }
336
337 if (!timelineSnapshot.has_timeline()) {
338 stream_renderer_error("Failed to restore timelines: missing timeline.");
339 return nullptr;
340 }
341 auto timelineOpt = Timeline::Restore(timelineSnapshot.timeline());
342 if (!timelineOpt) {
343 stream_renderer_error("Failed to restore timelines: failed to restore timeline.");
344 return nullptr;
345 }
346
347 timelines->mTimelineQueues[std::move(*ringOpt)] = std::move(*timelineOpt);
348 }
349
350 // Rebuild task index:
351 for (const auto& [_, timeline] : timelines->mTimelineQueues) {
352 for (const auto& timelineItem : timeline.mQueue) {
353 if (std::holds_alternative<std::shared_ptr<Task>>(timelineItem)) {
354 auto& task = std::get<std::shared_ptr<Task>>(timelineItem);
355 timelines->mTaskIdToTask[task->mId] = task;
356 }
357 }
358 }
359
360 return timelines;
361 }
362
363 #endif // GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT