diff options
author | 2015-12-04 17:59:42 +0000 | |
---|---|---|
committer | 2016-01-21 16:30:33 -0800 | |
commit | 3d3fe5026a6a3e951ef56ad16a100b8d5ae84574 (patch) | |
tree | 80cf611c06bd2e4b1ba0965db1bd5b3ccc8bb1e8 | |
parent | e501c39e2cbfbe4cac473e97f8cae55ace3bb48c (diff) |
Add choreographer API to the NDK.
Change-Id: Icb8cffd3cd3bd06814466be72db3e26f6a62cbc6
-rw-r--r-- | core/jni/android_view_DisplayEventReceiver.cpp | 108 | ||||
-rw-r--r-- | include/androidfw/DisplayEventDispatcher.h | 45 | ||||
-rw-r--r-- | libs/androidfw/Android.mk | 4 | ||||
-rw-r--r-- | libs/androidfw/DisplayEventDispatcher.cpp | 147 | ||||
-rw-r--r-- | native/android/Android.mk | 4 | ||||
-rw-r--r-- | native/android/choreographer.cpp | 205 |
6 files changed, 409 insertions, 104 deletions
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index 24eb961bd431..f9936aedaa5a 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -23,6 +23,7 @@ #include <inttypes.h> #include <android_runtime/AndroidRuntime.h> +#include <androidfw/DisplayEventDispatcher.h> #include <utils/Log.h> #include <utils/Looper.h> #include <utils/threads.h> @@ -48,14 +49,12 @@ static struct { } gDisplayEventReceiverClassInfo; -class NativeDisplayEventReceiver : public LooperCallback { +class NativeDisplayEventReceiver : public DisplayEventDispatcher { public: NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, const sp<MessageQueue>& messageQueue); - status_t initialize(); void dispose(); - status_t scheduleVsync(); protected: virtual ~NativeDisplayEventReceiver(); @@ -66,15 +65,14 @@ private: DisplayEventReceiver mReceiver; bool mWaitingForVsync; - virtual int handleEvent(int receiveFd, int events, void* data); - bool processPendingEvents(nsecs_t* outTimestamp, int32_t* id, uint32_t* outCount); - void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count); - void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected); + virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count); + virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected); }; NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, const sp<MessageQueue>& messageQueue) : + DisplayEventDispatcher(messageQueue->getLooper()), mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), mMessageQueue(messageQueue), mWaitingForVsync(false) { ALOGV("receiver %p ~ Initializing display event receiver.", this); @@ -85,21 +83,6 @@ NativeDisplayEventReceiver::~NativeDisplayEventReceiver() { env->DeleteGlobalRef(mReceiverWeakGlobal); } -status_t NativeDisplayEventReceiver::initialize() { - status_t result = mReceiver.initCheck(); - if (result) { - ALOGW("Failed to initialize display event receiver, status=%d", result); - return result; - } - - int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, - this, NULL); - if (rc < 0) { - return UNKNOWN_ERROR; - } - return OK; -} - void NativeDisplayEventReceiver::dispose() { ALOGV("receiver %p ~ Disposing display event receiver.", this); @@ -108,87 +91,6 @@ void NativeDisplayEventReceiver::dispose() { } } -status_t NativeDisplayEventReceiver::scheduleVsync() { - if (!mWaitingForVsync) { - ALOGV("receiver %p ~ Scheduling vsync.", this); - - // Drain all pending events. - nsecs_t vsyncTimestamp; - int32_t vsyncDisplayId; - uint32_t vsyncCount; - processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount); - - status_t status = mReceiver.requestNextVsync(); - if (status) { - ALOGW("Failed to request next vsync, status=%d", status); - return status; - } - - mWaitingForVsync = true; - } - return OK; -} - -int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) { - if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { - ALOGE("Display event receiver pipe was closed or an error occurred. " - "events=0x%x", events); - return 0; // remove the callback - } - - if (!(events & Looper::EVENT_INPUT)) { - ALOGW("Received spurious callback for unhandled poll event. " - "events=0x%x", events); - return 1; // keep the callback - } - - // Drain all pending events, keep the last vsync. - nsecs_t vsyncTimestamp; - int32_t vsyncDisplayId; - uint32_t vsyncCount; - if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { - ALOGV("receiver %p ~ Vsync pulse: timestamp=%" PRId64 ", id=%d, count=%d", - this, vsyncTimestamp, vsyncDisplayId, vsyncCount); - mWaitingForVsync = false; - dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount); - } - - return 1; // keep the callback -} - -bool NativeDisplayEventReceiver::processPendingEvents( - nsecs_t* outTimestamp, int32_t* outId, uint32_t* outCount) { - bool gotVsync = false; - DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - ssize_t n; - while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { - ALOGV("receiver %p ~ Read %d events.", this, int(n)); - for (ssize_t i = 0; i < n; i++) { - const DisplayEventReceiver::Event& ev = buf[i]; - switch (ev.header.type) { - case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: - // Later vsync events will just overwrite the info from earlier - // ones. That's fine, we only care about the most recent. - gotVsync = true; - *outTimestamp = ev.header.timestamp; - *outId = ev.header.id; - *outCount = ev.vsync.count; - break; - case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: - dispatchHotplug(ev.header.timestamp, ev.header.id, ev.hotplug.connected); - break; - default: - ALOGW("receiver %p ~ ignoring unknown event type %#x", this, ev.header.type); - break; - } - } - } - if (n < 0) { - ALOGW("Failed to get events from display event receiver, status=%d", status_t(n)); - } - return gotVsync; -} - void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) { JNIEnv* env = AndroidRuntime::getJNIEnv(); diff --git a/include/androidfw/DisplayEventDispatcher.h b/include/androidfw/DisplayEventDispatcher.h new file mode 100644 index 000000000000..3ade2156589a --- /dev/null +++ b/include/androidfw/DisplayEventDispatcher.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#include <gui/DisplayEventReceiver.h> +#include <utils/Log.h> +#include <utils/Looper.h> + +namespace android { + +class DisplayEventDispatcher : public LooperCallback { +public: + DisplayEventDispatcher(const sp<Looper>& looper); + + status_t initialize(); + void dispose(); + status_t scheduleVsync(); + +protected: + virtual ~DisplayEventDispatcher() = default; + +private: + sp<Looper> mLooper; + DisplayEventReceiver mReceiver; + bool mWaitingForVsync; + + virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) = 0; + virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected) = 0; + + virtual int handleEvent(int receiveFd, int events, void* data); + bool processPendingEvents(nsecs_t* outTimestamp, int32_t* id, uint32_t* outCount); +}; +} diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk index f682fb8684e8..4c2d35ada882 100644 --- a/libs/androidfw/Android.mk +++ b/libs/androidfw/Android.mk @@ -33,7 +33,8 @@ deviceSources := \ $(commonSources) \ BackupData.cpp \ BackupHelpers.cpp \ - CursorWindow.cpp + CursorWindow.cpp \ + DisplayEventDispatcher.cpp hostSources := $(commonSources) @@ -65,6 +66,7 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ liblog \ libcutils \ + libgui \ libutils \ libz diff --git a/libs/androidfw/DisplayEventDispatcher.cpp b/libs/androidfw/DisplayEventDispatcher.cpp new file mode 100644 index 000000000000..99cd1732031c --- /dev/null +++ b/libs/androidfw/DisplayEventDispatcher.cpp @@ -0,0 +1,147 @@ +/* + * 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 "DisplayEventDispatcher" + +#include <cinttypes> +#include <cstdint> + +#include <androidfw/DisplayEventDispatcher.h> +#include <gui/DisplayEventReceiver.h> +#include <utils/Log.h> +#include <utils/Looper.h> + +#include <utils/Timers.h> + +namespace android { + +// Number of events to read at a time from the DisplayEventDispatcher pipe. +// The value should be large enough that we can quickly drain the pipe +// using just a few large reads. +static const size_t EVENT_BUFFER_SIZE = 100; + +DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper) : + mLooper(looper), mWaitingForVsync(false) { + ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this); +} + +status_t DisplayEventDispatcher::initialize() { + status_t result = mReceiver.initCheck(); + if (result) { + ALOGW("Failed to initialize display event receiver, status=%d", result); + return result; + } + + int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, + this, NULL); + if (rc < 0) { + return UNKNOWN_ERROR; + } + return OK; +} + +void DisplayEventDispatcher::dispose() { + ALOGV("dispatcher %p ~ Disposing display event dispatcher.", this); + + if (!mReceiver.initCheck()) { + mLooper->removeFd(mReceiver.getFd()); + } +} + +status_t DisplayEventDispatcher::scheduleVsync() { + if (!mWaitingForVsync) { + ALOGV("dispatcher %p ~ Scheduling vsync.", this); + + // Drain all pending events. + nsecs_t vsyncTimestamp; + int32_t vsyncDisplayId; + uint32_t vsyncCount; + if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { + ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", + this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp))); + } + + status_t status = mReceiver.requestNextVsync(); + if (status) { + ALOGW("Failed to request next vsync, status=%d", status); + return status; + } + + mWaitingForVsync = true; + } + return OK; +} + +int DisplayEventDispatcher::handleEvent(int, int events, void*) { + if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { + ALOGE("Display event receiver pipe was closed or an error occurred. " + "events=0x%x", events); + return 0; // remove the callback + } + + if (!(events & Looper::EVENT_INPUT)) { + ALOGW("Received spurious callback for unhandled poll event. " + "events=0x%x", events); + return 1; // keep the callback + } + + // Drain all pending events, keep the last vsync. + nsecs_t vsyncTimestamp; + int32_t vsyncDisplayId; + uint32_t vsyncCount; + if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { + ALOGE("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", id=%d, count=%d", + this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount); + mWaitingForVsync = false; + dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount); + } + + return 1; // keep the callback +} + +bool DisplayEventDispatcher::processPendingEvents( + nsecs_t* outTimestamp, int32_t* outId, uint32_t* outCount) { + bool gotVsync = false; + DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; + ssize_t n; + while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + ALOGV("dispatcher %p ~ Read %d events.", this, int(n)); + for (ssize_t i = 0; i < n; i++) { + const DisplayEventReceiver::Event& ev = buf[i]; + switch (ev.header.type) { + case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: + // Later vsync events will just overwrite the info from earlier + // ones. That's fine, we only care about the most recent. + gotVsync = true; + *outTimestamp = ev.header.timestamp; + *outId = ev.header.id; + *outCount = ev.vsync.count; + break; + case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: + dispatchHotplug(ev.header.timestamp, ev.header.id, ev.hotplug.connected); + break; + default: + ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); + break; + } + } + } + if (n < 0) { + ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n)); + } + return gotVsync; +} +} diff --git a/native/android/Android.mk b/native/android/Android.mk index 1742beedcabd..5386e6fec7ec 100644 --- a/native/android/Android.mk +++ b/native/android/Android.mk @@ -7,6 +7,7 @@ include $(CLEAR_VARS) # LOCAL_SRC_FILES:= \ asset_manager.cpp \ + choreographer.cpp \ configuration.cpp \ input.cpp \ looper.cpp \ @@ -43,4 +44,7 @@ LOCAL_MODULE := libandroid LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code +# Required because of b/25642296 +LOCAL_CLANG_arm64 := false + include $(BUILD_SHARED_LIBRARY) diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp new file mode 100644 index 000000000000..cc2fd7758787 --- /dev/null +++ b/native/android/choreographer.cpp @@ -0,0 +1,205 @@ +/* + * 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 <cinttypes> +#include <queue> +#include <thread> + +#include <android/choreographer.h> +#include <androidfw/DisplayEventDispatcher.h> +#include <gui/ISurfaceComposer.h> +#include <utils/Looper.h> +#include <utils/Mutex.h> +#include <utils/Timers.h> + +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>& 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<FrameCallback> mCallbacks; + + mutable Mutex mLock; + + const sp<Looper> mLooper; + const std::thread::id mThreadId; +}; + + +thread_local Choreographer* gChoreographer; +Choreographer* Choreographer::getForThread() { + if (gChoreographer == nullptr) { + sp<Looper> 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>& 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<FrameCallback> 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*>(choreographer); +} + +static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) { + return reinterpret_cast<AChoreographer*>(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)); +} |