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:
parent
9bbbcf8c65
commit
478ae5eb5a
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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 -
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user