summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/surfaceflinger/Android.mk27
-rw-r--r--services/surfaceflinger/DisplayEventConnection.cpp62
-rw-r--r--services/surfaceflinger/DisplayEventConnection.h60
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.cpp34
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.h12
-rw-r--r--services/surfaceflinger/DisplayHardware/VSyncBarrier.cpp81
-rw-r--r--services/surfaceflinger/DisplayHardware/VSyncBarrier.h41
-rw-r--r--services/surfaceflinger/EventThread.cpp129
-rw-r--r--services/surfaceflinger/EventThread.h79
-rw-r--r--services/surfaceflinger/MessageQueue.cpp186
-rw-r--r--services/surfaceflinger/MessageQueue.h79
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp71
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h8
-rw-r--r--services/surfaceflinger/tests/vsync/Android.mk18
-rw-r--r--services/surfaceflinger/tests/vsync/vsync.cpp81
-rw-r--r--services/surfaceflinger/tests/waitforvsync/Android.mk14
-rw-r--r--services/surfaceflinger/tests/waitforvsync/waitforvsync.cpp45
17 files changed, 784 insertions, 243 deletions
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index f63c0c1..95d651a 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -2,19 +2,22 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- Layer.cpp \
- LayerBase.cpp \
- LayerDim.cpp \
- LayerScreenshot.cpp \
- DdmConnection.cpp \
- DisplayHardware/DisplayHardware.cpp \
+ EventThread.cpp \
+ Layer.cpp \
+ LayerBase.cpp \
+ LayerDim.cpp \
+ LayerScreenshot.cpp \
+ DdmConnection.cpp \
+ DisplayHardware/DisplayHardware.cpp \
DisplayHardware/DisplayHardwareBase.cpp \
- DisplayHardware/HWComposer.cpp \
- GLExtensions.cpp \
- MessageQueue.cpp \
- SurfaceFlinger.cpp \
- SurfaceTextureLayer.cpp \
- Transform.cpp \
+ DisplayHardware/HWComposer.cpp \
+ DisplayHardware/VSyncBarrier.cpp \
+ DisplayEventConnection.cpp \
+ GLExtensions.cpp \
+ MessageQueue.cpp \
+ SurfaceFlinger.cpp \
+ SurfaceTextureLayer.cpp \
+ Transform.cpp \
LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\"
diff --git a/services/surfaceflinger/DisplayEventConnection.cpp b/services/surfaceflinger/DisplayEventConnection.cpp
new file mode 100644
index 0000000..a0aa9c0
--- /dev/null
+++ b/services/surfaceflinger/DisplayEventConnection.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 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 <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IDisplayEventConnection.h>
+#include <gui/BitTube.h>
+#include <gui/DisplayEventReceiver.h>
+
+#include <utils/Errors.h>
+
+#include "SurfaceFlinger.h"
+#include "DisplayEventConnection.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+DisplayEventConnection::DisplayEventConnection(
+ const sp<SurfaceFlinger>& flinger)
+ : mFlinger(flinger), mChannel(new BitTube())
+{
+}
+
+DisplayEventConnection::~DisplayEventConnection() {
+ mFlinger->cleanupDisplayEventConnection(this);
+}
+
+void DisplayEventConnection::onFirstRef() {
+ // nothing to do here for now.
+}
+
+sp<BitTube> DisplayEventConnection::getDataChannel() const {
+ return mChannel;
+}
+
+status_t DisplayEventConnection::postEvent(const DisplayEventReceiver::Event& event)
+{
+ ssize_t size = mChannel->write(&event, sizeof(DisplayEventReceiver::Event));
+ return size < 0 ? status_t(size) : status_t(NO_ERROR);
+}
+
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/DisplayEventConnection.h b/services/surfaceflinger/DisplayEventConnection.h
new file mode 100644
index 0000000..46cf64b
--- /dev/null
+++ b/services/surfaceflinger/DisplayEventConnection.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ANDROID_SURFACE_FLINGER_DISPLAY_EVENT_CONNECTION_H
+#define ANDROID_SURFACE_FLINGER_DISPLAY_EVENT_CONNECTION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IDisplayEventConnection.h>
+
+#include <utils/Errors.h>
+#include <gui/DisplayEventReceiver.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class BitTube;
+class SurfaceFlinger;
+
+// ---------------------------------------------------------------------------
+
+class DisplayEventConnection : public BnDisplayEventConnection {
+public:
+ DisplayEventConnection(const sp<SurfaceFlinger>& flinger);
+
+ status_t postEvent(const DisplayEventReceiver::Event& event);
+
+private:
+ virtual ~DisplayEventConnection();
+ virtual void onFirstRef();
+ virtual sp<BitTube> getDataChannel() const;
+
+ sp<SurfaceFlinger> const mFlinger;
+ sp<BitTube> const mChannel;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif /* ANDROID_SURFACE_FLINGER_DISPLAY_EVENT_CONNECTION_H */
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index f94d321..3bbc75e 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -140,6 +140,7 @@ void DisplayHardware::init(uint32_t dpy)
mDpiX = mNativeWindow->xdpi;
mDpiY = mNativeWindow->ydpi;
mRefreshRate = fbDev->fps;
+ mNextFakeVSync = 0;
/* FIXME: this is a temporary HACK until we are able to report the refresh rate
@@ -152,6 +153,8 @@ void DisplayHardware::init(uint32_t dpy)
#warning "refresh rate set via makefile to REFRESH_RATE"
#endif
+ mRefreshPeriod = nsecs_t(1e9 / mRefreshRate);
+
EGLint w, h, dummy;
EGLint numConfigs=0;
EGLSurface surface;
@@ -346,6 +349,37 @@ uint32_t DisplayHardware::getPageFlipCount() const {
return mPageFlipCount;
}
+// this needs to be thread safe
+nsecs_t DisplayHardware::waitForVSync() const {
+ nsecs_t timestamp;
+ if (mVSync.wait(&timestamp) < 0) {
+ // vsync not supported!
+ usleep( getDelayToNextVSyncUs(&timestamp) );
+ }
+ return timestamp;
+}
+
+int32_t DisplayHardware::getDelayToNextVSyncUs(nsecs_t* timestamp) const {
+ Mutex::Autolock _l(mFakeVSyncMutex);
+ const nsecs_t period = mRefreshPeriod;
+ const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ nsecs_t next_vsync = mNextFakeVSync;
+ nsecs_t sleep = next_vsync - now;
+ if (sleep < 0) {
+ // we missed, find where the next vsync should be
+ sleep = (period - ((now - next_vsync) % period));
+ next_vsync = now + sleep;
+ }
+ mNextFakeVSync = next_vsync + period;
+ timestamp[0] = next_vsync;
+
+ // round to next microsecond
+ int32_t sleep_us = (sleep + 999LL) / 1000LL;
+
+ // guaranteed to be > 0
+ return sleep_us;
+}
+
status_t DisplayHardware::compositionComplete() const {
return mNativeWindow->compositionComplete();
}
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index f02c954..45d4b45 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -32,6 +32,7 @@
#include "GLExtensions.h"
#include "DisplayHardware/DisplayHardwareBase.h"
+#include "DisplayHardware/VSyncBarrier.h"
namespace android {
@@ -74,6 +75,9 @@ public:
uint32_t getMaxTextureSize() const;
uint32_t getMaxViewportDims() const;
+ // waits for the next vsync and returns the timestamp of when it happened
+ nsecs_t waitForVSync() const;
+
uint32_t getPageFlipCount() const;
EGLDisplay getEGLDisplay() const { return mDisplay; }
@@ -95,6 +99,7 @@ public:
private:
void init(uint32_t displayIndex) __attribute__((noinline));
void fini() __attribute__((noinline));
+ int32_t getDelayToNextVSyncUs(nsecs_t* timestamp) const;
sp<SurfaceFlinger> mFlinger;
EGLDisplay mDisplay;
@@ -112,7 +117,12 @@ private:
mutable uint32_t mPageFlipCount;
GLint mMaxViewportDims[2];
GLint mMaxTextureSize;
-
+ VSyncBarrier mVSync;
+
+ mutable Mutex mFakeVSyncMutex;
+ mutable nsecs_t mNextFakeVSync;
+ nsecs_t mRefreshPeriod;
+
HWComposer* mHwc;
sp<FramebufferNativeWindow> mNativeWindow;
diff --git a/services/surfaceflinger/DisplayHardware/VSyncBarrier.cpp b/services/surfaceflinger/DisplayHardware/VSyncBarrier.cpp
new file mode 100644
index 0000000..187da20
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/VSyncBarrier.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 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 <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+
+#include "DisplayHardware/VSyncBarrier.h"
+
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
+#endif
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+VSyncBarrier::VSyncBarrier() : mFd(-EINVAL) {
+#if HAS_WAITFORVSYNC
+ mFd = open("/dev/graphics/fb0", O_RDWR);
+ if (mFd < 0) {
+ mFd = -errno;
+ }
+ // try to see if FBIO_WAITFORVSYNC is supported
+ uint32_t crt = 0;
+ int err = ioctl(mFd, FBIO_WAITFORVSYNC, &crt);
+ if (err < 0) {
+ close(mFd);
+ mFd = -EINVAL;
+ }
+#endif
+}
+
+VSyncBarrier::~VSyncBarrier() {
+ if (mFd >= 0) {
+ close(mFd);
+ }
+}
+
+status_t VSyncBarrier::initCheck() const {
+ return mFd < 0 ? mFd : status_t(NO_ERROR);
+}
+
+// this must be thread-safe
+status_t VSyncBarrier::wait(nsecs_t* timestamp) const {
+ if (mFd < 0) {
+ return mFd;
+ }
+
+ int err;
+ uint32_t crt = 0;
+ do {
+ err = ioctl(mFd, FBIO_WAITFORVSYNC, &crt);
+ } while (err<0 && errno==EINTR);
+ if (err < 0) {
+ return -errno;
+ }
+ // ideally this would come from the driver
+ timestamp[0] = systemTime();
+ return NO_ERROR;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/VSyncBarrier.h b/services/surfaceflinger/DisplayHardware/VSyncBarrier.h
new file mode 100644
index 0000000..3c32950
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/VSyncBarrier.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ANDROID_SURFACE_FLINGER_VSYNCBARRIER_H_
+#define ANDROID_SURFACE_FLINGER_VSYNCBARRIER_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Timers.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class VSyncBarrier {
+ int mFd;
+public:
+ VSyncBarrier();
+ ~VSyncBarrier();
+ status_t initCheck() const;
+ status_t wait(nsecs_t* timestamp) const;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif /* ANDROID_SURFACE_FLINGER_VSYNCBARRIER_H_ */
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
new file mode 100644
index 0000000..edb06ba
--- /dev/null
+++ b/services/surfaceflinger/EventThread.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2011 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 <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IDisplayEventConnection.h>
+#include <gui/DisplayEventReceiver.h>
+
+#include <utils/Errors.h>
+
+#include "DisplayHardware/DisplayHardware.h"
+#include "DisplayEventConnection.h"
+#include "EventThread.h"
+#include "SurfaceFlinger.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+EventThread::EventThread(const sp<SurfaceFlinger>& flinger)
+ : mFlinger(flinger),
+ mHw(flinger->graphicPlane(0).displayHardware()),
+ mDeliveredEvents(0)
+{
+}
+
+void EventThread::onFirstRef() {
+ run("EventThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
+}
+
+status_t EventThread::registerDisplayEventConnection(
+ const sp<DisplayEventConnection>& connection) {
+ Mutex::Autolock _l(mLock);
+ mDisplayEventConnections.add(connection);
+ mCondition.signal();
+ return NO_ERROR;
+}
+
+status_t EventThread::unregisterDisplayEventConnection(
+ const wp<DisplayEventConnection>& connection) {
+ Mutex::Autolock _l(mLock);
+ mDisplayEventConnections.remove(connection);
+ mCondition.signal();
+ return NO_ERROR;
+}
+
+bool EventThread::threadLoop() {
+
+ nsecs_t timestamp;
+ Mutex::Autolock _l(mLock);
+ do {
+ // wait for listeners
+ while (!mDisplayEventConnections.size()) {
+ mCondition.wait(mLock);
+ }
+
+ // wait for vsync
+ mLock.unlock();
+ timestamp = mHw.waitForVSync();
+ mLock.lock();
+
+ // make sure we still have some listeners
+ } while (!mDisplayEventConnections.size());
+
+
+ // dispatch vsync events to listeners...
+ mDeliveredEvents++;
+ const size_t count = mDisplayEventConnections.size();
+
+ DisplayEventReceiver::Event vsync;
+ vsync.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+ vsync.header.timestamp = timestamp;
+ vsync.vsync.count = mDeliveredEvents;
+
+ for (size_t i=0 ; i<count ; i++) {
+ sp<DisplayEventConnection> conn(mDisplayEventConnections.itemAt(i).promote());
+ // make sure the connection didn't die
+ if (conn != NULL) {
+ status_t err = conn->postEvent(vsync);
+ if (err == -EAGAIN || err == -EWOULDBLOCK) {
+ // The destination doesn't accept events anymore, it's probably
+ // full. For now, we just drop the events on the floor.
+ // Note that some events cannot be dropped and would have to be
+ // re-sent later. Right-now we don't have the ability to do
+ // this, but it doesn't matter for VSYNC.
+ } else if (err < 0) {
+ // handle any other error on the pipe as fatal. the only
+ // reasonable thing to do is to clean-up this connection.
+ // The most common error we'll get here is -EPIPE.
+ mDisplayEventConnections.remove(conn);
+ }
+ }
+ }
+
+ return true;
+}
+
+status_t EventThread::readyToRun() {
+ LOGI("EventThread ready to run.");
+ return NO_ERROR;
+}
+
+void EventThread::dump(String8& result, char* buffer, size_t SIZE) const {
+ Mutex::Autolock _l(mLock);
+ result.append("VSYNC state:\n");
+ snprintf(buffer, SIZE, " numListeners=%u, events-delivered: %u\n",
+ mDisplayEventConnections.size(), mDeliveredEvents);
+ result.append(buffer);
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
new file mode 100644
index 0000000..0482ab7
--- /dev/null
+++ b/services/surfaceflinger/EventThread.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ANDROID_SURFACE_FLINGER_EVENT_THREAD_H
+#define ANDROID_SURFACE_FLINGER_EVENT_THREAD_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IDisplayEventConnection.h>
+
+#include <utils/Errors.h>
+#include <utils/threads.h>
+#include <utils/SortedVector.h>
+
+#include "DisplayEventConnection.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class SurfaceFlinger;
+class DisplayHardware;
+
+// ---------------------------------------------------------------------------
+
+class EventThread : public Thread {
+ friend class DisplayEventConnection;
+
+public:
+ EventThread(const sp<SurfaceFlinger>& flinger);
+
+ status_t registerDisplayEventConnection(
+ const sp<DisplayEventConnection>& connection);
+
+ status_t unregisterDisplayEventConnection(
+ const wp<DisplayEventConnection>& connection);
+
+ void dump(String8& result, char* buffer, size_t SIZE) const;
+
+private:
+ virtual bool threadLoop();
+ virtual status_t readyToRun();
+ virtual void onFirstRef();
+
+ // constants
+ sp<SurfaceFlinger> mFlinger;
+ const DisplayHardware& mHw;
+
+ mutable Mutex mLock;
+ mutable Condition mCondition;
+
+ // protected by mLock
+ SortedVector<wp<DisplayEventConnection> > mDisplayEventConnections;
+ size_t mDeliveredEvents;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif /* ANDROID_SURFACE_FLINGER_EVENT_THREAD_H */
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index aebe1b8..1846ccb 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -29,167 +29,79 @@ namespace android {
// ---------------------------------------------------------------------------
-void MessageList::insert(const sp<MessageBase>& node)
-{
- LIST::iterator cur(mList.begin());
- LIST::iterator end(mList.end());
- while (cur != end) {
- if (*node < **cur) {
- mList.insert(cur, node);
- return;
- }
- ++cur;
- }
- mList.insert(++end, node);
+MessageBase::MessageBase()
+ : MessageHandler() {
}
-void MessageList::remove(MessageList::LIST::iterator pos)
-{
- mList.erase(pos);
+MessageBase::~MessageBase() {
}
+void MessageBase::handleMessage(const Message&) {
+ this->handler();
+ barrier.open();
+};
+
// ---------------------------------------------------------------------------
MessageQueue::MessageQueue()
- : mInvalidate(false)
+ : mLooper(new Looper(true)),
+ mInvalidatePending(0)
{
- mInvalidateMessage = new MessageBase(INVALIDATE);
}
-MessageQueue::~MessageQueue()
-{
+MessageQueue::~MessageQueue() {
}
-sp<MessageBase> MessageQueue::waitMessage(nsecs_t timeout)
-{
- sp<MessageBase> result;
-
- bool again;
+void MessageQueue::waitMessage() {
do {
- const nsecs_t timeoutTime = systemTime() + timeout;
- while (true) {
- Mutex::Autolock _l(mLock);
- nsecs_t now = systemTime();
- nsecs_t nextEventTime = -1;
-
- LIST::iterator cur(mMessages.begin());
- if (cur != mMessages.end()) {
- result = *cur;
- }
-
- if (result != 0) {
- if (result->when <= now) {
- // there is a message to deliver
- mMessages.remove(cur);
- break;
- }
- nextEventTime = result->when;
- result = 0;
- }
-
- // see if we have an invalidate message
- if (mInvalidate) {
- mInvalidate = false;
- mInvalidateMessage->when = now;
- result = mInvalidateMessage;
- break;
- }
-
- if (timeout >= 0) {
- if (timeoutTime < now) {
- // we timed-out, return a NULL message
- result = 0;
- break;
- }
- if (nextEventTime > 0) {
- if (nextEventTime > timeoutTime) {
- nextEventTime = timeoutTime;
- }
- } else {
- nextEventTime = timeoutTime;
- }
- }
-
- if (nextEventTime >= 0) {
- //LOGD("nextEventTime = %lld ms", nextEventTime);
- if (nextEventTime > 0) {
- // we're about to wait, flush the binder command buffer
- IPCThreadState::self()->flushCommands();
- const nsecs_t reltime = nextEventTime - systemTime();
- if (reltime > 0) {
- mCondition.waitRelative(mLock, reltime);
- }
- }
- } else {
- //LOGD("going to wait");
- // we're about to wait, flush the binder command buffer
- IPCThreadState::self()->flushCommands();
- mCondition.wait(mLock);
- }
- }
- // here we're not holding the lock anymore
-
- if (result == 0)
+ // handle invalidate events first
+ if (android_atomic_and(0, &mInvalidatePending) != 0)
break;
- again = result->handler();
- if (again) {
- // the message has been processed. release our reference to it
- // without holding the lock.
- result->notify();
- result = 0;
- }
-
- } while (again);
+ IPCThreadState::self()->flushCommands();
- return result;
-}
+ int32_t ret = mLooper->pollOnce(-1);
+ switch (ret) {
+ case ALOOPER_POLL_WAKE:
+ // we got woken-up there is work to do in the main loop
+ continue;
-status_t MessageQueue::postMessage(
- const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags)
-{
- return queueMessage(message, relTime, flags);
-}
+ case ALOOPER_POLL_CALLBACK:
+ // callback was handled, loop again
+ continue;
-status_t MessageQueue::invalidate() {
- Mutex::Autolock _l(mLock);
- mInvalidate = true;
- mCondition.signal();
- return NO_ERROR;
-}
+ case ALOOPER_POLL_TIMEOUT:
+ // timeout (should not happen)
+ continue;
-status_t MessageQueue::queueMessage(
- const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags)
-{
- Mutex::Autolock _l(mLock);
- message->when = systemTime() + relTime;
- mMessages.insert(message);
-
- //LOGD("MessageQueue::queueMessage time = %lld ms", message->when);
- //dumpLocked(message);
-
- mCondition.signal();
- return NO_ERROR;
-}
+ case ALOOPER_POLL_ERROR:
+ LOGE("ALOOPER_POLL_ERROR");
+ continue;
-void MessageQueue::dump(const sp<MessageBase>& message)
-{
- Mutex::Autolock _l(mLock);
- dumpLocked(message);
+ default:
+ // should not happen
+ LOGE("Looper::pollOnce() returned unknown status %d", ret);
+ continue;
+ }
+ } while (true);
}
-void MessageQueue::dumpLocked(const sp<MessageBase>& message)
+status_t MessageQueue::postMessage(
+ const sp<MessageBase>& messageHandler, nsecs_t relTime)
{
- LIST::const_iterator cur(mMessages.begin());
- LIST::const_iterator end(mMessages.end());
- int c = 0;
- while (cur != end) {
- const char tick = (*cur == message) ? '>' : ' ';
- LOGD("%c %d: msg{.what=%08x, when=%lld}",
- tick, c, (*cur)->what, (*cur)->when);
- ++cur;
- c++;
+ const Message dummyMessage;
+ if (relTime > 0) {
+ mLooper->sendMessageDelayed(relTime, messageHandler, dummyMessage);
+ } else {
+ mLooper->sendMessage(messageHandler, dummyMessage);
}
+ return NO_ERROR;
+}
+
+status_t MessageQueue::invalidate() {
+ android_atomic_or(1, &mInvalidatePending);
+ mLooper->wake();
+ return NO_ERROR;
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
index 890f809..25030a6 100644
--- a/services/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/MessageQueue.h
@@ -23,7 +23,7 @@
#include <utils/threads.h>
#include <utils/Timers.h>
-#include <utils/List.h>
+#include <utils/Looper.h>
#include "Barrier.h"
@@ -31,92 +31,39 @@ namespace android {
// ---------------------------------------------------------------------------
-class MessageBase;
-
-class MessageList
-{
- List< sp<MessageBase> > mList;
- typedef List< sp<MessageBase> > LIST;
-public:
- inline LIST::iterator begin() { return mList.begin(); }
- inline LIST::const_iterator begin() const { return mList.begin(); }
- inline LIST::iterator end() { return mList.end(); }
- inline LIST::const_iterator end() const { return mList.end(); }
- inline bool isEmpty() const { return mList.empty(); }
- void insert(const sp<MessageBase>& node);
- void remove(LIST::iterator pos);
-};
-
-// ============================================================================
-
-class MessageBase :
- public LightRefBase<MessageBase>
+class MessageBase : public MessageHandler
{
public:
- nsecs_t when;
- uint32_t what;
- int32_t arg0;
-
- MessageBase() : when(0), what(0), arg0(0) { }
- MessageBase(uint32_t what, int32_t arg0=0)
- : when(0), what(what), arg0(arg0) { }
+ MessageBase();
// return true if message has a handler
- virtual bool handler() { return false; }
+ virtual bool handler() = 0;
// waits for the handler to be processed
void wait() const { barrier.wait(); }
-
- // releases all waiters. this is done automatically if
- // handler returns true
- void notify() const { barrier.open(); }
protected:
- virtual ~MessageBase() { }
+ virtual ~MessageBase();
private:
+ virtual void handleMessage(const Message& message);
+
mutable Barrier barrier;
- friend class LightRefBase<MessageBase>;
};
-inline bool operator < (const MessageBase& lhs, const MessageBase& rhs) {
- return lhs.when < rhs.when;
-}
-
// ---------------------------------------------------------------------------
-class MessageQueue
-{
- typedef List< sp<MessageBase> > LIST;
-public:
+class MessageQueue {
+ sp<Looper> mLooper;
+ volatile int32_t mInvalidatePending;
+public:
MessageQueue();
~MessageQueue();
- // pre-defined messages
- enum {
- INVALIDATE = '_upd'
- };
-
- sp<MessageBase> waitMessage(nsecs_t timeout = -1);
-
- status_t postMessage(const sp<MessageBase>& message,
- nsecs_t reltime=0, uint32_t flags = 0);
-
+ void waitMessage();
+ status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime=0);
status_t invalidate();
-
- void dump(const sp<MessageBase>& message);
-
-private:
- status_t queueMessage(const sp<MessageBase>& message,
- nsecs_t reltime, uint32_t flags);
- void dumpLocked(const sp<MessageBase>& message);
-
- Mutex mLock;
- Condition mCondition;
- MessageList mMessages;
- bool mInvalidate;
- sp<MessageBase> mInvalidateMessage;
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f38e948..51c2be8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -34,6 +34,8 @@
#include <binder/MemoryHeapBase.h>
#include <binder/PermissionCache.h>
+#include <gui/IDisplayEventConnection.h>
+
#include <utils/String8.h>
#include <utils/String16.h>
#include <utils/StopWatch.h>
@@ -46,6 +48,8 @@
#include <GLES/gl.h>
#include "clz.h"
+#include "DisplayEventConnection.h"
+#include "EventThread.h"
#include "GLExtensions.h"
#include "DdmConnection.h"
#include "Layer.h"
@@ -295,12 +299,16 @@ status_t SurfaceFlinger::readyToRun()
// put the origin in the left-bottom corner
glOrthof(0, w, 0, h, 0, 1); // l=0, r=w ; b=0, t=h
- mReadyToRunBarrier.open();
+
+ // start the EventThread
+ mEventThread = new EventThread(this);
/*
* We're now ready to accept clients...
*/
+ mReadyToRunBarrier.open();
+
// start boot animation
property_set("ctl.start", "bootanim");
@@ -313,25 +321,30 @@ status_t SurfaceFlinger::readyToRun()
#pragma mark Events Handler
#endif
-void SurfaceFlinger::waitForEvent()
-{
- while (true) {
- nsecs_t timeout = -1;
- sp<MessageBase> msg = mEventQueue.waitMessage(timeout);
- if (msg != 0) {
- switch (msg->what) {
- case MessageQueue::INVALIDATE:
- // invalidate message, just return to the main loop
- return;
- }
- }
- }
+void SurfaceFlinger::waitForEvent() {
+ mEventQueue.waitMessage();
}
void SurfaceFlinger::signalEvent() {
mEventQueue.invalidate();
}
+status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
+ nsecs_t reltime, uint32_t flags) {
+ return mEventQueue.postMessage(msg, reltime);
+}
+
+status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
+ nsecs_t reltime, uint32_t flags) {
+ status_t res = mEventQueue.postMessage(msg, reltime);
+ if (res == NO_ERROR) {
+ msg->wait();
+ }
+ return res;
+}
+
+// ----------------------------------------------------------------------------
+
bool SurfaceFlinger::authenticateSurfaceTexture(
const sp<ISurfaceTexture>& surfaceTexture) const {
Mutex::Autolock _l(mStateLock);
@@ -373,20 +386,17 @@ bool SurfaceFlinger::authenticateSurfaceTexture(
return false;
}
-status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
- nsecs_t reltime, uint32_t flags)
-{
- return mEventQueue.postMessage(msg, reltime, flags);
+// ----------------------------------------------------------------------------
+
+sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
+ sp<DisplayEventConnection> result(new DisplayEventConnection(this));
+ mEventThread->registerDisplayEventConnection(result);
+ return result;
}
-status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
- nsecs_t reltime, uint32_t flags)
-{
- status_t res = mEventQueue.postMessage(msg, reltime, flags);
- if (res == NO_ERROR) {
- msg->wait();
- }
- return res;
+void SurfaceFlinger::cleanupDisplayEventConnection(
+ const wp<DisplayEventConnection>& connection) {
+ mEventThread->unregisterDisplayEventConnection(connection);
}
// ----------------------------------------------------------------------------
@@ -445,7 +455,7 @@ bool SurfaceFlinger::threadLoop()
} else {
// pretend we did the post
hw.compositionComplete();
- usleep(16667); // 60 fps period
+ hw.waitForVSync();
}
return true;
}
@@ -1591,9 +1601,16 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
}
/*
+ * VSYNC state
+ */
+ mEventThread->dump(result, buffer, SIZE);
+
+ /*
* Dump HWComposer state
*/
HWComposer& hwc(hw.getHwComposer());
+ snprintf(buffer, SIZE, "h/w composer state:\n");
+ result.append(buffer);
snprintf(buffer, SIZE, " h/w composer %s and %s\n",
hwc.initCheck()==NO_ERROR ? "present" : "not present",
(mDebugDisableHWC || mDebugRegion) ? "disabled" : "enabled");
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 17028db..1039f47 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -46,6 +46,8 @@ namespace android {
class Client;
class DisplayHardware;
+class DisplayEventConnection;
+class EventThread;
class Layer;
class LayerDim;
class LayerScreenshot;
@@ -171,6 +173,7 @@ public:
int orientation, uint32_t flags);
virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags);
virtual bool authenticateSurfaceTexture(const sp<ISurfaceTexture>& surface) const;
+ virtual sp<IDisplayEventConnection> createDisplayEventConnection();
virtual status_t captureScreen(DisplayID dpy,
sp<IMemoryHeap>* heap,
@@ -222,6 +225,7 @@ private:
private:
friend class Client;
+ friend class DisplayEventConnection;
friend class LayerBase;
friend class LayerBaseClient;
friend class Layer;
@@ -331,6 +335,9 @@ private:
status_t electronBeamOffAnimationImplLocked();
status_t electronBeamOnAnimationImplLocked();
+ void cleanupDisplayEventConnection(
+ const wp<DisplayEventConnection>& connection);
+
void debugFlashRegions();
void debugShowFPS() const;
void drawWormhole() const;
@@ -361,6 +368,7 @@ private:
GLuint mWormholeTexName;
GLuint mProtectedTexName;
nsecs_t mBootTime;
+ sp<EventThread> mEventThread;
// Can only accessed from the main thread, these members
// don't need synchronization
diff --git a/services/surfaceflinger/tests/vsync/Android.mk b/services/surfaceflinger/tests/vsync/Android.mk
new file mode 100644
index 0000000..9181760
--- /dev/null
+++ b/services/surfaceflinger/tests/vsync/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ vsync.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libbinder \
+ libui \
+ libgui
+
+LOCAL_MODULE:= test-vsync-events
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/vsync/vsync.cpp b/services/surfaceflinger/tests/vsync/vsync.cpp
new file mode 100644
index 0000000..4f79080
--- /dev/null
+++ b/services/surfaceflinger/tests/vsync/vsync.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 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/Looper.h>
+
+using namespace android;
+
+int receiver(int fd, int events, void* data)
+{
+ DisplayEventReceiver* q = (DisplayEventReceiver*)data;
+
+ ssize_t n;
+ DisplayEventReceiver::Event buffer[1];
+
+ static nsecs_t oldTimeStamp = 0;
+
+ while ((n = q->getEvents(buffer, 1)) > 0) {
+ for (int i=0 ; i<n ; i++) {
+ if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ printf("event vsync: count=%d\t", buffer[i].vsync.count);
+ }
+ if (oldTimeStamp) {
+ float t = float(buffer[i].header.timestamp - oldTimeStamp) / s2ns(1);
+ printf("%f ms (%f Hz)\n", t*1000, 1.0/t);
+ }
+ oldTimeStamp = buffer[i].header.timestamp;
+ }
+ }
+ if (n<0) {
+ printf("error reading events (%s)\n", strerror(-n));
+ }
+ return 1;
+}
+
+int main(int argc, char** argv)
+{
+ DisplayEventReceiver myDisplayEvent;
+
+
+ sp<Looper> loop = new Looper(false);
+ loop->addFd(myDisplayEvent.getFd(), 0, ALOOPER_EVENT_INPUT, receiver,
+ &myDisplayEvent);
+
+ do {
+ //printf("about to poll...\n");
+ int32_t ret = loop->pollOnce(-1);
+ switch (ret) {
+ case ALOOPER_POLL_WAKE:
+ //("ALOOPER_POLL_WAKE\n");
+ break;
+ case ALOOPER_POLL_CALLBACK:
+ //("ALOOPER_POLL_CALLBACK\n");
+ break;
+ case ALOOPER_POLL_TIMEOUT:
+ printf("ALOOPER_POLL_TIMEOUT\n");
+ break;
+ case ALOOPER_POLL_ERROR:
+ printf("ALOOPER_POLL_TIMEOUT\n");
+ break;
+ default:
+ printf("ugh? poll returned %d\n", ret);
+ break;
+ }
+ } while (1);
+
+ return 0;
+}
diff --git a/services/surfaceflinger/tests/waitforvsync/Android.mk b/services/surfaceflinger/tests/waitforvsync/Android.mk
new file mode 100644
index 0000000..c25f5ab
--- /dev/null
+++ b/services/surfaceflinger/tests/waitforvsync/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ waitforvsync.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+
+LOCAL_MODULE:= test-waitforvsync
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/waitforvsync/waitforvsync.cpp b/services/surfaceflinger/tests/waitforvsync/waitforvsync.cpp
new file mode 100644
index 0000000..279b88b
--- /dev/null
+++ b/services/surfaceflinger/tests/waitforvsync/waitforvsync.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 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 <stdint.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
+#endif
+
+int main(int argc, char** argv) {
+ int fd = open("/dev/graphics/fb0", O_RDWR);
+ if (fd >= 0) {
+ do {
+ uint32_t crt = 0;
+ int err = ioctl(fd, FBIO_WAITFORVSYNC, &crt);
+ if (err < 0) {
+ printf("FBIO_WAITFORVSYNC error: %s\n", strerror(errno));
+ break;
+ }
+ } while(1);
+ close(fd);
+ }
+ return 0;
+}