Merge changes I4e5ff00c,Id5e3ca1d,I97cbba61

* changes:
  SurfaceFlinger: Remove display freezing code
  SurfaceFlinger: add some layer update tests
  SurfaceFlinger: make sync transactions explicit
This commit is contained in:
Jamie Gennis 2011-10-17 11:31:39 -07:00 committed by Android (Google) Code Review
commit cb2de7182e
10 changed files with 334 additions and 189 deletions

View File

@ -83,7 +83,11 @@ public:
eOrientationUnchanged = 4,
eOrientationSwapMask = 0x01
};
enum {
eSynchronous = 0x01,
};
enum {
eElectronBeamAnimationOn = 0x01,
eElectronBeamAnimationOff = 0x10
@ -103,7 +107,7 @@ public:
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
virtual void setTransactionState(const Vector<ComposerState>& state,
int orientation) = 0;
int orientation, uint32_t flags) = 0;
/* signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
@ -142,8 +146,6 @@ public:
GET_CBLK,
SET_TRANSACTION_STATE,
SET_ORIENTATION,
FREEZE_DISPLAY,
UNFREEZE_DISPLAY,
CAPTURE_SCREEN,
TURN_ELECTRON_BEAM_OFF,
TURN_ELECTRON_BEAM_ON,

View File

@ -112,7 +112,7 @@ public:
static void openGlobalTransaction();
//! Close a composer transaction on all active SurfaceComposerClients.
static void closeGlobalTransaction();
static void closeGlobalTransaction(bool synchronous = false);
//! Freeze the specified display but not transactions.
static status_t freezeDisplay(DisplayID dpy, uint32_t flags = 0);

View File

@ -79,7 +79,7 @@ public:
}
virtual void setTransactionState(const Vector<ComposerState>& state,
int orientation)
int orientation, uint32_t flags)
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@ -90,6 +90,7 @@ public:
b->write(data);
}
data.writeInt32(orientation);
data.writeInt32(flags);
remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
}
@ -204,7 +205,8 @@ status_t BnSurfaceComposer::onTransact(
state.add(s);
}
int orientation = data.readInt32();
setTransactionState(state, orientation);
uint32_t flags = data.readInt32();
setTransactionState(state, orientation, flags);
} break;
case BOOT_FINISHED: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);

View File

@ -92,11 +92,14 @@ class Composer : public Singleton<Composer>
mutable Mutex mLock;
SortedVector<ComposerState> mStates;
int mOrientation;
uint32_t mForceSynchronous;
Composer() : Singleton<Composer>(),
mOrientation(ISurfaceComposer::eOrientationUnchanged) { }
mOrientation(ISurfaceComposer::eOrientationUnchanged),
mForceSynchronous(0)
{ }
void closeGlobalTransactionImpl();
void closeGlobalTransactionImpl(bool synchronous);
layer_state_t* getLayerStateLocked(
const sp<SurfaceComposerClient>& client, SurfaceID id);
@ -123,8 +126,8 @@ public:
uint32_t tint);
status_t setOrientation(int orientation);
static void closeGlobalTransaction() {
Composer::getInstance().closeGlobalTransactionImpl();
static void closeGlobalTransaction(bool synchronous) {
Composer::getInstance().closeGlobalTransactionImpl(synchronous);
}
};
@ -132,11 +135,12 @@ ANDROID_SINGLETON_STATIC_INSTANCE(Composer);
// ---------------------------------------------------------------------------
void Composer::closeGlobalTransactionImpl() {
void Composer::closeGlobalTransactionImpl(bool synchronous) {
sp<ISurfaceComposer> sm(getComposerService());
Vector<ComposerState> transaction;
int orientation;
uint32_t flags = 0;
{ // scope for the lock
Mutex::Autolock _l(mLock);
@ -145,9 +149,14 @@ void Composer::closeGlobalTransactionImpl() {
orientation = mOrientation;
mOrientation = ISurfaceComposer::eOrientationUnchanged;
if (synchronous || mForceSynchronous) {
flags |= ISurfaceComposer::eSynchronous;
}
mForceSynchronous = false;
}
sm->setTransactionState(transaction, orientation);
sm->setTransactionState(transaction, orientation, flags);
}
layer_state_t* Composer::getLayerStateLocked(
@ -188,6 +197,10 @@ status_t Composer::setSize(const sp<SurfaceComposerClient>& client,
s->what |= ISurfaceComposer::eSizeChanged;
s->w = w;
s->h = h;
// Resizing a surface makes the transaction synchronous.
mForceSynchronous = true;
return NO_ERROR;
}
@ -270,6 +283,10 @@ status_t Composer::setFreezeTint(const sp<SurfaceComposerClient>& client,
status_t Composer::setOrientation(int orientation) {
Mutex::Autolock _l(mLock);
mOrientation = orientation;
// Changing the orientation makes the transaction synchronous.
mForceSynchronous = true;
return NO_ERROR;
}
@ -375,8 +392,8 @@ void SurfaceComposerClient::openGlobalTransaction() {
// Currently a no-op
}
void SurfaceComposerClient::closeGlobalTransaction() {
Composer::closeGlobalTransaction();
void SurfaceComposerClient::closeGlobalTransaction(bool synchronous) {
Composer::closeGlobalTransaction(synchronous);
}
// ----------------------------------------------------------------------------

View File

@ -368,18 +368,6 @@ uint32_t Layer::doTransaction(uint32_t flags)
mCurrentScalingMode);
if (!isFixedSize()) {
// we're being resized and there is a freeze display request,
// acquire a freeze lock, so that the screen stays put
// until we've redrawn at the new size; this is to avoid
// glitches upon orientation changes.
if (mFlinger->hasFreezeRequest()) {
// if the surface is hidden, don't try to acquire the
// freeze lock, since hidden surfaces may never redraw
if (!(front.flags & ISurfaceComposer::eLayerHidden)) {
mFreezeLock = mFlinger->getFreezeLock();
}
}
// this will make sure LayerBase::doTransaction doesn't update
// the drawing state's size
Layer::State& editDraw(mDrawingState);
@ -393,14 +381,6 @@ uint32_t Layer::doTransaction(uint32_t flags)
temp.requested_h);
}
if (temp.sequence != front.sequence) {
if (temp.flags & ISurfaceComposer::eLayerHidden || temp.alpha == 0) {
// this surface is now hidden, so it shouldn't hold a freeze lock
// (it may never redraw, which is fine if it is hidden)
mFreezeLock.clear();
}
}
return LayerBase::doTransaction(flags);
}
@ -474,7 +454,7 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions)
glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// update the layer size and release freeze-lock
// update the layer size if needed
const Layer::State& front(drawingState());
// FIXME: mPostedDirtyRegion = dirty & bounds
@ -511,9 +491,6 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions)
// recompute visible region
recomputeVisibleRegions = true;
// we now have the correct size, unfreeze the screen
mFreezeLock.clear();
}
LOGD_IF(DEBUG_RESIZE,
@ -546,11 +523,6 @@ void Layer::unlockPageFlip(
dirtyRegion.andSelf(visibleRegionScreen);
outDirtyRegion.orSelf(dirtyRegion);
}
if (visibleRegionScreen.isEmpty()) {
// an invisible layer should not hold a freeze-lock
// (because it may never be updated and therefore never release it)
mFreezeLock.clear();
}
}
void Layer::dump(String8& result, char* buffer, size_t SIZE) const
@ -568,9 +540,9 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const
snprintf(buffer, SIZE,
" "
"format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
" freezeLock=%p, transform-hint=0x%02x, queued-frames=%d\n",
" transform-hint=0x%02x, queued-frames=%d\n",
mFormat, w0, h0, s0,f0,
getFreezeLock().get(), getTransformHint(), mQueuedFrames);
getTransformHint(), mQueuedFrames);
result.append(buffer);

View File

@ -39,7 +39,6 @@ namespace android {
// ---------------------------------------------------------------------------
class FreezeLock;
class Client;
class GLExtensions;
@ -80,7 +79,6 @@ public:
virtual wp<IBinder> getSurfaceTextureBinder() const;
// only for debugging
inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; }
inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; }
protected:
@ -124,9 +122,6 @@ private:
bool mProtectedByApp; // application requires protected path to external sink
Region mPostedDirtyRegion;
// page-flip thread and transaction thread (currently main thread)
sp<FreezeLock> mFreezeLock;
// binder thread, transaction thread
mutable Mutex mLock;
};

View File

@ -79,15 +79,12 @@ const String16 sDump("android.permission.DUMP");
SurfaceFlinger::SurfaceFlinger()
: BnSurfaceComposer(), Thread(false),
mTransactionFlags(0),
mResizeTransationPending(false),
mTransationPending(false),
mLayersRemoved(false),
mBootTime(systemTime()),
mVisibleRegionsDirty(false),
mHwWorkListDirty(false),
mFreezeDisplay(false),
mElectronBeamAnimationMode(0),
mFreezeCount(0),
mFreezeDisplayTime(0),
mDebugRegion(0),
mDebugBackground(0),
mDebugDDMS(0),
@ -190,11 +187,6 @@ void SurfaceFlinger::binderDied(const wp<IBinder>& who)
{
// the window manager died on us. prepare its eulogy.
// unfreeze the screen in case it was... frozen
mFreezeDisplayTime = 0;
mFreezeCount = 0;
mFreezeDisplay = false;
// reset screen orientation
setOrientation(0, eOrientationDefault, 0);
@ -322,33 +314,7 @@ void SurfaceFlinger::waitForEvent()
{
while (true) {
nsecs_t timeout = -1;
const nsecs_t freezeDisplayTimeout = ms2ns(5000);
if (UNLIKELY(isFrozen())) {
// wait 5 seconds
const nsecs_t now = systemTime();
if (mFreezeDisplayTime == 0) {
mFreezeDisplayTime = now;
}
nsecs_t waitTime = freezeDisplayTimeout - (now - mFreezeDisplayTime);
timeout = waitTime>0 ? waitTime : 0;
}
sp<MessageBase> msg = mEventQueue.waitMessage(timeout);
// see if we timed out
if (isFrozen()) {
const nsecs_t now = systemTime();
nsecs_t frozenTime = (now - mFreezeDisplayTime);
if (frozenTime >= freezeDisplayTimeout) {
// we timed out and are still frozen
LOGW("timeout expired mFreezeDisplay=%d, mFreezeCount=%d",
mFreezeDisplay, mFreezeCount);
mFreezeDisplayTime = 0;
mFreezeCount = 0;
mFreezeDisplay = false;
}
}
if (msg != 0) {
switch (msg->what) {
case MessageQueue::INVALIDATE:
@ -451,7 +417,7 @@ bool SurfaceFlinger::threadLoop()
}
const DisplayHardware& hw(graphicPlane(0).displayHardware());
if (LIKELY(hw.canDraw() && !isFrozen())) {
if (LIKELY(hw.canDraw())) {
// repaint the framebuffer (if needed)
const int index = hw.getCurrentBufferIndex();
@ -582,13 +548,6 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
mDirtyRegion.set(hw.bounds());
}
if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) {
// freezing or unfreezing the display -> trigger animation if needed
mFreezeDisplay = mCurrentState.freezeDisplay;
if (mFreezeDisplay)
mFreezeDisplayTime = 0;
}
if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) {
// layers have been added
mVisibleRegionsDirty = true;
@ -614,11 +573,6 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
commitTransaction();
}
sp<FreezeLock> SurfaceFlinger::getFreezeLock() const
{
return new FreezeLock(const_cast<SurfaceFlinger *>(this));
}
void SurfaceFlinger::computeVisibleRegions(
const LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion)
{
@ -748,7 +702,7 @@ void SurfaceFlinger::computeVisibleRegions(
void SurfaceFlinger::commitTransaction()
{
mDrawingState = mCurrentState;
mResizeTransationPending = false;
mTransationPending = false;
mTransactionCV.broadcast();
}
@ -1235,15 +1189,14 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags)
void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state,
int orientation) {
int orientation, uint32_t flags) {
Mutex::Autolock _l(mStateLock);
uint32_t flags = 0;
uint32_t transactionFlags = 0;
if (mCurrentState.orientation != orientation) {
if (uint32_t(orientation)<=eOrientation270 || orientation==42) {
mCurrentState.orientation = orientation;
flags |= eTransactionNeeded;
mResizeTransationPending = true;
transactionFlags |= eTransactionNeeded;
} else if (orientation != eOrientationUnchanged) {
LOGW("setTransactionState: ignoring unrecognized orientation: %d",
orientation);
@ -1254,56 +1207,29 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state,
for (size_t i=0 ; i<count ; i++) {
const ComposerState& s(state[i]);
sp<Client> client( static_cast<Client *>(s.client.get()) );
flags |= setClientStateLocked(client, s.state);
transactionFlags |= setClientStateLocked(client, s.state);
}
if (flags) {
setTransactionFlags(flags);
if (transactionFlags) {
setTransactionFlags(transactionFlags);
}
signalEvent();
// if there is a transaction with a resize, wait for it to
// take effect before returning.
while (mResizeTransationPending) {
// if this is a synchronous transaction, wait for it to take effect before
// returning.
if (flags & eSynchronous) {
mTransationPending = true;
}
while (mTransationPending) {
status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
if (CC_UNLIKELY(err != NO_ERROR)) {
// just in case something goes wrong in SF, return to the
// called after a few seconds.
LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!");
mResizeTransationPending = false;
mTransationPending = false;
break;
}
}
}
status_t SurfaceFlinger::freezeDisplay(DisplayID dpy, uint32_t flags)
{
if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
return BAD_VALUE;
Mutex::Autolock _l(mStateLock);
mCurrentState.freezeDisplay = 1;
setTransactionFlags(eTransactionNeeded);
// flags is intended to communicate some sort of animation behavior
// (for instance fading)
return NO_ERROR;
}
status_t SurfaceFlinger::unfreezeDisplay(DisplayID dpy, uint32_t flags)
{
if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
return BAD_VALUE;
Mutex::Autolock _l(mStateLock);
mCurrentState.freezeDisplay = 0;
setTransactionFlags(eTransactionNeeded);
// flags is intended to communicate some sort of animation behavior
// (for instance fading)
return NO_ERROR;
}
int SurfaceFlinger::setOrientation(DisplayID dpy,
int orientation, uint32_t flags)
{
@ -1489,7 +1415,6 @@ uint32_t SurfaceFlinger::setClientStateLocked(
if (what & eSizeChanged) {
if (layer->setSize(s.w, s.h)) {
flags |= eTraversalNeeded;
mResizeTransationPending = true;
}
}
if (what & eAlphaChanged) {
@ -1609,8 +1534,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
mWormholeRegion.dump(result, "WormholeRegion");
const DisplayHardware& hw(graphicPlane(0).displayHardware());
snprintf(buffer, SIZE,
" display frozen: %s, freezeCount=%d, orientation=%d, canDraw=%d\n",
mFreezeDisplay?"yes":"no", mFreezeCount,
" orientation=%d, canDraw=%d\n",
mCurrentState.orientation, hw.canDraw());
result.append(buffer);
snprintf(buffer, SIZE,
@ -1663,8 +1587,6 @@ status_t SurfaceFlinger::onTransact(
case CREATE_CONNECTION:
case SET_TRANSACTION_STATE:
case SET_ORIENTATION:
case FREEZE_DISPLAY:
case UNFREEZE_DISPLAY:
case BOOT_FINISHED:
case TURN_ELECTRON_BEAM_OFF:
case TURN_ELECTRON_BEAM_ON:
@ -1736,10 +1658,6 @@ status_t SurfaceFlinger::onTransact(
GraphicLog::getInstance().setEnabled(enabled);
return NO_ERROR;
}
case 1007: // set mFreezeCount
mFreezeCount = data.readInt32();
mFreezeDisplayTime = 0;
return NO_ERROR;
case 1008: // toggle use of hw composer
n = data.readInt32();
mDebugDisableHWC = n ? 1 : 0;

View File

@ -46,7 +46,6 @@ namespace android {
class Client;
class DisplayHardware;
class FreezeLock;
class Layer;
class LayerDim;
struct surface_flinger_cblk_t;
@ -168,9 +167,7 @@ public:
virtual sp<IMemoryHeap> getCblk() const;
virtual void bootFinished();
virtual void setTransactionState(const Vector<ComposerState>& state,
int orientation);
virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags);
virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags);
int orientation, uint32_t flags);
virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags);
virtual bool authenticateSurfaceTexture(const sp<ISurfaceTexture>& surface) const;
@ -244,12 +241,10 @@ private:
struct State {
State() {
orientation = ISurfaceComposer::eOrientationDefault;
freezeDisplay = 0;
}
LayerVector layersSortedByZ;
uint8_t orientation;
uint8_t orientationFlags;
uint8_t freezeDisplay;
};
virtual bool threadLoop();
@ -308,20 +303,6 @@ private:
status_t renderScreenToTextureLocked(DisplayID dpy,
GLuint* textureName, GLfloat* uOut, GLfloat* vOut);
friend class FreezeLock;
sp<FreezeLock> getFreezeLock() const;
inline void incFreezeCount() {
if (mFreezeCount == 0)
mFreezeDisplayTime = 0;
mFreezeCount++;
}
inline void decFreezeCount() { if (mFreezeCount > 0) mFreezeCount--; }
inline bool hasFreezeRequest() const { return mFreezeDisplay; }
inline bool isFrozen() const {
return (mFreezeDisplay || mFreezeCount>0) && mBootFinished;
}
void debugFlashRegions();
void debugShowFPS() const;
void drawWormhole() const;
@ -341,7 +322,7 @@ private:
volatile int32_t mTransactionFlags;
Condition mTransactionCV;
SortedVector< sp<LayerBase> > mLayerPurgatory;
bool mResizeTransationPending;
bool mTransationPending;
// protected by mStateLock (but we could use another lock)
GraphicPlane mGraphicPlanes[1];
@ -364,10 +345,7 @@ private:
Region mWormholeRegion;
bool mVisibleRegionsDirty;
bool mHwWorkListDirty;
bool mFreezeDisplay;
int32_t mElectronBeamAnimationMode;
int32_t mFreezeCount;
nsecs_t mFreezeDisplayTime;
Vector< sp<LayerBase> > mVisibleLayersSortedByZ;
@ -402,20 +380,6 @@ private:
volatile int32_t mSecureFrameBuffer;
};
// ---------------------------------------------------------------------------
class FreezeLock : public LightRefBase<FreezeLock> {
SurfaceFlinger* mFlinger;
public:
FreezeLock(SurfaceFlinger* flinger)
: mFlinger(flinger) {
mFlinger->incFreezeCount();
}
~FreezeLock() {
mFlinger->decFreezeCount();
}
};
// ---------------------------------------------------------------------------
}; // namespace android

View File

@ -1 +1,40 @@
include $(call all-subdir-makefiles)
# Build the unit tests,
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := SurfaceFlinger_test
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := \
Transaction_test.cpp \
LOCAL_SHARED_LIBRARIES := \
libEGL \
libGLESv2 \
libandroid \
libbinder \
libcutils \
libgui \
libstlport \
libui \
libutils \
LOCAL_C_INCLUDES := \
bionic \
bionic/libstdc++/include \
external/gtest/include \
external/stlport/stlport \
# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
# to integrate with auto-test framework.
include $(BUILD_NATIVE_TEST)
# Include subdirectory makefiles
# ============================================================
# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
# team really wants is to build the stuff defined by this makefile.
ifeq (,$(ONE_SHOT_MAKEFILE))
include $(call first-makefiles-under,$(LOCAL_PATH))
endif

View File

@ -0,0 +1,236 @@
/*
* 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 <gtest/gtest.h>
#include <binder/IMemory.h>
#include <surfaceflinger/ISurfaceComposer.h>
#include <surfaceflinger/Surface.h>
#include <surfaceflinger/SurfaceComposerClient.h>
#include <utils/String8.h>
namespace android {
// Fill an RGBA_8888 formatted surface with a single color.
static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc,
uint8_t r, uint8_t g, uint8_t b) {
Surface::SurfaceInfo info;
sp<Surface> s = sc->getSurface();
ASSERT_TRUE(s != NULL);
ASSERT_EQ(NO_ERROR, s->lock(&info));
uint8_t* img = reinterpret_cast<uint8_t*>(info.bits);
for (uint32_t y = 0; y < info.h; y++) {
for (uint32_t x = 0; x < info.w; x++) {
uint8_t* pixel = img + (4 * (y*info.s + x));
pixel[0] = r;
pixel[1] = g;
pixel[2] = b;
pixel[3] = 255;
}
}
ASSERT_EQ(NO_ERROR, s->unlockAndPost());
}
// A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check
// individual pixel values for testing purposes.
class ScreenCapture : public RefBase {
public:
static void captureScreen(sp<ScreenCapture>* sc) {
sp<IMemoryHeap> heap;
uint32_t w=0, h=0;
PixelFormat fmt=0;
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
ASSERT_EQ(NO_ERROR, sf->captureScreen(0, &heap, &w, &h, &fmt, 0, 0,
0, INT_MAX));
ASSERT_TRUE(heap != NULL);
ASSERT_EQ(PIXEL_FORMAT_RGBA_8888, fmt);
*sc = new ScreenCapture(w, h, heap);
}
void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
const uint8_t* img = reinterpret_cast<const uint8_t*>(mHeap->base());
const uint8_t* pixel = img + (4 * (y*mWidth + x));
if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
String8 err(String8::format("pixel @ (%3d, %3d): "
"expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
EXPECT_EQ(String8(), err);
}
}
private:
ScreenCapture(uint32_t w, uint32_t h, const sp<IMemoryHeap>& heap) :
mWidth(w),
mHeight(h),
mHeap(heap)
{}
const uint32_t mWidth;
const uint32_t mHeight;
sp<IMemoryHeap> mHeap;
};
class LayerUpdateTest : public ::testing::Test {
protected:
virtual void SetUp() {
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
ssize_t displayWidth = mComposerClient->getDisplayWidth(0);
ssize_t displayHeight = mComposerClient->getDisplayHeight(0);
// Background surface
mBGSurfaceControl = mComposerClient->createSurface(
String8("BG Test Surface"), 0, displayWidth, displayHeight,
PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mBGSurfaceControl != NULL);
ASSERT_TRUE(mBGSurfaceControl->isValid());
fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
// Foreground surface
mFGSurfaceControl = mComposerClient->createSurface(
String8("FG Test Surface"), 0, 64, 64, PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mFGSurfaceControl != NULL);
ASSERT_TRUE(mFGSurfaceControl->isValid());
fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
// Synchronization surface
mSyncSurfaceControl = mComposerClient->createSurface(
String8("Sync Test Surface"), 0, 1, 1, PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mSyncSurfaceControl != NULL);
ASSERT_TRUE(mSyncSurfaceControl->isValid());
fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
SurfaceComposerClient::openGlobalTransaction();
ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT_MAX-2));
ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX-1));
ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64));
ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setLayer(INT_MAX-1));
ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setPosition(displayWidth-2,
displayHeight-2));
ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->show());
SurfaceComposerClient::closeGlobalTransaction(true);
}
virtual void TearDown() {
mComposerClient->dispose();
mBGSurfaceControl = 0;
mFGSurfaceControl = 0;
mSyncSurfaceControl = 0;
mComposerClient = 0;
}
void waitForPostedBuffers() {
// Since the sync surface is in synchronous mode (i.e. double buffered)
// posting three buffers to it should ensure that at least two
// SurfaceFlinger::handlePageFlip calls have been made, which should
// guaranteed that a buffer posted to another Surface has been retired.
fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
}
sp<SurfaceComposerClient> mComposerClient;
sp<SurfaceControl> mBGSurfaceControl;
sp<SurfaceControl> mFGSurfaceControl;
// This surface is used to ensure that the buffers posted to
// mFGSurfaceControl have been picked up by SurfaceFlinger.
sp<SurfaceControl> mSyncSurfaceControl;
};
TEST_F(LayerUpdateTest, LayerMoveWorks) {
sp<ScreenCapture> sc;
{
SCOPED_TRACE("before move");
ScreenCapture::captureScreen(&sc);
sc->checkPixel( 0, 12, 63, 63, 195);
sc->checkPixel( 75, 75, 195, 63, 63);
sc->checkPixel(145, 145, 63, 63, 195);
}
SurfaceComposerClient::openGlobalTransaction();
ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128));
SurfaceComposerClient::closeGlobalTransaction(true);
{
// This should reflect the new position, but not the new color.
SCOPED_TRACE("after move, before redraw");
ScreenCapture::captureScreen(&sc);
sc->checkPixel( 24, 24, 63, 63, 195);
sc->checkPixel( 75, 75, 63, 63, 195);
sc->checkPixel(145, 145, 195, 63, 63);
}
fillSurfaceRGBA8(mFGSurfaceControl, 63, 195, 63);
waitForPostedBuffers();
{
// This should reflect the new position and the new color.
SCOPED_TRACE("after redraw");
ScreenCapture::captureScreen(&sc);
sc->checkPixel( 24, 24, 63, 63, 195);
sc->checkPixel( 75, 75, 63, 63, 195);
sc->checkPixel(145, 145, 63, 195, 63);
}
}
TEST_F(LayerUpdateTest, LayerResizeWorks) {
sp<ScreenCapture> sc;
{
SCOPED_TRACE("before resize");
ScreenCapture::captureScreen(&sc);
sc->checkPixel( 0, 12, 63, 63, 195);
sc->checkPixel( 75, 75, 195, 63, 63);
sc->checkPixel(145, 145, 63, 63, 195);
}
LOGD("resizing");
SurfaceComposerClient::openGlobalTransaction();
ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setSize(128, 128));
SurfaceComposerClient::closeGlobalTransaction(true);
LOGD("resized");
{
// This should not reflect the new size or color because SurfaceFlinger
// has not yet received a buffer of the correct size.
SCOPED_TRACE("after resize, before redraw");
ScreenCapture::captureScreen(&sc);
sc->checkPixel( 0, 12, 63, 63, 195);
sc->checkPixel( 75, 75, 195, 63, 63);
sc->checkPixel(145, 145, 63, 63, 195);
}
LOGD("drawing");
fillSurfaceRGBA8(mFGSurfaceControl, 63, 195, 63);
waitForPostedBuffers();
LOGD("drawn");
{
// This should reflect the new size and the new color.
SCOPED_TRACE("after redraw");
ScreenCapture::captureScreen(&sc);
sc->checkPixel( 24, 24, 63, 63, 195);
sc->checkPixel( 75, 75, 63, 195, 63);
sc->checkPixel(145, 145, 63, 195, 63);
}
}
}