// 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 "host-common/GfxstreamFatalError.h" using TaskId = VirtioGpuTimelines::TaskId; using Ring = VirtioGpuTimelines::Ring; using FenceId = VirtioGpuTimelines::FenceId; using AutoLock = android::base::AutoLock; using emugl::ABORT_REASON_OTHER; using emugl::FatalError; std::unique_ptr VirtioGpuTimelines::create(bool withAsyncCallback) { return std::unique_ptr(new VirtioGpuTimelines(withAsyncCallback)); } VirtioGpuTimelines::VirtioGpuTimelines(bool withAsyncCallback) : mNextId(0), mWithAsyncCallback(withAsyncCallback) {} TaskId VirtioGpuTimelines::enqueueTask(const Ring& ring) { AutoLock lock(mLock); TaskId id = mNextId++; std::shared_ptr task(new Task(id, ring), [this](Task* task) { mTaskIdToTask.erase(task->mId); delete task; }); mTaskIdToTask[id] = task; mTimelineQueues[ring].emplace_back(std::move(task)); return id; } void VirtioGpuTimelines::enqueueFence(const Ring& ring, FenceId fenceId, FenceCompletionCallback fenceCompletionCallback) { AutoLock lock(mLock); auto fence = std::make_unique(fenceId, std::move(fenceCompletionCallback)); mTimelineQueues[ring].emplace_back(std::move(fence)); if (mWithAsyncCallback) { poll_locked(ring); } } void VirtioGpuTimelines::notifyTaskCompletion(TaskId taskId) { AutoLock lock(mLock); 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."; } task->mHasCompleted = true; if (mWithAsyncCallback) { poll_locked(task->mRing); } } void VirtioGpuTimelines::poll() { if (mWithAsyncCallback) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Can't call poll with async callback enabled."; } AutoLock lock(mLock); for (const auto& [ring, timeline] : mTimelineQueues) { poll_locked(ring); } } void VirtioGpuTimelines::poll_locked(const Ring& ring) { auto iTimelineQueue = mTimelineQueues.find(ring); if (iTimelineQueue == mTimelineQueues.end()) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Ring(" << to_string(ring) << ") doesn't exist."; } std::list &timelineQueue = iTimelineQueue->second; auto i = timelineQueue.begin(); for (; i != timelineQueue.end(); i++) { // This visitor will signal the fence and return whether the timeline // item is an incompleted task. struct { bool operator()(std::unique_ptr &fence) { fence->mCompletionCallback(); return false; } bool operator()(std::shared_ptr &task) { return !task->mHasCompleted; } } visitor; if (std::visit(visitor, *i)) { break; } } timelineQueue.erase(timelineQueue.begin(), i); }