1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "Choreographer" 18 //#define LOG_NDEBUG 0 19 20 #include <cinttypes> 21 #include <queue> 22 #include <thread> 23 24 #include <android/choreographer.h> 25 #include <androidfw/DisplayEventDispatcher.h> 26 #include <gui/ISurfaceComposer.h> 27 #include <utils/Looper.h> 28 #include <utils/Mutex.h> 29 #include <utils/Timers.h> 30 31 namespace android { 32 33 static inline const char* toString(bool value) { 34 return value ? "true" : "false"; 35 } 36 37 struct FrameCallback { 38 AChoreographer_frameCallback callback; 39 void* data; 40 nsecs_t dueTime; 41 42 inline bool operator<(const FrameCallback& rhs) const { 43 // Note that this is intentionally flipped because we want callbacks due sooner to be at 44 // the head of the queue 45 return dueTime > rhs.dueTime; 46 } 47 }; 48 49 50 class Choreographer : public DisplayEventDispatcher, public MessageHandler { 51 public: 52 void postFrameCallback(AChoreographer_frameCallback cb, void* data); 53 void postFrameCallbackDelayed(AChoreographer_frameCallback cb, void* data, nsecs_t delay); 54 55 enum { 56 MSG_SCHEDULE_CALLBACKS = 0, 57 MSG_SCHEDULE_VSYNC = 1 58 }; 59 virtual void handleMessage(const Message& message) override; 60 61 static Choreographer* getForThread(); 62 63 protected: 64 virtual ~Choreographer() = default; 65 66 private: 67 Choreographer(const sp<Looper>& looper); 68 Choreographer(const Choreographer&) = delete; 69 70 virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count); 71 virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected); 72 73 void scheduleCallbacks(); 74 75 // Protected by mLock 76 std::priority_queue<FrameCallback> mCallbacks; 77 78 mutable Mutex mLock; 79 80 const sp<Looper> mLooper; 81 const std::thread::id mThreadId; 82 }; 83 84 85 static thread_local Choreographer* gChoreographer; 86 Choreographer* Choreographer::getForThread() { 87 if (gChoreographer == nullptr) { 88 sp<Looper> looper = Looper::getForThread(); 89 if (!looper.get()) { 90 ALOGW("No looper prepared for thread"); 91 return nullptr; 92 } 93 gChoreographer = new Choreographer(looper); 94 status_t result = gChoreographer->initialize(); 95 if (result != OK) { 96 ALOGW("Failed to initialize"); 97 return nullptr; 98 } 99 } 100 return gChoreographer; 101 } 102 103 Choreographer::Choreographer(const sp<Looper>& looper) : 104 DisplayEventDispatcher(looper), mLooper(looper), mThreadId(std::this_thread::get_id()) { 105 } 106 107 void Choreographer::postFrameCallback(AChoreographer_frameCallback cb, void* data) { 108 postFrameCallbackDelayed(cb, data, 0); 109 } 110 111 void Choreographer::postFrameCallbackDelayed( 112 AChoreographer_frameCallback cb, void* data, nsecs_t delay) { 113 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); 114 FrameCallback callback{cb, data, now + delay}; 115 { 116 AutoMutex _l{mLock}; 117 mCallbacks.push(callback); 118 } 119 if (callback.dueTime <= now) { 120 if (std::this_thread::get_id() != mThreadId) { 121 Message m{MSG_SCHEDULE_VSYNC}; 122 mLooper->sendMessage(this, m); 123 } else { 124 scheduleVsync(); 125 } 126 } else { 127 Message m{MSG_SCHEDULE_CALLBACKS}; 128 mLooper->sendMessageDelayed(delay, this, m); 129 } 130 } 131 132 void Choreographer::scheduleCallbacks() { 133 AutoMutex _{mLock}; 134 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); 135 if (mCallbacks.top().dueTime <= now) { 136 ALOGV("choreographer %p ~ scheduling vsync", this); 137 scheduleVsync(); 138 return; 139 } 140 } 141 142 143 void Choreographer::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t) { 144 if (id != ISurfaceComposer::eDisplayIdMain) { 145 ALOGV("choreographer %p ~ ignoring vsync signal for non-main display (id=%d)", this, id); 146 scheduleVsync(); 147 return; 148 } 149 std::vector<FrameCallback> callbacks{}; 150 { 151 AutoMutex _l{mLock}; 152 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); 153 while (!mCallbacks.empty() && mCallbacks.top().dueTime < now) { 154 callbacks.push_back(mCallbacks.top()); 155 mCallbacks.pop(); 156 } 157 } 158 for (const auto& cb : callbacks) { 159 cb.callback(timestamp, cb.data); 160 } 161 } 162 163 void Choreographer::dispatchHotplug(nsecs_t, int32_t id, bool connected) { 164 ALOGV("choreographer %p ~ received hotplug event (id=%" PRId32 ", connected=%s), ignoring.", 165 this, id, toString(connected)); 166 } 167 168 void Choreographer::handleMessage(const Message& message) { 169 switch (message.what) { 170 case MSG_SCHEDULE_CALLBACKS: 171 scheduleCallbacks(); 172 break; 173 case MSG_SCHEDULE_VSYNC: 174 scheduleVsync(); 175 break; 176 } 177 } 178 179 } 180 181 /* Glue for the NDK interface */ 182 183 using android::Choreographer; 184 185 static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) { 186 return reinterpret_cast<Choreographer*>(choreographer); 187 } 188 189 static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) { 190 return reinterpret_cast<AChoreographer*>(choreographer); 191 } 192 193 AChoreographer* AChoreographer_getInstance() { 194 return Choreographer_to_AChoreographer(Choreographer::getForThread()); 195 } 196 197 void AChoreographer_postFrameCallback(AChoreographer* choreographer, 198 AChoreographer_frameCallback callback, void* data) { 199 AChoreographer_to_Choreographer(choreographer)->postFrameCallback(callback, data); 200 } 201 void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, 202 AChoreographer_frameCallback callback, void* data, long delayMillis) { 203 AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( 204 callback, data, ms2ns(delayMillis)); 205 } 206