From 478ae5eb5a0047e1b2988c896cff6363b455ee50 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 6 Dec 2011 17:22:19 -0800 Subject: [PATCH] Improve the VSYNC api a bit. - add the ability to set the vsync delivery rate, when the rate is set to N>1 (ie: receive every N vsync), SF process' is woken up for all of vsync, but clients only see the every N events. - add the concept of one-shot vsync events, with a call-back to request the next one. currently the call-back is a binder IPC. Change-Id: I09f71df0b0ba0d88ed997645e2e2497d553c9a1b --- include/gui/DisplayEventReceiver.h | 14 +++ include/gui/IDisplayEventConnection.h | 18 +++ libs/gui/DisplayEventReceiver.cpp | 20 ++++ libs/gui/IDisplayEventConnection.cpp | 25 +++++ .../surfaceflinger/DisplayEventConnection.cpp | 19 +++- .../surfaceflinger/DisplayEventConnection.h | 8 +- services/surfaceflinger/EventThread.cpp | 103 +++++++++++++++--- services/surfaceflinger/EventThread.h | 26 ++++- services/surfaceflinger/SurfaceFlinger.cpp | 8 +- services/surfaceflinger/SurfaceFlinger.h | 3 - 10 files changed, 209 insertions(+), 35 deletions(-) diff --git a/include/gui/DisplayEventReceiver.h b/include/gui/DisplayEventReceiver.h index 8d07c0e49..dccc1643c 100644 --- a/include/gui/DisplayEventReceiver.h +++ b/include/gui/DisplayEventReceiver.h @@ -94,6 +94,20 @@ public: */ ssize_t getEvents(Event* events, size_t count); + /* + * setVsyncRate() sets the Event::VSync delivery rate. A value of + * 1 returns every Event::VSync. A value of 2 returns every other event, + * etc... a value of 0 returns no event unless requestNextVsync() has + * been called. + */ + status_t setVsyncRate(uint32_t count); + + /* + * requestNextVsync() schedules the next Event::VSync. It has no effect + * if the vsync rate is > 0. + */ + status_t requestNextVsync(); + private: sp mEventConnection; sp mDataChannel; diff --git a/include/gui/IDisplayEventConnection.h b/include/gui/IDisplayEventConnection.h index 8728bb5ed..86247de62 100644 --- a/include/gui/IDisplayEventConnection.h +++ b/include/gui/IDisplayEventConnection.h @@ -33,9 +33,27 @@ class BitTube; class IDisplayEventConnection : public IInterface { public: + DECLARE_META_INTERFACE(DisplayEventConnection); + /* + * getDataChannel() returns a BitTube where to receive the events from + */ virtual sp getDataChannel() const = 0; + + /* + * setVsyncRate() sets the vsync event delivery rate. A value of + * 1 returns every vsync events. A value of 2 returns every other events, + * etc... a value of 0 returns no event unless requestNextVsync() has + * been called. + */ + virtual void setVsyncRate(uint32_t count) = 0; + + /* + * requestNextVsync() schedules the next vsync event. It has no effect + * if the vsync rate is > 0. + */ + virtual void requestNextVsync() = 0; // asynchronous }; // ---------------------------------------------------------------------------- diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index 3b29a113e..fee1febb9 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -58,6 +58,26 @@ int DisplayEventReceiver::getFd() const { return mDataChannel->getFd(); } +status_t DisplayEventReceiver::setVsyncRate(uint32_t count) { + if (int32_t(count) < 0) + return BAD_VALUE; + + if (mEventConnection != NULL) { + mEventConnection->setVsyncRate(count); + return NO_ERROR; + } + return NO_INIT; +} + +status_t DisplayEventReceiver::requestNextVsync() { + if (mEventConnection != NULL) { + mEventConnection->requestNextVsync(); + return NO_ERROR; + } + return NO_INIT; +} + + ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events, size_t count) { ssize_t size = mDataChannel->read(events, sizeof(events[0])*count); diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp index 44127fb4a..887d176b4 100644 --- a/libs/gui/IDisplayEventConnection.cpp +++ b/libs/gui/IDisplayEventConnection.cpp @@ -32,6 +32,8 @@ namespace android { enum { GET_DATA_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, + SET_VSYNC_RATE, + REQUEST_NEXT_VSYNC }; class BpDisplayEventConnection : public BpInterface @@ -49,6 +51,19 @@ public: remote()->transact(GET_DATA_CHANNEL, data, &reply); return new BitTube(reply); } + + virtual void setVsyncRate(uint32_t count) { + Parcel data, reply; + data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor()); + data.writeInt32(count); + remote()->transact(SET_VSYNC_RATE, data, &reply); + } + + virtual void requestNextVsync() { + Parcel data, reply; + data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor()); + remote()->transact(REQUEST_NEXT_VSYNC, data, &reply, IBinder::FLAG_ONEWAY); + } }; IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection"); @@ -65,6 +80,16 @@ status_t BnDisplayEventConnection::onTransact( channel->writeToParcel(reply); return NO_ERROR; } break; + case SET_VSYNC_RATE: { + CHECK_INTERFACE(IDisplayEventConnection, data, reply); + setVsyncRate(data.readInt32()); + return NO_ERROR; + } break; + case REQUEST_NEXT_VSYNC: { + CHECK_INTERFACE(IDisplayEventConnection, data, reply); + requestNextVsync(); + return NO_ERROR; + } break; } return BBinder::onTransact(code, data, reply, flags); } diff --git a/services/surfaceflinger/DisplayEventConnection.cpp b/services/surfaceflinger/DisplayEventConnection.cpp index a0aa9c019..77ecbd280 100644 --- a/services/surfaceflinger/DisplayEventConnection.cpp +++ b/services/surfaceflinger/DisplayEventConnection.cpp @@ -25,6 +25,7 @@ #include "SurfaceFlinger.h" #include "DisplayEventConnection.h" +#include "EventThread.h" // --------------------------------------------------------------------------- @@ -33,30 +34,38 @@ namespace android { // --------------------------------------------------------------------------- DisplayEventConnection::DisplayEventConnection( - const sp& flinger) - : mFlinger(flinger), mChannel(new BitTube()) + const sp& eventThread) + : mEventThread(eventThread), mChannel(new BitTube()) { } DisplayEventConnection::~DisplayEventConnection() { - mFlinger->cleanupDisplayEventConnection(this); + mEventThread->unregisterDisplayEventConnection(this); } void DisplayEventConnection::onFirstRef() { - // nothing to do here for now. + // NOTE: mEventThread doesn't hold a strong reference on us + mEventThread->registerDisplayEventConnection(this); } sp DisplayEventConnection::getDataChannel() const { return mChannel; } +void DisplayEventConnection::setVsyncRate(uint32_t count) { + mEventThread->setVsyncRate(count, this); +} + +void DisplayEventConnection::requestNextVsync() { + mEventThread->requestNextVsync(this); +} + 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 index 46cf64b4a..cc3ee3659 100644 --- a/services/surfaceflinger/DisplayEventConnection.h +++ b/services/surfaceflinger/DisplayEventConnection.h @@ -32,13 +32,13 @@ namespace android { // --------------------------------------------------------------------------- class BitTube; -class SurfaceFlinger; +class EventThread; // --------------------------------------------------------------------------- class DisplayEventConnection : public BnDisplayEventConnection { public: - DisplayEventConnection(const sp& flinger); + DisplayEventConnection(const sp& flinger); status_t postEvent(const DisplayEventReceiver::Event& event); @@ -46,8 +46,10 @@ private: virtual ~DisplayEventConnection(); virtual void onFirstRef(); virtual sp getDataChannel() const; + virtual void setVsyncRate(uint32_t count); + virtual void requestNextVsync(); // asynchronous - sp const mFlinger; + sp const mEventThread; sp const mChannel; }; diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp index 42477a917..dc39f883e 100644 --- a/services/surfaceflinger/EventThread.cpp +++ b/services/surfaceflinger/EventThread.cpp @@ -47,7 +47,8 @@ void EventThread::onFirstRef() { status_t EventThread::registerDisplayEventConnection( const sp& connection) { Mutex::Autolock _l(mLock); - mDisplayEventConnections.add(connection); + ConnectionInfo info; + mDisplayEventConnections.add(connection, info); mCondition.signal(); return NO_ERROR; } @@ -55,44 +56,97 @@ status_t EventThread::registerDisplayEventConnection( status_t EventThread::unregisterDisplayEventConnection( const wp& connection) { Mutex::Autolock _l(mLock); - mDisplayEventConnections.remove(connection); + mDisplayEventConnections.removeItem(connection); mCondition.signal(); return NO_ERROR; } -status_t EventThread::removeDisplayEventConnection( +void EventThread::removeDisplayEventConnection( const wp& connection) { Mutex::Autolock _l(mLock); - mDisplayEventConnections.remove(connection); - return NO_ERROR; + mDisplayEventConnections.removeItem(connection); +} + +EventThread::ConnectionInfo* EventThread::getConnectionInfoLocked( + const wp& connection) { + ssize_t index = mDisplayEventConnections.indexOfKey(connection); + if (index < 0) return NULL; + return &mDisplayEventConnections.editValueAt(index); +} + +void EventThread::setVsyncRate(uint32_t count, + const wp& connection) { + if (int32_t(count) >= 0) { // server must protect against bad params + Mutex::Autolock _l(mLock); + ConnectionInfo* info = getConnectionInfoLocked(connection); + if (info) { + info->count = (count == 0) ? -1 : count; + mCondition.signal(); + } + } +} + +void EventThread::requestNextVsync( + const wp& connection) { + Mutex::Autolock _l(mLock); + ConnectionInfo* info = getConnectionInfoLocked(connection); + if (info) { + if (info->count < 0) { + info->count = 0; + } + mCondition.signal(); + } } bool EventThread::threadLoop() { nsecs_t timestamp; DisplayEventReceiver::Event vsync; - SortedVector > displayEventConnections; + KeyedVector< wp, ConnectionInfo > displayEventConnections; { // scope for the lock Mutex::Autolock _l(mLock); do { // wait for listeners - while (!mDisplayEventConnections.size()) { + do { + bool waitForNextVsync = false; + size_t count = mDisplayEventConnections.size(); + for (size_t i=0 ; i= 1) { + // continuous mode + waitForNextVsync = true; + } else { + // one-shot event + if (info.count >= -1) { + ConnectionInfo& info( + mDisplayEventConnections.editValueAt(i)); + info.count--; + if (info.count == -1) { + // fired this time around + waitForNextVsync = true; + } + } + } + } + + if (waitForNextVsync) + break; + mCondition.wait(mLock); - } + } while(true); // wait for vsync mLock.unlock(); timestamp = mHw.waitForVSync(); mLock.lock(); + mDeliveredEvents++; // make sure we still have some listeners } while (!mDisplayEventConnections.size()); - // dispatch vsync events to listeners... - mDeliveredEvents++; - vsync.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; vsync.header.timestamp = timestamp; vsync.vsync.count = mDeliveredEvents; @@ -104,9 +158,30 @@ bool EventThread::threadLoop() { const size_t count = displayEventConnections.size(); for (size_t i=0 ; i conn(displayEventConnections.itemAt(i).promote()); + sp conn(displayEventConnections.keyAt(i).promote()); // make sure the connection didn't die if (conn != NULL) { + + const ConnectionInfo& info( + displayEventConnections.valueAt(i)); + + if ((info.count > 1) && (mDeliveredEvents % info.count)) { + // continuous event, but not time to send this event yet + continue; + } else if (info.count < -1) { + // disabled event + continue; + } else if (info.count == 0) { + // impossible by construction. but we prefer to be safe. + continue; + } + + // here, either: + // count = -1 : one-shot scheduled this time around + // count = 1 : continuous not rate-limited + // count > 1 : continuous, rate-limited + // Note: count == 0 is not possible by construction + status_t err = conn->postEvent(vsync); if (err == -EAGAIN || err == -EWOULDBLOCK) { // The destination doesn't accept events anymore, it's probably @@ -118,12 +193,12 @@ bool EventThread::threadLoop() { // 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. - removeDisplayEventConnection(displayEventConnections.itemAt(i)); + removeDisplayEventConnection(displayEventConnections.keyAt(i)); } } else { // somehow the connection is dead, but we still have it in our list // just clean the list. - removeDisplayEventConnection(displayEventConnections.itemAt(i)); + removeDisplayEventConnection(displayEventConnections.keyAt(i)); } } diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h index 4872c2b82..35bd299a8 100644 --- a/services/surfaceflinger/EventThread.h +++ b/services/surfaceflinger/EventThread.h @@ -24,7 +24,7 @@ #include #include -#include +#include #include "DisplayEventConnection.h" @@ -51,6 +51,11 @@ public: status_t unregisterDisplayEventConnection( const wp& connection); + void setVsyncRate(uint32_t count, + const wp& connection); + + void requestNextVsync(const wp& connection); + void dump(String8& result, char* buffer, size_t SIZE) const; private: @@ -58,7 +63,20 @@ private: virtual status_t readyToRun(); virtual void onFirstRef(); - status_t removeDisplayEventConnection( + struct ConnectionInfo { + ConnectionInfo() : count(-1) { } + + // count >= 1 : continuous event. count is the vsync rate + // count == 0 : one-shot event that has not fired + // count ==-1 : one-shot event that fired this round / disabled + // count ==-2 : one-shot event that fired the round before + int32_t count; + }; + + void removeDisplayEventConnection( + const wp& connection); + + ConnectionInfo* getConnectionInfoLocked( const wp& connection); // constants @@ -69,7 +87,9 @@ private: mutable Condition mCondition; // protected by mLock - SortedVector > mDisplayEventConnections; + KeyedVector< wp, ConnectionInfo > mDisplayEventConnections; + + // main thread only size_t mDeliveredEvents; }; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 58196d8f3..014c7e24a 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -389,16 +389,10 @@ bool SurfaceFlinger::authenticateSurfaceTexture( // ---------------------------------------------------------------------------- sp SurfaceFlinger::createDisplayEventConnection() { - sp result(new DisplayEventConnection(this)); - mEventThread->registerDisplayEventConnection(result); + sp result(new DisplayEventConnection(mEventThread)); return result; } -void SurfaceFlinger::cleanupDisplayEventConnection( - const wp& connection) { - mEventThread->unregisterDisplayEventConnection(connection); -} - // ---------------------------------------------------------------------------- #if 0 #pragma mark - diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index e6d2cd9eb..41caee35f 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -335,9 +335,6 @@ private: status_t electronBeamOffAnimationImplLocked(); status_t electronBeamOnAnimationImplLocked(); - void cleanupDisplayEventConnection( - const wp& connection); - void debugFlashRegions(); void debugShowFPS() const; void drawWormhole() const;