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     : mNextId(0), mFenceCompletionCallback(std::move(callback)) {}
34 
enqueueTask(const Ring & ring)35 TaskId VirtioGpuTimelines::enqueueTask(const Ring& ring) {
36     std::lock_guard<std::mutex> lock(mTimelinesMutex);
37 
38     TaskId id = mNextId++;
39 
40     const uint64_t traceId = gfxstream::host::GetUniqueTracingId();
41     GFXSTREAM_TRACE_EVENT_INSTANT(GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY,
42                                   "Queue timeline task", "Task ID", id,
43                                   GFXSTREAM_TRACE_FLOW(traceId));
44 
45     std::shared_ptr<Task> task(new Task(id, ring, traceId), [this](Task* task) {
46         mTaskIdToTask.erase(task->mId);
47         delete task;
48     });
49     mTaskIdToTask[id] = task;
50 
51     Timeline& timeline = GetOrCreateTimelineLocked(ring);
52     timeline.mQueue.emplace_back(std::move(task));
53     return id;
54 }
55 
enqueueFence(const Ring & ring,FenceId fenceId)56 void VirtioGpuTimelines::enqueueFence(const Ring& ring, FenceId fenceId) {
57     std::lock_guard<std::mutex> lock(mTimelinesMutex);
58 
59     Timeline& timeline = GetOrCreateTimelineLocked(ring);
60     timeline.mQueue.emplace_back(fenceId);
61 
62     poll_locked(ring);
63 }
64 
notifyTaskCompletion(TaskId taskId)65 void VirtioGpuTimelines::notifyTaskCompletion(TaskId taskId) {
66     std::lock_guard<std::mutex> lock(mTimelinesMutex);
67     auto iTask = mTaskIdToTask.find(taskId);
68     if (iTask == mTaskIdToTask.end()) {
69         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
70             << "Task(id = " << static_cast<uint64_t>(taskId) << ") can't be found";
71     }
72     std::shared_ptr<Task> task = iTask->second.lock();
73     if (task == nullptr) {
74         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
75             << "Task(id = " << static_cast<uint64_t>(taskId) << ") has been destroyed";
76     }
77     if (task->mId != taskId) {
78         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
79             << "Task id mismatch. Expected " << static_cast<uint64_t>(taskId) << " Actual "
80             << static_cast<uint64_t>(task->mId);
81     }
82     if (task->mHasCompleted) {
83         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
84             << "Task(id = " << static_cast<uint64_t>(taskId) << ") has been set to completed.";
85     }
86 
87     GFXSTREAM_TRACE_EVENT_INSTANT(GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY,
88                                   "Notify timeline task completed",
89                                   GFXSTREAM_TRACE_FLOW(task->mTraceId), "Task ID", task->mId);
90 
91     task->mHasCompleted = true;
92 
93     poll_locked(task->mRing);
94 }
95 
GetOrCreateTimelineLocked(const Ring & ring)96 VirtioGpuTimelines::Timeline& VirtioGpuTimelines::GetOrCreateTimelineLocked(const Ring& ring) {
97     auto [it, inserted] =
98         mTimelineQueues.emplace(std::piecewise_construct, std::make_tuple(ring), std::make_tuple());
99     Timeline& timeline = it->second;
100     if (inserted) {
101         timeline.mTraceTrackId = gfxstream::host::GetUniqueTracingId();
102 
103         const std::string timelineName = "Virtio Gpu Timeline " + to_string(ring);
104         GFXSTREAM_TRACE_NAME_TRACK(GFXSTREAM_TRACE_TRACK(timeline.mTraceTrackId), timelineName);
105 
106         GFXSTREAM_TRACE_EVENT_INSTANT(GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY,
107                                       "Create Timeline",
108                                       GFXSTREAM_TRACE_TRACK(timeline.mTraceTrackId));
109     }
110 
111     return timeline;
112 }
113 
poll()114 void VirtioGpuTimelines::poll() {
115     std::lock_guard<std::mutex> lock(mTimelinesMutex);
116     for (const auto& [ring, timeline] : mTimelineQueues) {
117         poll_locked(ring);
118     }
119 }
poll_locked(const Ring & ring)120 void VirtioGpuTimelines::poll_locked(const Ring& ring) {
121     auto timelineIt = mTimelineQueues.find(ring);
122     if (timelineIt == mTimelineQueues.end()) {
123         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
124             << "Ring(" << to_string(ring) << ") doesn't exist.";
125     }
126     Timeline& timeline = timelineIt->second;
127 
128     auto& timelineQueue = timeline.mQueue;
129     auto i = timelineQueue.begin();
130     for (; i != timelineQueue.end(); i++) {
131         bool shouldStop = std::visit(
132             [&](auto& arg) {
133                 using T = std::decay_t<decltype(arg)>;
134                 if constexpr (std::is_same_v<T, FenceId>) {
135                     auto& fenceId = arg;
136 
137                     GFXSTREAM_TRACE_EVENT_INSTANT(
138                         GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY, "Signal Virtio Gpu Fence",
139                         GFXSTREAM_TRACE_TRACK(timeline.mTraceTrackId), "Fence", fenceId);
140 
141                     mFenceCompletionCallback(ring, fenceId);
142 
143                     return false;
144                 } else if constexpr (std::is_same_v<T, std::shared_ptr<Task>>) {
145                     auto& task = arg;
146 
147                     const bool completed = task->mHasCompleted;
148                     if (completed) {
149                         GFXSTREAM_TRACE_EVENT_INSTANT(
150                             GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY, "Process Task Complete",
151                             GFXSTREAM_TRACE_TRACK(timeline.mTraceTrackId),
152                             GFXSTREAM_TRACE_FLOW(task->mTraceId), "Task", task->mId);
153                     }
154                     return !completed;
155                 }
156             },
157             *i);
158 
159         if (shouldStop) {
160             break;
161         }
162     }
163     timelineQueue.erase(timelineQueue.begin(), i);
164 }
165