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
This commit is contained in:
Mathias Agopian 2011-12-06 17:22:19 -08:00
parent 9bbbcf8c65
commit 478ae5eb5a
10 changed files with 209 additions and 35 deletions

View 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;

View 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
};
// ----------------------------------------------------------------------------

View 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);

View 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);
}

View 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

View 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;
};

View 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()) {
mCondition.wait(mLock);
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));
}
}

View 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;
};

View 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 -

View 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;