Home | History | Annotate | Download | only in android
      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