/* * Copyright (C) 2015 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. */ #define LOG_TAG "Choreographer" //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include namespace android { static inline const char* toString(bool value) { return value ? "true" : "false"; } struct FrameCallback { AChoreographer_frameCallback callback; void* data; nsecs_t dueTime; inline bool operator<(const FrameCallback& rhs) const { // Note that this is intentionally flipped because we want callbacks due sooner to be at // the head of the queue return dueTime > rhs.dueTime; } }; class Choreographer : public DisplayEventDispatcher, public MessageHandler { public: void postFrameCallback(AChoreographer_frameCallback cb, void* data); void postFrameCallbackDelayed(AChoreographer_frameCallback cb, void* data, nsecs_t delay); enum { MSG_SCHEDULE_CALLBACKS = 0, MSG_SCHEDULE_VSYNC = 1 }; virtual void handleMessage(const Message& message) override; static Choreographer* getForThread(); protected: virtual ~Choreographer() = default; private: Choreographer(const sp& looper); Choreographer(const Choreographer&) = delete; virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count); virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected); void scheduleCallbacks(); // Protected by mLock std::priority_queue mCallbacks; mutable Mutex mLock; const sp mLooper; const std::thread::id mThreadId; }; static thread_local Choreographer* gChoreographer; Choreographer* Choreographer::getForThread() { if (gChoreographer == nullptr) { sp looper = Looper::getForThread(); if (!looper.get()) { ALOGW("No looper prepared for thread"); return nullptr; } gChoreographer = new Choreographer(looper); status_t result = gChoreographer->initialize(); if (result != OK) { ALOGW("Failed to initialize"); return nullptr; } } return gChoreographer; } Choreographer::Choreographer(const sp& looper) : DisplayEventDispatcher(looper), mLooper(looper), mThreadId(std::this_thread::get_id()) { } void Choreographer::postFrameCallback(AChoreographer_frameCallback cb, void* data) { postFrameCallbackDelayed(cb, data, 0); } void Choreographer::postFrameCallbackDelayed( AChoreographer_frameCallback cb, void* data, nsecs_t delay) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); FrameCallback callback{cb, data, now + delay}; { AutoMutex _l{mLock}; mCallbacks.push(callback); } if (callback.dueTime <= now) { if (std::this_thread::get_id() != mThreadId) { Message m{MSG_SCHEDULE_VSYNC}; mLooper->sendMessage(this, m); } else { scheduleVsync(); } } else { Message m{MSG_SCHEDULE_CALLBACKS}; mLooper->sendMessageDelayed(delay, this, m); } } void Choreographer::scheduleCallbacks() { AutoMutex _{mLock}; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (mCallbacks.top().dueTime <= now) { ALOGV("choreographer %p ~ scheduling vsync", this); scheduleVsync(); return; } } void Choreographer::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t) { if (id != ISurfaceComposer::eDisplayIdMain) { ALOGV("choreographer %p ~ ignoring vsync signal for non-main display (id=%d)", this, id); scheduleVsync(); return; } std::vector callbacks{}; { AutoMutex _l{mLock}; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); while (!mCallbacks.empty() && mCallbacks.top().dueTime < now) { callbacks.push_back(mCallbacks.top()); mCallbacks.pop(); } } for (const auto& cb : callbacks) { cb.callback(timestamp, cb.data); } } void Choreographer::dispatchHotplug(nsecs_t, int32_t id, bool connected) { ALOGV("choreographer %p ~ received hotplug event (id=%" PRId32 ", connected=%s), ignoring.", this, id, toString(connected)); } void Choreographer::handleMessage(const Message& message) { switch (message.what) { case MSG_SCHEDULE_CALLBACKS: scheduleCallbacks(); break; case MSG_SCHEDULE_VSYNC: scheduleVsync(); break; } } } /* Glue for the NDK interface */ using android::Choreographer; static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) { return reinterpret_cast(choreographer); } static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) { return reinterpret_cast(choreographer); } AChoreographer* AChoreographer_getInstance() { return Choreographer_to_AChoreographer(Choreographer::getForThread()); } void AChoreographer_postFrameCallback(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data) { AChoreographer_to_Choreographer(choreographer)->postFrameCallback(callback, data); } void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data, long delayMillis) { AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( callback, data, ms2ns(delayMillis)); }