// Copyright 2021 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "VirtioGpuTimelines.h" #include #include #include "gfxstream/host/Tracing.h" #include "host-common/GfxstreamFatalError.h" using TaskId = VirtioGpuTimelines::TaskId; using Ring = VirtioGpuTimelines::Ring; using FenceId = VirtioGpuTimelines::FenceId; using emugl::ABORT_REASON_OTHER; using emugl::FatalError; std::unique_ptr VirtioGpuTimelines::create(FenceCompletionCallback callback) { return std::unique_ptr(new VirtioGpuTimelines(std::move(callback))); } VirtioGpuTimelines::VirtioGpuTimelines(FenceCompletionCallback callback) : mNextId(0), mFenceCompletionCallback(std::move(callback)) {} TaskId VirtioGpuTimelines::enqueueTask(const Ring& ring) { std::lock_guard lock(mTimelinesMutex); TaskId id = mNextId++; const uint64_t traceId = gfxstream::host::GetUniqueTracingId(); GFXSTREAM_TRACE_EVENT_INSTANT(GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY, "Queue timeline task", "Task ID", id, GFXSTREAM_TRACE_FLOW(traceId)); std::shared_ptr task(new Task(id, ring, traceId), [this](Task* task) { mTaskIdToTask.erase(task->mId); delete task; }); mTaskIdToTask[id] = task; Timeline& timeline = GetOrCreateTimelineLocked(ring); timeline.mQueue.emplace_back(std::move(task)); return id; } void VirtioGpuTimelines::enqueueFence(const Ring& ring, FenceId fenceId) { std::lock_guard lock(mTimelinesMutex); Timeline& timeline = GetOrCreateTimelineLocked(ring); timeline.mQueue.emplace_back(fenceId); poll_locked(ring); } void VirtioGpuTimelines::notifyTaskCompletion(TaskId taskId) { std::lock_guard lock(mTimelinesMutex); auto iTask = mTaskIdToTask.find(taskId); if (iTask == mTaskIdToTask.end()) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Task(id = " << static_cast(taskId) << ") can't be found"; } std::shared_ptr task = iTask->second.lock(); if (task == nullptr) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Task(id = " << static_cast(taskId) << ") has been destroyed"; } if (task->mId != taskId) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Task id mismatch. Expected " << static_cast(taskId) << " Actual " << static_cast(task->mId); } if (task->mHasCompleted) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Task(id = " << static_cast(taskId) << ") has been set to completed."; } GFXSTREAM_TRACE_EVENT_INSTANT(GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY, "Notify timeline task completed", GFXSTREAM_TRACE_FLOW(task->mTraceId), "Task ID", task->mId); task->mHasCompleted = true; poll_locked(task->mRing); } VirtioGpuTimelines::Timeline& VirtioGpuTimelines::GetOrCreateTimelineLocked(const Ring& ring) { auto [it, inserted] = mTimelineQueues.emplace(std::piecewise_construct, std::make_tuple(ring), std::make_tuple()); Timeline& timeline = it->second; if (inserted) { timeline.mTraceTrackId = gfxstream::host::GetUniqueTracingId(); const std::string timelineName = "Virtio Gpu Timeline " + to_string(ring); GFXSTREAM_TRACE_NAME_TRACK(GFXSTREAM_TRACE_TRACK(timeline.mTraceTrackId), timelineName); GFXSTREAM_TRACE_EVENT_INSTANT(GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY, "Create Timeline", GFXSTREAM_TRACE_TRACK(timeline.mTraceTrackId)); } return timeline; } void VirtioGpuTimelines::poll() { std::lock_guard lock(mTimelinesMutex); for (const auto& [ring, timeline] : mTimelineQueues) { poll_locked(ring); } } void VirtioGpuTimelines::poll_locked(const Ring& ring) { auto timelineIt = mTimelineQueues.find(ring); if (timelineIt == mTimelineQueues.end()) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Ring(" << to_string(ring) << ") doesn't exist."; } Timeline& timeline = timelineIt->second; auto& timelineQueue = timeline.mQueue; auto i = timelineQueue.begin(); for (; i != timelineQueue.end(); i++) { bool shouldStop = std::visit( [&](auto& arg) { using T = std::decay_t; if constexpr (std::is_same_v) { auto& fenceId = arg; GFXSTREAM_TRACE_EVENT_INSTANT( GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY, "Signal Virtio Gpu Fence", GFXSTREAM_TRACE_TRACK(timeline.mTraceTrackId), "Fence", fenceId); mFenceCompletionCallback(ring, fenceId); return false; } else if constexpr (std::is_same_v>) { auto& task = arg; const bool completed = task->mHasCompleted; if (completed) { GFXSTREAM_TRACE_EVENT_INSTANT( GFXSTREAM_TRACE_VIRTIO_GPU_TIMELINE_CATEGORY, "Process Task Complete", GFXSTREAM_TRACE_TRACK(timeline.mTraceTrackId), GFXSTREAM_TRACE_FLOW(task->mTraceId), "Task", task->mId); } return !completed; } }, *i); if (shouldStop) { break; } } timelineQueue.erase(timelineQueue.begin(), i); }