OSDN Git Service

Improve the VSYNC api a bit.
authorMathias Agopian <mathias@google.com>
Wed, 7 Dec 2011 01:22:19 +0000 (17:22 -0800)
committerMathias Agopian <mathias@google.com>
Wed, 7 Dec 2011 06:43:10 +0000 (22:43 -0800)
- 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
include/gui/IDisplayEventConnection.h
libs/gui/DisplayEventReceiver.cpp
libs/gui/IDisplayEventConnection.cpp
services/surfaceflinger/DisplayEventConnection.cpp
services/surfaceflinger/DisplayEventConnection.h
services/surfaceflinger/EventThread.cpp
services/surfaceflinger/EventThread.h
services/surfaceflinger/SurfaceFlinger.cpp
services/surfaceflinger/SurfaceFlinger.h

index 8d07c0e..dccc164 100644 (file)
@@ -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<IDisplayEventConnection> mEventConnection;
     sp<BitTube> mDataChannel;
index 8728bb5..86247de 100644 (file)
@@ -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<BitTube> 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
 };
 
 // ----------------------------------------------------------------------------
index 3b29a11..fee1feb 100644 (file)
@@ -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);
index 44127fb..887d176 100644 (file)
@@ -32,6 +32,8 @@ namespace android {
 
 enum {
     GET_DATA_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
+    SET_VSYNC_RATE,
+    REQUEST_NEXT_VSYNC
 };
 
 class BpDisplayEventConnection : public BpInterface<IDisplayEventConnection>
@@ -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);
 }
index a0aa9c0..77ecbd2 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "SurfaceFlinger.h"
 #include "DisplayEventConnection.h"
+#include "EventThread.h"
 
 // ---------------------------------------------------------------------------
 
@@ -33,30 +34,38 @@ namespace android {
 // ---------------------------------------------------------------------------
 
 DisplayEventConnection::DisplayEventConnection(
-        const sp<SurfaceFlinger>& flinger)
-    : mFlinger(flinger), mChannel(new BitTube())
+        const sp<EventThread>& 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<BitTube> 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
index 46cf64b..cc3ee36 100644 (file)
@@ -32,13 +32,13 @@ namespace android {
 // ---------------------------------------------------------------------------
 
 class BitTube;
-class SurfaceFlinger;
+class EventThread;
 
 // ---------------------------------------------------------------------------
 
 class DisplayEventConnection : public BnDisplayEventConnection {
 public:
-    DisplayEventConnection(const sp<SurfaceFlinger>& flinger);
+    DisplayEventConnection(const sp<EventThread>& flinger);
 
     status_t postEvent(const DisplayEventReceiver::Event& event);
 
@@ -46,8 +46,10 @@ private:
     virtual ~DisplayEventConnection();
     virtual void onFirstRef();
     virtual sp<BitTube> getDataChannel() const;
+    virtual void setVsyncRate(uint32_t count);
+    virtual void requestNextVsync();    // asynchronous
 
-    sp<SurfaceFlinger> const mFlinger;
+    sp<EventThread> const mEventThread;
     sp<BitTube> const mChannel;
 };
 
index 42477a9..dc39f88 100644 (file)
@@ -47,7 +47,8 @@ void EventThread::onFirstRef() {
 status_t EventThread::registerDisplayEventConnection(
         const sp<DisplayEventConnection>& 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<DisplayEventConnection>& 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<DisplayEventConnection>& connection) {
     Mutex::Autolock _l(mLock);
-    mDisplayEventConnections.remove(connection);
-    return NO_ERROR;
+    mDisplayEventConnections.removeItem(connection);
+}
+
+EventThread::ConnectionInfo* EventThread::getConnectionInfoLocked(
+        const wp<DisplayEventConnection>& connection) {
+    ssize_t index = mDisplayEventConnections.indexOfKey(connection);
+    if (index < 0) return NULL;
+    return &mDisplayEventConnections.editValueAt(index);
+}
+
+void EventThread::setVsyncRate(uint32_t count,
+        const wp<DisplayEventConnection>& 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<DisplayEventConnection>& 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<wp<DisplayEventConnection> > displayEventConnections;
+    KeyedVector< wp<DisplayEventConnection>, 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<count ; i++) {
+                    const ConnectionInfo& info(
+                            mDisplayEventConnections.valueAt(i));
+                    if (info.count >= 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<count ; i++) {
-        sp<DisplayEventConnection> conn(displayEventConnections.itemAt(i).promote());
+        sp<DisplayEventConnection> 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));
         }
     }
 
index 4872c2b..35bd299 100644 (file)
@@ -24,7 +24,7 @@
 
 #include <utils/Errors.h>
 #include <utils/threads.h>
-#include <utils/SortedVector.h>
+#include <utils/KeyedVector.h>
 
 #include "DisplayEventConnection.h"
 
@@ -51,6 +51,11 @@ public:
     status_t unregisterDisplayEventConnection(
             const wp<DisplayEventConnection>& connection);
 
+    void setVsyncRate(uint32_t count,
+            const wp<DisplayEventConnection>& connection);
+
+    void requestNextVsync(const wp<DisplayEventConnection>& 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<DisplayEventConnection>& connection);
+
+    ConnectionInfo* getConnectionInfoLocked(
             const wp<DisplayEventConnection>& connection);
 
     // constants
@@ -69,7 +87,9 @@ private:
     mutable Condition mCondition;
 
     // protected by mLock
-    SortedVector<wp<DisplayEventConnection> > mDisplayEventConnections;
+    KeyedVector< wp<DisplayEventConnection>, ConnectionInfo > mDisplayEventConnections;
+
+    // main thread only
     size_t mDeliveredEvents;
 };
 
index 58196d8..014c7e2 100644 (file)
@@ -389,16 +389,10 @@ bool SurfaceFlinger::authenticateSurfaceTexture(
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
-    sp<DisplayEventConnection> result(new DisplayEventConnection(this));
-    mEventThread->registerDisplayEventConnection(result);
+    sp<DisplayEventConnection> result(new DisplayEventConnection(mEventThread));
     return result;
 }
 
-void SurfaceFlinger::cleanupDisplayEventConnection(
-        const wp<DisplayEventConnection>& connection) {
-    mEventThread->unregisterDisplayEventConnection(connection);
-}
-
 // ----------------------------------------------------------------------------
 #if 0
 #pragma mark -
index e6d2cd9..41caee3 100644 (file)
@@ -335,9 +335,6 @@ private:
             status_t electronBeamOffAnimationImplLocked();
             status_t electronBeamOnAnimationImplLocked();
 
-            void cleanupDisplayEventConnection(
-                    const wp<DisplayEventConnection>& connection);
-
             void        debugFlashRegions();
             void        debugShowFPS() const;
             void        drawWormhole() const;