• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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