diff options
Diffstat (limited to 'services/surfaceflinger')
24 files changed, 1548 insertions, 622 deletions
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index f63c0c1..42e280f 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -2,36 +2,42 @@ 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\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +ifeq ($(TARGET_HAS_WAITFORVSYNC), true) + LOCAL_CFLAGS += -DHAS_WAITFORVSYNC +endif + ifeq ($(TARGET_BOARD_PLATFORM), omap3) LOCAL_CFLAGS += -DNO_RGBX_8888 endif ifeq ($(TARGET_BOARD_PLATFORM), omap4) LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY + LOCAL_CFLAGS += -DUSE_TRIPLE_BUFFERING endif ifeq ($(TARGET_BOARD_PLATFORM), s5pc110) LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE - LOCAL_CFLAGS += -DREFRESH_RATE=56 endif - LOCAL_SHARED_LIBRARIES := \ libcutils \ libhardware \ diff --git a/services/surfaceflinger/DisplayEventConnection.cpp b/services/surfaceflinger/DisplayEventConnection.cpp new file mode 100644 index 0000000..77ecbd2 --- /dev/null +++ b/services/surfaceflinger/DisplayEventConnection.cpp @@ -0,0 +1,71 @@ +/* + * 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" +#include "EventThread.h" + +// --------------------------------------------------------------------------- + +namespace android { + +// --------------------------------------------------------------------------- + +DisplayEventConnection::DisplayEventConnection( + const sp<EventThread>& eventThread) + : mEventThread(eventThread), mChannel(new BitTube()) +{ +} + +DisplayEventConnection::~DisplayEventConnection() { + mEventThread->unregisterDisplayEventConnection(this); +} + +void DisplayEventConnection::onFirstRef() { + // 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 diff --git a/services/surfaceflinger/DisplayEventConnection.h b/services/surfaceflinger/DisplayEventConnection.h new file mode 100644 index 0000000..cc3ee36 --- /dev/null +++ b/services/surfaceflinger/DisplayEventConnection.h @@ -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. + */ + +#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 EventThread; + +// --------------------------------------------------------------------------- + +class DisplayEventConnection : public BnDisplayEventConnection { +public: + DisplayEventConnection(const sp<EventThread>& flinger); + + status_t postEvent(const DisplayEventReceiver::Event& event); + +private: + virtual ~DisplayEventConnection(); + virtual void onFirstRef(); + virtual sp<BitTube> getDataChannel() const; + virtual void setVsyncRate(uint32_t count); + virtual void requestNextVsync(); // asynchronous + + sp<EventThread> const mEventThread; + 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 61096e5..986aec5 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,12 +349,52 @@ uint32_t DisplayHardware::getPageFlipCount() const { return mPageFlipCount; } -status_t DisplayHardware::compositionComplete() const { - return mNativeWindow->compositionComplete(); +// this needs to be thread safe +nsecs_t DisplayHardware::waitForRefresh() const { + nsecs_t timestamp; + if (mVSync.wait(×tamp) < 0) { + // vsync not supported! + usleep( getDelayToNextVSyncUs(×tamp) ); + } + mLastHwVSync = timestamp; // FIXME: Not thread safe + return timestamp; +} + +nsecs_t DisplayHardware::getRefreshTimestamp() const { + // this returns the last refresh timestamp. + // if the last one is not available, we estimate it based on + // the refresh period and whatever closest timestamp we have. + nsecs_t now = systemTime(); + return now - ((now - mLastHwVSync) % mRefreshPeriod); +} + +nsecs_t DisplayHardware::getRefreshPeriod() const { + return mRefreshPeriod; } -int DisplayHardware::getCurrentBufferIndex() const { - return mNativeWindow->getCurrentBufferIndex(); +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(); } void DisplayHardware::flip(const Region& dirty) const diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h index f02c954..02be4dc 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,11 @@ public: uint32_t getMaxTextureSize() const; uint32_t getMaxViewportDims() const; + // waits for the next vsync and returns the timestamp of when it happened + nsecs_t waitForRefresh() const; + nsecs_t getRefreshPeriod() const; + nsecs_t getRefreshTimestamp() const; + uint32_t getPageFlipCount() const; EGLDisplay getEGLDisplay() const { return mDisplay; } @@ -89,12 +95,10 @@ public: } inline Rect bounds() const { return getBounds(); } - // only for debugging - int getCurrentBufferIndex() const; - 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 +116,13 @@ private: mutable uint32_t mPageFlipCount; GLint mMaxViewportDims[2]; GLint mMaxTextureSize; - + VSyncBarrier mVSync; + + mutable Mutex mFakeVSyncMutex; + mutable nsecs_t mNextFakeVSync; + nsecs_t mRefreshPeriod; + mutable nsecs_t mLastHwVSync; + HWComposer* mHwc; sp<FramebufferNativeWindow> mNativeWindow; diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp index f4afeea..69f1aca 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp +++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2012 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. @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <assert.h> #include <errno.h> #include <stdlib.h> #include <stdio.h> @@ -22,15 +21,6 @@ #include <unistd.h> #include <fcntl.h> -#include <signal.h> -#include <termios.h> -#include <sys/ioctl.h> -#include <sys/mman.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/resource.h> - -#include <linux/unistd.h> #include <utils/Log.h> @@ -45,39 +35,22 @@ static char const * const kWakeFileName = "/sys/power/wait_for_fb_wake"; // ---------------------------------------------------------------------------- -DisplayHardwareBase::DisplayEventThreadBase::DisplayEventThreadBase( +DisplayHardwareBase::DisplayEventThread::DisplayEventThread( const sp<SurfaceFlinger>& flinger) : Thread(false), mFlinger(flinger) { } -DisplayHardwareBase::DisplayEventThreadBase::~DisplayEventThreadBase() { +DisplayHardwareBase::DisplayEventThread::~DisplayEventThread() { } -// ---------------------------------------------------------------------------- - -DisplayHardwareBase::DisplayEventThread::DisplayEventThread( - const sp<SurfaceFlinger>& flinger) - : DisplayEventThreadBase(flinger) -{ +status_t DisplayHardwareBase::DisplayEventThread::initCheck() const { + return ((access(kSleepFileName, R_OK) == 0 && + access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT; } -DisplayHardwareBase::DisplayEventThread::~DisplayEventThread() -{ -} +bool DisplayHardwareBase::DisplayEventThread::threadLoop() { -bool DisplayHardwareBase::DisplayEventThread::threadLoop() -{ - int err = 0; - char buf; - int fd; - - fd = open(kSleepFileName, O_RDONLY, 0); - do { - err = read(fd, &buf, 1); - } while (err < 0 && errno == EINTR); - close(fd); - ALOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); - if (err >= 0) { + if (waitForFbSleep() == NO_ERROR) { sp<SurfaceFlinger> flinger = mFlinger.promote(); ALOGD("About to give-up screen, flinger = %p", flinger.get()); if (flinger != 0) { @@ -85,39 +58,51 @@ bool DisplayHardwareBase::DisplayEventThread::threadLoop() flinger->screenReleased(0); mBarrier.wait(); } + if (waitForFbWake() == NO_ERROR) { + sp<SurfaceFlinger> flinger = mFlinger.promote(); + ALOGD("Screen about to return, flinger = %p", flinger.get()); + if (flinger != 0) { + flinger->screenAcquired(0); + } + return true; + } } - fd = open(kWakeFileName, O_RDONLY, 0); + + // error, exit the thread + return false; +} + +status_t DisplayHardwareBase::DisplayEventThread::waitForFbSleep() { + int err = 0; + char buf; + int fd = open(kSleepFileName, O_RDONLY, 0); + // if the file doesn't exist, the error will be caught in read() below do { - err = read(fd, &buf, 1); + err = read(fd, &buf, 1); } while (err < 0 && errno == EINTR); close(fd); - ALOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); - if (err >= 0) { - sp<SurfaceFlinger> flinger = mFlinger.promote(); - ALOGD("Screen about to return, flinger = %p", flinger.get()); - if (flinger != 0) - flinger->screenAcquired(0); - } - return true; + ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); + return err < 0 ? -errno : int(NO_ERROR); } -status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const -{ - mBarrier.open(); - return NO_ERROR; +status_t DisplayHardwareBase::DisplayEventThread::waitForFbWake() { + int err = 0; + char buf; + int fd = open(kWakeFileName, O_RDONLY, 0); + // if the file doesn't exist, the error will be caught in read() below + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + close(fd); + ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); + return err < 0 ? -errno : int(NO_ERROR); } -status_t DisplayHardwareBase::DisplayEventThread::readyToRun() -{ +status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const { + mBarrier.open(); return NO_ERROR; } -status_t DisplayHardwareBase::DisplayEventThread::initCheck() const -{ - return ((access(kSleepFileName, R_OK) == 0 && - access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT; -} - // ---------------------------------------------------------------------------- DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger, @@ -127,35 +112,35 @@ DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger, mDisplayEventThread = new DisplayEventThread(flinger); } -DisplayHardwareBase::~DisplayHardwareBase() -{ +void DisplayHardwareBase::startSleepManagement() const { + if (mDisplayEventThread->initCheck() == NO_ERROR) { + mDisplayEventThread->run("DisplayEventThread", PRIORITY_URGENT_DISPLAY); + } else { + ALOGW("/sys/power/wait_for_fb_{wake|sleep} don't exist"); + } +} + +DisplayHardwareBase::~DisplayHardwareBase() { // request exit mDisplayEventThread->requestExitAndWait(); } -bool DisplayHardwareBase::canDraw() const -{ +bool DisplayHardwareBase::canDraw() const { return mScreenAcquired; } -void DisplayHardwareBase::releaseScreen() const -{ +void DisplayHardwareBase::releaseScreen() const { status_t err = mDisplayEventThread->releaseScreen(); if (err >= 0) { mScreenAcquired = false; } } -void DisplayHardwareBase::acquireScreen() const -{ - status_t err = mDisplayEventThread->acquireScreen(); - if (err >= 0) { - mScreenAcquired = true; - } +void DisplayHardwareBase::acquireScreen() const { + mScreenAcquired = true; } -bool DisplayHardwareBase::isScreenAcquired() const -{ +bool DisplayHardwareBase::isScreenAcquired() const { return mScreenAcquired; } diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h index ef2df43..fba211b 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h +++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2012 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. @@ -20,8 +20,6 @@ #include <stdint.h> #include <utils/RefBase.h> #include <utils/threads.h> -#include <linux/kd.h> -#include <linux/vt.h> #include "Barrier.h" namespace android { @@ -31,11 +29,13 @@ class SurfaceFlinger; class DisplayHardwareBase { public: - DisplayHardwareBase( - const sp<SurfaceFlinger>& flinger, - uint32_t displayIndex); + DisplayHardwareBase( + const sp<SurfaceFlinger>& flinger, + uint32_t displayIndex); - ~DisplayHardwareBase(); + ~DisplayHardwareBase(); + + void startSleepManagement() const; // console management void releaseScreen() const; @@ -46,34 +46,21 @@ public: private: - class DisplayEventThreadBase : public Thread { - protected: + class DisplayEventThread : public Thread { wp<SurfaceFlinger> mFlinger; - public: - DisplayEventThreadBase(const sp<SurfaceFlinger>& flinger); - virtual ~DisplayEventThreadBase(); - virtual void onFirstRef() { - run("DisplayEventThread", PRIORITY_URGENT_DISPLAY); - } - virtual status_t acquireScreen() const { return NO_ERROR; }; - virtual status_t releaseScreen() const { return NO_ERROR; }; - virtual status_t initCheck() const = 0; - }; - - class DisplayEventThread : public DisplayEventThreadBase - { mutable Barrier mBarrier; + status_t waitForFbSleep(); + status_t waitForFbWake(); public: - DisplayEventThread(const sp<SurfaceFlinger>& flinger); + DisplayEventThread(const sp<SurfaceFlinger>& flinger); virtual ~DisplayEventThread(); virtual bool threadLoop(); - virtual status_t readyToRun(); - virtual status_t releaseScreen() const; - virtual status_t initCheck() const; + status_t releaseScreen() const; + status_t initCheck() const; }; - sp<DisplayEventThreadBase> mDisplayEventThread; - mutable int mScreenAcquired; + sp<DisplayEventThread> mDisplayEventThread; + mutable int mScreenAcquired; }; }; // namespace android 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..af0da0b --- /dev/null +++ b/services/surfaceflinger/EventThread.cpp @@ -0,0 +1,231 @@ +/* + * 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()), + mLastVSyncTimestamp(0), + mDeliveredEvents(0) +{ +} + +void EventThread::onFirstRef() { + run("EventThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE); +} + +sp<DisplayEventConnection> EventThread::createEventConnection() const { + return new DisplayEventConnection(const_cast<EventThread*>(this)); +} + +nsecs_t EventThread::getLastVSyncTimestamp() const { + Mutex::Autolock _l(mLock); + return mLastVSyncTimestamp; +} + +nsecs_t EventThread::getVSyncPeriod() const { + return mHw.getRefreshPeriod(); + +} + +status_t EventThread::registerDisplayEventConnection( + const sp<DisplayEventConnection>& connection) { + Mutex::Autolock _l(mLock); + ConnectionInfo info; + mDisplayEventConnections.add(connection, info); + mCondition.signal(); + return NO_ERROR; +} + +status_t EventThread::unregisterDisplayEventConnection( + const wp<DisplayEventConnection>& connection) { + Mutex::Autolock _l(mLock); + mDisplayEventConnections.removeItem(connection); + mCondition.signal(); + return NO_ERROR; +} + +void EventThread::removeDisplayEventConnection( + const wp<DisplayEventConnection>& connection) { + Mutex::Autolock _l(mLock); + 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) { + const int32_t new_count = (count == 0) ? -1 : count; + if (info->count != new_count) { + info->count = new_count; + mCondition.signal(); + } + } + } +} + +void EventThread::requestNextVsync( + const wp<DisplayEventConnection>& connection) { + Mutex::Autolock _l(mLock); + ConnectionInfo* info = getConnectionInfoLocked(connection); + if (info && info->count < 0) { + info->count = 0; + mCondition.signal(); + } +} + +bool EventThread::threadLoop() { + + nsecs_t timestamp; + DisplayEventReceiver::Event vsync; + Vector< wp<DisplayEventConnection> > displayEventConnections; + + { // scope for the lock + Mutex::Autolock _l(mLock); + do { + // see if we need to wait for the VSYNC at all + 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 >= 0) { + // at least one continuous mode or active one-shot event + waitForNextVsync = true; + break; + } + } + + if (waitForNextVsync) + break; + + mCondition.wait(mLock); + } while(true); + + // at least one listener requested VSYNC + mLock.unlock(); + timestamp = mHw.waitForRefresh(); + mLock.lock(); + mDeliveredEvents++; + mLastVSyncTimestamp = timestamp; + + // now see if we still need to report this VSYNC event + const size_t count = mDisplayEventConnections.size(); + for (size_t i=0 ; i<count ; i++) { + bool reportVsync = false; + const ConnectionInfo& info( + mDisplayEventConnections.valueAt(i)); + if (info.count >= 1) { + if (info.count==1 || (mDeliveredEvents % info.count) == 0) { + // continuous event, and time to report it + reportVsync = true; + } + } else if (info.count >= -1) { + ConnectionInfo& info( + mDisplayEventConnections.editValueAt(i)); + if (info.count == 0) { + // fired this time around + reportVsync = true; + } + info.count--; + } + if (reportVsync) { + displayEventConnections.add(mDisplayEventConnections.keyAt(i)); + } + } + } while (!displayEventConnections.size()); + + // dispatch vsync events to listeners... + vsync.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; + vsync.header.timestamp = timestamp; + vsync.vsync.count = mDeliveredEvents; + } + + const size_t count = displayEventConnections.size(); + for (size_t i=0 ; i<count ; i++) { + sp<DisplayEventConnection> conn(displayEventConnections[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. + removeDisplayEventConnection(displayEventConnections[i]); + } + } else { + // somehow the connection is dead, but we still have it in our list + // just clean the list. + removeDisplayEventConnection(displayEventConnections[i]); + } + } + + // clear all our references without holding mLock + displayEventConnections.clear(); + + return true; +} + +status_t EventThread::readyToRun() { + ALOGI("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..3a3071e --- /dev/null +++ b/services/surfaceflinger/EventThread.h @@ -0,0 +1,110 @@ +/* + * 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/KeyedVector.h> + +#include "DisplayEventConnection.h" + +// --------------------------------------------------------------------------- + +namespace android { + +// --------------------------------------------------------------------------- + +class SurfaceFlinger; +class DisplayHardware; +class DisplayEventConnection; + +// --------------------------------------------------------------------------- + +class EventThread : public Thread { + friend class DisplayEventConnection; + +public: + EventThread(const sp<SurfaceFlinger>& flinger); + + sp<DisplayEventConnection> createEventConnection() const; + + status_t registerDisplayEventConnection( + const sp<DisplayEventConnection>& connection); + + status_t unregisterDisplayEventConnection( + const wp<DisplayEventConnection>& connection); + + void setVsyncRate(uint32_t count, + const wp<DisplayEventConnection>& connection); + + void requestNextVsync(const wp<DisplayEventConnection>& connection); + + nsecs_t getLastVSyncTimestamp() const; + + nsecs_t getVSyncPeriod() const; + + void dump(String8& result, char* buffer, size_t SIZE) const; + +private: + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + + 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 + sp<SurfaceFlinger> mFlinger; + const DisplayHardware& mHw; + + mutable Mutex mLock; + mutable Condition mCondition; + + // protected by mLock + KeyedVector< wp<DisplayEventConnection>, ConnectionInfo > mDisplayEventConnections; + nsecs_t mLastVSyncTimestamp; + + // main thread only + size_t mDeliveredEvents; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif /* ANDROID_SURFACE_FLINGER_EVENT_THREAD_H */ diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index d4c4b1f..3e6b872 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -38,6 +38,7 @@ #include "Layer.h" #include "SurfaceFlinger.h" #include "SurfaceTextureLayer.h" +#include <math.h> #define DEBUG_RESIZE 0 @@ -54,6 +55,9 @@ Layer::Layer(SurfaceFlinger* flinger, mCurrentTransform(0), mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mCurrentOpacity(true), + mRefreshPending(0), + mFrameLatencyNeeded(false), + mFrameLatencyOffset(0), mFormat(PIXEL_FORMAT_NONE), mGLExtensions(GLExtensions::getInstance()), mOpaqueLayer(true), @@ -65,6 +69,17 @@ Layer::Layer(SurfaceFlinger* flinger, glGenTextures(1, &mTextureName); } +void Layer::onLayerDisplayed() { + if (mFrameLatencyNeeded) { + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + mFrameStats[mFrameLatencyOffset].timestamp = mSurfaceTexture->getTimestamp(); + mFrameStats[mFrameLatencyOffset].set = systemTime(); + mFrameStats[mFrameLatencyOffset].vsync = hw.getRefreshTimestamp(); + mFrameLatencyOffset = (mFrameLatencyOffset + 1) % 128; + mFrameLatencyNeeded = false; + } +} + void Layer::onFirstRef() { LayerBaseClient::onFirstRef(); @@ -83,7 +98,12 @@ void Layer::onFirstRef() mSurfaceTexture = new SurfaceTextureLayer(mTextureName, this); mSurfaceTexture->setFrameAvailableListener(new FrameQueuedListener(this)); mSurfaceTexture->setSynchronousMode(true); +#ifdef USE_TRIPLE_BUFFERING +#warning "using triple buffering" + mSurfaceTexture->setBufferCountServer(3); +#else mSurfaceTexture->setBufferCountServer(2); +#endif } Layer::~Layer() @@ -94,7 +114,7 @@ Layer::~Layer() void Layer::onFrameQueued() { android_atomic_inc(&mQueuedFrames); - mFlinger->signalEvent(); + mFlinger->signalLayerUpdate(); } // called with SurfaceFlinger::mStateLock as soon as the layer is entered @@ -388,16 +408,37 @@ bool Layer::isCropped() const { // pageflip handling... // ---------------------------------------------------------------------------- +bool Layer::onPreComposition() +{ + // if there was more than one pending update, request a refresh + if (mRefreshPending >= 2) { + mRefreshPending = 0; + return true; + } + mRefreshPending = 0; + return false; +} + void Layer::lockPageFlip(bool& recomputeVisibleRegions) { if (mQueuedFrames > 0) { + + // if we've already called updateTexImage() without going through + // a composition step, we have to skip this layer at this point + // because we cannot call updateTeximage() without a corresponding + // compositionComplete() call. + // we'll trigger an update in onPreComposition(). + if (mRefreshPending++) { + return; + } + // Capture the old state of the layer for comparisons later const bool oldOpacity = isOpaque(); sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer; // signal another event if we have more frames pending if (android_atomic_dec(&mQueuedFrames) > 1) { - mFlinger->signalEvent(); + mFlinger->signalLayerUpdate(); } if (mSurfaceTexture->updateTexImage() < NO_ERROR) { @@ -408,6 +449,7 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) // update the active buffer mActiveBuffer = mSurfaceTexture->getCurrentBuffer(); + mFrameLatencyNeeded = true; const Rect crop(mSurfaceTexture->getCurrentCrop()); const uint32_t transform(mSurfaceTexture->getCurrentTransform()); @@ -499,6 +541,10 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) void Layer::unlockPageFlip( const Transform& planeTransform, Region& outDirtyRegion) { + if (mRefreshPending >= 2) { + return; + } + Region dirtyRegion(mPostedDirtyRegion); if (!dirtyRegion.isEmpty()) { mPostedDirtyRegion.clear(); @@ -532,9 +578,9 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const snprintf(buffer, SIZE, " " "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X]," - " transform-hint=0x%02x, queued-frames=%d\n", + " transform-hint=0x%02x, queued-frames=%d, mRefreshPending=%d\n", mFormat, w0, h0, s0,f0, - getTransformHint(), mQueuedFrames); + getTransformHint(), mQueuedFrames, mRefreshPending); result.append(buffer); @@ -543,6 +589,32 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const } } +void Layer::dumpStats(String8& result, char* buffer, size_t SIZE) const +{ + LayerBaseClient::dumpStats(result, buffer, SIZE); + const size_t o = mFrameLatencyOffset; + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const nsecs_t period = hw.getRefreshPeriod(); + result.appendFormat("%lld\n", period); + for (size_t i=0 ; i<128 ; i++) { + const size_t index = (o+i) % 128; + const nsecs_t time_app = mFrameStats[index].timestamp; + const nsecs_t time_set = mFrameStats[index].set; + const nsecs_t time_vsync = mFrameStats[index].vsync; + result.appendFormat("%lld\t%lld\t%lld\n", + time_app, + time_vsync, + time_set); + } + result.append("\n"); +} + +void Layer::clearStats() +{ + LayerBaseClient::clearStats(); + memset(mFrameStats, 0, sizeof(mFrameStats)); +} + uint32_t Layer::getEffectiveUsage(uint32_t usage) const { // TODO: should we do something special if mSecure is set? diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 2b9471b..bf30608 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -34,6 +34,7 @@ #include "LayerBase.h" #include "SurfaceTextureLayer.h" #include "Transform.h" +#include <utils/Timers.h> namespace android { @@ -78,12 +79,17 @@ public: // LayerBaseClient interface virtual wp<IBinder> getSurfaceTextureBinder() const; + virtual void onLayerDisplayed(); + virtual bool onPreComposition(); + // only for debugging inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; } protected: virtual void onFirstRef(); virtual void dump(String8& result, char* scratch, size_t size) const; + virtual void dumpStats(String8& result, char* buffer, size_t SIZE) const; + virtual void clearStats(); private: friend class SurfaceTextureLayer; @@ -110,6 +116,19 @@ private: uint32_t mCurrentTransform; uint32_t mCurrentScalingMode; bool mCurrentOpacity; + size_t mRefreshPending; + bool mFrameLatencyNeeded; + int mFrameLatencyOffset; + + struct Statistics { + Statistics() : timestamp(0), set(0), vsync(0) { } + nsecs_t timestamp; // buffer timestamp + nsecs_t set; // buffer displayed timestamp + nsecs_t vsync; // vsync immediately before set + }; + + // protected by mLock + Statistics mFrameStats[128]; // constants PixelFormat mFormat; @@ -121,9 +140,6 @@ private: bool mSecure; // no screenshots bool mProtectedByApp; // application requires protected path to external sink Region mPostedDirtyRegion; - - // binder thread, transaction thread - mutable Mutex mLock; }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp index f04add1..e764001 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -47,8 +47,7 @@ LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) mOrientation(0), mPlaneOrientation(0), mTransactionFlags(0), - mPremultipliedAlpha(true), mName("unnamed"), mDebug(false), - mInvalidate(0) + mPremultipliedAlpha(true), mName("unnamed"), mDebug(false) { const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); mFlags = hw.getFlags(); @@ -240,7 +239,7 @@ void LayerBase::validateVisibility(const Transform& planeTransform) for (size_t i=0 ; i<4 ; i++) mVertices[i][1] = hw_h - mVertices[i][1]; - if (UNLIKELY(transformed)) { + if (CC_UNLIKELY(transformed)) { // NOTE: here we could also punt if we have too many rectangles // in the transparent region if (tr.preserveRects()) { @@ -262,23 +261,11 @@ void LayerBase::validateVisibility(const Transform& planeTransform) mTransformedBounds = tr.makeBounds(w, h); } -void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) -{ +void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) { } void LayerBase::unlockPageFlip( - const Transform& planeTransform, Region& outDirtyRegion) -{ - if ((android_atomic_and(~1, &mInvalidate)&1) == 1) { - outDirtyRegion.orSelf(visibleRegionScreen); - } -} - -void LayerBase::invalidate() -{ - if ((android_atomic_or(1, &mInvalidate)&1) == 0) { - mFlinger->signalEvent(); - } + const Transform& planeTransform, Region& outDirtyRegion) { } void LayerBase::drawRegion(const Region& reg) const @@ -416,7 +403,7 @@ void LayerBase::drawWithOpenGL(const Region& clip) const const State& s(drawingState()); GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; - if (UNLIKELY(s.alpha < 0xFF)) { + if (CC_UNLIKELY(s.alpha < 0xFF)) { const GLfloat alpha = s.alpha * (1.0f/255.0f); if (mPremultipliedAlpha) { glColor4f(alpha, alpha, alpha, alpha); @@ -471,13 +458,21 @@ void LayerBase::drawWithOpenGL(const Region& clip) const void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const { const Layer::State& s(drawingState()); + + snprintf(buffer, SIZE, + "+ %s %p (%s)\n", + getTypeId(), this, getName().string()); + result.append(buffer); + + s.transparentRegion.dump(result, "transparentRegion"); + transparentRegionScreen.dump(result, "transparentRegionScreen"); + visibleRegionScreen.dump(result, "visibleRegionScreen"); + snprintf(buffer, SIZE, - "+ %s %p (%s)\n" " " "z=%9d, pos=(%g,%g), size=(%4d,%4d), " "isOpaque=%1d, needsDithering=%1d, invalidate=%1d, " "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", - getTypeId(), this, getName().string(), s.z, s.transform.tx(), s.transform.ty(), s.w, s.h, isOpaque(), needsDithering(), contentDirty, s.alpha, s.flags, @@ -486,11 +481,15 @@ void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const result.append(buffer); } -void LayerBase::shortDump(String8& result, char* scratch, size_t size) const -{ +void LayerBase::shortDump(String8& result, char* scratch, size_t size) const { LayerBase::dump(result, scratch, size); } +void LayerBase::dumpStats(String8& result, char* scratch, size_t SIZE) const { +} + +void LayerBase::clearStats() { +} // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h index 7f62145..b8f7680 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -103,8 +103,6 @@ public: Rect visibleBounds() const; void drawRegion(const Region& reg) const; - void invalidate(); - virtual sp<LayerBaseClient> getLayerBaseClient() const { return 0; } virtual sp<Layer> getLayer() const { return 0; } @@ -204,11 +202,22 @@ public: /** called with the state lock when the surface is removed from the * current list */ - virtual void onRemoved() { }; - + virtual void onRemoved() { } + + /** called after page-flip + */ + virtual void onLayerDisplayed() { } + + /** called before composition. + * returns true if the layer has pending updates. + */ + virtual bool onPreComposition() { return false; } + /** always call base class first */ virtual void dump(String8& result, char* scratch, size_t size) const; virtual void shortDump(String8& result, char* scratch, size_t size) const; + virtual void dumpStats(String8& result, char* buffer, size_t SIZE) const; + virtual void clearStats(); enum { // flags for doTransaction() @@ -271,10 +280,6 @@ protected: mutable bool mDebug; - // atomic - volatile int32_t mInvalidate; - - public: // called from class SurfaceFlinger virtual ~LayerBase(); diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp index 9441019..290fff4 100644 --- a/services/surfaceflinger/MessageQueue.cpp +++ b/services/surfaceflinger/MessageQueue.cpp @@ -18,178 +18,146 @@ #include <errno.h> #include <sys/types.h> +#include <binder/IPCThreadState.h> + #include <utils/threads.h> #include <utils/Timers.h> #include <utils/Log.h> -#include <binder/IPCThreadState.h> + +#include <gui/IDisplayEventConnection.h> +#include <gui/BitTube.h> #include "MessageQueue.h" +#include "EventThread.h" +#include "SurfaceFlinger.h" 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; +MessageBase::MessageBase() + : MessageHandler() { +} + +MessageBase::~MessageBase() { +} + +void MessageBase::handleMessage(const Message&) { + this->handler(); + barrier.open(); +}; + +// --------------------------------------------------------------------------- + +void MessageQueue::Handler::signalRefresh() { + if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) { + mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH)); } - mList.insert(++end, node); } -void MessageList::remove(MessageList::LIST::iterator pos) -{ - mList.erase(pos); +void MessageQueue::Handler::signalInvalidate() { + if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) { + mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE)); + } +} + +void MessageQueue::Handler::handleMessage(const Message& message) { + switch (message.what) { + case INVALIDATE: + android_atomic_and(~eventMaskInvalidate, &mEventMask); + mQueue.mFlinger->onMessageReceived(message.what); + break; + case REFRESH: + android_atomic_and(~eventMaskRefresh, &mEventMask); + mQueue.mFlinger->onMessageReceived(message.what); + break; + } } // --------------------------------------------------------------------------- MessageQueue::MessageQueue() - : mInvalidate(false) { - mInvalidateMessage = new MessageBase(INVALIDATE); } -MessageQueue::~MessageQueue() +MessageQueue::~MessageQueue() { +} + +void MessageQueue::init(const sp<SurfaceFlinger>& flinger) { + mFlinger = flinger; + mLooper = new Looper(true); + mHandler = new Handler(*this); } -sp<MessageBase> MessageQueue::waitMessage(nsecs_t timeout) +void MessageQueue::setEventThread(const sp<EventThread>& eventThread) { - sp<MessageBase> result; + mEventThread = eventThread; + mEvents = eventThread->createEventConnection(); + mEventTube = mEvents->getDataChannel(); + mLooper->addFd(mEventTube->getFd(), 0, ALOOPER_EVENT_INPUT, + MessageQueue::cb_eventReceiver, this); +} - 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) { - //ALOGD("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 { - //ALOGD("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) - break; - - again = result->handler(); - if (again) { - // the message has been processed. release our reference to it - // without holding the lock. - result->notify(); - result = 0; + IPCThreadState::self()->flushCommands(); + int32_t ret = mLooper->pollOnce(-1); + switch (ret) { + case ALOOPER_POLL_WAKE: + case ALOOPER_POLL_CALLBACK: + continue; + case ALOOPER_POLL_ERROR: + ALOGE("ALOOPER_POLL_ERROR"); + case ALOOPER_POLL_TIMEOUT: + // timeout (should not happen) + continue; + default: + // should not happen + ALOGE("Looper::pollOnce() returned unknown status %d", ret); + continue; } - - } while (again); - - return result; + } while (true); } status_t MessageQueue::postMessage( - const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags) + const sp<MessageBase>& messageHandler, nsecs_t relTime) { - return queueMessage(message, relTime, flags); + const Message dummyMessage; + if (relTime > 0) { + mLooper->sendMessageDelayed(relTime, messageHandler, dummyMessage); + } else { + mLooper->sendMessage(messageHandler, dummyMessage); + } + return NO_ERROR; } -status_t MessageQueue::invalidate() { - Mutex::Autolock _l(mLock); - mInvalidate = true; - mCondition.signal(); - return NO_ERROR; +void MessageQueue::invalidate() { +// mHandler->signalInvalidate(); + mEvents->requestNextVsync(); } -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); - - //ALOGD("MessageQueue::queueMessage time = %lld ms", message->when); - //dumpLocked(message); - - mCondition.signal(); - return NO_ERROR; +void MessageQueue::refresh() { + mEvents->requestNextVsync(); } -void MessageQueue::dump(const sp<MessageBase>& message) -{ - Mutex::Autolock _l(mLock); - dumpLocked(message); +int MessageQueue::cb_eventReceiver(int fd, int events, void* data) { + MessageQueue* queue = reinterpret_cast<MessageQueue *>(data); + return queue->eventReceiver(fd, events); } -void MessageQueue::dumpLocked(const sp<MessageBase>& message) -{ - LIST::const_iterator cur(mMessages.begin()); - LIST::const_iterator end(mMessages.end()); - int c = 0; - while (cur != end) { - const char tick = (*cur == message) ? '>' : ' '; - ALOGD("%c %d: msg{.what=%08x, when=%lld}", - tick, c, (*cur)->what, (*cur)->when); - ++cur; - c++; +int MessageQueue::eventReceiver(int fd, int events) { + ssize_t n; + DisplayEventReceiver::Event buffer[8]; + while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) { + for (int i=0 ; i<n ; i++) { + if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { + mHandler->signalRefresh(); + break; + } + } } + return 1; } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h index 890f809..ea29e7e 100644 --- a/services/surfaceflinger/MessageQueue.h +++ b/services/surfaceflinger/MessageQueue.h @@ -23,100 +23,85 @@ #include <utils/threads.h> #include <utils/Timers.h> -#include <utils/List.h> +#include <utils/Looper.h> + +#include <gui/DisplayEventReceiver.h> #include "Barrier.h" 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 IDisplayEventConnection; +class EventThread; +class SurfaceFlinger; -// ============================================================================ +// --------------------------------------------------------------------------- -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 { + class Handler : public MessageHandler { + enum { + eventMaskInvalidate = 0x1, + eventMaskRefresh = 0x2 + }; + MessageQueue& mQueue; + int32_t mEventMask; + public: + Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) { } + virtual void handleMessage(const Message& message); + void signalRefresh(); + void signalInvalidate(); + }; - MessageQueue(); - ~MessageQueue(); + friend class Handler; + + sp<SurfaceFlinger> mFlinger; + sp<Looper> mLooper; + sp<EventThread> mEventThread; + sp<IDisplayEventConnection> mEvents; + sp<BitTube> mEventTube; + sp<Handler> mHandler; - // pre-defined messages + + static int cb_eventReceiver(int fd, int events, void* data); + int eventReceiver(int fd, int events); + +public: enum { - INVALIDATE = '_upd' + INVALIDATE = 0, + REFRESH = 1, }; - sp<MessageBase> waitMessage(nsecs_t timeout = -1); - - status_t postMessage(const sp<MessageBase>& message, - nsecs_t reltime=0, uint32_t flags = 0); - - status_t invalidate(); - - void dump(const sp<MessageBase>& message); + MessageQueue(); + ~MessageQueue(); + void init(const sp<SurfaceFlinger>& flinger); + void setEventThread(const sp<EventThread>& events); -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; + void waitMessage(); + status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime=0); + void invalidate(); + void refresh(); }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 98277b4..ab09bfa 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -34,18 +34,21 @@ #include <binder/MemoryHeapBase.h> #include <binder/PermissionCache.h> +#include <gui/IDisplayEventConnection.h> + #include <utils/String8.h> #include <utils/String16.h> #include <utils/StopWatch.h> #include <ui/GraphicBufferAllocator.h> -#include <ui/GraphicLog.h> #include <ui/PixelFormat.h> #include <pixelflinger/pixelflinger.h> #include <GLES/gl.h> #include "clz.h" +#include "DisplayEventConnection.h" +#include "EventThread.h" #include "GLExtensions.h" #include "DdmConnection.h" #include "Layer.h" @@ -56,15 +59,9 @@ #include "DisplayHardware/DisplayHardware.h" #include "DisplayHardware/HWComposer.h" +#include <private/android_filesystem_config.h> #include <private/surfaceflinger/SharedBufferStack.h> -/* ideally AID_GRAPHICS would be in a semi-public header - * or there would be a way to map a user/group name to its id - */ -#ifndef AID_GRAPHICS -#define AID_GRAPHICS 1003 -#endif - #define EGL_VERSION_HW_ANDROID 0x3143 #define DISPLAY_COUNT 1 @@ -128,11 +125,34 @@ void SurfaceFlinger::init() ALOGI_IF(mDebugDDMS, "DDMS debugging enabled"); } +void SurfaceFlinger::onFirstRef() +{ + mEventQueue.init(this); + + run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY); + + // Wait for the main thread to be done with its initialization + mReadyToRunBarrier.wait(); +} + + SurfaceFlinger::~SurfaceFlinger() { glDeleteTextures(1, &mWormholeTexName); } +void SurfaceFlinger::binderDied(const wp<IBinder>& who) +{ + // the window manager died on us. prepare its eulogy. + + // reset screen orientation + Vector<ComposerState> state; + setTransactionState(state, eOrientationDefault, 0); + + // restart the boot-animation + property_set("ctl.start", "bootanim"); +} + sp<IMemoryHeap> SurfaceFlinger::getCblk() const { return mServerHeap; @@ -186,25 +206,6 @@ void SurfaceFlinger::bootFinished() property_set("ctl.stop", "bootanim"); } -void SurfaceFlinger::binderDied(const wp<IBinder>& who) -{ - // the window manager died on us. prepare its eulogy. - - // reset screen orientation - setOrientation(0, eOrientationDefault, 0); - - // restart the boot-animation - property_set("ctl.start", "bootanim"); -} - -void SurfaceFlinger::onFirstRef() -{ - run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY); - - // Wait for the main thread to be done with its initialization - mReadyToRunBarrier.wait(); -} - static inline uint16_t pack565(int r, int g, int b) { return (r<<11)|(g<<5)|b; } @@ -295,12 +296,18 @@ 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); + mEventQueue.setEventThread(mEventThread); + hw.startSleepManagement(); /* * We're now ready to accept clients... */ + mReadyToRunBarrier.open(); + // start boot animation property_set("ctl.start", "bootanim"); @@ -308,29 +315,6 @@ status_t SurfaceFlinger::readyToRun() } // ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#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::signalEvent() { - mEventQueue.invalidate(); -} bool SurfaceFlinger::authenticateSurfaceTexture( const sp<ISurfaceTexture>& surfaceTexture) const { @@ -373,92 +357,121 @@ bool SurfaceFlinger::authenticateSurfaceTexture( return false; } +// ---------------------------------------------------------------------------- + +sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() { + return mEventThread->createEventConnection(); +} + +// ---------------------------------------------------------------------------- + +void SurfaceFlinger::waitForEvent() { + mEventQueue.waitMessage(); +} + +void SurfaceFlinger::signalTransaction() { + mEventQueue.invalidate(); +} + +void SurfaceFlinger::signalLayerUpdate() { + mEventQueue.invalidate(); +} + +void SurfaceFlinger::signalRefresh() { + mEventQueue.refresh(); +} + status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg, - nsecs_t reltime, uint32_t flags) -{ - return mEventQueue.postMessage(msg, reltime, flags); + 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, flags); + nsecs_t reltime, uint32_t flags) { + status_t res = mEventQueue.postMessage(msg, reltime); if (res == NO_ERROR) { msg->wait(); } return res; } -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Main loop -#endif - bool SurfaceFlinger::threadLoop() { waitForEvent(); + return true; +} - // check for transactions - if (UNLIKELY(mConsoleSignals)) { - handleConsoleEvents(); - } - - // if we're in a global transaction, don't do anything. - const uint32_t mask = eTransactionNeeded | eTraversalNeeded; - uint32_t transactionFlags = peekTransactionFlags(mask); - if (UNLIKELY(transactionFlags)) { - handleTransaction(transactionFlags); - } +void SurfaceFlinger::onMessageReceived(int32_t what) +{ + switch (what) { + case MessageQueue::REFRESH: { +// case MessageQueue::INVALIDATE: { + // check for transactions + if (CC_UNLIKELY(mConsoleSignals)) { + handleConsoleEvents(); + } - // post surfaces (if needed) - handlePageFlip(); + // if we're in a global transaction, don't do anything. + const uint32_t mask = eTransactionNeeded | eTraversalNeeded; + uint32_t transactionFlags = peekTransactionFlags(mask); + if (CC_UNLIKELY(transactionFlags)) { + handleTransaction(transactionFlags); + } - if (mDirtyRegion.isEmpty()) { - // nothing new to do. - return true; - } + // post surfaces (if needed) + handlePageFlip(); - if (UNLIKELY(mHwWorkListDirty)) { - // build the h/w work list - handleWorkList(); - } +// signalRefresh(); +// +// } break; +// +// case MessageQueue::REFRESH: { - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - if (LIKELY(hw.canDraw())) { - // repaint the framebuffer (if needed) + handleRefresh(); - const int index = hw.getCurrentBufferIndex(); - GraphicLog& logger(GraphicLog::getInstance()); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); - logger.log(GraphicLog::SF_REPAINT, index); - handleRepaint(); +// if (mDirtyRegion.isEmpty()) { +// return; +// } - // inform the h/w that we're done compositing - logger.log(GraphicLog::SF_COMPOSITION_COMPLETE, index); - hw.compositionComplete(); + if (CC_UNLIKELY(mHwWorkListDirty)) { + // build the h/w work list + handleWorkList(); + } - logger.log(GraphicLog::SF_SWAP_BUFFERS, index); - postFramebuffer(); + if (CC_LIKELY(hw.canDraw())) { + // repaint the framebuffer (if needed) + handleRepaint(); + // inform the h/w that we're done compositing + hw.compositionComplete(); + postFramebuffer(); + } else { + // pretend we did the post + hw.compositionComplete(); + } - logger.log(GraphicLog::SF_REPAINT_DONE, index); - } else { - // pretend we did the post - hw.compositionComplete(); - usleep(16667); // 60 fps period + } break; } - return true; } void SurfaceFlinger::postFramebuffer() { - // this should never happen. we do the flip anyways so we don't - // risk to cause a deadlock with hwc - ALOGW_IF(mSwapRegion.isEmpty(), "mSwapRegion is empty"); + // mSwapRegion can be empty here is some cases, for instance if a hidden + // or fully transparent window is updating. + // in that case, we need to flip anyways to not risk a deadlock with + // h/w composer. + const DisplayHardware& hw(graphicPlane(0).displayHardware()); const nsecs_t now = systemTime(); mDebugInSwapBuffers = now; hw.flip(mSwapRegion); + + size_t numLayers = mVisibleLayersSortedByZ.size(); + for (size_t i = 0; i < numLayers; i++) { + mVisibleLayersSortedByZ[i]->onLayerDisplayed(); + } + mLastSwapBufferTime = systemTime() - now; mDebugInSwapBuffers = 0; mSwapRegion.clear(); @@ -625,7 +638,7 @@ void SurfaceFlinger::computeVisibleRegions( // handle hidden surfaces by setting the visible region to empty - if (LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) { + if (CC_LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) { const bool translucent = !layer->isOpaque(); const Rect bounds(layer->visibleBounds()); visibleRegion.set(bounds); @@ -725,13 +738,13 @@ void SurfaceFlinger::commitTransaction() void SurfaceFlinger::handlePageFlip() { - bool visibleRegions = mVisibleRegionsDirty; + const DisplayHardware& hw = graphicPlane(0).displayHardware(); + const Region screenRegion(hw.bounds()); + const LayerVector& currentLayers(mDrawingState.layersSortedByZ); - visibleRegions |= lockPageFlip(currentLayers); + const bool visibleRegions = lockPageFlip(currentLayers); - const DisplayHardware& hw = graphicPlane(0).displayHardware(); - const Region screenRegion(hw.bounds()); - if (visibleRegions) { + if (visibleRegions || mVisibleRegionsDirty) { Region opaqueRegion; computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion); @@ -778,7 +791,7 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers) { const GraphicPlane& plane(graphicPlane(0)); const Transform& planeTransform(plane.transform()); - size_t count = currentLayers.size(); + const size_t count = currentLayers.size(); sp<LayerBase> const* layers = currentLayers.array(); for (size_t i=0 ; i<count ; i++) { const sp<LayerBase>& layer(layers[i]); @@ -786,6 +799,23 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers) } } +void SurfaceFlinger::handleRefresh() +{ + bool needInvalidate = false; + const LayerVector& currentLayers(mDrawingState.layersSortedByZ); + const size_t count = currentLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer(currentLayers[i]); + if (layer->onPreComposition()) { + needInvalidate = true; + } + } + if (needInvalidate) { + signalLayerUpdate(); + } +} + + void SurfaceFlinger::handleWorkList() { mHwWorkListDirty = false; @@ -810,7 +840,7 @@ void SurfaceFlinger::handleRepaint() // compute the invalid region mSwapRegion.orSelf(mDirtyRegion); - if (UNLIKELY(mDebugRegion)) { + if (CC_UNLIKELY(mDebugRegion)) { debugFlashRegions(); } @@ -964,7 +994,7 @@ void SurfaceFlinger::composeSurfaces(const Region& dirty) HWComposer& hwc(hw.getHwComposer()); const size_t fbLayerCount = hwc.getLayerCount(HWC_FRAMEBUFFER); - if (UNLIKELY(fbLayerCount && !mWormholeRegion.isEmpty())) { + if (CC_UNLIKELY(fbLayerCount && !mWormholeRegion.isEmpty())) { // should never happen unless the window manager has a bug // draw something... drawWormhole(); @@ -1049,7 +1079,7 @@ void SurfaceFlinger::drawWormhole() const const int32_t width = hw.getWidth(); const int32_t height = hw.getHeight(); - if (LIKELY(!mDebugBackground)) { + if (CC_LIKELY(!mDebugBackground)) { glClearColor(0,0,0,0); Region::const_iterator it = region.begin(); Region::const_iterator const end = region.end(); @@ -1093,23 +1123,6 @@ void SurfaceFlinger::drawWormhole() const } } -void SurfaceFlinger::debugShowFPS() const -{ - static int mFrameCount; - static int mLastFrameCount = 0; - static nsecs_t mLastFpsTime = 0; - static float mFps = 0; - mFrameCount++; - nsecs_t now = systemTime(); - nsecs_t diff = now - mLastFpsTime; - if (diff > ms2ns(250)) { - mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff; - mLastFpsTime = now; - mLastFrameCount = mFrameCount; - } - // XXX: mFPS has the value we want - } - status_t SurfaceFlinger::addLayer(const sp<LayerBase>& layer) { Mutex::Autolock _l(mStateLock); @@ -1200,7 +1213,7 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) { uint32_t old = android_atomic_or(flags, &mTransactionFlags); if ((old & flags)==0) { // wake the server up - signalEvent(); + signalTransaction(); } return old; } @@ -1250,26 +1263,6 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state, } } -int SurfaceFlinger::setOrientation(DisplayID dpy, - int orientation, uint32_t flags) -{ - if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) - return BAD_VALUE; - - Mutex::Autolock _l(mStateLock); - if (mCurrentState.orientation != orientation) { - if (uint32_t(orientation)<=eOrientation270 || orientation==42) { - mCurrentState.orientationFlags = flags; - mCurrentState.orientation = orientation; - setTransactionFlags(eTransactionNeeded); - mTransactionCV.wait(mStateLock); - } else { - orientation = BAD_VALUE; - } - } - return orientation; -} - sp<ISurface> SurfaceFlinger::createSurface( ISurfaceComposerClient::surface_data_t* params, const String8& name, @@ -1352,7 +1345,7 @@ sp<Layer> SurfaceFlinger::createNormalSurface( sp<Layer> layer = new Layer(this, display, client); status_t err = layer->setBuffers(w, h, format, flags); - if (LIKELY(err != NO_ERROR)) { + if (CC_LIKELY(err != NO_ERROR)) { ALOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err)); layer.clear(); } @@ -1471,14 +1464,14 @@ void SurfaceFlinger::screenReleased(int dpy) { // this may be called by a signal handler, we can't do too much in here android_atomic_or(eConsoleReleased, &mConsoleSignals); - signalEvent(); + signalTransaction(); } void SurfaceFlinger::screenAcquired(int dpy) { // this may be called by a signal handler, we can't do too much in here android_atomic_or(eConsoleAcquired, &mConsoleSignals); - signalEvent(); + signalTransaction(); } status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) @@ -1494,14 +1487,6 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) IPCThreadState::self()->getCallingUid()); result.append(buffer); } else { - - // figure out if we're stuck somewhere - const nsecs_t now = systemTime(); - const nsecs_t inSwapBuffers(mDebugInSwapBuffers); - const nsecs_t inTransaction(mDebugInTransaction); - nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0; - nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0; - // Try to get the main lock, but don't insist if we can't // (this would indicate SF is stuck, but we want to be able to // print something in dumpsys). @@ -1517,110 +1502,205 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) result.append(buffer); } - /* - * Dump the visible layer list - */ - const LayerVector& currentLayers = mCurrentState.layersSortedByZ; - const size_t count = currentLayers.size(); - snprintf(buffer, SIZE, "Visible layers (count = %d)\n", count); - result.append(buffer); - for (size_t i=0 ; i<count ; i++) { - const sp<LayerBase>& layer(currentLayers[i]); - layer->dump(result, buffer, SIZE); - const Layer::State& s(layer->drawingState()); - s.transparentRegion.dump(result, "transparentRegion"); - layer->transparentRegionScreen.dump(result, "transparentRegionScreen"); - layer->visibleRegionScreen.dump(result, "visibleRegionScreen"); - } - - /* - * Dump the layers in the purgatory - */ + bool dumpAll = true; + size_t index = 0; + size_t numArgs = args.size(); + if (numArgs) { + dumpAll = false; - const size_t purgatorySize = mLayerPurgatory.size(); - snprintf(buffer, SIZE, "Purgatory state (%d entries)\n", purgatorySize); - result.append(buffer); - for (size_t i=0 ; i<purgatorySize ; i++) { - const sp<LayerBase>& layer(mLayerPurgatory.itemAt(i)); - layer->shortDump(result, buffer, SIZE); - } + if ((index < numArgs) && + (args[index] == String16("--list"))) { + index++; + listLayersLocked(args, index, result, buffer, SIZE); + } - /* - * Dump SurfaceFlinger global state - */ + if ((index < numArgs) && + (args[index] == String16("--latency"))) { + index++; + dumpStatsLocked(args, index, result, buffer, SIZE); + } - snprintf(buffer, SIZE, "SurfaceFlinger global state:\n"); - result.append(buffer); + if ((index < numArgs) && + (args[index] == String16("--latency-clear"))) { + index++; + clearStatsLocked(args, index, result, buffer, SIZE); + } + } - const GLExtensions& extensions(GLExtensions::getInstance()); - snprintf(buffer, SIZE, "GLES: %s, %s, %s\n", - extensions.getVendor(), - extensions.getRenderer(), - extensions.getVersion()); - result.append(buffer); + if (dumpAll) { + dumpAllLocked(result, buffer, SIZE); + } - snprintf(buffer, SIZE, "EGL : %s\n", - eglQueryString(graphicPlane(0).getEGLDisplay(), - EGL_VERSION_HW_ANDROID)); - result.append(buffer); + if (locked) { + mStateLock.unlock(); + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} - snprintf(buffer, SIZE, "EXTS: %s\n", extensions.getExtension()); +void SurfaceFlinger::listLayersLocked(const Vector<String16>& args, size_t& index, + String8& result, char* buffer, size_t SIZE) const +{ + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer(currentLayers[i]); + snprintf(buffer, SIZE, "%s\n", layer->getName().string()); result.append(buffer); + } +} - mWormholeRegion.dump(result, "WormholeRegion"); - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - snprintf(buffer, SIZE, - " orientation=%d, canDraw=%d\n", - mCurrentState.orientation, hw.canDraw()); - result.append(buffer); - snprintf(buffer, SIZE, - " last eglSwapBuffers() time: %f us\n" - " last transaction time : %f us\n" - " refresh-rate : %f fps\n" - " x-dpi : %f\n" - " y-dpi : %f\n", - mLastSwapBufferTime/1000.0, - mLastTransactionTime/1000.0, - hw.getRefreshRate(), - hw.getDpiX(), - hw.getDpiY()); - result.append(buffer); +void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index, + String8& result, char* buffer, size_t SIZE) const +{ + String8 name; + if (index < args.size()) { + name = String8(args[index]); + index++; + } - if (inSwapBuffersDuration || !locked) { - snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n", - inSwapBuffersDuration/1000.0); + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer(currentLayers[i]); + if (name.isEmpty()) { + snprintf(buffer, SIZE, "%s\n", layer->getName().string()); result.append(buffer); } + if (name.isEmpty() || (name == layer->getName())) { + layer->dumpStats(result, buffer, SIZE); + } + } +} - if (inTransactionDuration || !locked) { - snprintf(buffer, SIZE, " transaction time: %f us\n", - inTransactionDuration/1000.0); - result.append(buffer); +void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index, + String8& result, char* buffer, size_t SIZE) const +{ + String8 name; + if (index < args.size()) { + name = String8(args[index]); + index++; + } + + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer(currentLayers[i]); + if (name.isEmpty() || (name == layer->getName())) { + layer->clearStats(); } + } +} - /* - * Dump HWComposer state - */ - HWComposer& hwc(hw.getHwComposer()); - snprintf(buffer, SIZE, " h/w composer %s and %s\n", - hwc.initCheck()==NO_ERROR ? "present" : "not present", - (mDebugDisableHWC || mDebugRegion) ? "disabled" : "enabled"); - result.append(buffer); - hwc.dump(result, buffer, SIZE, mVisibleLayersSortedByZ); +void SurfaceFlinger::dumpAllLocked( + String8& result, char* buffer, size_t SIZE) const +{ + // figure out if we're stuck somewhere + const nsecs_t now = systemTime(); + const nsecs_t inSwapBuffers(mDebugInSwapBuffers); + const nsecs_t inTransaction(mDebugInTransaction); + nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0; + nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0; - /* - * Dump gralloc state - */ - const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); - alloc.dump(result); - hw.dump(result); + /* + * Dump the visible layer list + */ + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + snprintf(buffer, SIZE, "Visible layers (count = %d)\n", count); + result.append(buffer); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer(currentLayers[i]); + layer->dump(result, buffer, SIZE); + } - if (locked) { - mStateLock.unlock(); - } + /* + * Dump the layers in the purgatory + */ + + const size_t purgatorySize = mLayerPurgatory.size(); + snprintf(buffer, SIZE, "Purgatory state (%d entries)\n", purgatorySize); + result.append(buffer); + for (size_t i=0 ; i<purgatorySize ; i++) { + const sp<LayerBase>& layer(mLayerPurgatory.itemAt(i)); + layer->shortDump(result, buffer, SIZE); } - write(fd, result.string(), result.size()); - return NO_ERROR; + + /* + * Dump SurfaceFlinger global state + */ + + snprintf(buffer, SIZE, "SurfaceFlinger global state:\n"); + result.append(buffer); + + const GLExtensions& extensions(GLExtensions::getInstance()); + snprintf(buffer, SIZE, "GLES: %s, %s, %s\n", + extensions.getVendor(), + extensions.getRenderer(), + extensions.getVersion()); + result.append(buffer); + + snprintf(buffer, SIZE, "EGL : %s\n", + eglQueryString(graphicPlane(0).getEGLDisplay(), + EGL_VERSION_HW_ANDROID)); + result.append(buffer); + + snprintf(buffer, SIZE, "EXTS: %s\n", extensions.getExtension()); + result.append(buffer); + + mWormholeRegion.dump(result, "WormholeRegion"); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + snprintf(buffer, SIZE, + " orientation=%d, canDraw=%d\n", + mCurrentState.orientation, hw.canDraw()); + result.append(buffer); + snprintf(buffer, SIZE, + " last eglSwapBuffers() time: %f us\n" + " last transaction time : %f us\n" + " transaction-flags : %08x\n" + " refresh-rate : %f fps\n" + " x-dpi : %f\n" + " y-dpi : %f\n", + mLastSwapBufferTime/1000.0, + mLastTransactionTime/1000.0, + mTransactionFlags, + hw.getRefreshRate(), + hw.getDpiX(), + hw.getDpiY()); + result.append(buffer); + + snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n", + inSwapBuffersDuration/1000.0); + result.append(buffer); + + snprintf(buffer, SIZE, " transaction time: %f us\n", + inTransactionDuration/1000.0); + result.append(buffer); + + /* + * 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"); + result.append(buffer); + hwc.dump(result, buffer, SIZE, mVisibleLayersSortedByZ); + + /* + * Dump gralloc state + */ + const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); + alloc.dump(result); + hw.dump(result); } status_t SurfaceFlinger::onTransact( @@ -1665,7 +1745,7 @@ status_t SurfaceFlinger::onTransact( status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags); if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { CHECK_INTERFACE(ISurfaceComposer, data, reply); - if (UNLIKELY(!PermissionCache::checkCallingPermission(sHardwareTest))) { + if (CC_UNLIKELY(!PermissionCache::checkCallingPermission(sHardwareTest))) { IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); @@ -1696,11 +1776,6 @@ status_t SurfaceFlinger::onTransact( setTransactionFlags(eTransactionNeeded|eTraversalNeeded); return NO_ERROR; } - case 1006:{ // enable/disable GraphicLog - int enabled = data.readInt32(); - GraphicLog::getInstance().setEnabled(enabled); - return NO_ERROR; - } case 1008: // toggle use of hw composer n = data.readInt32(); mDebugDisableHWC = n ? 1 : 0; @@ -1734,7 +1809,7 @@ void SurfaceFlinger::repaintEverything() { const DisplayHardware& hw(graphicPlane(0).displayHardware()); const Rect bounds(hw.getBounds()); setInvalidateRegion(Region(bounds)); - signalEvent(); + signalTransaction(); } void SurfaceFlinger::setInvalidateRegion(const Region& reg) { @@ -2210,7 +2285,7 @@ status_t SurfaceFlinger::turnElectronBeamOnImplLocked(int32_t mode) // make sure to redraw the whole screen when the animation is done mDirtyRegion.set(hw.bounds()); - signalEvent(); + signalTransaction(); return NO_ERROR; } @@ -2250,7 +2325,7 @@ status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy, status_t result = PERMISSION_DENIED; // only one display supported for now - if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) + if (CC_UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) return BAD_VALUE; if (!GLExtensions::getInstance().haveFramebufferObject()) @@ -2372,7 +2447,7 @@ status_t SurfaceFlinger::captureScreen(DisplayID dpy, uint32_t minLayerZ, uint32_t maxLayerZ) { // only one display supported for now - if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) + if (CC_UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) return BAD_VALUE; if (!GLExtensions::getInstance().haveFramebufferObject()) @@ -2500,7 +2575,7 @@ status_t Client::onTransact( const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); const int self_pid = getpid(); - if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) { + if (CC_UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) { // we're called from a different process, do the real check if (!PermissionCache::checkCallingPermission(sAccessSurfaceFlinger)) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 17b80a6..fcd9361 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -20,6 +20,8 @@ #include <stdint.h> #include <sys/types.h> +#include <cutils/compiler.h> + #include <utils/Atomic.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> @@ -46,14 +48,13 @@ namespace android { class Client; class DisplayHardware; +class DisplayEventConnection; +class EventThread; class Layer; class LayerDim; class LayerScreenshot; struct surface_flinger_cblk_t; -#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) -#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) - // --------------------------------------------------------------------------- class Client : public BnSurfaceComposerClient @@ -169,8 +170,8 @@ public: virtual void bootFinished(); virtual void setTransactionState(const Vector<ComposerState>& state, 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, @@ -189,6 +190,8 @@ public: status_t renderScreenToTextureLocked(DisplayID dpy, GLuint* textureName, GLfloat* uOut, GLfloat* vOut); + void onMessageReceived(int32_t what); + status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime=0, uint32_t flags = 0); @@ -222,6 +225,7 @@ private: private: friend class Client; + friend class DisplayEventConnection; friend class LayerBase; friend class LayerBaseClient; friend class Layer; @@ -281,7 +285,10 @@ private: public: // hack to work around gcc 4.0.3 bug const GraphicPlane& graphicPlane(int dpy) const; GraphicPlane& graphicPlane(int dpy); - void signalEvent(); + + void signalTransaction(); + void signalLayerUpdate(); + void signalRefresh(); void repaintEverything(); private: @@ -298,6 +305,7 @@ private: void handlePageFlip(); bool lockPageFlip(const LayerVector& currentLayers); void unlockPageFlip(const LayerVector& currentLayers); + void handleRefresh(); void handleWorkList(); void handleRepaint(); void postFramebuffer(); @@ -332,9 +340,15 @@ private: status_t electronBeamOnAnimationImplLocked(); void debugFlashRegions(); - void debugShowFPS() const; void drawWormhole() const; + void listLayersLocked(const Vector<String16>& args, size_t& index, + String8& result, char* buffer, size_t SIZE) const; + void dumpStatsLocked(const Vector<String16>& args, size_t& index, + String8& result, char* buffer, size_t SIZE) const; + void clearStatsLocked(const Vector<String16>& args, size_t& index, + String8& result, char* buffer, size_t SIZE) const; + void dumpAllLocked(String8& result, char* buffer, size_t SIZE) const; mutable MessageQueue mEventQueue; @@ -362,6 +376,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/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp index 259b937..49e8e63 100644 --- a/services/surfaceflinger/SurfaceTextureLayer.cpp +++ b/services/surfaceflinger/SurfaceTextureLayer.cpp @@ -94,6 +94,10 @@ status_t SurfaceTextureLayer::connect(int api, *outTransform = orientation; } switch(api) { + case NATIVE_WINDOW_API_CPU: + // SurfaceTextureClient supports only 2 buffers for CPU connections + this->setBufferCountServer(2); + break; case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: // Camera preview and videos are rate-limited on the producer 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..b0d54c4 --- /dev/null +++ b/services/surfaceflinger/tests/vsync/vsync.cpp @@ -0,0 +1,83 @@ +/* + * 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); + + myDisplayEvent.setVsyncRate(1); + + 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; +} |