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 #ifndef VIRTIO_GPU_TIMELINES_H 15 #define VIRTIO_GPU_TIMELINES_H 16 17 #include <atomic> 18 #include <functional> 19 #include <list> 20 #include <memory> 21 #include <mutex> 22 #include <sstream> 23 #include <string> 24 #include <unordered_map> 25 #include <variant> 26 27 #ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT 28 #include "VirtioGpuTimelinesSnapshot.pb.h" 29 #endif // GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT 30 #include "aemu/base/ThreadAnnotations.h" 31 #include "gfxstream/virtio-gpu-gfxstream-renderer.h" 32 33 typedef uint32_t VirtioGpuCtxId; 34 typedef uint8_t VirtioGpuRingIdx; 35 36 struct VirtioGpuRingGlobal {}; 37 struct VirtioGpuRingContextSpecific { 38 VirtioGpuCtxId mCtxId; 39 VirtioGpuRingIdx mRingIdx; 40 }; 41 using VirtioGpuRing = std::variant<VirtioGpuRingGlobal, VirtioGpuRingContextSpecific>; 42 43 template <> 44 struct std::hash<VirtioGpuRingGlobal> { 45 std::size_t operator()(VirtioGpuRingGlobal const&) const noexcept { return 0; } 46 }; 47 48 inline bool operator==(const VirtioGpuRingGlobal&, const VirtioGpuRingGlobal&) { return true; } 49 50 template <> 51 struct std::hash<VirtioGpuRingContextSpecific> { 52 std::size_t operator()(VirtioGpuRingContextSpecific const& ringContextSpecific) const noexcept { 53 std::size_t ctxHash = std::hash<VirtioGpuCtxId>{}(ringContextSpecific.mCtxId); 54 std::size_t ringHash = std::hash<VirtioGpuRingIdx>{}(ringContextSpecific.mRingIdx); 55 // Use the hash_combine from 56 // https://www.boost.org/doc/libs/1_78_0/boost/container_hash/hash.hpp. 57 std::size_t res = ctxHash; 58 res ^= ringHash + 0x9e3779b9 + (res << 6) + (res >> 2); 59 return res; 60 } 61 }; 62 63 inline bool operator==(const VirtioGpuRingContextSpecific& lhs, 64 const VirtioGpuRingContextSpecific& rhs) { 65 return lhs.mCtxId == rhs.mCtxId && lhs.mRingIdx == rhs.mRingIdx; 66 } 67 68 inline std::string to_string(const VirtioGpuRing& ring) { 69 struct { 70 std::string operator()(const VirtioGpuRingGlobal&) { return "global"; } 71 std::string operator()(const VirtioGpuRingContextSpecific& ring) { 72 std::stringstream ss; 73 ss << "context specific {ctx = " << ring.mCtxId << ", ring = " << (int)ring.mRingIdx 74 << "}"; 75 return ss.str(); 76 } 77 } visitor; 78 return std::visit(visitor, ring); 79 } 80 81 class VirtioGpuTimelines { 82 public: 83 using FenceId = uint64_t; 84 using Ring = VirtioGpuRing; 85 using TaskId = uint64_t; 86 using FenceCompletionCallback = std::function<void(const Ring&, FenceId)>; 87 88 static std::unique_ptr<VirtioGpuTimelines> create(FenceCompletionCallback callback); 89 90 TaskId enqueueTask(const Ring&); 91 void enqueueFence(const Ring&, FenceId); 92 void notifyTaskCompletion(TaskId); 93 void poll(); 94 95 #ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT 96 std::optional<gfxstream::host::snapshot::VirtioGpuTimelinesSnapshot> Snapshot() const; 97 98 static std::unique_ptr<VirtioGpuTimelines> Restore( 99 FenceCompletionCallback callback, 100 const gfxstream::host::snapshot::VirtioGpuTimelinesSnapshot& snapshot); 101 #endif 102 103 private: 104 VirtioGpuTimelines(FenceCompletionCallback callback); 105 106 struct Task { 107 Task(TaskId id, const Ring& ring, uint64_t traceId) 108 : mId(id), mRing(ring), mTraceId(traceId), mHasCompleted(false) {} 109 110 // LINT.IfChange(virtio_gpu_timeline_task) 111 TaskId mId; 112 Ring mRing; 113 uint64_t mTraceId; 114 std::atomic_bool mHasCompleted; 115 // LINT.ThenChange(VirtioGpuTimelinesSnapshot.proto:virtio_gpu_timeline_task) 116 }; 117 118 // LINT.IfChange(virtio_gpu_timeline_item) 119 using TimelineItem = std::variant<FenceId, std::shared_ptr<Task>>; 120 // LINT.ThenChange(VirtioGpuTimelinesSnapshot.proto:virtio_gpu_timeline_item) 121 122 #ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT 123 static std::optional<gfxstream::host::snapshot::VirtioGpuTimelineItem> SnapshotTimelineItem( 124 const TimelineItem& timelineItem); 125 126 static std::optional<TimelineItem> RestoreTimelineItem( 127 const gfxstream::host::snapshot::VirtioGpuTimelineItem& snapshot); 128 #endif 129 130 struct Timeline { 131 // LINT.IfChange(virtio_gpu_timeline) 132 uint64_t mTraceTrackId; 133 std::list<TimelineItem> mQueue; 134 // LINT.ThenChange(VirtioGpuTimelinesSnapshot.proto:virtio_gpu_timeline) 135 136 #ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT 137 std::optional<gfxstream::host::snapshot::VirtioGpuTimeline> Snapshot() const; 138 139 static std::optional<Timeline> Restore( 140 const gfxstream::host::snapshot::VirtioGpuTimeline& snapshot); 141 #endif 142 }; 143 144 Timeline& GetOrCreateTimelineLocked(const Ring& ring) REQUIRES(mTimelinesMutex); 145 146 // Go over the timeline, signal any fences without pending tasks, and remove 147 // timeline items that are no longer needed. 148 void poll_locked(const Ring&) REQUIRES(mTimelinesMutex); 149 150 FenceCompletionCallback mFenceCompletionCallback; 151 152 mutable std::mutex mTimelinesMutex; 153 // The mTaskIdToTask cache must be destroyed after the actual owner of Task, 154 // mTimelineQueues, is destroyed, because the deleter of Task will 155 // automatically remove the entry in mTaskIdToTask. 156 std::unordered_map<TaskId, std::weak_ptr<Task>> mTaskIdToTask; 157 158 // LINT.IfChange(virtio_gpu_timelines) 159 std::atomic<TaskId> mNextId; 160 std::unordered_map<Ring, Timeline> mTimelineQueues GUARDED_BY(mTimelinesMutex); 161 // LINT.ThenChange(VirtioGpuTimelinesSnapshot.proto:virtio_gpu_timelines) 162 }; 163 164 #endif // VIRTIO_GPU_TIMELINES_H 165