diff options
Diffstat (limited to 'libs')
45 files changed, 3279 insertions, 1361 deletions
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 7e416b9..6e83faa 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -371,11 +371,6 @@ int IPCThreadState::getCallingUid() return mCallingUid; } -int IPCThreadState::getOrigCallingUid() -{ - return mOrigCallingUid; -} - int64_t IPCThreadState::clearCallingIdentity() { int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; @@ -646,7 +641,6 @@ IPCThreadState::IPCThreadState() { pthread_setspecific(gTLS, this); clearCaller(); - mOrigCallingUid = mCallingUid; mIn.setDataCapacity(256); mOut.setDataCapacity(256); } @@ -998,7 +992,6 @@ status_t IPCThreadState::executeCommand(int32_t cmd) mCallingPid = tr.sender_pid; mCallingUid = tr.sender_euid; - mOrigCallingUid = tr.sender_euid; int curPrio = getpriority(PRIO_PROCESS, mMyThreadId); if (gDisableBackgroundScheduling) { @@ -1056,7 +1049,6 @@ status_t IPCThreadState::executeCommand(int32_t cmd) mCallingPid = origPid; mCallingUid = origUid; - mOrigCallingUid = origUid; IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); diff --git a/libs/binder/MemoryBase.cpp b/libs/binder/MemoryBase.cpp index 033066b..5c82330 100644 --- a/libs/binder/MemoryBase.cpp +++ b/libs/binder/MemoryBase.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define LOG_TAG "MemoryBase" #include <stdlib.h> #include <stdint.h> @@ -44,3 +45,11 @@ MemoryBase::~MemoryBase() // --------------------------------------------------------------------------- }; // namespace android + +// Backwards compatibility for libdatabase_sqlcipher (http://b/8253769). +extern "C" void _ZN7android10MemoryBaseC1ERKNS_2spINS_11IMemoryHeapEEEij(void*, void*, ssize_t, size_t); +extern "C" void _ZN7android10MemoryBaseC1ERKNS_2spINS_11IMemoryHeapEEElj(void* obj, void* h, long o, unsigned int size) { + _ZN7android10MemoryBaseC1ERKNS_2spINS_11IMemoryHeapEEEij(obj, h, o, size); + ALOGW("Using temporary compatibility workaround for usage of MemoryBase " + "private API. Please fix your application!"); +} diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 4c15913..c7bdcbc 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -627,11 +627,27 @@ status_t Parcel::writeFloat(float val) return writeAligned(val); } +#if defined(__mips__) && defined(__mips_hard_float) + +status_t Parcel::writeDouble(double val) +{ + union { + double d; + unsigned long long ll; + } u; + u.d = val; + return writeAligned(u.ll); +} + +#else + status_t Parcel::writeDouble(double val) { return writeAligned(val); } +#endif + status_t Parcel::writeIntPtr(intptr_t val) { return writeAligned(val); @@ -962,17 +978,44 @@ float Parcel::readFloat() const return readAligned<float>(); } +#if defined(__mips__) && defined(__mips_hard_float) + status_t Parcel::readDouble(double *pArg) const { - return readAligned(pArg); + union { + double d; + unsigned long long ll; + } u; + status_t status; + status = readAligned(&u.ll); + *pArg = u.d; + return status; +} + +double Parcel::readDouble() const +{ + union { + double d; + unsigned long long ll; + } u; + u.ll = readAligned<unsigned long long>(); + return u.d; } +#else + +status_t Parcel::readDouble(double *pArg) const +{ + return readAligned(pArg); +} double Parcel::readDouble() const { return readAligned<double>(); } +#endif + status_t Parcel::readIntPtr(intptr_t *pArg) const { return readAligned(pArg); @@ -1429,6 +1472,8 @@ status_t Parcel::continueWrite(size_t desired) if (objectsSize) { objects = (size_t*)malloc(objectsSize*sizeof(size_t)); if (!objects) { + free(data); + mError = NO_MEMORY; return NO_MEMORY; } @@ -1509,7 +1554,7 @@ status_t Parcel::continueWrite(size_t desired) mError = NO_MEMORY; return NO_MEMORY; } - + if(!(mDataCapacity == 0 && mObjects == NULL && mObjectsCapacity == 0)) { ALOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired); diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index 8224847..d970a33 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -4,6 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ BitTube.cpp \ BufferQueue.cpp \ + ConsumerBase.cpp \ DisplayEventReceiver.cpp \ IDisplayEventConnection.cpp \ ISensorEventConnection.cpp \ @@ -21,17 +22,19 @@ LOCAL_SRC_FILES:= \ LayerState.cpp \ Surface.cpp \ SurfaceComposerClient.cpp \ - DummyConsumer.cpp + DummyConsumer.cpp \ + CpuConsumer.cpp \ + BufferItemConsumer.cpp \ + GuiConfig.cpp LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libutils \ libbinder \ - libhardware \ - libhardware_legacy \ - libui \ + libcutils \ libEGL \ libGLESv2 \ + libsync \ + libui \ + libutils \ LOCAL_MODULE:= libgui @@ -42,13 +45,17 @@ endif ifeq ($(TARGET_BOARD_PLATFORM), s5pc110) LOCAL_CFLAGS += -DUSE_FENCE_SYNC endif +ifeq ($(TARGET_BOARD_PLATFORM), exynos5) + LOCAL_CFLAGS += -DUSE_NATIVE_FENCE_SYNC + LOCAL_CFLAGS += -DUSE_WAIT_SYNC +endif ifneq ($(filter generic%,$(TARGET_DEVICE)),) # Emulator build LOCAL_CFLAGS += -DUSE_FENCE_SYNC endif -ifeq ($(TARGET_BOARD_PLATFORM), tegra) - LOCAL_CFLAGS += -DALLOW_DEQUEUE_CURRENT_BUFFER +ifeq ($(TARGET_BOARD_PLATFORM), msm8960) + LOCAL_CFLAGS += -DUSE_NATIVE_FENCE_SYNC endif include $(BUILD_SHARED_LIBRARY) diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp new file mode 100644 index 0000000..5079883 --- /dev/null +++ b/libs/gui/BufferItemConsumer.cpp @@ -0,0 +1,96 @@ +/* + * 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. + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "BufferItemConsumer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <utils/Log.h> + +#include <gui/BufferItemConsumer.h> + +#define BI_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) +#define BI_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) +#define BI_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) +#define BI_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) +#define BI_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) + +namespace android { + +BufferItemConsumer::BufferItemConsumer(uint32_t consumerUsage, + int bufferCount, bool synchronousMode) : + ConsumerBase(new BufferQueue(true) ) +{ + mBufferQueue->setConsumerUsageBits(consumerUsage); + mBufferQueue->setSynchronousMode(synchronousMode); + mBufferQueue->setMaxAcquiredBufferCount(bufferCount); +} + +BufferItemConsumer::~BufferItemConsumer() { +} + +void BufferItemConsumer::setName(const String8& name) { + Mutex::Autolock _l(mMutex); + mName = name; + mBufferQueue->setConsumerName(name); +} + +status_t BufferItemConsumer::acquireBuffer(BufferItem *item, bool waitForFence) { + status_t err; + + if (!item) return BAD_VALUE; + + Mutex::Autolock _l(mMutex); + + err = acquireBufferLocked(item); + if (err != OK) { + if (err != NO_BUFFER_AVAILABLE) { + BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); + } + return err; + } + + if (waitForFence && item->mFence.get()) { + err = item->mFence->waitForever(1000, "BufferItemConsumer::acquireBuffer"); + if (err != OK) { + BI_LOGE("Failed to wait for fence of acquired buffer: %s (%d)", + strerror(-err), err); + return err; + } + } + + item->mGraphicBuffer = mSlots[item->mBuf].mGraphicBuffer; + + return OK; +} + +status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, + const sp<Fence>& releaseFence) { + status_t err; + + Mutex::Autolock _l(mMutex); + + err = addReleaseFenceLocked(item.mBuf, releaseFence); + + err = releaseBufferLocked(item.mBuf, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + if (err != OK) { + BI_LOGE("Failed to release buffer: %s (%d)", + strerror(-err), err); + } + return err; +} + +} // namespace android diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index a0774cf..086e298 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -32,23 +32,6 @@ #include <gui/SurfaceTexture.h> #include <utils/Trace.h> -// This compile option causes SurfaceTexture to return the buffer that is currently -// attached to the GL texture from dequeueBuffer when no other buffers are -// available. It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do -// implicit cross-process synchronization to prevent the buffer from being -// written to before the buffer has (a) been detached from the GL texture and -// (b) all GL reads from the buffer have completed. - -// During refactoring, do not support dequeuing the current buffer -#undef ALLOW_DEQUEUE_CURRENT_BUFFER - -#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER -#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true -#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled" -#else -#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false -#endif - // Macros for including the BufferQueue name in log messages #define ST_LOGV(x, ...) ALOGV("[%s] "x, mConsumerName.string(), ##__VA_ARGS__) #define ST_LOGD(x, ...) ALOGD("[%s] "x, mConsumerName.string(), ##__VA_ARGS__) @@ -81,23 +64,20 @@ static const char* scalingModeName(int scalingMode) { } } -BufferQueue::BufferQueue( bool allowSynchronousMode, int bufferCount ) : +BufferQueue::BufferQueue(bool allowSynchronousMode, + const sp<IGraphicBufferAlloc>& allocator) : mDefaultWidth(1), mDefaultHeight(1), - mPixelFormat(PIXEL_FORMAT_RGBA_8888), - mMinUndequeuedBuffers(bufferCount), - mMinAsyncBufferSlots(bufferCount + 1), - mMinSyncBufferSlots(bufferCount), - mBufferCount(mMinAsyncBufferSlots), - mClientBufferCount(0), - mServerBufferCount(mMinAsyncBufferSlots), + mMaxAcquiredBufferCount(1), + mDefaultMaxBufferCount(2), + mOverrideMaxBufferCount(0), mSynchronousMode(false), mAllowSynchronousMode(allowSynchronousMode), mConnectedApi(NO_CONNECTED_API), mAbandoned(false), mFrameCounter(0), mBufferHasBeenQueued(false), - mDefaultBufferFormat(0), + mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888), mConsumerUsageBits(0), mTransformHint(0) { @@ -105,10 +85,14 @@ BufferQueue::BufferQueue( bool allowSynchronousMode, int bufferCount ) : mConsumerName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); ST_LOGV("BufferQueue"); - sp<ISurfaceComposer> composer(ComposerService::getComposerService()); - mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); - if (mGraphicBufferAlloc == 0) { - ST_LOGE("createGraphicBufferAlloc() failed in BufferQueue()"); + if (allocator == NULL) { + sp<ISurfaceComposer> composer(ComposerService::getComposerService()); + mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); + if (mGraphicBufferAlloc == 0) { + ST_LOGE("createGraphicBufferAlloc() failed in BufferQueue()"); + } + } else { + mGraphicBufferAlloc = allocator; } } @@ -116,38 +100,13 @@ BufferQueue::~BufferQueue() { ST_LOGV("~BufferQueue"); } -status_t BufferQueue::setBufferCountServerLocked(int bufferCount) { - if (bufferCount > NUM_BUFFER_SLOTS) +status_t BufferQueue::setDefaultMaxBufferCountLocked(int count) { + if (count < 2 || count > NUM_BUFFER_SLOTS) return BAD_VALUE; - // special-case, nothing to do - if (bufferCount == mBufferCount) - return OK; - - if (!mClientBufferCount && - bufferCount >= mBufferCount) { - // easy, we just have more buffers - mBufferCount = bufferCount; - mServerBufferCount = bufferCount; - mDequeueCondition.broadcast(); - } else { - // we're here because we're either - // - reducing the number of available buffers - // - or there is a client-buffer-count in effect - - // less than 2 buffers is never allowed - if (bufferCount < 2) - return BAD_VALUE; - - // when there is non client-buffer-count in effect, the client is not - // allowed to dequeue more than one buffer at a time, - // so the next time they dequeue a buffer, we know that they don't - // own one. the actual resizing will happen during the next - // dequeueBuffer. + mDefaultMaxBufferCount = count; + mDequeueCondition.broadcast(); - mServerBufferCount = bufferCount; - mDequeueCondition.broadcast(); - } return OK; } @@ -174,6 +133,7 @@ status_t BufferQueue::setConsumerUsageBits(uint32_t usage) { } status_t BufferQueue::setTransformHint(uint32_t hint) { + ST_LOGV("setTransformHint: %02x", hint); Mutex::Autolock lock(mMutex); mTransformHint = hint; return OK; @@ -196,20 +156,19 @@ status_t BufferQueue::setBufferCount(int bufferCount) { } // Error out if the user has dequeued buffers - for (int i=0 ; i<mBufferCount ; i++) { + int maxBufferCount = getMaxBufferCountLocked(); + for (int i=0 ; i<maxBufferCount; i++) { if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { ST_LOGE("setBufferCount: client owns some buffers"); return -EINVAL; } } - const int minBufferSlots = mSynchronousMode ? - mMinSyncBufferSlots : mMinAsyncBufferSlots; + const int minBufferSlots = getMinMaxBufferCountLocked(); if (bufferCount == 0) { - mClientBufferCount = 0; - bufferCount = (mServerBufferCount >= minBufferSlots) ? - mServerBufferCount : minBufferSlots; - return setBufferCountServerLocked(bufferCount); + mOverrideMaxBufferCount = 0; + mDequeueCondition.broadcast(); + return OK; } if (bufferCount < minBufferSlots) { @@ -220,11 +179,11 @@ status_t BufferQueue::setBufferCount(int bufferCount) { // here we're guaranteed that the client doesn't have dequeued buffers // and will release all of its buffer references. + // + // XXX: Should this use drainQueueAndFreeBuffersLocked instead? freeAllBuffersLocked(); - mBufferCount = bufferCount; - mClientBufferCount = bufferCount; + mOverrideMaxBufferCount = bufferCount; mBufferHasBeenQueued = false; - mQueue.clear(); mDequeueCondition.broadcast(); listener = mConsumerListener; } // scope for lock @@ -255,11 +214,10 @@ int BufferQueue::query(int what, int* outValue) value = mDefaultHeight; break; case NATIVE_WINDOW_FORMAT: - value = mPixelFormat; + value = mDefaultBufferFormat; break; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: - value = mSynchronousMode ? - (mMinUndequeuedBuffers-1) : mMinUndequeuedBuffers; + value = getMinUndequeuedBufferCountLocked(); break; case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: value = (mQueue.size() >= 2); @@ -279,9 +237,17 @@ status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) { ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!"); return NO_INIT; } - if (slot < 0 || mBufferCount <= slot) { + int maxBufferCount = getMaxBufferCountLocked(); + if (slot < 0 || maxBufferCount <= slot) { ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d", - mBufferCount, slot); + maxBufferCount, slot); + return BAD_VALUE; + } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { + // XXX: I vaguely recall there was some reason this can be valid, but + // for the life of me I can't recall under what circumstances that's + // the case. + ST_LOGE("requestBuffer: slot %d is not owned by the client (state=%d)", + slot, mSlots[slot].mBufferState); return BAD_VALUE; } mSlots[slot].mRequestBufferCalled = true; @@ -289,8 +255,8 @@ status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) { return NO_ERROR; } -status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, - uint32_t format, uint32_t usage) { +status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { ATRACE_CALL(); ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage); @@ -301,7 +267,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, status_t returnFlags(OK); EGLDisplay dpy = EGL_NO_DISPLAY; - EGLSyncKHR fence = EGL_NO_SYNC_KHR; + EGLSyncKHR eglFence = EGL_NO_SYNC_KHR; { // Scope for the lock Mutex::Autolock lock(mMutex); @@ -313,7 +279,6 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, usage |= mConsumerUsageBits; int found = -1; - int foundSync = -1; int dequeuedCount = 0; bool tryAgain = true; while (tryAgain) { @@ -322,104 +287,67 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, return NO_INIT; } - // We need to wait for the FIFO to drain if the number of buffer - // needs to change. - // - // The condition "number of buffers needs to change" is true if - // - the client doesn't care about how many buffers there are - // - AND the actual number of buffer is different from what was - // set in the last setBufferCountServer() - // - OR - - // setBufferCountServer() was set to a value incompatible with - // the synchronization mode (for instance because the sync mode - // changed since) - // - // As long as this condition is true AND the FIFO is not empty, we - // wait on mDequeueCondition. - - const int minBufferCountNeeded = mSynchronousMode ? - mMinSyncBufferSlots : mMinAsyncBufferSlots; - - const bool numberOfBuffersNeedsToChange = !mClientBufferCount && - ((mServerBufferCount != mBufferCount) || - (mServerBufferCount < minBufferCountNeeded)); - - if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { - // wait for the FIFO to drain - mDequeueCondition.wait(mMutex); - // NOTE: we continue here because we need to reevaluate our - // whole state (eg: we could be abandoned or disconnected) - continue; - } + const int maxBufferCount = getMaxBufferCountLocked(); - if (numberOfBuffersNeedsToChange) { - // here we're guaranteed that mQueue is empty - freeAllBuffersLocked(); - mBufferCount = mServerBufferCount; - if (mBufferCount < minBufferCountNeeded) - mBufferCount = minBufferCountNeeded; - mBufferHasBeenQueued = false; - returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; + // Free up any buffers that are in slots beyond the max buffer + // count. + for (int i = maxBufferCount; i < NUM_BUFFER_SLOTS; i++) { + assert(mSlots[i].mBufferState == BufferSlot::FREE); + if (mSlots[i].mGraphicBuffer != NULL) { + freeBufferLocked(i); + returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; + } } // look for a free buffer to give to the client found = INVALID_BUFFER_SLOT; - foundSync = INVALID_BUFFER_SLOT; dequeuedCount = 0; - for (int i = 0; i < mBufferCount; i++) { + for (int i = 0; i < maxBufferCount; i++) { const int state = mSlots[i].mBufferState; if (state == BufferSlot::DEQUEUED) { dequeuedCount++; } - // this logic used to be if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) - // but dequeuing the current buffer is disabled. - if (false) { - // This functionality has been temporarily removed so - // BufferQueue and SurfaceTexture can be refactored into - // separate objects - } else { - if (state == BufferSlot::FREE) { - /* We return the oldest of the free buffers to avoid - * stalling the producer if possible. This is because - * the consumer may still have pending reads of the - * buffers in flight. - */ - bool isOlder = mSlots[i].mFrameNumber < - mSlots[found].mFrameNumber; - if (found < 0 || isOlder) { - foundSync = i; - found = i; - } + if (state == BufferSlot::FREE) { + /* We return the oldest of the free buffers to avoid + * stalling the producer if possible. This is because + * the consumer may still have pending reads of the + * buffers in flight. + */ + if ((found < 0) || + mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) { + found = i; } } } // clients are not allowed to dequeue more than one buffer // if they didn't set a buffer count. - if (!mClientBufferCount && dequeuedCount) { + if (!mOverrideMaxBufferCount && dequeuedCount) { ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " "setting the buffer count"); return -EINVAL; } // See whether a buffer has been queued since the last - // setBufferCount so we know whether to perform the - // mMinUndequeuedBuffers check below. + // setBufferCount so we know whether to perform the min undequeued + // buffers check below. if (mBufferHasBeenQueued) { // make sure the client is not trying to dequeue more buffers // than allowed. - const int avail = mBufferCount - (dequeuedCount+1); - if (avail < (mMinUndequeuedBuffers-int(mSynchronousMode))) { - ST_LOGE("dequeueBuffer: mMinUndequeuedBuffers=%d exceeded " - "(dequeued=%d)", - mMinUndequeuedBuffers-int(mSynchronousMode), - dequeuedCount); + const int newUndequeuedCount = maxBufferCount - (dequeuedCount+1); + const int minUndequeuedCount = getMinUndequeuedBufferCountLocked(); + if (newUndequeuedCount < minUndequeuedCount) { + ST_LOGE("dequeueBuffer: min undequeued buffer count (%d) " + "exceeded (dequeued=%d undequeudCount=%d)", + minUndequeuedCount, dequeuedCount, + newUndequeuedCount); return -EBUSY; } } - // if no buffer is found, wait for a buffer to be released + // If no buffer is found, wait for a buffer to be released or for + // the max buffer count to change. tryAgain = found == INVALID_BUFFER_SLOT; if (tryAgain) { mDequeueCondition.wait(mMutex); @@ -445,12 +373,6 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, h = mDefaultHeight; } - const bool updateFormat = (format != 0); - if (!updateFormat) { - // keep the current (or default) format - format = mPixelFormat; - } - // buffer is now in DEQUEUED (but can also be current at the same time, // if we're in synchronous mode) mSlots[buf].mBufferState = BufferSlot::DEQUEUED; @@ -462,35 +384,49 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, (uint32_t(buffer->format) != format) || ((uint32_t(buffer->usage) & usage) != usage)) { - status_t error; - sp<GraphicBuffer> graphicBuffer( - mGraphicBufferAlloc->createGraphicBuffer( - w, h, format, usage, &error)); - if (graphicBuffer == 0) { - ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " - "failed"); - return error; - } - if (updateFormat) { - mPixelFormat = format; - } - mSlots[buf].mAcquireCalled = false; - mSlots[buf].mGraphicBuffer = graphicBuffer; + mSlots[buf].mGraphicBuffer = NULL; mSlots[buf].mRequestBufferCalled = false; - mSlots[buf].mFence = EGL_NO_SYNC_KHR; + mSlots[buf].mEglFence = EGL_NO_SYNC_KHR; + mSlots[buf].mFence.clear(); mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; } dpy = mSlots[buf].mEglDisplay; - fence = mSlots[buf].mFence; - mSlots[buf].mFence = EGL_NO_SYNC_KHR; + eglFence = mSlots[buf].mEglFence; + outFence = mSlots[buf].mFence; + mSlots[buf].mEglFence = EGL_NO_SYNC_KHR; + mSlots[buf].mFence.clear(); } // end lock scope - if (fence != EGL_NO_SYNC_KHR) { - EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + if (returnFlags & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) { + status_t error; + sp<GraphicBuffer> graphicBuffer( + mGraphicBufferAlloc->createGraphicBuffer( + w, h, format, usage, &error)); + if (graphicBuffer == 0) { + ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " + "failed"); + return error; + } + + { // Scope for the lock + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + + mSlots[*outBuf].mGraphicBuffer = graphicBuffer; + } + } + + + if (eglFence != EGL_NO_SYNC_KHR) { + EGLint result = eglClientWaitSyncKHR(dpy, eglFence, 0, 1000000000); // If something goes wrong, log the error, but return the buffer without // synchronizing access to it. It's too late at this point to abort the // dequeue operation. @@ -499,7 +435,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { ST_LOGE("dequeueBuffer: timeout waiting for fence"); } - eglDestroySyncKHR(dpy, fence); + eglDestroySyncKHR(dpy, eglFence); } ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, @@ -549,8 +485,9 @@ status_t BufferQueue::queueBuffer(int buf, uint32_t transform; int scalingMode; int64_t timestamp; + sp<Fence> fence; - input.deflate(×tamp, &crop, &scalingMode, &transform); + input.deflate(×tamp, &crop, &scalingMode, &transform, &fence); ST_LOGV("queueBuffer: slot=%d time=%#llx crop=[%d,%d,%d,%d] tr=%#x " "scale=%s", @@ -565,9 +502,10 @@ status_t BufferQueue::queueBuffer(int buf, ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!"); return NO_INIT; } - if (buf < 0 || buf >= mBufferCount) { + int maxBufferCount = getMaxBufferCountLocked(); + if (buf < 0 || buf >= maxBufferCount) { ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d", - mBufferCount, buf); + maxBufferCount, buf); return -EINVAL; } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { ST_LOGE("queueBuffer: slot %d is not owned by the client " @@ -617,6 +555,7 @@ status_t BufferQueue::queueBuffer(int buf, mSlots[buf].mTimestamp = timestamp; mSlots[buf].mCrop = crop; mSlots[buf].mTransform = transform; + mSlots[buf].mFence = fence; switch (scalingMode) { case NATIVE_WINDOW_SCALING_MODE_FREEZE: @@ -650,7 +589,7 @@ status_t BufferQueue::queueBuffer(int buf, return OK; } -void BufferQueue::cancelBuffer(int buf) { +void BufferQueue::cancelBuffer(int buf, sp<Fence> fence) { ATRACE_CALL(); ST_LOGV("cancelBuffer: slot=%d", buf); Mutex::Autolock lock(mMutex); @@ -660,9 +599,10 @@ void BufferQueue::cancelBuffer(int buf) { return; } - if (buf < 0 || buf >= mBufferCount) { + int maxBufferCount = getMaxBufferCountLocked(); + if (buf < 0 || buf >= maxBufferCount) { ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", - mBufferCount, buf); + maxBufferCount, buf); return; } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", @@ -671,6 +611,7 @@ void BufferQueue::cancelBuffer(int buf) { } mSlots[buf].mBufferState = BufferSlot::FREE; mSlots[buf].mFrameNumber = 0; + mSlots[buf].mFence = fence; mDequeueCondition.broadcast(); } @@ -781,11 +722,14 @@ void BufferQueue::dump(String8& result, const char* prefix, fifo.append(buffer); } + int maxBufferCount = getMaxBufferCountLocked(); + snprintf(buffer, SIZE, - "%s-BufferQueue mBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], " - "mPixelFormat=%d, FIFO(%d)={%s}\n", - prefix, mBufferCount, mSynchronousMode, mDefaultWidth, - mDefaultHeight, mPixelFormat, fifoSize, fifo.string()); + "%s-BufferQueue maxBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], " + "default-format=%d, transform-hint=%02x, FIFO(%d)={%s}\n", + prefix, maxBufferCount, mSynchronousMode, mDefaultWidth, + mDefaultHeight, mDefaultBufferFormat, mTransformHint, + fifoSize, fifo.string()); result.append(buffer); @@ -801,7 +745,7 @@ void BufferQueue::dump(String8& result, const char* prefix, } } stateName; - for (int i=0 ; i<mBufferCount ; i++) { + for (int i=0 ; i<maxBufferCount ; i++) { const BufferSlot& slot(mSlots[i]); snprintf(buffer, SIZE, "%s%s[%02d] " @@ -827,20 +771,22 @@ void BufferQueue::dump(String8& result, const char* prefix, } } -void BufferQueue::freeBufferLocked(int i) { - mSlots[i].mGraphicBuffer = 0; - if (mSlots[i].mBufferState == BufferSlot::ACQUIRED) { - mSlots[i].mNeedsCleanupOnRelease = true; +void BufferQueue::freeBufferLocked(int slot) { + ST_LOGV("freeBufferLocked: slot=%d", slot); + mSlots[slot].mGraphicBuffer = 0; + if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) { + mSlots[slot].mNeedsCleanupOnRelease = true; } - mSlots[i].mBufferState = BufferSlot::FREE; - mSlots[i].mFrameNumber = 0; - mSlots[i].mAcquireCalled = false; + mSlots[slot].mBufferState = BufferSlot::FREE; + mSlots[slot].mFrameNumber = 0; + mSlots[slot].mAcquireCalled = false; // destroy fence as BufferQueue now takes ownership - if (mSlots[i].mFence != EGL_NO_SYNC_KHR) { - eglDestroySyncKHR(mSlots[i].mEglDisplay, mSlots[i].mFence); - mSlots[i].mFence = EGL_NO_SYNC_KHR; + if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) { + eglDestroySyncKHR(mSlots[slot].mEglDisplay, mSlots[slot].mEglFence); + mSlots[slot].mEglFence = EGL_NO_SYNC_KHR; } + mSlots[slot].mFence.clear(); } void BufferQueue::freeAllBuffersLocked() { @@ -856,6 +802,23 @@ void BufferQueue::freeAllBuffersLocked() { status_t BufferQueue::acquireBuffer(BufferItem *buffer) { ATRACE_CALL(); Mutex::Autolock _l(mMutex); + + // Check that the consumer doesn't currently have the maximum number of + // buffers acquired. We allow the max buffer count to be exceeded by one + // buffer, so that the consumer can successfully set up the newly acquired + // buffer before releasing the old one. + int numAcquiredBuffers = 0; + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (mSlots[i].mBufferState == BufferSlot::ACQUIRED) { + numAcquiredBuffers++; + } + } + if (numAcquiredBuffers >= mMaxAcquiredBufferCount+1) { + ST_LOGE("acquireBuffer: max acquired buffer count reached: %d (max=%d)", + numAcquiredBuffers, mMaxAcquiredBufferCount); + return INVALID_OPERATION; + } + // check if queue is empty // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. @@ -876,9 +839,13 @@ status_t BufferQueue::acquireBuffer(BufferItem *buffer) { buffer->mFrameNumber = mSlots[buf].mFrameNumber; buffer->mTimestamp = mSlots[buf].mTimestamp; buffer->mBuf = buf; - mSlots[buf].mAcquireCalled = true; + buffer->mFence = mSlots[buf].mFence; + mSlots[buf].mAcquireCalled = true; + mSlots[buf].mNeedsCleanupOnRelease = false; mSlots[buf].mBufferState = BufferSlot::ACQUIRED; + mSlots[buf].mFence.clear(); + mQueue.erase(front); mDequeueCondition.broadcast(); @@ -891,7 +858,7 @@ status_t BufferQueue::acquireBuffer(BufferItem *buffer) { } status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display, - EGLSyncKHR fence) { + EGLSyncKHR eglFence, const sp<Fence>& fence) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(buf); @@ -902,6 +869,7 @@ status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display, } mSlots[buf].mEglDisplay = display; + mSlots[buf].mEglFence = eglFence; mSlots[buf].mFence = fence; // The buffer can now only be released if its in the acquired state @@ -987,10 +955,25 @@ status_t BufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h) return OK; } -status_t BufferQueue::setBufferCountServer(int bufferCount) { +status_t BufferQueue::setDefaultMaxBufferCount(int bufferCount) { ATRACE_CALL(); Mutex::Autolock lock(mMutex); - return setBufferCountServerLocked(bufferCount); + return setDefaultMaxBufferCountLocked(bufferCount); +} + +status_t BufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { + ATRACE_CALL(); + Mutex::Autolock lock(mMutex); + if (maxAcquiredBuffers < 1 || maxAcquiredBuffers > MAX_MAX_ACQUIRED_BUFFERS) { + ST_LOGE("setMaxAcquiredBufferCount: invalid count specified: %d", + maxAcquiredBuffers); + return BAD_VALUE; + } + if (mConnectedApi != NO_CONNECTED_API) { + return INVALID_OPERATION; + } + mMaxAcquiredBufferCount = maxAcquiredBuffers; + return OK; } void BufferQueue::freeAllBuffersExceptHeadLocked() { @@ -1034,6 +1017,41 @@ status_t BufferQueue::drainQueueAndFreeBuffersLocked() { return err; } +int BufferQueue::getMinMaxBufferCountLocked() const { + return getMinUndequeuedBufferCountLocked() + 1; +} + +int BufferQueue::getMinUndequeuedBufferCountLocked() const { + return mSynchronousMode ? mMaxAcquiredBufferCount : + mMaxAcquiredBufferCount + 1; +} + +int BufferQueue::getMaxBufferCountLocked() const { + int minMaxBufferCount = getMinMaxBufferCountLocked(); + + int maxBufferCount = mDefaultMaxBufferCount; + if (maxBufferCount < minMaxBufferCount) { + maxBufferCount = minMaxBufferCount; + } + if (mOverrideMaxBufferCount != 0) { + assert(mOverrideMaxBufferCount >= minMaxBufferCount); + maxBufferCount = mOverrideMaxBufferCount; + } + + // Any buffers that are dequeued by the producer or sitting in the queue + // waiting to be consumed need to have their slots preserved. Such + // buffers will temporarily keep the max buffer count up until the slots + // no longer need to be preserved. + for (int i = maxBufferCount; i < NUM_BUFFER_SLOTS; i++) { + BufferSlot::BufferState state = mSlots[i].mBufferState; + if (state == BufferSlot::QUEUED || state == BufferSlot::DEQUEUED) { + maxBufferCount = i + 1; + } + } + + return maxBufferCount; +} + BufferQueue::ProxyConsumerListener::ProxyConsumerListener( const wp<BufferQueue::ConsumerListener>& consumerListener): mConsumerListener(consumerListener) {} diff --git a/libs/gui/CleanSpec.mk b/libs/gui/CleanSpec.mk new file mode 100644 index 0000000..5a5144c --- /dev/null +++ b/libs/gui/CleanSpec.mk @@ -0,0 +1,52 @@ +# 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. +# 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. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ +$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "libgui*" -print0 | xargs -0 rm -f) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgui_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgui_intermediates) diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp new file mode 100644 index 0000000..624d7e0 --- /dev/null +++ b/libs/gui/ConsumerBase.cpp @@ -0,0 +1,236 @@ +/* + * 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. + */ + +#define LOG_TAG "ConsumerBase" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <hardware/hardware.h> + +#include <gui/IGraphicBufferAlloc.h> +#include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/ConsumerBase.h> + +#include <private/gui/ComposerService.h> + +#include <utils/Log.h> +#include <utils/String8.h> +#include <utils/Trace.h> + +// Macros for including the ConsumerBase name in log messages +#define CB_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CB_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CB_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CB_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CB_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) + +namespace android { + +// Get an ID that's unique within this process. +static int32_t createProcessUniqueId() { + static volatile int32_t globalCounter = 0; + return android_atomic_inc(&globalCounter); +} + +ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) : + mAbandoned(false), + mBufferQueue(bufferQueue) { + // Choose a name using the PID and a process-unique ID. + mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); + + // Note that we can't create an sp<...>(this) in a ctor that will not keep a + // reference once the ctor ends, as that would cause the refcount of 'this' + // dropping to 0 at the end of the ctor. Since all we need is a wp<...> + // that's what we create. + wp<BufferQueue::ConsumerListener> listener; + sp<BufferQueue::ConsumerListener> proxy; + listener = static_cast<BufferQueue::ConsumerListener*>(this); + proxy = new BufferQueue::ProxyConsumerListener(listener); + + status_t err = mBufferQueue->consumerConnect(proxy); + if (err != NO_ERROR) { + CB_LOGE("SurfaceTexture: error connecting to BufferQueue: %s (%d)", + strerror(-err), err); + } else { + mBufferQueue->setConsumerName(mName); + } +} + +ConsumerBase::~ConsumerBase() { + CB_LOGV("~ConsumerBase"); + abandon(); +} + +void ConsumerBase::freeBufferLocked(int slotIndex) { + CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); + mSlots[slotIndex].mGraphicBuffer = 0; + mSlots[slotIndex].mFence = 0; +} + +// Used for refactoring, should not be in final interface +sp<BufferQueue> ConsumerBase::getBufferQueue() const { + Mutex::Autolock lock(mMutex); + return mBufferQueue; +} + +void ConsumerBase::onFrameAvailable() { + CB_LOGV("onFrameAvailable"); + + sp<FrameAvailableListener> listener; + { // scope for the lock + Mutex::Autolock lock(mMutex); + listener = mFrameAvailableListener; + } + + if (listener != NULL) { + CB_LOGV("actually calling onFrameAvailable"); + listener->onFrameAvailable(); + } +} + +void ConsumerBase::onBuffersReleased() { + Mutex::Autolock lock(mMutex); + + CB_LOGV("onBuffersReleased"); + + if (mAbandoned) { + // Nothing to do if we're already abandoned. + return; + } + + uint32_t mask = 0; + mBufferQueue->getReleasedBuffers(&mask); + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + if (mask & (1 << i)) { + freeBufferLocked(i); + } + } +} + +void ConsumerBase::abandon() { + CB_LOGV("abandon"); + Mutex::Autolock lock(mMutex); + + if (!mAbandoned) { + abandonLocked(); + mAbandoned = true; + } +} + +void ConsumerBase::abandonLocked() { + CB_LOGV("abandonLocked"); + for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + freeBufferLocked(i); + } + // disconnect from the BufferQueue + mBufferQueue->consumerDisconnect(); + mBufferQueue.clear(); +} + +void ConsumerBase::setFrameAvailableListener( + const sp<FrameAvailableListener>& listener) { + CB_LOGV("setFrameAvailableListener"); + Mutex::Autolock lock(mMutex); + mFrameAvailableListener = listener; +} + +void ConsumerBase::dump(String8& result) const { + char buffer[1024]; + dump(result, "", buffer, 1024); +} + +void ConsumerBase::dump(String8& result, const char* prefix, + char* buffer, size_t size) const { + Mutex::Autolock _l(mMutex); + dumpLocked(result, prefix, buffer, size); +} + +void ConsumerBase::dumpLocked(String8& result, const char* prefix, + char* buffer, size_t SIZE) const { + snprintf(buffer, SIZE, "%smAbandoned=%d\n", prefix, int(mAbandoned)); + result.append(buffer); + + if (!mAbandoned) { + mBufferQueue->dump(result, prefix, buffer, SIZE); + } +} + +status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item) { + status_t err = mBufferQueue->acquireBuffer(item); + if (err != NO_ERROR) { + return err; + } + + if (item->mGraphicBuffer != NULL) { + mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer; + } + + mSlots[item->mBuf].mFence = item->mFence; + + CB_LOGV("acquireBufferLocked: -> slot=%d", item->mBuf); + + return OK; +} + +status_t ConsumerBase::addReleaseFence(int slot, const sp<Fence>& fence) { + Mutex::Autolock lock(mMutex); + return addReleaseFenceLocked(slot, fence); +} + +status_t ConsumerBase::addReleaseFenceLocked(int slot, const sp<Fence>& fence) { + CB_LOGV("addReleaseFenceLocked: slot=%d", slot); + + if (!mSlots[slot].mFence.get()) { + mSlots[slot].mFence = fence; + } else { + sp<Fence> mergedFence = Fence::merge( + String8::format("%.28s:%d", mName.string(), slot), + mSlots[slot].mFence, fence); + if (!mergedFence.get()) { + CB_LOGE("failed to merge release fences"); + // synchronization is broken, the best we can do is hope fences + // signal in order so the new fence will act like a union + mSlots[slot].mFence = fence; + return BAD_VALUE; + } + mSlots[slot].mFence = mergedFence; + } + + return OK; +} + +status_t ConsumerBase::releaseBufferLocked(int slot, EGLDisplay display, + EGLSyncKHR eglFence) { + CB_LOGV("releaseBufferLocked: slot=%d", slot); + status_t err = mBufferQueue->releaseBuffer(slot, display, eglFence, + mSlots[slot].mFence); + if (err == BufferQueue::STALE_BUFFER_SLOT) { + freeBufferLocked(slot); + } + + mSlots[slot].mFence.clear(); + + return err; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp new file mode 100644 index 0000000..710e1af --- /dev/null +++ b/libs/gui/CpuConsumer.cpp @@ -0,0 +1,159 @@ +/* + * 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. + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "CpuConsumer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <utils/Log.h> + +#include <gui/CpuConsumer.h> + +#define CC_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CC_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CC_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CC_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CC_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) + +namespace android { + +CpuConsumer::CpuConsumer(uint32_t maxLockedBuffers) : + ConsumerBase(new BufferQueue(true) ), + mMaxLockedBuffers(maxLockedBuffers), + mCurrentLockedBuffers(0) +{ + + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + mBufferPointers[i] = NULL; + } + + mBufferQueue->setSynchronousMode(true); + mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN); + mBufferQueue->setMaxAcquiredBufferCount(maxLockedBuffers); +} + +CpuConsumer::~CpuConsumer() { +} + +void CpuConsumer::setName(const String8& name) { + Mutex::Autolock _l(mMutex); + mName = name; + mBufferQueue->setConsumerName(name); +} + +status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) { + status_t err; + + if (!nativeBuffer) return BAD_VALUE; + if (mCurrentLockedBuffers == mMaxLockedBuffers) { + return INVALID_OPERATION; + } + + BufferQueue::BufferItem b; + + Mutex::Autolock _l(mMutex); + + err = acquireBufferLocked(&b); + if (err != OK) { + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + return BAD_VALUE; + } else { + CC_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); + return err; + } + } + + int buf = b.mBuf; + + if (b.mFence.get()) { + err = b.mFence->waitForever(1000, "CpuConsumer::lockNextBuffer"); + if (err != OK) { + CC_LOGE("Failed to wait for fence of acquired buffer: %s (%d)", + strerror(-err), err); + return err; + } + } + + err = mSlots[buf].mGraphicBuffer->lock( + GraphicBuffer::USAGE_SW_READ_OFTEN, + b.mCrop, + &mBufferPointers[buf]); + + if (mBufferPointers[buf] != NULL && err != OK) { + CC_LOGE("Unable to lock buffer for CPU reading: %s (%d)", strerror(-err), + err); + return err; + } + + nativeBuffer->data = reinterpret_cast<uint8_t*>(mBufferPointers[buf]); + nativeBuffer->width = mSlots[buf].mGraphicBuffer->getWidth(); + nativeBuffer->height = mSlots[buf].mGraphicBuffer->getHeight(); + nativeBuffer->format = mSlots[buf].mGraphicBuffer->getPixelFormat(); + nativeBuffer->stride = mSlots[buf].mGraphicBuffer->getStride(); + + nativeBuffer->crop = b.mCrop; + nativeBuffer->transform = b.mTransform; + nativeBuffer->scalingMode = b.mScalingMode; + nativeBuffer->timestamp = b.mTimestamp; + nativeBuffer->frameNumber = b.mFrameNumber; + + mCurrentLockedBuffers++; + + return OK; +} + +status_t CpuConsumer::unlockBuffer(const LockedBuffer &nativeBuffer) { + Mutex::Autolock _l(mMutex); + int slotIndex = 0; + status_t err; + + void *bufPtr = reinterpret_cast<void *>(nativeBuffer.data); + for (; slotIndex < BufferQueue::NUM_BUFFER_SLOTS; slotIndex++) { + if (bufPtr == mBufferPointers[slotIndex]) break; + } + if (slotIndex == BufferQueue::NUM_BUFFER_SLOTS) { + CC_LOGE("%s: Can't find buffer to free", __FUNCTION__); + return BAD_VALUE; + } + + mBufferPointers[slotIndex] = NULL; + err = mSlots[slotIndex].mGraphicBuffer->unlock(); + if (err != OK) { + CC_LOGE("%s: Unable to unlock graphic buffer %d", __FUNCTION__, slotIndex); + return err; + } + releaseBufferLocked(slotIndex, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + + mCurrentLockedBuffers--; + + return OK; +} + +void CpuConsumer::freeBufferLocked(int slotIndex) { + if (mBufferPointers[slotIndex] != NULL) { + status_t err; + CC_LOGW("Buffer %d freed while locked by consumer", slotIndex); + mBufferPointers[slotIndex] = NULL; + err = mSlots[slotIndex].mGraphicBuffer->unlock(); + if (err != OK) { + CC_LOGE("%s: Unable to unlock graphic buffer %d", __FUNCTION__, + slotIndex); + } + mCurrentLockedBuffers--; + } + ConsumerBase::freeBufferLocked(slotIndex); +} + +} // namespace android diff --git a/libs/gui/GuiConfig.cpp b/libs/gui/GuiConfig.cpp new file mode 100644 index 0000000..bafd21a --- /dev/null +++ b/libs/gui/GuiConfig.cpp @@ -0,0 +1,38 @@ +/* + * 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. + * 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/GuiConfig.h> + +namespace android { + +void appendGuiConfigString(String8& configStr) +{ + static const char* config = + " [libgui" +#ifdef USE_FENCE_SYNC + " USE_FENCE_SYNC" +#endif +#ifdef USE_NATIVE_FENCE_SYNC + " USE_NATIVE_FENCE_SYNC" +#endif +#ifdef USE_WAIT_SYNC + " USE_WAIT_SYNC" +#endif + "]"; + configStr.append(config); +} + +}; // namespace android diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp index a70a5e8..139f219 100644 --- a/libs/gui/IGraphicBufferAlloc.cpp +++ b/libs/gui/IGraphicBufferAlloc.cpp @@ -55,7 +55,7 @@ public: status_t result = reply.readInt32(); if (result == NO_ERROR) { graphicBuffer = new GraphicBuffer(); - reply.read(*graphicBuffer); + result = reply.read(*graphicBuffer); // reply.readStrongBinder(); // here we don't even have to read the BufferReference from // the parcel, it'll die with the parcel. diff --git a/libs/gui/ISensorServer.cpp b/libs/gui/ISensorServer.cpp index 7111092..0b76f37 100644 --- a/libs/gui/ISensorServer.cpp +++ b/libs/gui/ISensorServer.cpp @@ -55,7 +55,7 @@ public: int32_t n = reply.readInt32(); v.setCapacity(n); while (n--) { - reply.read(static_cast<Flattenable&>(s)); + reply.read(s); v.add(s); } return v; @@ -84,7 +84,7 @@ status_t BnSensorServer::onTransact( size_t n = v.size(); reply->writeInt32(n); for (size_t i=0 ; i<n ; i++) { - reply->write(static_cast<const Flattenable&>(v[i])); + reply->write(v[i]); } return NO_ERROR; } break; diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 1f1794c..85a9488 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -68,26 +68,29 @@ public: return interface_cast<IGraphicBufferAlloc>(reply.readStrongBinder()); } - virtual sp<IMemoryHeap> getCblk() const + virtual void setTransactionState( + const Vector<ComposerState>& state, + const Vector<DisplayState>& displays, + uint32_t flags) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::GET_CBLK, data, &reply); - return interface_cast<IMemoryHeap>(reply.readStrongBinder()); - } - - virtual void setTransactionState(const Vector<ComposerState>& state, - int orientation, uint32_t flags) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - Vector<ComposerState>::const_iterator b(state.begin()); - Vector<ComposerState>::const_iterator e(state.end()); - data.writeInt32(state.size()); - for ( ; b != e ; ++b ) { - b->write(data); + { + Vector<ComposerState>::const_iterator b(state.begin()); + Vector<ComposerState>::const_iterator e(state.end()); + data.writeInt32(state.size()); + for ( ; b != e ; ++b ) { + b->write(data); + } + } + { + Vector<DisplayState>::const_iterator b(displays.begin()); + Vector<DisplayState>::const_iterator e(displays.end()); + data.writeInt32(displays.size()); + for ( ; b != e ; ++b ) { + b->write(data); + } } - data.writeInt32(orientation); data.writeInt32(flags); remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply); } @@ -99,15 +102,15 @@ public: remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply); } - virtual status_t captureScreen(DisplayID dpy, - sp<IMemoryHeap>* heap, + virtual status_t captureScreen( + const sp<IBinder>& display, sp<IMemoryHeap>* heap, uint32_t* width, uint32_t* height, PixelFormat* format, uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeInt32(dpy); + data.writeStrongBinder(display); data.writeInt32(reqWidth); data.writeInt32(reqHeight); data.writeInt32(minLayerZ); @@ -120,24 +123,6 @@ public: return reply.readInt32(); } - virtual status_t turnElectronBeamOff(int32_t mode) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeInt32(mode); - remote()->transact(BnSurfaceComposer::TURN_ELECTRON_BEAM_OFF, data, &reply); - return reply.readInt32(); - } - - virtual status_t turnElectronBeamOn(int32_t mode) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeInt32(mode); - remote()->transact(BnSurfaceComposer::TURN_ELECTRON_BEAM_ON, data, &reply); - return reply.readInt32(); - } - virtual bool authenticateSurfaceTexture( const sp<ISurfaceTexture>& surfaceTexture) const { @@ -193,6 +178,51 @@ public: result = interface_cast<IDisplayEventConnection>(reply.readStrongBinder()); return result; } + + virtual sp<IBinder> createDisplay(const String8& displayName, bool secure) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeString8(displayName); + data.writeInt32(secure ? 1 : 0); + remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply); + return reply.readStrongBinder(); + } + + virtual sp<IBinder> getBuiltInDisplay(int32_t id) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeInt32(id); + remote()->transact(BnSurfaceComposer::GET_BUILT_IN_DISPLAY, data, &reply); + return reply.readStrongBinder(); + } + + virtual void blank(const sp<IBinder>& display) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + remote()->transact(BnSurfaceComposer::BLANK, data, &reply); + } + + virtual void unblank(const sp<IBinder>& display) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + remote()->transact(BnSurfaceComposer::UNBLANK, data, &reply); + } + + virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply); + memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo)); + return reply.readInt32(); + } }; IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer"); @@ -223,22 +253,24 @@ status_t BnSurfaceComposer::onTransact( s.read(data); state.add(s); } - int orientation = data.readInt32(); + count = data.readInt32(); + DisplayState d; + Vector<DisplayState> displays; + displays.setCapacity(count); + for (size_t i=0 ; i<count ; i++) { + d.read(data); + displays.add(d); + } uint32_t flags = data.readInt32(); - setTransactionState(state, orientation, flags); + setTransactionState(state, displays, flags); } break; case BOOT_FINISHED: { CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); } break; - case GET_CBLK: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> b = getCblk()->asBinder(); - reply->writeStrongBinder(b); - } break; case CAPTURE_SCREEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - DisplayID dpy = data.readInt32(); + sp<IBinder> display = data.readStrongBinder(); uint32_t reqWidth = data.readInt32(); uint32_t reqHeight = data.readInt32(); uint32_t minLayerZ = data.readInt32(); @@ -246,7 +278,7 @@ status_t BnSurfaceComposer::onTransact( sp<IMemoryHeap> heap; uint32_t w, h; PixelFormat f; - status_t res = captureScreen(dpy, &heap, &w, &h, &f, + status_t res = captureScreen(display, &heap, &w, &h, &f, reqWidth, reqHeight, minLayerZ, maxLayerZ); reply->writeStrongBinder(heap->asBinder()); reply->writeInt32(w); @@ -254,18 +286,6 @@ status_t BnSurfaceComposer::onTransact( reply->writeInt32(f); reply->writeInt32(res); } break; - case TURN_ELECTRON_BEAM_OFF: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - int32_t mode = data.readInt32(); - status_t res = turnElectronBeamOff(mode); - reply->writeInt32(res); - } break; - case TURN_ELECTRON_BEAM_ON: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - int32_t mode = data.readInt32(); - status_t res = turnElectronBeamOn(mode); - reply->writeInt32(res); - } break; case AUTHENTICATE_SURFACE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<ISurfaceTexture> surfaceTexture = @@ -279,6 +299,39 @@ status_t BnSurfaceComposer::onTransact( reply->writeStrongBinder(connection->asBinder()); return NO_ERROR; } break; + case CREATE_DISPLAY: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + String8 displayName = data.readString8(); + bool secure = bool(data.readInt32()); + sp<IBinder> display(createDisplay(displayName, secure)); + reply->writeStrongBinder(display); + return NO_ERROR; + } break; + case GET_BUILT_IN_DISPLAY: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + int32_t id = data.readInt32(); + sp<IBinder> display(getBuiltInDisplay(id)); + reply->writeStrongBinder(display); + return NO_ERROR; + } break; + case BLANK: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> display = data.readStrongBinder(); + blank(display); + } break; + case UNBLANK: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> display = data.readStrongBinder(); + unblank(display); + } break; + case GET_DISPLAY_INFO: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + DisplayInfo info; + sp<IBinder> display = data.readStrongBinder(); + status_t result = getDisplayInfo(display, &info); + memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo)); + reply->writeInt32(result); + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index ca9ed5b..8f7bc05 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -52,7 +52,6 @@ public: virtual sp<ISurface> createSurface( surface_data_t* params, const String8& name, - DisplayID display, uint32_t w, uint32_t h, PixelFormat format, @@ -61,7 +60,6 @@ public: Parcel data, reply; data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); data.writeString8(name); - data.writeInt32(display); data.writeInt32(w); data.writeInt32(h); data.writeInt32(format); @@ -93,12 +91,11 @@ status_t BnSurfaceComposerClient::onTransact( CHECK_INTERFACE(ISurfaceComposerClient, data, reply); surface_data_t params; String8 name = data.readString8(); - DisplayID display = data.readInt32(); uint32_t w = data.readInt32(); uint32_t h = data.readInt32(); PixelFormat format = data.readInt32(); uint32_t flags = data.readInt32(); - sp<ISurface> s = createSurface(¶ms, name, display, w, h, + sp<ISurface> s = createSurface(¶ms, name, w, h, format, flags); params.writeToParcel(reply); reply->writeStrongBinder(s->asBinder()); diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp index 3eb5e7a..a0b1e74 100644 --- a/libs/gui/ISurfaceTexture.cpp +++ b/libs/gui/ISurfaceTexture.cpp @@ -81,8 +81,8 @@ public: return result; } - virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h, - uint32_t format, uint32_t usage) { + virtual status_t dequeueBuffer(int *buf, sp<Fence>& fence, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { Parcel data, reply; data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); data.writeInt32(w); @@ -94,6 +94,12 @@ public: return result; } *buf = reply.readInt32(); + fence.clear(); + bool hasFence = reply.readInt32(); + if (hasFence) { + fence = new Fence(); + reply.read(*fence.get()); + } result = reply.readInt32(); return result; } @@ -103,7 +109,7 @@ public: Parcel data, reply; data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); data.writeInt32(buf); - memcpy(data.writeInplace(sizeof(input)), &input, sizeof(input)); + data.write(input); status_t result = remote()->transact(QUEUE_BUFFER, data, &reply); if (result != NO_ERROR) { return result; @@ -113,10 +119,15 @@ public: return result; } - virtual void cancelBuffer(int buf) { + virtual void cancelBuffer(int buf, sp<Fence> fence) { Parcel data, reply; + bool hasFence = fence.get() && fence->isValid(); data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); data.writeInt32(buf); + data.writeInt32(hasFence); + if (hasFence) { + data.write(*fence.get()); + } remote()->transact(CANCEL_BUFFER, data, &reply); } @@ -205,28 +216,38 @@ status_t BnSurfaceTexture::onTransact( uint32_t format = data.readInt32(); uint32_t usage = data.readInt32(); int buf; - int result = dequeueBuffer(&buf, w, h, format, usage); + sp<Fence> fence; + int result = dequeueBuffer(&buf, fence, w, h, format, usage); + bool hasFence = fence.get() && fence->isValid(); reply->writeInt32(buf); + reply->writeInt32(hasFence); + if (hasFence) { + reply->write(*fence.get()); + } reply->writeInt32(result); return NO_ERROR; } break; case QUEUE_BUFFER: { CHECK_INTERFACE(ISurfaceTexture, data, reply); int buf = data.readInt32(); - QueueBufferInput const* const input = - reinterpret_cast<QueueBufferInput const *>( - data.readInplace(sizeof(QueueBufferInput))); + QueueBufferInput input(data); QueueBufferOutput* const output = reinterpret_cast<QueueBufferOutput *>( reply->writeInplace(sizeof(QueueBufferOutput))); - status_t result = queueBuffer(buf, *input, output); + status_t result = queueBuffer(buf, input, output); reply->writeInt32(result); return NO_ERROR; } break; case CANCEL_BUFFER: { CHECK_INTERFACE(ISurfaceTexture, data, reply); int buf = data.readInt32(); - cancelBuffer(buf); + sp<Fence> fence; + bool hasFence = data.readInt32(); + if (hasFence) { + fence = new Fence(); + data.read(*fence.get()); + } + cancelBuffer(buf, fence); return NO_ERROR; } break; case QUERY: { @@ -268,4 +289,62 @@ status_t BnSurfaceTexture::onTransact( // ---------------------------------------------------------------------------- +static bool isValid(const sp<Fence>& fence) { + return fence.get() && fence->isValid(); +} + +ISurfaceTexture::QueueBufferInput::QueueBufferInput(const Parcel& parcel) { + parcel.read(*this); +} + +size_t ISurfaceTexture::QueueBufferInput::getFlattenedSize() const +{ + return sizeof(timestamp) + + sizeof(crop) + + sizeof(scalingMode) + + sizeof(transform) + + sizeof(bool) + + (isValid(fence) ? fence->getFlattenedSize() : 0); +} + +size_t ISurfaceTexture::QueueBufferInput::getFdCount() const +{ + return isValid(fence) ? fence->getFdCount() : 0; +} + +status_t ISurfaceTexture::QueueBufferInput::flatten(void* buffer, size_t size, + int fds[], size_t count) const +{ + status_t err = NO_ERROR; + bool haveFence = isValid(fence); + char* p = (char*)buffer; + memcpy(p, ×tamp, sizeof(timestamp)); p += sizeof(timestamp); + memcpy(p, &crop, sizeof(crop)); p += sizeof(crop); + memcpy(p, &scalingMode, sizeof(scalingMode)); p += sizeof(scalingMode); + memcpy(p, &transform, sizeof(transform)); p += sizeof(transform); + memcpy(p, &haveFence, sizeof(haveFence)); p += sizeof(haveFence); + if (haveFence) { + err = fence->flatten(p, size - (p - (char*)buffer), fds, count); + } + return err; +} + +status_t ISurfaceTexture::QueueBufferInput::unflatten(void const* buffer, + size_t size, int fds[], size_t count) +{ + status_t err = NO_ERROR; + bool haveFence; + const char* p = (const char*)buffer; + memcpy(×tamp, p, sizeof(timestamp)); p += sizeof(timestamp); + memcpy(&crop, p, sizeof(crop)); p += sizeof(crop); + memcpy(&scalingMode, p, sizeof(scalingMode)); p += sizeof(scalingMode); + memcpy(&transform, p, sizeof(transform)); p += sizeof(transform); + memcpy(&haveFence, p, sizeof(haveFence)); p += sizeof(haveFence); + if (haveFence) { + fence = new Fence(); + err = fence->unflatten(p, size - (p - (const char*)buffer), fds, count); + } + return err; +} + }; // namespace android diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 224c305..e2604f8 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -17,6 +17,7 @@ #include <utils/Errors.h> #include <binder/Parcel.h> #include <gui/ISurfaceComposerClient.h> +#include <gui/ISurfaceTexture.h> #include <private/gui/LayerState.h> namespace android { @@ -25,14 +26,7 @@ status_t layer_state_t::write(Parcel& output) const { status_t err; - size_t len = transparentRegion.write(NULL, 0); - err = output.writeInt32(len); - if (err < NO_ERROR) return err; - - void* buf = output.writeInplace(len); - if (buf == NULL) return NO_MEMORY; - - err = transparentRegion.write(buf, len); + err = output.write(transparentRegion); if (err < NO_ERROR) return err; // NOTE: regions are at the end of the structure @@ -45,11 +39,8 @@ status_t layer_state_t::write(Parcel& output) const status_t layer_state_t::read(const Parcel& input) { status_t err; - size_t len = input.readInt32(); - void const* buf = input.readInplace(len); - if (buf == NULL) return NO_MEMORY; - err = transparentRegion.read(buf); + err = input.read(transparentRegion); if (err < NO_ERROR) return err; // NOTE: regions are at the end of the structure @@ -69,4 +60,28 @@ status_t ComposerState::read(const Parcel& input) { return state.read(input); } + +status_t DisplayState::write(Parcel& output) const { + output.writeStrongBinder(token); + output.writeStrongBinder(surface->asBinder()); + output.writeInt32(what); + output.writeInt32(layerStack); + output.writeInt32(orientation); + output.write(viewport); + output.write(frame); + return NO_ERROR; +} + +status_t DisplayState::read(const Parcel& input) { + token = input.readStrongBinder(); + surface = interface_cast<ISurfaceTexture>(input.readStrongBinder()); + what = input.readInt32(); + layerStack = input.readInt32(); + orientation = input.readInt32(); + input.read(viewport); + input.read(frame); + return NO_ERROR; +} + + }; // namespace android diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp index 5cc76b4..c52a88f 100644 --- a/libs/gui/Sensor.cpp +++ b/libs/gui/Sensor.cpp @@ -98,7 +98,7 @@ int32_t Sensor::getVersion() const { return mVersion; } -size_t Sensor::getFlattenedSize() const +size_t Sensor::getSize() const { return sizeof(int32_t) + ((mName.length() + 3) & ~3) + sizeof(int32_t) + ((mVendor.length() + 3) & ~3) + @@ -107,11 +107,6 @@ size_t Sensor::getFlattenedSize() const sizeof(int32_t); } -size_t Sensor::getFdCount() const -{ - return 0; -} - static inline size_t write(void* buffer, size_t offset, const String8& value) { memcpy(static_cast<char*>(buffer) + offset, value.string(), value.length()); @@ -130,12 +125,8 @@ size_t write(void* buffer, size_t offset, int32_t value) { return sizeof(int32_t); } -status_t Sensor::flatten(void* buffer, size_t size, - int fds[], size_t count) const +status_t Sensor::flatten(void* buffer) const { - if (size < Sensor::getFlattenedSize()) - return -ENOMEM; - size_t offset = 0; offset += write(buffer, offset, int32_t(mName.length())); offset += write(buffer, offset, mName); @@ -149,7 +140,6 @@ status_t Sensor::flatten(void* buffer, size_t size, offset += write(buffer, offset, mResolution); offset += write(buffer, offset, mPower); offset += write(buffer, offset, mMinDelay); - return NO_ERROR; } @@ -171,8 +161,7 @@ size_t read(void const* buffer, size_t offset, int32_t* value) { return sizeof(int32_t); } -status_t Sensor::unflatten(void const* buffer, size_t size, - int fds[], size_t count) +status_t Sensor::unflatten(void const* buffer, size_t size) { int32_t len; size_t offset = 0; @@ -188,7 +177,6 @@ status_t Sensor::unflatten(void const* buffer, size_t size, offset += read(buffer, offset, &mResolution); offset += read(buffer, offset, &mPower); offset += read(buffer, offset, &mMinDelay); - return NO_ERROR; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index d7590f0..1745061 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -92,6 +92,12 @@ bool SurfaceControl::isSameSurface( return lhs->mSurface->asBinder() == rhs->mSurface->asBinder(); } +status_t SurfaceControl::setLayerStack(int32_t layerStack) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setLayerStack(mToken, layerStack); +} status_t SurfaceControl::setLayer(int32_t layer) { status_t err = validate(); if (err < 0) return err; @@ -116,23 +122,11 @@ status_t SurfaceControl::hide() { const sp<SurfaceComposerClient>& client(mClient); return client->hide(mToken); } -status_t SurfaceControl::show(int32_t layer) { - status_t err = validate(); - if (err < 0) return err; - const sp<SurfaceComposerClient>& client(mClient); - return client->show(mToken, layer); -} -status_t SurfaceControl::freeze() { +status_t SurfaceControl::show() { status_t err = validate(); if (err < 0) return err; const sp<SurfaceComposerClient>& client(mClient); - return client->freeze(mToken); -} -status_t SurfaceControl::unfreeze() { - status_t err = validate(); - if (err < 0) return err; - const sp<SurfaceComposerClient>& client(mClient); - return client->unfreeze(mToken); + return client->show(mToken); } status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) { status_t err = validate(); @@ -158,12 +152,6 @@ status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtd const sp<SurfaceComposerClient>& client(mClient); return client->setMatrix(mToken, dsdx, dtdx, dsdy, dtdy); } -status_t SurfaceControl::setFreezeTint(uint32_t tint) { - status_t err = validate(); - if (err < 0) return err; - const sp<SurfaceComposerClient>& client(mClient); - return client->setFreezeTint(mToken, tint); -} status_t SurfaceControl::setCrop(const Rect& crop) { status_t err = validate(); if (err < 0) return err; @@ -317,8 +305,11 @@ void Surface::init(const sp<ISurfaceTexture>& surfaceTexture) setUsage(GraphicBuffer::USAGE_HW_RENDER); } + // TODO: the display metrics should come from the display manager DisplayInfo dinfo; - SurfaceComposerClient::getDisplayInfo(0, &dinfo); + sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay( + ISurfaceComposer::eDisplayIdMain); + SurfaceComposerClient::getDisplayInfo(display, &dinfo); const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi; const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi; const_cast<uint32_t&>(ANativeWindow::flags) = 0; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 8fa2167..80dd6ee 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -38,7 +38,6 @@ #include <private/gui/ComposerService.h> #include <private/gui/LayerState.h> -#include <private/gui/SharedBufferStack.h> namespace android { // --------------------------------------------------------------------------- @@ -47,37 +46,55 @@ ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService); ComposerService::ComposerService() : Singleton<ComposerService>() { + Mutex::Autolock _l(mLock); + connectLocked(); +} + +void ComposerService::connectLocked() { const String16 name("SurfaceFlinger"); while (getService(name, &mComposerService) != NO_ERROR) { usleep(250000); } - mServerCblkMemory = mComposerService->getCblk(); - mServerCblk = static_cast<surface_flinger_cblk_t volatile *>( - mServerCblkMemory->getBase()); -} - -sp<ISurfaceComposer> ComposerService::getComposerService() { - return ComposerService::getInstance().mComposerService; -} + assert(mComposerService != NULL); + + // Create the death listener. + class DeathObserver : public IBinder::DeathRecipient { + ComposerService& mComposerService; + virtual void binderDied(const wp<IBinder>& who) { + ALOGW("ComposerService remote (surfaceflinger) died [%p]", + who.unsafe_get()); + mComposerService.composerServiceDied(); + } + public: + DeathObserver(ComposerService& mgr) : mComposerService(mgr) { } + }; -surface_flinger_cblk_t const volatile * ComposerService::getControlBlock() { - return ComposerService::getInstance().mServerCblk; + mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this)); + mComposerService->asBinder()->linkToDeath(mDeathObserver); } -static inline sp<ISurfaceComposer> getComposerService() { - return ComposerService::getComposerService(); +/*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() { + ComposerService& instance = ComposerService::getInstance(); + Mutex::Autolock _l(instance.mLock); + if (instance.mComposerService == NULL) { + ComposerService::getInstance().connectLocked(); + assert(instance.mComposerService != NULL); + ALOGD("ComposerService reconnected"); + } + return instance.mComposerService; } -static inline surface_flinger_cblk_t const volatile * get_cblk() { - return ComposerService::getControlBlock(); +void ComposerService::composerServiceDied() +{ + Mutex::Autolock _l(mLock); + mComposerService = NULL; + mDeathObserver = NULL; } // --------------------------------------------------------------------------- -// NOTE: this is NOT a member function (it's a friend defined with its -// declaration). static inline -int compare_type( const ComposerState& lhs, const ComposerState& rhs) { +int compare_type(const ComposerState& lhs, const ComposerState& rhs) { if (lhs.client < rhs.client) return -1; if (lhs.client > rhs.client) return 1; if (lhs.state.surface < rhs.state.surface) return -1; @@ -85,26 +102,37 @@ int compare_type( const ComposerState& lhs, const ComposerState& rhs) { return 0; } +static inline +int compare_type(const DisplayState& lhs, const DisplayState& rhs) { + return compare_type(lhs.token, rhs.token); +} + class Composer : public Singleton<Composer> { friend class Singleton<Composer>; mutable Mutex mLock; - SortedVector<ComposerState> mStates; - int mOrientation; + SortedVector<ComposerState> mComposerStates; + SortedVector<DisplayState > mDisplayStates; uint32_t mForceSynchronous; + bool mAnimation; Composer() : Singleton<Composer>(), - mOrientation(ISurfaceComposer::eOrientationUnchanged), - mForceSynchronous(0) + mForceSynchronous(0), + mAnimation(false) { } void closeGlobalTransactionImpl(bool synchronous); + void setAnimationTransactionImpl(); layer_state_t* getLayerStateLocked( const sp<SurfaceComposerClient>& client, SurfaceID id); + DisplayState& getDisplayStateLocked(const sp<IBinder>& token); + public: + sp<IBinder> createDisplay(const String8& displayName, bool secure); + sp<IBinder> getBuiltInDisplay(int32_t id); status_t setPosition(const sp<SurfaceComposerClient>& client, SurfaceID id, float x, float y); @@ -121,12 +149,22 @@ public: float alpha); status_t setMatrix(const sp<SurfaceComposerClient>& client, SurfaceID id, float dsdx, float dtdx, float dsdy, float dtdy); - status_t setFreezeTint( - const sp<SurfaceComposerClient>& client, SurfaceID id, - uint32_t tint); status_t setOrientation(int orientation); status_t setCrop(const sp<SurfaceComposerClient>& client, SurfaceID id, const Rect& crop); + status_t setLayerStack(const sp<SurfaceComposerClient>& client, + SurfaceID id, uint32_t layerStack); + + void setDisplaySurface(const sp<IBinder>& token, const sp<ISurfaceTexture>& surface); + void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack); + void setDisplayProjection(const sp<IBinder>& token, + uint32_t orientation, + const Rect& layerStackRect, + const Rect& displayRect); + + static void setAnimationTransaction() { + Composer::getInstance().setAnimationTransactionImpl(); + } static void closeGlobalTransaction(bool synchronous) { Composer::getInstance().closeGlobalTransactionImpl(synchronous); @@ -137,28 +175,47 @@ ANDROID_SINGLETON_STATIC_INSTANCE(Composer); // --------------------------------------------------------------------------- +sp<IBinder> Composer::createDisplay(const String8& displayName, bool secure) { + return ComposerService::getComposerService()->createDisplay(displayName, + secure); +} + +sp<IBinder> Composer::getBuiltInDisplay(int32_t id) { + return ComposerService::getComposerService()->getBuiltInDisplay(id); +} + void Composer::closeGlobalTransactionImpl(bool synchronous) { - sp<ISurfaceComposer> sm(getComposerService()); + sp<ISurfaceComposer> sm(ComposerService::getComposerService()); Vector<ComposerState> transaction; - int orientation; + Vector<DisplayState> displayTransaction; uint32_t flags = 0; { // scope for the lock Mutex::Autolock _l(mLock); - transaction = mStates; - mStates.clear(); + transaction = mComposerStates; + mComposerStates.clear(); - orientation = mOrientation; - mOrientation = ISurfaceComposer::eOrientationUnchanged; + displayTransaction = mDisplayStates; + mDisplayStates.clear(); if (synchronous || mForceSynchronous) { flags |= ISurfaceComposer::eSynchronous; } + if (mAnimation) { + flags |= ISurfaceComposer::eAnimation; + } + mForceSynchronous = false; + mAnimation = false; } - sm->setTransactionState(transaction, orientation, flags); + sm->setTransactionState(transaction, displayTransaction, flags); +} + +void Composer::setAnimationTransactionImpl() { + Mutex::Autolock _l(mLock); + mAnimation = true; } layer_state_t* Composer::getLayerStateLocked( @@ -168,13 +225,13 @@ layer_state_t* Composer::getLayerStateLocked( s.client = client->mClient; s.state.surface = id; - ssize_t index = mStates.indexOf(s); + ssize_t index = mComposerStates.indexOf(s); if (index < 0) { // we don't have it, add an initialized layer_state to our list - index = mStates.add(s); + index = mComposerStates.add(s); } - ComposerState* const out = mStates.editArray(); + ComposerState* const out = mComposerStates.editArray(); return &(out[index].state); } @@ -184,7 +241,7 @@ status_t Composer::setPosition(const sp<SurfaceComposerClient>& client, layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::ePositionChanged; + s->what |= layer_state_t::ePositionChanged; s->x = x; s->y = y; return NO_ERROR; @@ -196,7 +253,7 @@ status_t Composer::setSize(const sp<SurfaceComposerClient>& client, layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eSizeChanged; + s->what |= layer_state_t::eSizeChanged; s->w = w; s->h = h; @@ -212,7 +269,7 @@ status_t Composer::setLayer(const sp<SurfaceComposerClient>& client, layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eLayerChanged; + s->what |= layer_state_t::eLayerChanged; s->z = z; return NO_ERROR; } @@ -224,7 +281,7 @@ status_t Composer::setFlags(const sp<SurfaceComposerClient>& client, layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eVisibilityChanged; + s->what |= layer_state_t::eVisibilityChanged; s->flags &= ~mask; s->flags |= (flags & mask); s->mask |= mask; @@ -238,7 +295,7 @@ status_t Composer::setTransparentRegionHint( layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eTransparentRegionChanged; + s->what |= layer_state_t::eTransparentRegionChanged; s->transparentRegion = transparentRegion; return NO_ERROR; } @@ -249,11 +306,22 @@ status_t Composer::setAlpha(const sp<SurfaceComposerClient>& client, layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eAlphaChanged; + s->what |= layer_state_t::eAlphaChanged; s->alpha = alpha; return NO_ERROR; } +status_t Composer::setLayerStack(const sp<SurfaceComposerClient>& client, + SurfaceID id, uint32_t layerStack) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= layer_state_t::eLayerStackChanged; + s->layerStack = layerStack; + return NO_ERROR; +} + status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client, SurfaceID id, float dsdx, float dtdx, float dsdy, float dtdy) { @@ -261,7 +329,7 @@ status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client, layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eMatrixChanged; + s->what |= layer_state_t::eMatrixChanged; layer_state_t::matrix22_t matrix; matrix.dsdx = dsdx; matrix.dtdx = dtdx; @@ -271,36 +339,58 @@ status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client, return NO_ERROR; } -status_t Composer::setFreezeTint(const sp<SurfaceComposerClient>& client, - SurfaceID id, uint32_t tint) { +status_t Composer::setCrop(const sp<SurfaceComposerClient>& client, + SurfaceID id, const Rect& crop) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eFreezeTintChanged; - s->tint = tint; + s->what |= layer_state_t::eCropChanged; + s->crop = crop; return NO_ERROR; } -status_t Composer::setOrientation(int orientation) { - Mutex::Autolock _l(mLock); - mOrientation = orientation; +// --------------------------------------------------------------------------- - // Changing the orientation makes the transaction synchronous. - mForceSynchronous = true; +DisplayState& Composer::getDisplayStateLocked(const sp<IBinder>& token) { + DisplayState s; + s.token = token; + ssize_t index = mDisplayStates.indexOf(s); + if (index < 0) { + // we don't have it, add an initialized layer_state to our list + s.what = 0; + index = mDisplayStates.add(s); + } + return mDisplayStates.editItemAt(index); +} - return NO_ERROR; +void Composer::setDisplaySurface(const sp<IBinder>& token, + const sp<ISurfaceTexture>& surface) { + Mutex::Autolock _l(mLock); + DisplayState& s(getDisplayStateLocked(token)); + s.surface = surface; + s.what |= DisplayState::eSurfaceChanged; } -status_t Composer::setCrop(const sp<SurfaceComposerClient>& client, - SurfaceID id, const Rect& crop) { +void Composer::setDisplayLayerStack(const sp<IBinder>& token, + uint32_t layerStack) { Mutex::Autolock _l(mLock); - layer_state_t* s = getLayerStateLocked(client, id); - if (!s) - return BAD_INDEX; - s->what |= ISurfaceComposer::eCropChanged; - s->crop = crop; - return NO_ERROR; + DisplayState& s(getDisplayStateLocked(token)); + s.layerStack = layerStack; + s.what |= DisplayState::eLayerStackChanged; +} + +void Composer::setDisplayProjection(const sp<IBinder>& token, + uint32_t orientation, + const Rect& layerStackRect, + const Rect& displayRect) { + Mutex::Autolock _l(mLock); + DisplayState& s(getDisplayStateLocked(token)); + s.orientation = orientation; + s.viewport = layerStackRect; + s.frame = displayRect; + s.what |= DisplayState::eDisplayProjectionChanged; + mForceSynchronous = true; // TODO: do we actually still need this? } // --------------------------------------------------------------------------- @@ -311,7 +401,7 @@ SurfaceComposerClient::SurfaceComposerClient() } void SurfaceComposerClient::onFirstRef() { - sp<ISurfaceComposer> sm(getComposerService()); + sp<ISurfaceComposer> sm(ComposerService::getComposerService()); if (sm != 0) { sp<ISurfaceComposerClient> conn = sm->createConnection(); if (conn != 0) { @@ -336,7 +426,7 @@ sp<IBinder> SurfaceComposerClient::connection() const { status_t SurfaceComposerClient::linkToComposerDeath( const sp<IBinder::DeathRecipient>& recipient, void* cookie, uint32_t flags) { - sp<ISurfaceComposer> sm(getComposerService()); + sp<ISurfaceComposer> sm(ComposerService::getComposerService()); return sm->asBinder()->linkToDeath(recipient, cookie, flags); } @@ -352,25 +442,7 @@ void SurfaceComposerClient::dispose() { } sp<SurfaceControl> SurfaceComposerClient::createSurface( - DisplayID display, - uint32_t w, - uint32_t h, - PixelFormat format, - uint32_t flags) -{ - String8 name; - const size_t SIZE = 128; - char buffer[SIZE]; - snprintf(buffer, SIZE, "<pid_%d>", getpid()); - name.append(buffer); - - return SurfaceComposerClient::createSurface(name, display, - w, h, format, flags); -} - -sp<SurfaceControl> SurfaceComposerClient::createSurface( const String8& name, - DisplayID display, uint32_t w, uint32_t h, PixelFormat format, @@ -380,7 +452,7 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface( if (mStatus == NO_ERROR) { ISurfaceComposerClient::surface_data_t data; sp<ISurface> surface = mClient->createSurface(&data, name, - display, w, h, format, flags); + w, h, format, flags); if (surface != 0) { result = new SurfaceControl(this, surface, data); } @@ -388,6 +460,15 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface( return result; } +sp<IBinder> SurfaceComposerClient::createDisplay(const String8& displayName, + bool secure) { + return Composer::getInstance().createDisplay(displayName, secure); +} + +sp<IBinder> SurfaceComposerClient::getBuiltInDisplay(int32_t id) { + return Composer::getInstance().getBuiltInDisplay(id); +} + status_t SurfaceComposerClient::destroySurface(SurfaceID sid) { if (mStatus != NO_ERROR) return mStatus; @@ -409,16 +490,16 @@ void SurfaceComposerClient::closeGlobalTransaction(bool synchronous) { Composer::closeGlobalTransaction(synchronous); } +void SurfaceComposerClient::setAnimationTransaction() { + Composer::setAnimationTransaction(); +} + // ---------------------------------------------------------------------------- status_t SurfaceComposerClient::setCrop(SurfaceID id, const Rect& crop) { return getComposer().setCrop(this, id, crop); } -status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) { - return getComposer().setFreezeTint(this, id, tint); -} - status_t SurfaceComposerClient::setPosition(SurfaceID id, float x, float y) { return getComposer().setPosition(this, id, x, y); } @@ -433,26 +514,14 @@ status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) { status_t SurfaceComposerClient::hide(SurfaceID id) { return getComposer().setFlags(this, id, - ISurfaceComposer::eLayerHidden, - ISurfaceComposer::eLayerHidden); -} - -status_t SurfaceComposerClient::show(SurfaceID id, int32_t) { - return getComposer().setFlags(this, id, - 0, - ISurfaceComposer::eLayerHidden); -} - -status_t SurfaceComposerClient::freeze(SurfaceID id) { - return getComposer().setFlags(this, id, - ISurfaceComposer::eLayerFrozen, - ISurfaceComposer::eLayerFrozen); + layer_state_t::eLayerHidden, + layer_state_t::eLayerHidden); } -status_t SurfaceComposerClient::unfreeze(SurfaceID id) { +status_t SurfaceComposerClient::show(SurfaceID id) { return getComposer().setFlags(this, id, 0, - ISurfaceComposer::eLayerFrozen); + layer_state_t::eLayerHidden); } status_t SurfaceComposerClient::setFlags(SurfaceID id, uint32_t flags, @@ -469,89 +538,49 @@ status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) { return getComposer().setAlpha(this, id, alpha); } +status_t SurfaceComposerClient::setLayerStack(SurfaceID id, uint32_t layerStack) { + return getComposer().setLayerStack(this, id, layerStack); +} + status_t SurfaceComposerClient::setMatrix(SurfaceID id, float dsdx, float dtdx, float dsdy, float dtdy) { return getComposer().setMatrix(this, id, dsdx, dtdx, dsdy, dtdy); } -status_t SurfaceComposerClient::setOrientation(DisplayID dpy, - int orientation, uint32_t flags) -{ - return Composer::getInstance().setOrientation(orientation); -} - // ---------------------------------------------------------------------------- -status_t SurfaceComposerClient::getDisplayInfo( - DisplayID dpy, DisplayInfo* info) -{ - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) - return BAD_VALUE; - - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - volatile display_cblk_t const * dcblk = cblk->displays + dpy; - - info->w = dcblk->w; - info->h = dcblk->h; - info->orientation = dcblk->orientation; - info->xdpi = dcblk->xdpi; - info->ydpi = dcblk->ydpi; - info->fps = dcblk->fps; - info->density = dcblk->density; - return getPixelFormatInfo(dcblk->format, &(info->pixelFormatInfo)); +void SurfaceComposerClient::setDisplaySurface(const sp<IBinder>& token, + const sp<ISurfaceTexture>& surface) { + Composer::getInstance().setDisplaySurface(token, surface); } -ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) -{ - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) - return BAD_VALUE; - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - volatile display_cblk_t const * dcblk = cblk->displays + dpy; - return dcblk->w; +void SurfaceComposerClient::setDisplayLayerStack(const sp<IBinder>& token, + uint32_t layerStack) { + Composer::getInstance().setDisplayLayerStack(token, layerStack); } -ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) -{ - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) - return BAD_VALUE; - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - volatile display_cblk_t const * dcblk = cblk->displays + dpy; - return dcblk->h; +void SurfaceComposerClient::setDisplayProjection(const sp<IBinder>& token, + uint32_t orientation, + const Rect& layerStackRect, + const Rect& displayRect) { + Composer::getInstance().setDisplayProjection(token, orientation, + layerStackRect, displayRect); } -ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy) -{ - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) - return BAD_VALUE; - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - volatile display_cblk_t const * dcblk = cblk->displays + dpy; - return dcblk->orientation; -} +// ---------------------------------------------------------------------------- -ssize_t SurfaceComposerClient::getNumberOfDisplays() +status_t SurfaceComposerClient::getDisplayInfo( + const sp<IBinder>& display, DisplayInfo* info) { - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - uint32_t connected = cblk->connected; - int n = 0; - while (connected) { - if (connected&1) n++; - connected >>= 1; - } - return n; + return ComposerService::getComposerService()->getDisplayInfo(display, info); } -// ---------------------------------------------------------------------------- - -status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags) -{ - // This has been made a no-op because it can cause Gralloc buffer deadlocks. - return NO_ERROR; +void SurfaceComposerClient::blankDisplay(const sp<IBinder>& token) { + ComposerService::getComposerService()->blank(token); } -status_t SurfaceComposerClient::unfreezeDisplay(DisplayID dpy, uint32_t flags) -{ - // This has been made a no-op because it can cause Gralloc buffer deadlocks. - return NO_ERROR; +void SurfaceComposerClient::unblankDisplay(const sp<IBinder>& token) { + ComposerService::getComposerService()->unblank(token); } // ---------------------------------------------------------------------------- @@ -560,30 +589,32 @@ ScreenshotClient::ScreenshotClient() : mWidth(0), mHeight(0), mFormat(PIXEL_FORMAT_NONE) { } -status_t ScreenshotClient::update() { +status_t ScreenshotClient::update(const sp<IBinder>& display) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == NULL) return NO_INIT; mHeap = 0; - return s->captureScreen(0, &mHeap, + return s->captureScreen(display, &mHeap, &mWidth, &mHeight, &mFormat, 0, 0, 0, -1UL); } -status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight) { +status_t ScreenshotClient::update(const sp<IBinder>& display, + uint32_t reqWidth, uint32_t reqHeight) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == NULL) return NO_INIT; mHeap = 0; - return s->captureScreen(0, &mHeap, + return s->captureScreen(display, &mHeap, &mWidth, &mHeight, &mFormat, reqWidth, reqHeight, 0, -1UL); } -status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight, +status_t ScreenshotClient::update(const sp<IBinder>& display, + uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == NULL) return NO_INIT; mHeap = 0; - return s->captureScreen(0, &mHeap, + return s->captureScreen(display, &mHeap, &mWidth, &mHeight, &mFormat, reqWidth, reqHeight, minLayerZ, maxLayerZ); } diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 55be4bc..b4dfb5e 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -39,14 +39,28 @@ #include <utils/String8.h> #include <utils/Trace.h> -// This compile option makes SurfaceTexture use the EGL_KHR_fence_sync extension -// to synchronize access to the buffers. It will cause dequeueBuffer to stall, -// waiting for the GL reads for the buffer being dequeued to complete before -// allowing the buffer to be dequeued. +// This compile option makes SurfaceTexture use the +// EGL_ANDROID_native_fence_sync extension to create Android native fences to +// signal when all GLES reads for a given buffer have completed. It is not +// compatible with using the EGL_KHR_fence_sync extension for the same +// purpose. +#ifdef USE_NATIVE_FENCE_SYNC #ifdef USE_FENCE_SYNC -#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER -#error "USE_FENCE_SYNC and ALLOW_DEQUEUE_CURRENT_BUFFER are incompatible" +#error "USE_NATIVE_FENCE_SYNC and USE_FENCE_SYNC are incompatible" #endif +static const bool useNativeFenceSync = true; +#else +static const bool useNativeFenceSync = false; +#endif + +// This compile option makes SurfaceTexture use the EGL_ANDROID_sync_wait +// extension to insert server-side waits into the GLES command stream. This +// feature requires the EGL_ANDROID_native_fence_sync and +// EGL_ANDROID_wait_sync extensions. +#ifdef USE_WAIT_SYNC +static const bool useWaitSync = true; +#else +static const bool useWaitSync = false; #endif // Macros for including the SurfaceTexture name in log messages @@ -98,14 +112,10 @@ static float mtxRot270[16] = { static void mtxMul(float out[16], const float a[16], const float b[16]); -// Get an ID that's unique within this process. -static int32_t createProcessUniqueId() { - static volatile int32_t globalCounter = 0; - return android_atomic_inc(&globalCounter); -} SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) : + ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue), mCurrentTransform(0), mCurrentTimestamp(0), mFilteringEnabled(true), @@ -118,52 +128,20 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, mTexTarget(texTarget), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), - mAbandoned(false), mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mAttached(true) { - // Choose a name using the PID and a process-unique ID. - mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); ST_LOGV("SurfaceTexture"); - if (bufferQueue == 0) { - ST_LOGV("Creating a new BufferQueue"); - mBufferQueue = new BufferQueue(allowSynchronousMode); - } - else { - mBufferQueue = bufferQueue; - } memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix)); - // Note that we can't create an sp<...>(this) in a ctor that will not keep a - // reference once the ctor ends, as that would cause the refcount of 'this' - // dropping to 0 at the end of the ctor. Since all we need is a wp<...> - // that's what we create. - wp<BufferQueue::ConsumerListener> listener; - sp<BufferQueue::ConsumerListener> proxy; - listener = static_cast<BufferQueue::ConsumerListener*>(this); - proxy = new BufferQueue::ProxyConsumerListener(listener); - - status_t err = mBufferQueue->consumerConnect(proxy); - if (err != NO_ERROR) { - ST_LOGE("SurfaceTexture: error connecting to BufferQueue: %s (%d)", - strerror(-err), err); - } else { - mBufferQueue->setConsumerName(mName); - mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); - } -} - -SurfaceTexture::~SurfaceTexture() { - ST_LOGV("~SurfaceTexture"); - - abandon(); + mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); } -status_t SurfaceTexture::setBufferCountServer(int bufferCount) { +status_t SurfaceTexture::setDefaultMaxBufferCount(int bufferCount) { Mutex::Autolock lock(mMutex); - return mBufferQueue->setBufferCountServer(bufferCount); + return mBufferQueue->setDefaultMaxBufferCount(bufferCount); } @@ -176,10 +154,48 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) } status_t SurfaceTexture::updateTexImage() { - return SurfaceTexture::updateTexImage(NULL); + return SurfaceTexture::updateTexImage(NULL, false); +} + +status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) { + status_t err = ConsumerBase::acquireBufferLocked(item); + if (err != NO_ERROR) { + return err; + } + + int slot = item->mBuf; + if (item->mGraphicBuffer != NULL) { + if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage); + mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; + } + } + + // Update the GL texture object. We may have to do this even when + // item.mGraphicBuffer == NULL, if we destroyed the EGLImage when + // detaching from a context but the buffer has not been re-allocated. + if (mEglSlots[slot].mEglImage == EGL_NO_IMAGE_KHR) { + EGLImageKHR image = createImage(mEglDisplay, mSlots[slot].mGraphicBuffer); + if (image == EGL_NO_IMAGE_KHR) { + return UNKNOWN_ERROR; + } + mEglSlots[slot].mEglImage = image; + } + + return NO_ERROR; } -status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) { +status_t SurfaceTexture::releaseBufferLocked(int buf, EGLDisplay display, + EGLSyncKHR eglFence) { + status_t err = ConsumerBase::releaseBufferLocked(buf, mEglDisplay, + eglFence); + + mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; + + return err; +} + +status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) { ATRACE_CALL(); ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); @@ -219,106 +235,84 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) { // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. - err = mBufferQueue->acquireBuffer(&item); + err = acquireBufferLocked(&item); if (err == NO_ERROR) { int buf = item.mBuf; - // This buffer was newly allocated, so we need to clean up on our side - if (item.mGraphicBuffer != NULL) { - mEGLSlots[buf].mGraphicBuffer = 0; - if (mEGLSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(dpy, mEGLSlots[buf].mEglImage); - mEGLSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; - } - mEGLSlots[buf].mGraphicBuffer = item.mGraphicBuffer; - } // we call the rejecter here, in case the caller has a reason to // not accept this buffer. this is used by SurfaceFlinger to // reject buffers which have the wrong size - if (rejecter && rejecter->reject(mEGLSlots[buf].mGraphicBuffer, item)) { - mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence); - mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR; + if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) { + releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR); glBindTexture(mTexTarget, mTexName); return NO_ERROR; } - // Update the GL texture object. We may have to do this even when - // item.mGraphicBuffer == NULL, if we destroyed the EGLImage when - // detaching from a context but the buffer has not been re-allocated. - EGLImageKHR image = mEGLSlots[buf].mEglImage; - if (image == EGL_NO_IMAGE_KHR) { - if (mEGLSlots[buf].mGraphicBuffer == NULL) { - ST_LOGE("updateTexImage: buffer at slot %d is null", buf); - err = BAD_VALUE; - } else { - image = createImage(dpy, mEGLSlots[buf].mGraphicBuffer); - mEGLSlots[buf].mEglImage = image; - if (image == EGL_NO_IMAGE_KHR) { - // NOTE: if dpy was invalid, createImage() is guaranteed to - // fail. so we'd end up here. - err = UNKNOWN_ERROR; - } - } + GLint error; + while ((error = glGetError()) != GL_NO_ERROR) { + ST_LOGW("updateTexImage: clearing GL error: %#04x", error); } - if (err == NO_ERROR) { - GLint error; - while ((error = glGetError()) != GL_NO_ERROR) { - ST_LOGW("updateTexImage: clearing GL error: %#04x", error); - } - - glBindTexture(mTexTarget, mTexName); - glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); + EGLImageKHR image = mEglSlots[buf].mEglImage; + glBindTexture(mTexTarget, mTexName); + glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); - while ((error = glGetError()) != GL_NO_ERROR) { - ST_LOGE("updateTexImage: error binding external texture image %p " - "(slot %d): %#04x", image, buf, error); - err = UNKNOWN_ERROR; - } + while ((error = glGetError()) != GL_NO_ERROR) { + ST_LOGE("updateTexImage: error binding external texture image %p " + "(slot %d): %#04x", image, buf, error); + err = UNKNOWN_ERROR; + } - if (err == NO_ERROR) { - err = syncForReleaseLocked(dpy); - } + if (err == NO_ERROR) { + err = syncForReleaseLocked(dpy); } if (err != NO_ERROR) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. - mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence); - mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR; + releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR); return err; } ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, - buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0); + buf, mSlots[buf].mGraphicBuffer->handle); // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - status_t status = mBufferQueue->releaseBuffer(mCurrentTexture, dpy, - mEGLSlots[mCurrentTexture].mFence); - - mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR; - if (status == BufferQueue::STALE_BUFFER_SLOT) { - freeBufferLocked(mCurrentTexture); - } else if (status != NO_ERROR) { - ST_LOGE("updateTexImage: released invalid buffer"); + status_t status = releaseBufferLocked(mCurrentTexture, dpy, + mEglSlots[mCurrentTexture].mEglFence); + if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) { + ST_LOGE("updateTexImage: failed to release buffer: %s (%d)", + strerror(-status), status); err = status; } } // Update the SurfaceTexture state. mCurrentTexture = buf; - mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer; + mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; - computeCurrentTransformMatrix(); + mCurrentFence = item.mFence; + if (!skipSync) { + // SurfaceFlinger needs to lazily perform GLES synchronization + // only when it's actually going to use GLES for compositing. + // Eventually SurfaceFlinger should have its own consumer class, + // but for now we'll just hack it in to SurfaceTexture. + // SurfaceFlinger is responsible for calling doGLFenceWait before + // texturing from this SurfaceTexture. + doGLFenceWaitLocked(); + } + computeCurrentTransformMatrixLocked(); } else { if (err < 0) { - ALOGE("updateTexImage failed on acquire %d", err); + ST_LOGE("updateTexImage: acquire failed: %s (%d)", + strerror(-err), err); + return err; } // We always bind the texture even if we don't update its contents. glBindTexture(mTexTarget, mTexName); @@ -328,6 +322,17 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) { return err; } +void SurfaceTexture::setReleaseFence(int fenceFd) { + sp<Fence> fence(new Fence(fenceFd)); + if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) + return; + status_t err = addReleaseFence(mCurrentTexture, fence); + if (err != OK) { + ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)", + strerror(-err), err); + } +} + status_t SurfaceTexture::detachFromContext() { ATRACE_CALL(); ST_LOGV("detachFromContext"); @@ -371,10 +376,10 @@ status_t SurfaceTexture::detachFromContext() { // SurfaceTexture gets attached to a new OpenGL ES context (and thus gets a // new EGLDisplay). for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { - EGLImageKHR img = mEGLSlots[i].mEglImage; + EGLImageKHR img = mEglSlots[i].mEglImage; if (img != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(mEglDisplay, img); - mEGLSlots[i].mEglImage = EGL_NO_IMAGE_KHR; + mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR; } } @@ -461,36 +466,61 @@ status_t SurfaceTexture::attachToContext(GLuint tex) { status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) { ST_LOGV("syncForReleaseLocked"); - if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - EGLSyncKHR fence = mEGLSlots[mCurrentTexture].mFence; - if (fence != EGL_NO_SYNC_KHR) { - // There is already a fence for the current slot. We need to wait - // on that before replacing it with another fence to ensure that all - // outstanding buffer accesses have completed before the producer - // accesses it. - EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); - if (result == EGL_FALSE) { - ST_LOGE("syncForReleaseLocked: error waiting for previous " - "fence: %#x", eglGetError()); + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + if (useNativeFenceSync) { + EGLSyncKHR sync = eglCreateSyncKHR(dpy, + EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); + if (sync == EGL_NO_SYNC_KHR) { + ST_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", + eglGetError()); return UNKNOWN_ERROR; - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - ST_LOGE("syncForReleaseLocked: timeout waiting for previous " - "fence"); - return TIMED_OUT; } - eglDestroySyncKHR(dpy, fence); - } + glFlush(); + int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); + eglDestroySyncKHR(dpy, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ST_LOGE("syncForReleaseLocked: error dup'ing native fence " + "fd: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + sp<Fence> fence(new Fence(fenceFd)); + status_t err = addReleaseFenceLocked(mCurrentTexture, fence); + if (err != OK) { + ST_LOGE("syncForReleaseLocked: error adding release fence: " + "%s (%d)", strerror(-err), err); + return err; + } + } else if (mUseFenceSync) { + EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence; + if (fence != EGL_NO_SYNC_KHR) { + // There is already a fence for the current slot. We need to + // wait on that before replacing it with another fence to + // ensure that all outstanding buffer accesses have completed + // before the producer accesses it. + EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + if (result == EGL_FALSE) { + ST_LOGE("syncForReleaseLocked: error waiting for previous " + "fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ST_LOGE("syncForReleaseLocked: timeout waiting for previous " + "fence"); + return TIMED_OUT; + } + eglDestroySyncKHR(dpy, fence); + } - // Create a fence for the outstanding accesses in the current OpenGL ES - // context. - fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); - if (fence == EGL_NO_SYNC_KHR) { - ST_LOGE("syncForReleaseLocked: error creating fence: %#x", - eglGetError()); - return UNKNOWN_ERROR; + // Create a fence for the outstanding accesses in the current + // OpenGL ES context. + fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); + if (fence == EGL_NO_SYNC_KHR) { + ST_LOGE("syncForReleaseLocked: error creating fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + mEglSlots[mCurrentTexture].mEglFence = fence; } - glFlush(); - mEGLSlots[mCurrentTexture].mFence = fence; } return OK; @@ -526,15 +556,24 @@ void SurfaceTexture::getTransformMatrix(float mtx[16]) { void SurfaceTexture::setFilteringEnabled(bool enabled) { Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!"); + return; + } bool needsRecompute = mFilteringEnabled != enabled; mFilteringEnabled = enabled; - if (needsRecompute) { - computeCurrentTransformMatrix(); + + if (needsRecompute && mCurrentTextureBuf==NULL) { + ST_LOGD("setFilteringEnabled called with mCurrentTextureBuf == NULL"); + } + + if (needsRecompute && mCurrentTextureBuf != NULL) { + computeCurrentTransformMatrixLocked(); } } -void SurfaceTexture::computeCurrentTransformMatrix() { - ST_LOGV("computeCurrentTransformMatrix"); +void SurfaceTexture::computeCurrentTransformMatrixLocked() { + ST_LOGV("computeCurrentTransformMatrixLocked"); float xform[16]; for (int i = 0; i < 16; i++) { @@ -563,6 +602,11 @@ void SurfaceTexture::computeCurrentTransformMatrix() { } sp<GraphicBuffer>& buf(mCurrentTextureBuf); + + if (buf == NULL) { + ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL"); + } + Rect cropRect = mCurrentCrop; float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; float bufferWidth = buf->getWidth(); @@ -587,11 +631,13 @@ void SurfaceTexture::computeCurrentTransformMatrix() { // We know there's no subsampling of any channels, so we // only need to shrink by a half a pixel. shrinkAmount = 0.5; + break; default: // If we don't recognize the format, we must assume the // worst case (that we care about), which is YUV420. shrinkAmount = 1.0; + break; } } @@ -631,13 +677,6 @@ nsecs_t SurfaceTexture::getTimestamp() { return mCurrentTimestamp; } -void SurfaceTexture::setFrameAvailableListener( - const sp<FrameAvailableListener>& listener) { - ST_LOGV("setFrameAvailableListener"); - Mutex::Autolock lock(mMutex); - mFrameAvailableListener = listener; -} - EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer) { EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); @@ -705,6 +744,76 @@ uint32_t SurfaceTexture::getCurrentScalingMode() const { return mCurrentScalingMode; } +sp<Fence> SurfaceTexture::getCurrentFence() const { + Mutex::Autolock lock(mMutex); + return mCurrentFence; +} + +status_t SurfaceTexture::doGLFenceWait() const { + Mutex::Autolock lock(mMutex); + return doGLFenceWaitLocked(); +} + +status_t SurfaceTexture::doGLFenceWaitLocked() const { + + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { + ST_LOGE("doGLFenceWait: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { + ST_LOGE("doGLFenceWait: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (mCurrentFence != NULL) { + if (useWaitSync) { + // Create an EGLSyncKHR from the current fence. + int fenceFd = mCurrentFence->dup(); + if (fenceFd == -1) { + ST_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + EGLint attribs[] = { + EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, + EGL_NONE + }; + EGLSyncKHR sync = eglCreateSyncKHR(dpy, + EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + close(fenceFd); + ST_LOGE("doGLFenceWait: error creating EGL fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + + // XXX: The spec draft is inconsistent as to whether this should + // return an EGLint or void. Ignore the return value for now, as + // it's not strictly needed. + eglWaitSyncANDROID(dpy, sync, 0); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(dpy, sync); + if (eglErr != EGL_SUCCESS) { + ST_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", + eglErr); + return UNKNOWN_ERROR; + } + } else { + status_t err = mCurrentFence->waitForever(1000, + "SurfaceTexture::doGLFenceWaitLocked"); + if (err != NO_ERROR) { + ST_LOGE("doGLFenceWait: error waiting for fence: %d", err); + return err; + } + } + } + + return NO_ERROR; +} + bool SurfaceTexture::isSynchronousMode() const { Mutex::Autolock lock(mMutex); return mBufferQueue->isSynchronousMode(); @@ -712,35 +821,22 @@ bool SurfaceTexture::isSynchronousMode() const { void SurfaceTexture::freeBufferLocked(int slotIndex) { ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); - mEGLSlots[slotIndex].mGraphicBuffer = 0; if (slotIndex == mCurrentTexture) { mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; } - EGLImageKHR img = mEGLSlots[slotIndex].mEglImage; + EGLImageKHR img = mEglSlots[slotIndex].mEglImage; if (img != EGL_NO_IMAGE_KHR) { ST_LOGV("destroying EGLImage dpy=%p img=%p", mEglDisplay, img); eglDestroyImageKHR(mEglDisplay, img); } - mEGLSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR; + mEglSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR; + ConsumerBase::freeBufferLocked(slotIndex); } -void SurfaceTexture::abandon() { - ST_LOGV("abandon"); - Mutex::Autolock lock(mMutex); - - if (!mAbandoned) { - mAbandoned = true; - mCurrentTextureBuf.clear(); - - // destroy all egl buffers - for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { - freeBufferLocked(i); - } - - // disconnect from the BufferQueue - mBufferQueue->consumerDisconnect(); - mBufferQueue.clear(); - } +void SurfaceTexture::abandonLocked() { + ST_LOGV("abandonLocked"); + mCurrentTextureBuf.clear(); + ConsumerBase::abandonLocked(); } void SurfaceTexture::setName(const String8& name) { @@ -772,71 +868,18 @@ status_t SurfaceTexture::setSynchronousMode(bool enabled) { return mBufferQueue->setSynchronousMode(enabled); } -// Used for refactoring, should not be in final interface -sp<BufferQueue> SurfaceTexture::getBufferQueue() const { - Mutex::Autolock lock(mMutex); - return mBufferQueue; -} - -void SurfaceTexture::onFrameAvailable() { - ST_LOGV("onFrameAvailable"); - - sp<FrameAvailableListener> listener; - { // scope for the lock - Mutex::Autolock lock(mMutex); - listener = mFrameAvailableListener; - } - - if (listener != NULL) { - ST_LOGV("actually calling onFrameAvailable"); - listener->onFrameAvailable(); - } -} - -void SurfaceTexture::onBuffersReleased() { - ST_LOGV("onBuffersReleased"); - - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - // Nothing to do if we're already abandoned. - return; - } - - uint32_t mask = 0; - mBufferQueue->getReleasedBuffers(&mask); - for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { - if (mask & (1 << i)) { - freeBufferLocked(i); - } - } -} - -void SurfaceTexture::dump(String8& result) const -{ - char buffer[1024]; - dump(result, "", buffer, 1024); -} - -void SurfaceTexture::dump(String8& result, const char* prefix, - char* buffer, size_t SIZE) const +void SurfaceTexture::dumpLocked(String8& result, const char* prefix, + char* buffer, size_t size) const { - Mutex::Autolock _l(mMutex); - snprintf(buffer, SIZE, "%smTexName=%d, mAbandoned=%d\n", prefix, mTexName, - int(mAbandoned)); - result.append(buffer); - - snprintf(buffer, SIZE, - "%snext : {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d}\n", - prefix, mCurrentCrop.left, - mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom, - mCurrentTransform, mCurrentTexture - ); + snprintf(buffer, size, + "%smTexName=%d mCurrentTexture=%d\n" + "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", + prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, + mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom, + mCurrentTransform); result.append(buffer); - if (!mAbandoned) { - mBufferQueue->dump(result, prefix, buffer, SIZE); - } + ConsumerBase::dumpLocked(result, prefix, buffer, size); } static void mtxMul(float out[16], const float a[16], const float b[16]) { diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index f60f902..afdbf04 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -23,6 +23,8 @@ #include <utils/Log.h> #include <utils/Trace.h> +#include <ui/Fence.h> + #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceTexture.h> @@ -62,11 +64,15 @@ void SurfaceTextureClient::init() { ANativeWindow::setSwapInterval = hook_setSwapInterval; ANativeWindow::dequeueBuffer = hook_dequeueBuffer; ANativeWindow::cancelBuffer = hook_cancelBuffer; - ANativeWindow::lockBuffer = hook_lockBuffer; ANativeWindow::queueBuffer = hook_queueBuffer; ANativeWindow::query = hook_query; ANativeWindow::perform = hook_perform; + ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED; + ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED; + ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED; + ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED; + const_cast<int&>(ANativeWindow::minSwapInterval) = 0; const_cast<int&>(ANativeWindow::maxSwapInterval) = 1; @@ -103,27 +109,57 @@ int SurfaceTextureClient::hook_setSwapInterval(ANativeWindow* window, int interv } int SurfaceTextureClient::hook_dequeueBuffer(ANativeWindow* window, - ANativeWindowBuffer** buffer) { + ANativeWindowBuffer** buffer, int* fenceFd) { SurfaceTextureClient* c = getSelf(window); - return c->dequeueBuffer(buffer); + return c->dequeueBuffer(buffer, fenceFd); } int SurfaceTextureClient::hook_cancelBuffer(ANativeWindow* window, + ANativeWindowBuffer* buffer, int fenceFd) { + SurfaceTextureClient* c = getSelf(window); + return c->cancelBuffer(buffer, fenceFd); +} + +int SurfaceTextureClient::hook_queueBuffer(ANativeWindow* window, + ANativeWindowBuffer* buffer, int fenceFd) { + SurfaceTextureClient* c = getSelf(window); + return c->queueBuffer(buffer, fenceFd); +} + +int SurfaceTextureClient::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer** buffer) { + SurfaceTextureClient* c = getSelf(window); + ANativeWindowBuffer* buf; + int fenceFd = -1; + int result = c->dequeueBuffer(&buf, &fenceFd); + sp<Fence> fence(new Fence(fenceFd)); + int waitResult = fence->waitForever(1000, "dequeueBuffer_DEPRECATED"); + if (waitResult != OK) { + ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", + waitResult); + c->cancelBuffer(buf, -1); + return waitResult; + } + *buffer = buf; + return result; +} + +int SurfaceTextureClient::hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer) { SurfaceTextureClient* c = getSelf(window); - return c->cancelBuffer(buffer); + return c->cancelBuffer(buffer, -1); } -int SurfaceTextureClient::hook_lockBuffer(ANativeWindow* window, +int SurfaceTextureClient::hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer) { SurfaceTextureClient* c = getSelf(window); - return c->lockBuffer(buffer); + return c->lockBuffer_DEPRECATED(buffer); } -int SurfaceTextureClient::hook_queueBuffer(ANativeWindow* window, +int SurfaceTextureClient::hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer) { SurfaceTextureClient* c = getSelf(window); - return c->queueBuffer(buffer); + return c->queueBuffer(buffer, -1); } int SurfaceTextureClient::hook_query(const ANativeWindow* window, @@ -157,14 +193,16 @@ int SurfaceTextureClient::setSwapInterval(int interval) { return res; } -int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) { +int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer, + int* fenceFd) { ATRACE_CALL(); ALOGV("SurfaceTextureClient::dequeueBuffer"); Mutex::Autolock lock(mMutex); int buf = -1; int reqW = mReqWidth ? mReqWidth : mUserWidth; int reqH = mReqHeight ? mReqHeight : mUserHeight; - status_t result = mSurfaceTexture->dequeueBuffer(&buf, reqW, reqH, + sp<Fence> fence; + status_t result = mSurfaceTexture->dequeueBuffer(&buf, fence, reqW, reqH, mReqFormat, mReqUsage); if (result < 0) { ALOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)" @@ -185,11 +223,25 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) { return result; } } + + if (fence.get()) { + *fenceFd = fence->dup(); + if (*fenceFd == -1) { + ALOGE("dequeueBuffer: error duping fence: %d", errno); + // dup() should never fail; something is badly wrong. Soldier on + // and hope for the best; the worst that should happen is some + // visible corruption that lasts until the next frame. + } + } else { + *fenceFd = -1; + } + *buffer = gbuf.get(); return OK; } -int SurfaceTextureClient::cancelBuffer(android_native_buffer_t* buffer) { +int SurfaceTextureClient::cancelBuffer(android_native_buffer_t* buffer, + int fenceFd) { ATRACE_CALL(); ALOGV("SurfaceTextureClient::cancelBuffer"); Mutex::Autolock lock(mMutex); @@ -197,7 +249,8 @@ int SurfaceTextureClient::cancelBuffer(android_native_buffer_t* buffer) { if (i < 0) { return i; } - mSurfaceTexture->cancelBuffer(i); + sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : NULL); + mSurfaceTexture->cancelBuffer(i, fence); return OK; } @@ -214,13 +267,13 @@ int SurfaceTextureClient::getSlotFromBufferLocked( return BAD_VALUE; } -int SurfaceTextureClient::lockBuffer(android_native_buffer_t* buffer) { +int SurfaceTextureClient::lockBuffer_DEPRECATED(android_native_buffer_t* buffer) { ALOGV("SurfaceTextureClient::lockBuffer"); Mutex::Autolock lock(mMutex); return OK; } -int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) { +int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { ATRACE_CALL(); ALOGV("SurfaceTextureClient::queueBuffer"); Mutex::Autolock lock(mMutex); @@ -237,13 +290,15 @@ int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) { return i; } + // Make sure the crop rectangle is entirely inside the buffer. Rect crop; mCrop.intersect(Rect(buffer->width, buffer->height), &crop); + sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : NULL); ISurfaceTexture::QueueBufferOutput output; ISurfaceTexture::QueueBufferInput input(timestamp, crop, mScalingMode, - mTransform); + mTransform, fence); status_t err = mSurfaceTexture->queueBuffer(i, input, &output); if (err != OK) { ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err); @@ -692,82 +747,87 @@ status_t SurfaceTextureClient::lock( } ANativeWindowBuffer* out; - status_t err = dequeueBuffer(&out); + int fenceFd = -1; + status_t err = dequeueBuffer(&out, &fenceFd); ALOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err)); if (err == NO_ERROR) { sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out)); - err = lockBuffer(backBuffer.get()); - ALOGE_IF(err, "lockBuffer (handle=%p) failed (%s)", - backBuffer->handle, strerror(-err)); - if (err == NO_ERROR) { - const Rect bounds(backBuffer->width, backBuffer->height); - - Region newDirtyRegion; - if (inOutDirtyBounds) { - newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds)); - newDirtyRegion.andSelf(bounds); - } else { - newDirtyRegion.set(bounds); - } + sp<Fence> fence(new Fence(fenceFd)); - // figure out if we can copy the frontbuffer back - const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); - const bool canCopyBack = (frontBuffer != 0 && - backBuffer->width == frontBuffer->width && - backBuffer->height == frontBuffer->height && - backBuffer->format == frontBuffer->format); - - if (canCopyBack) { - // copy the area that is invalid and not repainted this round - const Region copyback(mDirtyRegion.subtract(newDirtyRegion)); - if (!copyback.isEmpty()) - copyBlt(backBuffer, frontBuffer, copyback); - } else { - // if we can't copy-back anything, modify the user's dirty - // region to make sure they redraw the whole buffer - newDirtyRegion.set(bounds); - mDirtyRegion.clear(); - Mutex::Autolock lock(mMutex); - for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) { - mSlots[i].dirtyRegion.clear(); - } - } + err = fence->waitForever(1000, "SurfaceTextureClient::lock"); + if (err != OK) { + ALOGE("Fence::wait failed (%s)", strerror(-err)); + cancelBuffer(out, fenceFd); + return err; + } + const Rect bounds(backBuffer->width, backBuffer->height); - { // scope for the lock - Mutex::Autolock lock(mMutex); - int backBufferSlot(getSlotFromBufferLocked(backBuffer.get())); - if (backBufferSlot >= 0) { - Region& dirtyRegion(mSlots[backBufferSlot].dirtyRegion); - mDirtyRegion.subtract(dirtyRegion); - dirtyRegion = newDirtyRegion; - } - } + Region newDirtyRegion; + if (inOutDirtyBounds) { + newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds)); + newDirtyRegion.andSelf(bounds); + } else { + newDirtyRegion.set(bounds); + } - mDirtyRegion.orSelf(newDirtyRegion); - if (inOutDirtyBounds) { - *inOutDirtyBounds = newDirtyRegion.getBounds(); + // figure out if we can copy the frontbuffer back + const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); + const bool canCopyBack = (frontBuffer != 0 && + backBuffer->width == frontBuffer->width && + backBuffer->height == frontBuffer->height && + backBuffer->format == frontBuffer->format); + + if (canCopyBack) { + // copy the area that is invalid and not repainted this round + const Region copyback(mDirtyRegion.subtract(newDirtyRegion)); + if (!copyback.isEmpty()) + copyBlt(backBuffer, frontBuffer, copyback); + } else { + // if we can't copy-back anything, modify the user's dirty + // region to make sure they redraw the whole buffer + newDirtyRegion.set(bounds); + mDirtyRegion.clear(); + Mutex::Autolock lock(mMutex); + for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) { + mSlots[i].dirtyRegion.clear(); } + } + - void* vaddr; - status_t res = backBuffer->lock( - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, - newDirtyRegion.bounds(), &vaddr); - - ALOGW_IF(res, "failed locking buffer (handle = %p)", - backBuffer->handle); - - if (res != 0) { - err = INVALID_OPERATION; - } else { - mLockedBuffer = backBuffer; - outBuffer->width = backBuffer->width; - outBuffer->height = backBuffer->height; - outBuffer->stride = backBuffer->stride; - outBuffer->format = backBuffer->format; - outBuffer->bits = vaddr; + { // scope for the lock + Mutex::Autolock lock(mMutex); + int backBufferSlot(getSlotFromBufferLocked(backBuffer.get())); + if (backBufferSlot >= 0) { + Region& dirtyRegion(mSlots[backBufferSlot].dirtyRegion); + mDirtyRegion.subtract(dirtyRegion); + dirtyRegion = newDirtyRegion; } } + + mDirtyRegion.orSelf(newDirtyRegion); + if (inOutDirtyBounds) { + *inOutDirtyBounds = newDirtyRegion.getBounds(); + } + + void* vaddr; + status_t res = backBuffer->lock( + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + newDirtyRegion.bounds(), &vaddr); + + ALOGW_IF(res, "failed locking buffer (handle = %p)", + backBuffer->handle); + + if (res != 0) { + err = INVALID_OPERATION; + } else { + mLockedBuffer = backBuffer; + outBuffer->width = backBuffer->width; + outBuffer->height = backBuffer->height; + outBuffer->stride = backBuffer->stride; + outBuffer->format = backBuffer->format; + outBuffer->bits = vaddr; + } } return err; } @@ -782,7 +842,7 @@ status_t SurfaceTextureClient::unlockAndPost() status_t err = mLockedBuffer->unlock(); ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle); - err = queueBuffer(mLockedBuffer.get()); + err = queueBuffer(mLockedBuffer.get(), -1); ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)", mLockedBuffer->handle, strerror(-err)); diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk index 741534b..4a6f74f 100644 --- a/libs/gui/tests/Android.mk +++ b/libs/gui/tests/Android.mk @@ -2,14 +2,16 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := SurfaceTexture_test +LOCAL_MODULE := libgui_test LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ - Surface_test.cpp \ + BufferQueue_test.cpp \ + CpuConsumer_test.cpp \ SurfaceTextureClient_test.cpp \ SurfaceTexture_test.cpp \ + Surface_test.cpp \ LOCAL_SHARED_LIBRARIES := \ libEGL \ @@ -18,6 +20,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libgui \ libstlport \ + libsync \ libui \ libutils \ diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp new file mode 100644 index 0000000..817abb4 --- /dev/null +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -0,0 +1,117 @@ +/* + * 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. + * 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. + */ + +#define LOG_TAG "BufferQueue_test" +//#define LOG_NDEBUG 0 + +#include <gtest/gtest.h> + +#include <utils/String8.h> +#include <utils/threads.h> + +#include <ui/GraphicBuffer.h> +#include <ui/FramebufferNativeWindow.h> + +#include <gui/BufferQueue.h> + +namespace android { + +class BufferQueueTest : public ::testing::Test { +protected: + + BufferQueueTest() {} + + virtual void SetUp() { + const ::testing::TestInfo* const testInfo = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGV("Begin test: %s.%s", testInfo->test_case_name(), + testInfo->name()); + + mBQ = new BufferQueue(); + } + + virtual void TearDown() { + mBQ.clear(); + + const ::testing::TestInfo* const testInfo = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGV("End test: %s.%s", testInfo->test_case_name(), + testInfo->name()); + } + + sp<BufferQueue> mBQ; +}; + +struct DummyConsumer : public BufferQueue::ConsumerListener { + virtual void onFrameAvailable() {} + virtual void onBuffersReleased() {} +}; + +TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { + sp<DummyConsumer> dc(new DummyConsumer); + mBQ->consumerConnect(dc); + ISurfaceTexture::QueueBufferOutput qbo; + mBQ->connect(NATIVE_WINDOW_API_CPU, &qbo); + mBQ->setBufferCount(4); + + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buf; + ISurfaceTexture::QueueBufferInput qbi(0, Rect(0, 0, 1, 1), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, fence); + BufferQueue::BufferItem item; + + for (int i = 0; i < 2; i++) { + ASSERT_EQ(ISurfaceTexture::BUFFER_NEEDS_REALLOCATION, + mBQ->dequeueBuffer(&slot, fence, 1, 1, 0, + GRALLOC_USAGE_SW_READ_OFTEN)); + ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf)); + ASSERT_EQ(OK, mBQ->queueBuffer(slot, qbi, &qbo)); + ASSERT_EQ(OK, mBQ->acquireBuffer(&item)); + } + + ASSERT_EQ(ISurfaceTexture::BUFFER_NEEDS_REALLOCATION, + mBQ->dequeueBuffer(&slot, fence, 1, 1, 0, + GRALLOC_USAGE_SW_READ_OFTEN)); + ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf)); + ASSERT_EQ(OK, mBQ->queueBuffer(slot, qbi, &qbo)); + + // Acquire the third buffer, which should fail. + ASSERT_EQ(INVALID_OPERATION, mBQ->acquireBuffer(&item)); +} + +TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) { + sp<DummyConsumer> dc(new DummyConsumer); + mBQ->consumerConnect(dc); + + ASSERT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(0)); + ASSERT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(-3)); + ASSERT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount( + BufferQueue::MAX_MAX_ACQUIRED_BUFFERS+1)); + ASSERT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(100)); +} + +TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { + sp<DummyConsumer> dc(new DummyConsumer); + mBQ->consumerConnect(dc); + + ASSERT_EQ(OK, mBQ->setMaxAcquiredBufferCount(1)); + ASSERT_EQ(OK, mBQ->setMaxAcquiredBufferCount(2)); + ASSERT_EQ(OK, mBQ->setMaxAcquiredBufferCount( + BufferQueue::MAX_MAX_ACQUIRED_BUFFERS)); +} + +} // namespace android diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp new file mode 100644 index 0000000..72a36bf --- /dev/null +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -0,0 +1,509 @@ +/* + * 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. + * 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. + */ + +#define LOG_TAG "CpuConsumer_test" +//#define LOG_NDEBUG 0 +//#define LOG_NNDEBUG 0 + +#ifdef LOG_NNDEBUG +#define ALOGVV(...) ALOGV(__VA_ARGS__) +#else +#define ALOGVV(...) ((void)0) +#endif + +#include <gtest/gtest.h> +#include <gui/CpuConsumer.h> +#include <gui/SurfaceTextureClient.h> +#include <ui/GraphicBuffer.h> +#include <utils/String8.h> +#include <utils/Thread.h> +#include <utils/Mutex.h> +#include <utils/Condition.h> + +#include <ui/FramebufferNativeWindow.h> + +namespace android { + +struct CpuConsumerTestParams { + uint32_t width; + uint32_t height; + int maxLockedBuffers; + PixelFormat format; +}; + +::std::ostream& operator<<(::std::ostream& os, const CpuConsumerTestParams& p) { + return os << "[ (" << p.width << ", " << p.height << "), B:" + << p.maxLockedBuffers << ", F:0x" + << ::std::hex << p.format << "]"; +} + +class CpuConsumerTest : public ::testing::TestWithParam<CpuConsumerTestParams> { +protected: + + virtual void SetUp() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + CpuConsumerTestParams params = GetParam(); + ALOGV("** Starting test %s (%d x %d, %d, 0x%x)", + test_info->name(), + params.width, params.height, + params.maxLockedBuffers, params.format); + mCC = new CpuConsumer(params.maxLockedBuffers); + String8 name("CpuConsumer_Under_Test"); + mCC->setName(name); + mSTC = new SurfaceTextureClient(mCC->getProducerInterface()); + mANW = mSTC; + } + + virtual void TearDown() { + mANW.clear(); + mSTC.clear(); + mCC.clear(); + } + + class FrameWaiter : public CpuConsumer::FrameAvailableListener { + public: + FrameWaiter(): + mPendingFrames(0) { + } + + void waitForFrame() { + Mutex::Autolock lock(mMutex); + while (mPendingFrames == 0) { + mCondition.wait(mMutex); + } + mPendingFrames--; + } + + virtual void onFrameAvailable() { + Mutex::Autolock lock(mMutex); + mPendingFrames++; + mCondition.signal(); + } + + int mPendingFrames; + Mutex mMutex; + Condition mCondition; + }; + + // Note that SurfaceTexture will lose the notifications + // onBuffersReleased and onFrameAvailable as there is currently + // no way to forward the events. This DisconnectWaiter will not let the + // disconnect finish until finishDisconnect() is called. It will + // also block until a disconnect is called + class DisconnectWaiter : public BufferQueue::ConsumerListener { + public: + DisconnectWaiter () : + mWaitForDisconnect(false), + mPendingFrames(0) { + } + + void waitForFrame() { + Mutex::Autolock lock(mMutex); + while (mPendingFrames == 0) { + mFrameCondition.wait(mMutex); + } + mPendingFrames--; + } + + virtual void onFrameAvailable() { + Mutex::Autolock lock(mMutex); + mPendingFrames++; + mFrameCondition.signal(); + } + + virtual void onBuffersReleased() { + Mutex::Autolock lock(mMutex); + while (!mWaitForDisconnect) { + mDisconnectCondition.wait(mMutex); + } + } + + void finishDisconnect() { + Mutex::Autolock lock(mMutex); + mWaitForDisconnect = true; + mDisconnectCondition.signal(); + } + + private: + Mutex mMutex; + + bool mWaitForDisconnect; + Condition mDisconnectCondition; + + int mPendingFrames; + Condition mFrameCondition; + }; + + sp<CpuConsumer> mCC; + sp<SurfaceTextureClient> mSTC; + sp<ANativeWindow> mANW; +}; + +#define ASSERT_NO_ERROR(err, msg) \ + ASSERT_EQ(NO_ERROR, err) << msg << strerror(-err) + +void checkPixel(const CpuConsumer::LockedBuffer &buf, + uint32_t x, uint32_t y, uint32_t r, uint32_t g, uint32_t b) { + // Ignores components that don't exist for given pixel + switch(buf.format) { + case HAL_PIXEL_FORMAT_RAW_SENSOR: { + String8 msg; + uint16_t *bPtr = (uint16_t*)buf.data; + bPtr += y * buf.stride + x; + // GRBG Bayer mosaic; only check the matching channel + switch( ((y & 1) << 1) | (x & 1) ) { + case 0: // G + case 3: // G + EXPECT_EQ(g, *bPtr); + break; + case 1: // R + EXPECT_EQ(r, *bPtr); + break; + case 2: // B + EXPECT_EQ(b, *bPtr); + break; + } + break; + } + default: { + ADD_FAILURE() << "Unknown format for check:" << buf.format; + break; + } + } +} + +// Fill a YV12 buffer with a multi-colored checkerboard pattern +void fillYV12Buffer(uint8_t* buf, int w, int h, int stride); + +// Fill a RAW sensor buffer with a multi-colored checkerboard pattern. +// Assumes GRBG mosaic ordering. Result should be a grid in a 2x2 pattern +// of [ R, B; G, W] +void fillBayerRawBuffer(uint8_t* buf, int w, int h, int stride) { + ALOGVV("fillBayerRawBuffer: %p with %d x %d, stride %d", buf, w, h ,stride); + // Blocks need to be even-width/height, aim for 8-wide otherwise + const int blockWidth = (w > 16 ? w / 8 : 2) & ~0x1; + const int blockHeight = (h > 16 ? h / 8 : 2) & ~0x1; + for (int y = 0; y < h; y+=2) { + uint16_t *bPtr1 = ((uint16_t*)buf) + stride*y; + uint16_t *bPtr2 = bPtr1 + stride; + for (int x = 0; x < w; x+=2) { + int blockX = (x / blockWidth ) & 1; + int blockY = (y / blockHeight) & 1; + unsigned short r = (blockX == blockY) ? 1000 : 200; + unsigned short g = blockY ? 1000: 200; + unsigned short b = blockX ? 1000: 200; + // GR row + *bPtr1++ = g; + *bPtr1++ = r; + // BG row + *bPtr2++ = b; + *bPtr2++ = g; + } + } + +} + +void checkBayerRawBuffer(const CpuConsumer::LockedBuffer &buf) { + uint32_t w = buf.width; + uint32_t h = buf.height; + const int blockWidth = (w > 16 ? w / 8 : 2) & ~0x1; + const int blockHeight = (h > 16 ? h / 8 : 2) & ~0x1; + const int blockRows = h / blockHeight; + const int blockCols = w / blockWidth; + + // Top-left square is red + checkPixel(buf, 0, 0, 1000, 200, 200); + checkPixel(buf, 1, 0, 1000, 200, 200); + checkPixel(buf, 0, 1, 1000, 200, 200); + checkPixel(buf, 1, 1, 1000, 200, 200); + + // One-right square is blue + checkPixel(buf, blockWidth, 0, 200, 200, 1000); + checkPixel(buf, blockWidth + 1, 0, 200, 200, 1000); + checkPixel(buf, blockWidth, 1, 200, 200, 1000); + checkPixel(buf, blockWidth + 1, 1, 200, 200, 1000); + + // One-down square is green + checkPixel(buf, 0, blockHeight, 200, 1000, 200); + checkPixel(buf, 1, blockHeight, 200, 1000, 200); + checkPixel(buf, 0, blockHeight + 1, 200, 1000, 200); + checkPixel(buf, 1, blockHeight + 1, 200, 1000, 200); + + // One-diag square is white + checkPixel(buf, blockWidth, blockHeight, 1000, 1000, 1000); + checkPixel(buf, blockWidth + 1, blockHeight, 1000, 1000, 1000); + checkPixel(buf, blockWidth, blockHeight + 1, 1000, 1000, 1000); + checkPixel(buf, blockWidth + 1, blockHeight + 1, 1000, 1000, 1000); + + // Test bottom-right pixel + const int maxBlockX = ((w-1) / blockWidth) & 0x1; + const int maxBlockY = ((w-1) / blockHeight) & 0x1; + unsigned short maxR = (maxBlockX == maxBlockY) ? 1000 : 200; + unsigned short maxG = maxBlockY ? 1000: 200; + unsigned short maxB = maxBlockX ? 1000: 200; + checkPixel(buf, w-1, h-1, maxR, maxG, maxB); +} + +void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, + const android_native_rect_t& rect); + +void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride); + +void fillRGBA8BufferSolid(uint8_t* buf, int w, int h, int stride, uint8_t r, + uint8_t g, uint8_t b, uint8_t a); + +// Configures the ANativeWindow producer-side interface based on test parameters +void configureANW(const sp<ANativeWindow>& anw, + const CpuConsumerTestParams& params, + int maxBufferSlack) { + status_t err; + err = native_window_set_buffers_geometry(anw.get(), + params.width, params.height, params.format); + ASSERT_NO_ERROR(err, "set_buffers_geometry error: "); + + err = native_window_set_usage(anw.get(), + GRALLOC_USAGE_SW_WRITE_OFTEN); + ASSERT_NO_ERROR(err, "set_usage error: "); + + int minUndequeuedBuffers; + err = anw.get()->query(anw.get(), + NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &minUndequeuedBuffers); + ASSERT_NO_ERROR(err, "query error: "); + + ALOGVV("Setting buffer count to %d", + maxBufferSlack + 1 + minUndequeuedBuffers); + err = native_window_set_buffer_count(anw.get(), + maxBufferSlack + 1 + minUndequeuedBuffers); + ASSERT_NO_ERROR(err, "set_buffer_count error: "); + +} + +// Produce one frame of image data; assumes format and resolution configuration +// is already done. +void produceOneFrame(const sp<ANativeWindow>& anw, + const CpuConsumerTestParams& params, + int64_t timestamp, uint32_t *stride) { + status_t err; + ANativeWindowBuffer* anb; + ALOGVV("Dequeue buffer from %p", anw.get()); + err = native_window_dequeue_buffer_and_wait(anw.get(), &anb); + ASSERT_NO_ERROR(err, "dequeueBuffer error: "); + + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + + *stride = buf->getStride(); + uint8_t* img = NULL; + + ALOGVV("Lock buffer from %p for write", anw.get()); + err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + ASSERT_NO_ERROR(err, "lock error: "); + + switch (params.format) { + case HAL_PIXEL_FORMAT_YV12: + fillYV12Buffer(img, params.width, params.height, *stride); + break; + case HAL_PIXEL_FORMAT_RAW_SENSOR: + fillBayerRawBuffer(img, params.width, params.height, buf->getStride()); + break; + default: + FAIL() << "Unknown pixel format under test!"; + break; + } + ALOGVV("Unlock buffer from %p", anw.get()); + err = buf->unlock(); + ASSERT_NO_ERROR(err, "unlock error: "); + + ALOGVV("Set timestamp to %p", anw.get()); + err = native_window_set_buffers_timestamp(anw.get(), timestamp); + ASSERT_NO_ERROR(err, "set_buffers_timestamp error: "); + + ALOGVV("Queue buffer to %p", anw.get()); + err = anw->queueBuffer(anw.get(), buf->getNativeBuffer(), -1); + ASSERT_NO_ERROR(err, "queueBuffer error:"); +}; + +// This test is disabled because the HAL_PIXEL_FORMAT_RAW_SENSOR format is not +// supported on all devices. +TEST_P(CpuConsumerTest, DISABLED_FromCpuSingle) { + status_t err; + CpuConsumerTestParams params = GetParam(); + + // Set up + + ASSERT_NO_FATAL_FAILURE(configureANW(mANW, params, 1)); + + // Produce + + const int64_t time = 12345678L; + uint32_t stride; + ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time, + &stride)); + + // Consume + + CpuConsumer::LockedBuffer b; + err = mCC->lockNextBuffer(&b); + ASSERT_NO_ERROR(err, "getNextBuffer error: "); + + ASSERT_TRUE(b.data != NULL); + EXPECT_EQ(params.width, b.width); + EXPECT_EQ(params.height, b.height); + EXPECT_EQ(params.format, b.format); + EXPECT_EQ(stride, b.stride); + EXPECT_EQ(time, b.timestamp); + + checkBayerRawBuffer(b); + mCC->unlockBuffer(b); +} + +// This test is disabled because the HAL_PIXEL_FORMAT_RAW_SENSOR format is not +// supported on all devices. +TEST_P(CpuConsumerTest, DISABLED_FromCpuManyInQueue) { + status_t err; + CpuConsumerTestParams params = GetParam(); + + const int numInQueue = 5; + // Set up + + ASSERT_NO_FATAL_FAILURE(configureANW(mANW, params, numInQueue)); + + // Produce + + const int64_t time[numInQueue] = { 1L, 2L, 3L, 4L, 5L}; + uint32_t stride[numInQueue]; + + for (int i = 0; i < numInQueue; i++) { + ALOGV("Producing frame %d", i); + ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time[i], + &stride[i])); + } + + // Consume + + for (int i = 0; i < numInQueue; i++) { + ALOGV("Consuming frame %d", i); + CpuConsumer::LockedBuffer b; + err = mCC->lockNextBuffer(&b); + ASSERT_NO_ERROR(err, "getNextBuffer error: "); + + ASSERT_TRUE(b.data != NULL); + EXPECT_EQ(params.width, b.width); + EXPECT_EQ(params.height, b.height); + EXPECT_EQ(params.format, b.format); + EXPECT_EQ(stride[i], b.stride); + EXPECT_EQ(time[i], b.timestamp); + + checkBayerRawBuffer(b); + + mCC->unlockBuffer(b); + } +} + +// This test is disabled because the HAL_PIXEL_FORMAT_RAW_SENSOR format is not +// supported on all devices. +TEST_P(CpuConsumerTest, DISABLED_FromCpuLockMax) { + status_t err; + CpuConsumerTestParams params = GetParam(); + + // Set up + + ASSERT_NO_FATAL_FAILURE(configureANW(mANW, params, params.maxLockedBuffers + 1)); + + // Produce + + const int64_t time = 1234L; + uint32_t stride; + + for (int i = 0; i < params.maxLockedBuffers + 1; i++) { + ALOGV("Producing frame %d", i); + ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time, + &stride)); + } + + // Consume + + CpuConsumer::LockedBuffer *b = new CpuConsumer::LockedBuffer[params.maxLockedBuffers]; + for (int i = 0; i < params.maxLockedBuffers; i++) { + ALOGV("Locking frame %d", i); + err = mCC->lockNextBuffer(&b[i]); + ASSERT_NO_ERROR(err, "getNextBuffer error: "); + + ASSERT_TRUE(b[i].data != NULL); + EXPECT_EQ(params.width, b[i].width); + EXPECT_EQ(params.height, b[i].height); + EXPECT_EQ(params.format, b[i].format); + EXPECT_EQ(stride, b[i].stride); + EXPECT_EQ(time, b[i].timestamp); + + checkBayerRawBuffer(b[i]); + } + + ALOGV("Locking frame %d (too many)", params.maxLockedBuffers); + CpuConsumer::LockedBuffer bTooMuch; + err = mCC->lockNextBuffer(&bTooMuch); + ASSERT_TRUE(err == INVALID_OPERATION) << "Allowing too many locks"; + + ALOGV("Unlocking frame 0"); + err = mCC->unlockBuffer(b[0]); + ASSERT_NO_ERROR(err, "Could not unlock buffer 0: "); + + ALOGV("Locking frame %d (should work now)", params.maxLockedBuffers); + err = mCC->lockNextBuffer(&bTooMuch); + ASSERT_NO_ERROR(err, "Did not allow new lock after unlock"); + + ASSERT_TRUE(bTooMuch.data != NULL); + EXPECT_EQ(params.width, bTooMuch.width); + EXPECT_EQ(params.height, bTooMuch.height); + EXPECT_EQ(params.format, bTooMuch.format); + EXPECT_EQ(stride, bTooMuch.stride); + EXPECT_EQ(time, bTooMuch.timestamp); + + checkBayerRawBuffer(bTooMuch); + + ALOGV("Unlocking extra buffer"); + err = mCC->unlockBuffer(bTooMuch); + ASSERT_NO_ERROR(err, "Could not unlock extra buffer: "); + + ALOGV("Locking frame %d (no more available)", params.maxLockedBuffers + 1); + err = mCC->lockNextBuffer(&b[0]); + ASSERT_EQ(BAD_VALUE, err) << "Not out of buffers somehow"; + + for (int i = 1; i < params.maxLockedBuffers; i++) { + mCC->unlockBuffer(b[i]); + } + + delete[] b; + +} + +CpuConsumerTestParams rawTestSets[] = { + { 512, 512, 1, HAL_PIXEL_FORMAT_RAW_SENSOR}, + { 512, 512, 3, HAL_PIXEL_FORMAT_RAW_SENSOR}, + { 2608, 1960, 1, HAL_PIXEL_FORMAT_RAW_SENSOR}, + { 2608, 1960, 3, HAL_PIXEL_FORMAT_RAW_SENSOR}, + { 100, 100, 1, HAL_PIXEL_FORMAT_RAW_SENSOR}, + { 100, 100, 3, HAL_PIXEL_FORMAT_RAW_SENSOR} +}; + +INSTANTIATE_TEST_CASE_P(RawTests, + CpuConsumerTest, + ::testing::ValuesIn(rawTestSets)); + +} // namespace android diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index 7d8dd33..ec14a0d 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -180,129 +180,129 @@ TEST_F(SurfaceTextureClientTest, BufferGeometryInvalidSizesFail) { TEST_F(SurfaceTextureClientTest, DefaultGeometryValues) { ANativeWindowBuffer* buf; - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(1, buf->width); EXPECT_EQ(1, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, BufferGeometryCanBeSet) { ANativeWindowBuffer* buf; EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 16, 8, PIXEL_FORMAT_RGB_565)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, BufferGeometryDefaultSizeSetFormat) { ANativeWindowBuffer* buf; EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 0, 0, PIXEL_FORMAT_RGB_565)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(1, buf->width); EXPECT_EQ(1, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, BufferGeometrySetSizeDefaultFormat) { ANativeWindowBuffer* buf; EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 16, 8, 0)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeUnset) { ANativeWindowBuffer* buf; EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 16, 8, 0)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 0, 0, 0)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(1, buf->width); EXPECT_EQ(1, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeChangedWithoutFormat) { ANativeWindowBuffer* buf; EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 0, 0, PIXEL_FORMAT_RGB_565)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(1, buf->width); EXPECT_EQ(1, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); - EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 16, 8, 0)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); + EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 16, 8)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSize) { sp<SurfaceTexture> st(mST); ANativeWindowBuffer* buf; EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeAfterDequeue) { ANativeWindowBuffer* buf[2]; ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0])); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0], -1)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1], -1)); EXPECT_EQ(OK, mST->setDefaultBufferSize(16, 8)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); EXPECT_EQ(16, buf[0]->width); EXPECT_EQ(16, buf[1]->width); EXPECT_EQ(8, buf[0]->height); EXPECT_EQ(8, buf[1]->height); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0])); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0], -1)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1], -1)); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeVsGeometry) { ANativeWindowBuffer* buf[2]; ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); EXPECT_EQ(OK, mST->setDefaultBufferSize(16, 8)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); EXPECT_EQ(16, buf[0]->width); EXPECT_EQ(16, buf[1]->width); EXPECT_EQ(8, buf[0]->height); EXPECT_EQ(8, buf[1]->height); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0])); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0], -1)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1], -1)); EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 12, 24, 0)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); EXPECT_EQ(12, buf[0]->width); EXPECT_EQ(12, buf[1]->width); EXPECT_EQ(24, buf[0]->height); EXPECT_EQ(24, buf[1]->height); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0])); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0], -1)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1], -1)); } TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) { @@ -310,18 +310,18 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) { ASSERT_EQ(OK, mST->setSynchronousMode(false)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(OK, mST->updateTexImage()); ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(OK, mST->updateTexImage()); @@ -332,15 +332,15 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeSlowRetire) { android_native_buffer_t* buf[3]; ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); EXPECT_NE(buf[0], buf[1]); EXPECT_NE(buf[1], buf[2]); EXPECT_NE(buf[2], buf[0]); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[0]); EXPECT_EQ(OK, mST->updateTexImage()); @@ -353,19 +353,19 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeFastRetire) { android_native_buffer_t* buf[3]; ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); EXPECT_NE(buf[0], buf[1]); EXPECT_NE(buf[1], buf[2]); EXPECT_NE(buf[2], buf[0]); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[0]); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[1]); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[2]); } @@ -375,20 +375,20 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) { ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[0]); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[1]); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); EXPECT_NE(buf[1], buf[2]); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[2]); } @@ -400,16 +400,16 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeDequeueCurrent) android_native_buffer_t* firstBuf; ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &firstBuf)); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), firstBuf)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &firstBuf)); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), firstBuf, -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), firstBuf); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2], -1)); EXPECT_NE(buf[0], buf[1]); EXPECT_NE(buf[1], buf[2]); EXPECT_NE(buf[2], buf[0]); @@ -422,24 +422,24 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) { ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); // We should be able to dequeue all the buffers before we've queued mANWy. - EXPECT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - EXPECT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); - EXPECT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); + EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); + EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[2])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[2], -1)); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[1]); - EXPECT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); + EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); // Once we've queued a buffer, however we should not be able to dequeue more // than (buffer-count - MIN_UNDEQUEUED_BUFFERS), which is 2 in this case. - EXPECT_EQ(-EBUSY, mANW->dequeueBuffer(mANW.get(), &buf[1])); + EXPECT_EQ(-EBUSY, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0])); - ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[2])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0], -1)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[2], -1)); } TEST_F(SurfaceTextureClientTest, SetCropCropsCrop) { @@ -449,8 +449,8 @@ TEST_F(SurfaceTextureClientTest, SetCropCropsCrop) { ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 4, 4)); android_native_buffer_t* buf; - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf)); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf, -1)); ASSERT_EQ(OK, mST->updateTexImage()); Rect crop = mST->getCurrentCrop(); @@ -500,20 +500,20 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) { ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); // dequeue/queue/update so we have a current buffer - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); mST->updateTexImage(); MyThread* thread = new MyThread(mST); sp<Thread> threadBase(thread); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); thread->run(); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); - //ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); - //ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); + //ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); + //ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2], -1)); thread->bufferDequeued(); thread->requestExitAndWait(); } @@ -522,8 +522,8 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixReturnsVerticalFlip) { android_native_buffer_t* buf[3]; float mtx[16] = {}; ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, mST->updateTexImage()); mST->getTransformMatrix(mtx); @@ -552,8 +552,8 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) android_native_buffer_t* buf[3]; float mtx[16] = {}; ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, mST->updateTexImage()); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 6)); // frees buffers mST->getTransformMatrix(mtx); @@ -590,22 +590,22 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWi ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); ASSERT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 8, 8, 0)); - ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &crop)); - ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, mST->updateTexImage()); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 6)); // frees buffers mST->getTransformMatrix(mtx); - // This accounts for the 1 texel shrink for each edge that's included in the + // This accounts for the .5 texel shrink for each edge that's included in the // transform matrix to avoid texturing outside the crop region. - EXPECT_EQ(.375f, mtx[0]); + EXPECT_EQ(0.5, mtx[0]); EXPECT_EQ(0.f, mtx[1]); EXPECT_EQ(0.f, mtx[2]); EXPECT_EQ(0.f, mtx[3]); EXPECT_EQ(0.f, mtx[4]); - EXPECT_EQ(-.375f, mtx[5]); + EXPECT_EQ(-0.5, mtx[5]); EXPECT_EQ(0.f, mtx[6]); EXPECT_EQ(0.f, mtx[7]); @@ -614,8 +614,8 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWi EXPECT_EQ(1.f, mtx[10]); EXPECT_EQ(0.f, mtx[11]); - EXPECT_EQ(.125f, mtx[12]); - EXPECT_EQ(.5f, mtx[13]); + EXPECT_EQ(0.0625f, mtx[12]); + EXPECT_EQ(0.5625f, mtx[13]); EXPECT_EQ(0.f, mtx[14]); EXPECT_EQ(1.f, mtx[15]); } diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index 078c17b..d9b40cf 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -83,7 +83,7 @@ protected: ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); mSurfaceControl = mComposerClient->createSurface( - String8("Test Surface"), 0, + String8("Test Surface"), getSurfaceWidth(), getSurfaceHeight(), PIXEL_FORMAT_RGB_888, 0); @@ -672,18 +672,19 @@ void fillRGBA8BufferSolid(uint8_t* buf, int w, int h, int stride, uint8_t r, // Calls to this function should be wrapped in an ASSERT_NO_FATAL_FAILURE(). void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) { android_native_buffer_t* anb; - ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &anb)); + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), + &anb)); ASSERT_TRUE(anb != NULL); sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - ASSERT_EQ(NO_ERROR, anw->lockBuffer(anw.get(), buf->getNativeBuffer())); uint8_t* img = NULL; ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img))); fillRGBA8Buffer(img, buf->getWidth(), buf->getHeight(), buf->getStride()); ASSERT_EQ(NO_ERROR, buf->unlock()); - ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf->getNativeBuffer())); + ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf->getNativeBuffer(), + -1)); } TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { @@ -696,20 +697,21 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); ANativeWindowBuffer* anb; - ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); ASSERT_TRUE(anb != NULL); sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); // Fill the buffer with the a checkerboard pattern uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); buf->unlock(); - ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), + -1)); - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); @@ -741,20 +743,21 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) { GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); ANativeWindowBuffer* anb; - ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); ASSERT_TRUE(anb != NULL); sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); // Fill the buffer with the a checkerboard pattern uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); buf->unlock(); - ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), + -1)); - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); @@ -801,21 +804,20 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop)); ANativeWindowBuffer* anb; - ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); ASSERT_TRUE(anb != NULL); sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), - buf->getNativeBuffer())); uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop); buf->unlock(); ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), - buf->getNativeBuffer())); + buf->getNativeBuffer(), -1)); - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); @@ -846,7 +848,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { enum { numFrames = 1024 }; ASSERT_EQ(NO_ERROR, mST->setSynchronousMode(true)); - ASSERT_EQ(NO_ERROR, mST->setBufferCountServer(2)); + ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), @@ -877,7 +879,8 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { virtual bool threadLoop() { for (int i = 0; i < numFrames; i++) { ANativeWindowBuffer* anb; - if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + if (native_window_dequeue_buffer_and_wait(mANW.get(), + &anb) != NO_ERROR) { return false; } if (anb == NULL) { @@ -885,10 +888,6 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { } sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - if (mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()) - != NO_ERROR) { - return false; - } const int yuvTexOffsetY = 0; int stride = buf->getStride(); @@ -932,7 +931,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { } buf->unlock(); - if (mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()) + if (mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), -1) != NO_ERROR) { return false; } @@ -967,7 +966,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { if (i > 1) { mFW->waitForFrame(); } - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); drawTexture(); for (int j = 0; j < numTestPixels; j++) { @@ -998,7 +997,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferNpot) { ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); @@ -1040,7 +1039,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferPow2) { ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); @@ -1093,13 +1092,14 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { for (int numFrames =0 ; numFrames < 2; numFrames ++) { - if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + if (native_window_dequeue_buffer_and_wait(mANW.get(), + &anb) != NO_ERROR) { return false; } if (anb == NULL) { return false; } - if (mANW->queueBuffer(mANW.get(), anb) + if (mANW->queueBuffer(mANW.get(), anb, -1) != NO_ERROR) { return false; } @@ -1147,11 +1147,11 @@ TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) { ANativeWindowBuffer *anb; - EXPECT_EQ (OK, mANW->dequeueBuffer(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); - EXPECT_EQ (OK, mANW->dequeueBuffer(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); EXPECT_EQ(OK,mST->updateTexImage()); EXPECT_EQ(OK,mST->updateTexImage()); @@ -1163,8 +1163,8 @@ TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) { ASSERT_EQ(OK, mST->setSynchronousMode(true)); - EXPECT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); // Will fail here if mCurrentTexture is not cleared properly mFW->waitForFrame(); @@ -1193,10 +1193,10 @@ TEST_F(SurfaceTextureGLTest, ScaleToWindowMode) { android_native_rect_t odd = {23, 78, 123, 477}; ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &odd)); - EXPECT_EQ (OK, mANW->dequeueBuffer(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); mFW->waitForFrame(); - EXPECT_EQ(OK,mST->updateTexImage()); + EXPECT_EQ(OK, mST->updateTexImage()); Rect r = mST->getCurrentCrop(); assertRectEq(Rect(23, 78, 123, 477), r); @@ -1227,10 +1227,10 @@ TEST_F(SurfaceTextureGLTest, CroppedScalingMode) { // The crop is in the shape of (320, 180) === 16 x 9 android_native_rect_t standard = {10, 20, 330, 200}; ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &standard)); - EXPECT_EQ (OK, mANW->dequeueBuffer(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); mFW->waitForFrame(); - EXPECT_EQ(OK,mST->updateTexImage()); + EXPECT_EQ(OK, mST->updateTexImage()); Rect r = mST->getCurrentCrop(); // crop should be the same as crop (same aspect ratio) assertRectEq(Rect(10, 20, 330, 200), r); @@ -1238,10 +1238,10 @@ TEST_F(SurfaceTextureGLTest, CroppedScalingMode) { // make this wider then desired aspect 239 x 100 (2.39:1) android_native_rect_t wide = {20, 30, 259, 130}; ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &wide)); - EXPECT_EQ (OK, mANW->dequeueBuffer(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); mFW->waitForFrame(); - EXPECT_EQ(OK,mST->updateTexImage()); + EXPECT_EQ(OK, mST->updateTexImage()); r = mST->getCurrentCrop(); // crop should be the same height, but have cropped left and right borders // offset is 30.6 px L+, R- @@ -1250,10 +1250,10 @@ TEST_F(SurfaceTextureGLTest, CroppedScalingMode) { // This image is taller then desired aspect 400 x 300 (4:3) android_native_rect_t narrow = {0, 0, 400, 300}; ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &narrow)); - EXPECT_EQ (OK, mANW->dequeueBuffer(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); mFW->waitForFrame(); - EXPECT_EQ(OK,mST->updateTexImage()); + EXPECT_EQ(OK, mST->updateTexImage()); r = mST->getCurrentCrop(); // crop should be the same width, but have cropped top and bottom borders // offset is 37.5 px @@ -1278,31 +1278,34 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { ANativeWindowBuffer* anb; // Frame 1 - if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + if (native_window_dequeue_buffer_and_wait(mANW.get(), + &anb) != NO_ERROR) { return false; } if (anb == NULL) { return false; } - if (mANW->queueBuffer(mANW.get(), anb) + if (mANW->queueBuffer(mANW.get(), anb, -1) != NO_ERROR) { return false; } // Frame 2 - if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + if (native_window_dequeue_buffer_and_wait(mANW.get(), + &anb) != NO_ERROR) { return false; } if (anb == NULL) { return false; } - if (mANW->queueBuffer(mANW.get(), anb) + if (mANW->queueBuffer(mANW.get(), anb, -1) != NO_ERROR) { return false; } // Frame 3 - error expected - mDequeueError = mANW->dequeueBuffer(mANW.get(), &anb); + mDequeueError = native_window_dequeue_buffer_and_wait(mANW.get(), + &anb); return false; } @@ -1318,7 +1321,7 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { }; ASSERT_EQ(OK, mST->setSynchronousMode(true)); - ASSERT_EQ(OK, mST->setBufferCountServer(2)); + ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2)); sp<Thread> pt(new ProducerThread(mANW)); pt->run(); @@ -1346,26 +1349,29 @@ TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) { // make sure it works with small textures mST->setDefaultBufferSize(16, texHeight); - EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + EXPECT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); EXPECT_EQ(16, anb->width); EXPECT_EQ(texHeight, anb->height); - EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb, -1)); EXPECT_EQ(NO_ERROR, mST->updateTexImage()); // make sure it works with GL_MAX_TEXTURE_SIZE mST->setDefaultBufferSize(maxTextureSize, texHeight); - EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + EXPECT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); EXPECT_EQ(maxTextureSize, anb->width); EXPECT_EQ(texHeight, anb->height); - EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb, -1)); EXPECT_EQ(NO_ERROR, mST->updateTexImage()); // make sure it fails with GL_MAX_TEXTURE_SIZE+1 mST->setDefaultBufferSize(maxTextureSize+1, texHeight); - EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + EXPECT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); EXPECT_EQ(maxTextureSize+1, anb->width); EXPECT_EQ(texHeight, anb->height); - EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb, -1)); ASSERT_NE(NO_ERROR, mST->updateTexImage()); } @@ -1408,12 +1414,68 @@ protected: EGLContext mProducerEglContext; }; +TEST_F(SurfaceTextureGLToGLTest, TransformHintGetsRespected) { + const uint32_t texWidth = 32; + const uint32_t texHeight = 64; + + mST->setDefaultBufferSize(texWidth, texHeight); + mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90); + + // This test requires 3 buffers to avoid deadlock because we're + // both producer and consumer, and only using one thread. + mST->setDefaultMaxBufferCount(3); + + // Do the producer side of things + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Start a buffer with our chosen size and transform hint moving + // through the system. + glClear(GL_COLOR_BUFFER_BIT); // give the driver something to do + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + mST->updateTexImage(); // consume it + // Swap again. + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + mST->updateTexImage(); + + // The current buffer should either show the effects of the transform + // hint (in the form of an inverse transform), or show that the + // transform hint has been ignored. + sp<GraphicBuffer> buf = mST->getCurrentBuffer(); + if (mST->getCurrentTransform() == NATIVE_WINDOW_TRANSFORM_ROT_270) { + ASSERT_EQ(texWidth, buf->getHeight()); + ASSERT_EQ(texHeight, buf->getWidth()); + } else { + ASSERT_EQ(texWidth, buf->getWidth()); + ASSERT_EQ(texHeight, buf->getHeight()); + } + + // Reset the transform hint and confirm that it takes. + mST->setTransformHint(0); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + mST->updateTexImage(); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + mST->updateTexImage(); + + buf = mST->getCurrentBuffer(); + ASSERT_EQ((uint32_t) 0, mST->getCurrentTransform()); + ASSERT_EQ(texWidth, buf->getWidth()); + ASSERT_EQ(texHeight, buf->getHeight()); +} + TEST_F(SurfaceTextureGLToGLTest, TexturingFromGLFilledRGBABufferPow2) { const int texWidth = 64; const int texHeight = 64; mST->setDefaultBufferSize(texWidth, texHeight); + // This test requires 3 buffers to complete run on a single thread. + mST->setDefaultMaxBufferCount(3); + // Do the producer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext)); @@ -1447,8 +1509,9 @@ TEST_F(SurfaceTextureGLToGLTest, TexturingFromGLFilledRGBABufferPow2) { glDisable(GL_SCISSOR_TEST); - mST->updateTexImage(); // Skip the first frame, which was empty - mST->updateTexImage(); + // Skip the first frame, which was empty + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); @@ -1502,7 +1565,7 @@ TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) { mEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); mFW->waitForFrame(); - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); buffers[i] = mST->getCurrentBuffer(); } @@ -1578,7 +1641,7 @@ TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) { TEST_F(SurfaceTextureGLToGLTest, EglSurfaceDefaultsToSynchronousMode) { // This test requires 3 buffers to run on a single thread. - mST->setBufferCountServer(3); + mST->setDefaultMaxBufferCount(3); ASSERT_TRUE(mST->isSynchronousMode()); @@ -1605,6 +1668,9 @@ TEST_F(SurfaceTextureGLToGLTest, TexturingFromUserSizedGLFilledBuffer) { enum { texWidth = 64 }; enum { texHeight = 64 }; + // This test requires 3 buffers to complete run on a single thread. + mST->setDefaultMaxBufferCount(3); + // Set the user buffer size. native_window_set_buffers_user_dimensions(mANW.get(), texWidth, texHeight); @@ -1633,8 +1699,9 @@ TEST_F(SurfaceTextureGLToGLTest, TexturingFromUserSizedGLFilledBuffer) { glDisable(GL_SCISSOR_TEST); - mST->updateTexImage(); // Skip the first frame, which was empty - mST->updateTexImage(); + // Skip the first frame, which was empty + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); @@ -1658,6 +1725,9 @@ TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedUserSizedGLFilledBuffer) enum { texWidth = 64 }; enum { texHeight = 16 }; + // This test requires 3 buffers to complete run on a single thread. + mST->setDefaultMaxBufferCount(3); + // Set the transform hint. mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90); @@ -1690,8 +1760,9 @@ TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedUserSizedGLFilledBuffer) glDisable(GL_SCISSOR_TEST); - mST->updateTexImage(); // Skip the first frame, which was empty - mST->updateTexImage(); + // Skip the first frame, which was empty + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); @@ -1715,6 +1786,9 @@ TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedGLFilledBuffer) { enum { texWidth = 64 }; enum { texHeight = 16 }; + // This test requires 3 buffers to complete run on a single thread. + mST->setDefaultMaxBufferCount(3); + // Set the transform hint. mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90); @@ -1747,8 +1821,9 @@ TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedGLFilledBuffer) { glDisable(GL_SCISSOR_TEST); - mST->updateTexImage(); // Skip the first frame, which was empty - mST->updateTexImage(); + // Skip the first frame, which was empty + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); @@ -1938,7 +2013,7 @@ TEST_F(SurfaceTextureGLThreadToGLTest, runProducerThread(new PT()); mFC->waitForFrame(); - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); mFC->finishFrame(); // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! @@ -1958,7 +2033,7 @@ TEST_F(SurfaceTextureGLThreadToGLTest, mFC->waitForFrame(); mFC->finishFrame(); - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } @@ -1984,7 +2059,7 @@ TEST_F(SurfaceTextureGLThreadToGLTest, for (int i = 0; i < NUM_ITERATIONS; i++) { mFC->waitForFrame(); ALOGV("+updateTexImage"); - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); ALOGV("-updateTexImage"); mFC->finishFrame(); @@ -2014,7 +2089,7 @@ TEST_F(SurfaceTextureGLThreadToGLTest, mFC->waitForFrame(); mFC->finishFrame(); ALOGV("+updateTexImage"); - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); ALOGV("-updateTexImage"); // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! @@ -2039,7 +2114,7 @@ TEST_F(SurfaceTextureGLThreadToGLTest, }; ASSERT_EQ(OK, mST->setSynchronousMode(true)); - ASSERT_EQ(OK, mST->setBufferCountServer(2)); + ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2)); runProducerThread(new PT()); @@ -2053,10 +2128,10 @@ TEST_F(SurfaceTextureGLThreadToGLTest, // We must call updateTexImage to consume the first frame so that the // SurfaceTexture is able to reduce the buffer count to 2. This is because // the GL driver may dequeue a buffer when the EGLSurface is created, and - // that happens before we call setBufferCountServer. It's possible that the + // that happens before we call setDefaultMaxBufferCount. It's possible that the // driver does not dequeue a buffer at EGLSurface creation time, so we // cannot rely on this to cause the second dequeueBuffer call to block. - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); mFC->waitForFrame(); mFC->finishFrame(); @@ -2075,15 +2150,15 @@ TEST_F(SurfaceTextureGLThreadToGLTest, } // Consume the two pending buffers to unblock the producer thread. - mST->updateTexImage(); - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); // Consume the remaining buffers from the producer thread. for (int i = 0; i < NUM_ITERATIONS-3; i++) { mFC->waitForFrame(); mFC->finishFrame(); ALOGV("+updateTexImage"); - mST->updateTexImage(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); ALOGV("-updateTexImage"); } } @@ -2134,11 +2209,11 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); android_native_buffer_t* anb; - ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); ASSERT_TRUE(anb != NULL); sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); // Fill the buffer with green uint8_t* img = NULL; @@ -2146,7 +2221,8 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255, 0, 255); buf->unlock(); - ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), + -1)); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); @@ -2157,12 +2233,11 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { for (int i = 0; i < 4; i++) { SCOPED_TRACE(String8::format("frame %d", i).string()); - ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); ASSERT_TRUE(anb != NULL); buf = new GraphicBuffer(anb, false); - ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), - buf->getNativeBuffer())); // Fill the buffer with red ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, @@ -2171,7 +2246,7 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { 0, 255); ASSERT_EQ(NO_ERROR, buf->unlock()); ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), - buf->getNativeBuffer())); + buf->getNativeBuffer(), -1)); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); @@ -2580,7 +2655,7 @@ TEST_F(SurfaceTextureMultiContextGLTest, TEST_F(SurfaceTextureMultiContextGLTest, UpdateTexImageSucceedsForBufferConsumedBeforeDetach) { ASSERT_EQ(NO_ERROR, mST->setSynchronousMode(true)); - ASSERT_EQ(NO_ERROR, mST->setBufferCountServer(2)); + ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2)); // produce two frames and consume them both on the primary context ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index b585d68..545b547 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -33,7 +33,7 @@ protected: ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); mSurfaceControl = mComposerClient->createSurface( - String8("Test Surface"), 0, 32, 32, PIXEL_FORMAT_RGBA_8888, 0); + String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0); ASSERT_TRUE(mSurfaceControl != NULL); ASSERT_TRUE(mSurfaceControl->isValid()); @@ -85,7 +85,8 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { 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, 64, 64, 0, + sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &heap, &w, &h, &fmt, 64, 64, 0, 0x7fffffff)); ASSERT_TRUE(heap != NULL); @@ -97,26 +98,27 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3)); ANativeWindowBuffer* buf = 0; - status_t err = anw->dequeueBuffer(anw.get(), &buf); + status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf); if (err) { // we could fail if GRALLOC_USAGE_PROTECTED is not supported. // that's okay as long as this is the reason for the failure. // try again without the GRALLOC_USAGE_PROTECTED bit. ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), 0)); - ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &buf)); + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), + &buf)); return; } - ASSERT_EQ(NO_ERROR, anw->cancelBuffer(anw.get(), buf)); + ASSERT_EQ(NO_ERROR, anw->cancelBuffer(anw.get(), buf, -1)); for (int i = 0; i < 4; i++) { // Loop to make sure SurfaceFlinger has retired a protected buffer. - ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &buf)); - ASSERT_EQ(NO_ERROR, anw->lockBuffer(anw.get(), buf)); - ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf)); + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), + &buf)); + ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1)); } heap = 0; w = h = fmt = 0; - ASSERT_EQ(NO_ERROR, sf->captureScreen(0, &heap, &w, &h, &fmt, + ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &heap, &w, &h, &fmt, 64, 64, 0, 0x7fffffff)); ASSERT_TRUE(heap != NULL); } diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index 5aff7a4..0d2e44c 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -16,18 +16,21 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + Fence.cpp \ FramebufferNativeWindow.cpp \ GraphicBuffer.cpp \ GraphicBufferAllocator.cpp \ GraphicBufferMapper.cpp \ PixelFormat.cpp \ Rect.cpp \ - Region.cpp + Region.cpp \ + UiConfig.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ - libutils \ - libhardware + libhardware \ + libsync \ + libutils ifneq ($(BOARD_FRAMEBUFFER_FORCE_FORMAT),) LOCAL_CFLAGS += -DFRAMEBUFFER_FORCE_FORMAT=$(BOARD_FRAMEBUFFER_FORCE_FORMAT) diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp new file mode 100644 index 0000000..d214b97 --- /dev/null +++ b/libs/ui/Fence.cpp @@ -0,0 +1,121 @@ +/* + * 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. + * 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. + */ + +#define LOG_TAG "Fence" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#include <sync/sync.h> +#include <ui/Fence.h> +#include <unistd.h> +#include <utils/Log.h> +#include <utils/Trace.h> + +namespace android { + +const sp<Fence> Fence::NO_FENCE = sp<Fence>(); + +Fence::Fence() : + mFenceFd(-1) { +} + +Fence::Fence(int fenceFd) : + mFenceFd(fenceFd) { +} + +Fence::~Fence() { + if (mFenceFd != -1) { + close(mFenceFd); + } +} + +status_t Fence::wait(unsigned int timeout) { + ATRACE_CALL(); + if (mFenceFd == -1) { + return NO_ERROR; + } + int err = sync_wait(mFenceFd, timeout); + return err < 0 ? -errno : status_t(NO_ERROR); +} + +status_t Fence::waitForever(unsigned int warningTimeout, const char* logname) { + ATRACE_CALL(); + if (mFenceFd == -1) { + return NO_ERROR; + } + int err = sync_wait(mFenceFd, warningTimeout); + if (err < 0 && errno == ETIME) { + ALOGE("%s: fence %d didn't signal in %u ms", logname, mFenceFd, + warningTimeout); + err = sync_wait(mFenceFd, TIMEOUT_NEVER); + } + return err < 0 ? -errno : status_t(NO_ERROR); +} + +sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1, + const sp<Fence>& f2) { + ATRACE_CALL(); + int result = sync_merge(name.string(), f1->mFenceFd, f2->mFenceFd); + if (result == -1) { + status_t err = -errno; + ALOGE("merge: sync_merge(\"%s\", %d, %d) returned an error: %s (%d)", + name.string(), f1->mFenceFd, f2->mFenceFd, + strerror(-err), err); + return NO_FENCE; + } + return sp<Fence>(new Fence(result)); +} + +int Fence::dup() const { + if (mFenceFd == -1) { + return -1; + } + return ::dup(mFenceFd); +} + +size_t Fence::getFlattenedSize() const { + return 0; +} + +size_t Fence::getFdCount() const { + return 1; +} + +status_t Fence::flatten(void* buffer, size_t size, int fds[], + size_t count) const { + if (size != 0 || count != 1) { + return BAD_VALUE; + } + + fds[0] = mFenceFd; + return NO_ERROR; +} + +status_t Fence::unflatten(void const* buffer, size_t size, int fds[], + size_t count) { + if (size != 0 || count != 1) { + return BAD_VALUE; + } + if (mFenceFd != -1) { + // Don't unflatten if we already have a valid fd. + return INVALID_OPERATION; + } + + mFenceFd = fds[0]; + return NO_ERROR; +} + +} // namespace android diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index dec99b6..31a69b2 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -28,6 +28,7 @@ #include <utils/RefBase.h> #include <ui/ANativeObjectBase.h> +#include <ui/Fence.h> #include <ui/FramebufferNativeWindow.h> #include <ui/Rect.h> @@ -92,8 +93,13 @@ FramebufferNativeWindow::FramebufferNativeWindow() mUpdateOnDemand = (fbDev->setUpdateRect != 0); // initialize the buffer FIFO - mNumBuffers = NUM_FRAME_BUFFERS; - mNumFreeBuffers = NUM_FRAME_BUFFERS; + if(fbDev->numFramebuffers >= MIN_NUM_FRAME_BUFFERS && + fbDev->numFramebuffers <= MAX_NUM_FRAME_BUFFERS){ + mNumBuffers = fbDev->numFramebuffers; + } else { + mNumBuffers = MIN_NUM_FRAME_BUFFERS; + } + mNumFreeBuffers = mNumBuffers; mBufferHead = mNumBuffers-1; /* @@ -145,19 +151,23 @@ FramebufferNativeWindow::FramebufferNativeWindow() ANativeWindow::setSwapInterval = setSwapInterval; ANativeWindow::dequeueBuffer = dequeueBuffer; - ANativeWindow::lockBuffer = lockBuffer; ANativeWindow::queueBuffer = queueBuffer; ANativeWindow::query = query; ANativeWindow::perform = perform; + + ANativeWindow::dequeueBuffer_DEPRECATED = dequeueBuffer_DEPRECATED; + ANativeWindow::lockBuffer_DEPRECATED = lockBuffer_DEPRECATED; + ANativeWindow::queueBuffer_DEPRECATED = queueBuffer_DEPRECATED; } FramebufferNativeWindow::~FramebufferNativeWindow() { if (grDev) { - if (buffers[0] != NULL) - grDev->free(grDev, buffers[0]->handle); - if (buffers[1] != NULL) - grDev->free(grDev, buffers[1]->handle); + for(int i = 0; i < mNumBuffers; i++) { + if (buffers[i] != NULL) { + grDev->free(grDev, buffers[i]->handle); + } + } gralloc_close(grDev); } @@ -207,9 +217,24 @@ int FramebufferNativeWindow::getCurrentBufferIndex() const return index; } -int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, +int FramebufferNativeWindow::dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer) { + int fenceFd = -1; + int result = dequeueBuffer(window, buffer, &fenceFd); + sp<Fence> fence(new Fence(fenceFd)); + int waitResult = fence->wait(Fence::TIMEOUT_NEVER); + if (waitResult != OK) { + ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an " + "error: %d", waitResult); + return waitResult; + } + return result; +} + +int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, + ANativeWindowBuffer** buffer, int* fenceFd) +{ FramebufferNativeWindow* self = getSelf(window); Mutex::Autolock _l(self->mutex); framebuffer_device_t* fb = self->fbDev; @@ -218,43 +243,45 @@ int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, if (self->mBufferHead >= self->mNumBuffers) self->mBufferHead = 0; - // wait for a free buffer - while (!self->mNumFreeBuffers) { + // wait for a free non-front buffer + while (self->mNumFreeBuffers < 2) { self->mCondition.wait(self->mutex); } + ALOG_ASSERT(self->buffers[index] != self->front); + // get this buffer self->mNumFreeBuffers--; self->mCurrentBufferIndex = index; *buffer = self->buffers[index].get(); + *fenceFd = -1; return 0; } -int FramebufferNativeWindow::lockBuffer(ANativeWindow* window, +int FramebufferNativeWindow::lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer) { - FramebufferNativeWindow* self = getSelf(window); - Mutex::Autolock _l(self->mutex); - - const int index = self->mCurrentBufferIndex; - - // wait that the buffer we're locking is not front anymore - while (self->front == buffer) { - self->mCondition.wait(self->mutex); - } - return NO_ERROR; } -int FramebufferNativeWindow::queueBuffer(ANativeWindow* window, +int FramebufferNativeWindow::queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer) { + return queueBuffer(window, buffer, -1); +} + +int FramebufferNativeWindow::queueBuffer(ANativeWindow* window, + ANativeWindowBuffer* buffer, int fenceFd) +{ FramebufferNativeWindow* self = getSelf(window); Mutex::Autolock _l(self->mutex); framebuffer_device_t* fb = self->fbDev; buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle; + sp<Fence> fence(new Fence(fenceFd)); + fence->wait(Fence::TIMEOUT_NEVER); + const int index = self->mCurrentBufferIndex; int res = fb->post(fb, handle); self->front = static_cast<NativeBuffer*>(buffer); diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 57063e5..b9cab85 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -258,7 +258,12 @@ status_t GraphicBuffer::unflatten(void const* buffer, size_t size, mOwner = ownHandle; if (handle != 0) { - mBufferMapper.registerBuffer(handle); + status_t err = mBufferMapper.registerBuffer(handle); + if (err != NO_ERROR) { + ALOGE("unflatten: registerBuffer failed: %s (%d)", + strerror(-err), err); + return err; + } } return NO_ERROR; diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index ff550d9..fb43410 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -90,6 +90,105 @@ void GraphicBufferAllocator::dumpToSystemLog() ALOGD("%s", s.string()); } +class BufferLiberatorThread : public Thread { +public: + + static void queueCaptiveBuffer(buffer_handle_t handle) { + size_t queueSize; + { + Mutex::Autolock lock(sMutex); + if (sThread == NULL) { + sThread = new BufferLiberatorThread; + sThread->run("BufferLiberator"); + } + + sThread->mQueue.push_back(handle); + sThread->mQueuedCondition.signal(); + queueSize = sThread->mQueue.size(); + } + } + + static void waitForLiberation() { + Mutex::Autolock lock(sMutex); + + waitForLiberationLocked(); + } + + static void maybeWaitForLiberation() { + Mutex::Autolock lock(sMutex); + if (sThread != NULL) { + if (sThread->mQueue.size() > 8) { + waitForLiberationLocked(); + } + } + } + +private: + + BufferLiberatorThread() {} + + virtual bool threadLoop() { + buffer_handle_t handle; + { // Scope for mutex + Mutex::Autolock lock(sMutex); + while (mQueue.isEmpty()) { + mQueuedCondition.wait(sMutex); + } + handle = mQueue[0]; + } + + status_t err; + GraphicBufferAllocator& gba(GraphicBufferAllocator::get()); + { // Scope for tracing + ATRACE_NAME("gralloc::free"); + err = gba.mAllocDev->free(gba.mAllocDev, handle); + } + ALOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err)); + + if (err == NO_ERROR) { + Mutex::Autolock _l(GraphicBufferAllocator::sLock); + KeyedVector<buffer_handle_t, GraphicBufferAllocator::alloc_rec_t>& + list(GraphicBufferAllocator::sAllocList); + list.removeItem(handle); + } + + { // Scope for mutex + Mutex::Autolock lock(sMutex); + mQueue.removeAt(0); + mFreedCondition.broadcast(); + } + + return true; + } + + static void waitForLiberationLocked() { + if (sThread == NULL) { + return; + } + + const nsecs_t timeout = 500 * 1000 * 1000; + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t timeToStop = now + timeout; + while (!sThread->mQueue.isEmpty() && now < timeToStop) { + sThread->mFreedCondition.waitRelative(sMutex, timeToStop - now); + now = systemTime(SYSTEM_TIME_MONOTONIC); + } + + if (!sThread->mQueue.isEmpty()) { + ALOGW("waitForLiberationLocked timed out"); + } + } + + static Mutex sMutex; + static sp<BufferLiberatorThread> sThread; + Vector<buffer_handle_t> mQueue; + Condition mQueuedCondition; + Condition mFreedCondition; +}; + +Mutex BufferLiberatorThread::sMutex; +sp<BufferLiberatorThread> BufferLiberatorThread::sThread; + status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format, int usage, buffer_handle_t* handle, int32_t* stride) { @@ -100,13 +199,24 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma w = h = 1; // we have a h/w allocator and h/w buffer is requested - status_t err; - + status_t err; + + // If too many async frees are queued up then wait for some of them to + // complete before attempting to allocate more memory. This is exercised + // by the android.opengl.cts.GLSurfaceViewTest CTS test. + BufferLiberatorThread::maybeWaitForLiberation(); + err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride); + if (err != NO_ERROR) { + ALOGW("WOW! gralloc alloc failed, waiting for pending frees!"); + BufferLiberatorThread::waitForLiberation(); + err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride); + } + ALOGW_IF(err, "alloc(%u, %u, %d, %08x, ...) failed %d (%s)", w, h, format, usage, err, strerror(-err)); - + if (err == NO_ERROR) { Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); @@ -129,21 +239,11 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma return err; } + status_t GraphicBufferAllocator::free(buffer_handle_t handle) { - ATRACE_CALL(); - status_t err; - - err = mAllocDev->free(mAllocDev, handle); - - ALOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err)); - if (err == NO_ERROR) { - Mutex::Autolock _l(sLock); - KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); - list.removeItem(handle); - } - - return err; + BufferLiberatorThread::queueCaptiveBuffer(handle); + return NO_ERROR; } // --------------------------------------------------------------------------- diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index 2c7cdf0..932ef68 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -20,6 +20,7 @@ #include <utils/Log.h> #include <utils/String8.h> +#include <utils/CallStack.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -48,28 +49,20 @@ enum { // ---------------------------------------------------------------------------- -Region::Region() - : mBounds(0,0) -{ +Region::Region() { + mStorage.add(Rect(0,0)); } Region::Region(const Region& rhs) - : mBounds(rhs.mBounds), mStorage(rhs.mStorage) + : mStorage(rhs.mStorage) { #if VALIDATE_REGIONS validate(rhs, "rhs copy-ctor"); #endif } -Region::Region(const Rect& rhs) - : mBounds(rhs) -{ -} - -Region::Region(const void* buffer) -{ - status_t err = read(buffer); - ALOGE_IF(err<0, "error %s reading Region from buffer", strerror(err)); +Region::Region(const Rect& rhs) { + mStorage.add(rhs); } Region::~Region() @@ -82,43 +75,45 @@ Region& Region::operator = (const Region& rhs) validate(*this, "this->operator="); validate(rhs, "rhs.operator="); #endif - mBounds = rhs.mBounds; mStorage = rhs.mStorage; return *this; } Region& Region::makeBoundsSelf() { - mStorage.clear(); + if (mStorage.size() >= 2) { + const Rect bounds(getBounds()); + mStorage.clear(); + mStorage.add(bounds); + } return *this; } void Region::clear() { - mBounds.clear(); mStorage.clear(); + mStorage.add(Rect(0,0)); } void Region::set(const Rect& r) { - mBounds = r; mStorage.clear(); + mStorage.add(r); } void Region::set(uint32_t w, uint32_t h) { - mBounds = Rect(int(w), int(h)); mStorage.clear(); + mStorage.add(Rect(w,h)); } // ---------------------------------------------------------------------------- void Region::addRectUnchecked(int l, int t, int r, int b) { - mStorage.add(Rect(l,t,r,b)); -#if VALIDATE_REGIONS - validate(*this, "addRectUnchecked"); -#endif + Rect rect(l,t,r,b); + size_t where = mStorage.size() - 1; + mStorage.insertAt(rect, where, 1); } // ---------------------------------------------------------------------------- @@ -258,7 +253,7 @@ const Region Region::operation(const Region& rhs, int dx, int dy, int op) const // to obtain an optimal region. class Region::rasterizer : public region_operator<Rect>::region_rasterizer { - Rect& bounds; + Rect bounds; Vector<Rect>& storage; Rect* head; Rect* tail; @@ -266,10 +261,7 @@ class Region::rasterizer : public region_operator<Rect>::region_rasterizer Rect* cur; public: rasterizer(Region& reg) - : bounds(reg.mBounds), storage(reg.mStorage), head(), tail(), cur() { - bounds.top = bounds.bottom = 0; - bounds.left = INT_MAX; - bounds.right = INT_MIN; + : bounds(INT_MAX, 0, INT_MIN, 0), storage(reg.mStorage), head(), tail(), cur() { storage.clear(); } @@ -287,6 +279,7 @@ public: bounds.left = 0; bounds.right = 0; } + storage.add(bounds); } virtual void operator()(const Rect& rect) { @@ -342,44 +335,72 @@ private: } }; -bool Region::validate(const Region& reg, const char* name) +bool Region::validate(const Region& reg, const char* name, bool silent) { bool result = true; const_iterator cur = reg.begin(); const_iterator const tail = reg.end(); - const_iterator prev = cur++; + const_iterator prev = cur; Rect b(*prev); while (cur != tail) { - b.left = b.left < cur->left ? b.left : cur->left; - b.top = b.top < cur->top ? b.top : cur->top; - b.right = b.right > cur->right ? b.right : cur->right; - b.bottom = b.bottom > cur->bottom ? b.bottom : cur->bottom; - if (cur->top == prev->top) { - if (cur->bottom != prev->bottom) { - ALOGE("%s: invalid span %p", name, cur); + if (cur->isValid() == false) { + ALOGE_IF(!silent, "%s: region contains an invalid Rect", name); + result = false; + } + if (cur->right > region_operator<Rect>::max_value) { + ALOGE_IF(!silent, "%s: rect->right > max_value", name); + result = false; + } + if (cur->bottom > region_operator<Rect>::max_value) { + ALOGE_IF(!silent, "%s: rect->right > max_value", name); + result = false; + } + if (prev != cur) { + b.left = b.left < cur->left ? b.left : cur->left; + b.top = b.top < cur->top ? b.top : cur->top; + b.right = b.right > cur->right ? b.right : cur->right; + b.bottom = b.bottom > cur->bottom ? b.bottom : cur->bottom; + if ((*prev < *cur) == false) { + ALOGE_IF(!silent, "%s: region's Rects not sorted", name); result = false; - } else if (cur->left < prev->right) { - ALOGE("%s: spans overlap horizontally prev=%p, cur=%p", + } + if (cur->top == prev->top) { + if (cur->bottom != prev->bottom) { + ALOGE_IF(!silent, "%s: invalid span %p", name, cur); + result = false; + } else if (cur->left < prev->right) { + ALOGE_IF(!silent, + "%s: spans overlap horizontally prev=%p, cur=%p", + name, prev, cur); + result = false; + } + } else if (cur->top < prev->bottom) { + ALOGE_IF(!silent, + "%s: spans overlap vertically prev=%p, cur=%p", name, prev, cur); result = false; } - } else if (cur->top < prev->bottom) { - ALOGE("%s: spans overlap vertically prev=%p, cur=%p", - name, prev, cur); - result = false; + prev = cur; } - prev = cur; cur++; } if (b != reg.getBounds()) { result = false; - ALOGE("%s: invalid bounds [%d,%d,%d,%d] vs. [%d,%d,%d,%d]", name, + ALOGE_IF(!silent, + "%s: invalid bounds [%d,%d,%d,%d] vs. [%d,%d,%d,%d]", name, b.left, b.top, b.right, b.bottom, reg.getBounds().left, reg.getBounds().top, reg.getBounds().right, reg.getBounds().bottom); } - if (result == false) { + if (reg.mStorage.size() == 2) { + result = false; + ALOGE_IF(!silent, "%s: mStorage size is 2, which is never valid", name); + } + if (result == false && !silent) { reg.dump(name); + CallStack stack; + stack.update(); + stack.dump(""); } return result; } @@ -535,11 +556,10 @@ void Region::boolean_operation(int op, Region& dst, void Region::translate(Region& reg, int dx, int dy) { - if (!reg.isEmpty()) { + if ((dx || dy) && !reg.isEmpty()) { #if VALIDATE_REGIONS validate(reg, "translate (before)"); #endif - reg.mBounds.translate(dx, dy); size_t count = reg.mStorage.size(); Rect* rects = reg.mStorage.editArray(); while (count) { @@ -561,73 +581,54 @@ void Region::translate(Region& dst, const Region& reg, int dx, int dy) // ---------------------------------------------------------------------------- -ssize_t Region::write(void* buffer, size_t size) const -{ +size_t Region::getSize() const { + return mStorage.size() * sizeof(Rect); +} + +status_t Region::flatten(void* buffer) const { #if VALIDATE_REGIONS - validate(*this, "write(buffer)"); + validate(*this, "Region::flatten"); #endif - const size_t count = mStorage.size(); - const size_t sizeNeeded = sizeof(int32_t) + (1+count)*sizeof(Rect); - if (buffer != NULL) { - if (sizeNeeded > size) return NO_MEMORY; - int32_t* const p = static_cast<int32_t*>(buffer); - *p = count; - memcpy(p+1, &mBounds, sizeof(Rect)); - if (count) { - memcpy(p+5, mStorage.array(), count*sizeof(Rect)); - } - } - return ssize_t(sizeNeeded); + Rect* rects = reinterpret_cast<Rect*>(buffer); + memcpy(rects, mStorage.array(), mStorage.size() * sizeof(Rect)); + return NO_ERROR; } -ssize_t Region::read(const void* buffer) -{ - int32_t const* const p = static_cast<int32_t const*>(buffer); - const size_t count = *p; - memcpy(&mBounds, p+1, sizeof(Rect)); - mStorage.clear(); - if (count) { - mStorage.insertAt(0, count); - memcpy(mStorage.editArray(), p+5, count*sizeof(Rect)); +status_t Region::unflatten(void const* buffer, size_t size) { + Region result; + if (size >= sizeof(Rect)) { + Rect const* rects = reinterpret_cast<Rect const*>(buffer); + size_t count = size / sizeof(Rect); + if (count > 0) { + result.mStorage.clear(); + ssize_t err = result.mStorage.insertAt(0, count); + if (err < 0) { + return status_t(err); + } + memcpy(result.mStorage.editArray(), rects, count*sizeof(Rect)); + } } #if VALIDATE_REGIONS - validate(*this, "read(buffer)"); + validate(result, "Region::unflatten"); #endif - return ssize_t(sizeof(int32_t) + (1+count)*sizeof(Rect)); -} -ssize_t Region::writeEmpty(void* buffer, size_t size) -{ - const size_t sizeNeeded = sizeof(int32_t) + sizeof(Rect); - if (sizeNeeded > size) return NO_MEMORY; - int32_t* const p = static_cast<int32_t*>(buffer); - memset(p, 0, sizeNeeded); - return ssize_t(sizeNeeded); -} - -bool Region::isEmpty(void* buffer) -{ - int32_t const* const p = static_cast<int32_t const*>(buffer); - Rect const* const b = reinterpret_cast<Rect const *>(p+1); - return b->isEmpty(); + if (!result.validate(result, "Region::unflatten", true)) { + ALOGE("Region::unflatten() failed, invalid region"); + return BAD_VALUE; + } + mStorage = result.mStorage; + return NO_ERROR; } // ---------------------------------------------------------------------------- Region::const_iterator Region::begin() const { - return isRect() ? &mBounds : mStorage.array(); + return mStorage.array(); } Region::const_iterator Region::end() const { - if (isRect()) { - if (isEmpty()) { - return &mBounds; - } else { - return &mBounds + 1; - } - } else { - return mStorage.array() + mStorage.size(); - } + size_t numRects = isRect() ? 1 : mStorage.size() - 1; + return mStorage.array() + numRects; } Rect const* Region::getArray(size_t* count) const { @@ -637,14 +638,16 @@ Rect const* Region::getArray(size_t* count) const { return b; } -size_t Region::getRects(Vector<Rect>& rectList) const -{ - rectList = mStorage; - if (rectList.isEmpty()) { - rectList.clear(); - rectList.add(mBounds); +SharedBuffer const* Region::getSharedBuffer(size_t* count) const { + // We can get to the SharedBuffer of a Vector<Rect> because Rect has + // a trivial destructor. + SharedBuffer const* sb = SharedBuffer::bufferFromData(mStorage.array()); + if (count) { + size_t numRects = isRect() ? 1 : mStorage.size() - 1; + count[0] = numRects; } - return rectList.size(); + sb->acquire(); + return sb; } // ---------------------------------------------------------------------------- diff --git a/libs/ui/UiConfig.cpp b/libs/ui/UiConfig.cpp new file mode 100644 index 0000000..8b2130e --- /dev/null +++ b/libs/ui/UiConfig.cpp @@ -0,0 +1,36 @@ +/* + * 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. + * 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 <ui/UiConfig.h> + +namespace android { + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +void appendUiConfigString(String8& configStr) +{ + static const char* config = + " [libui" +#ifdef FRAMEBUFFER_FORCE_FORMAT + " FRAMEBUFFER_FORCE_FORMAT=" TOSTRING(FRAMEBUFFER_FORCE_FORMAT) +#endif + "]"; + configStr.append(config); +} + + +}; // namespace android diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index ddfb83e..c9f8fd4 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -26,6 +26,7 @@ commonSources:= \ FileMap.cpp \ Flattenable.cpp \ LinearTransform.cpp \ + Log.cpp \ PropertyMap.cpp \ RefBase.cpp \ SharedBuffer.cpp \ diff --git a/libs/utils/LinearTransform.cpp b/libs/utils/LinearTransform.cpp index d752415..b7d28d4 100644 --- a/libs/utils/LinearTransform.cpp +++ b/libs/utils/LinearTransform.cpp @@ -114,6 +114,7 @@ static bool linear_transform_s64_to_s64( int64_t basis1, int32_t N, uint32_t D, + bool invert_frac, int64_t basis2, int64_t* out) { uint64_t scaled, res; @@ -137,8 +138,8 @@ static bool linear_transform_s64_to_s64( is_neg = !is_neg; if (!scale_u64_to_u64(abs_val, - ABS(N), - D, + invert_frac ? D : ABS(N), + invert_frac ? ABS(N) : D, &scaled, is_neg)) return false; // overflow/undeflow @@ -191,6 +192,7 @@ bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const { a_zero, a_to_b_numer, a_to_b_denom, + false, b_zero, b_out); } @@ -201,8 +203,9 @@ bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const { return linear_transform_s64_to_s64(b_in, b_zero, - a_to_b_denom, a_to_b_numer, + a_to_b_denom, + true, a_zero, a_out); } diff --git a/libs/utils/Log.cpp b/libs/utils/Log.cpp new file mode 100644 index 0000000..bffb56e --- /dev/null +++ b/libs/utils/Log.cpp @@ -0,0 +1,37 @@ +/* + * 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. + * 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. + */ + +#define LOG_TAG "Log" + +#include <utils/Log.h> +#include <utils/Timers.h> + +namespace android { + +LogIfSlow::LogIfSlow(const char* tag, android_LogPriority priority, + int timeoutMillis, const char* message) : + mTag(tag), mPriority(priority), mTimeoutMillis(timeoutMillis), mMessage(message), + mStart(systemTime(SYSTEM_TIME_BOOTTIME)) { +} + +LogIfSlow::~LogIfSlow() { + int durationMillis = nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_BOOTTIME) - mStart); + if (durationMillis > mTimeoutMillis) { + LOG_PRI(mPriority, mTag, "%s: %dms", mMessage, durationMillis); + } +} + +} // namespace android diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp index 8b8ac10..ec2d82e 100644 --- a/libs/utils/SystemClock.cpp +++ b/libs/utils/SystemClock.cpp @@ -106,7 +106,74 @@ int64_t uptimeMillis() */ int64_t elapsedRealtime() { + return nanoseconds_to_milliseconds(elapsedRealtimeNano()); +} + +#define METHOD_CLOCK_GETTIME 0 +#define METHOD_IOCTL 1 +#define METHOD_SYSTEMTIME 2 + +static const char *gettime_method_names[] = { + "clock_gettime", + "ioctl", + "systemTime", +}; + +static inline void checkTimeStamps(int64_t timestamp, + int64_t volatile *prevTimestampPtr, + int volatile *prevMethodPtr, + int curMethod) +{ + /* + * Disable the check for SDK since the prebuilt toolchain doesn't contain + * gettid, and int64_t is different on the ARM platform + * (ie long vs long long). + */ +#ifdef ARCH_ARM + int64_t prevTimestamp = *prevTimestampPtr; + int prevMethod = *prevMethodPtr; + + if (timestamp < prevTimestamp) { + ALOGW("time going backwards: prev %lld(%s) vs now %lld(%s), tid=%d", + prevTimestamp, gettime_method_names[prevMethod], + timestamp, gettime_method_names[curMethod], + gettid()); + } + // NOTE - not atomic and may generate spurious warnings if the 64-bit + // write is interrupted or not observed as a whole. + *prevTimestampPtr = timestamp; + *prevMethodPtr = curMethod; +#endif +} + +/* + * native public static long elapsedRealtimeNano(); + */ +int64_t elapsedRealtimeNano() +{ #ifdef HAVE_ANDROID_OS + struct timespec ts; + int result; + int64_t timestamp; + static volatile int64_t prevTimestamp; + static volatile int prevMethod; + +#if 0 + /* + * b/7100774 + * clock_gettime appears to have clock skews and can sometimes return + * backwards values. Disable its use until we find out what's wrong. + */ + result = clock_gettime(CLOCK_BOOTTIME, &ts); + if (result == 0) { + timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, + METHOD_CLOCK_GETTIME); + return timestamp; + } +#endif + + // CLOCK_BOOTTIME doesn't exist, fallback to /dev/alarm static int s_fd = -1; if (s_fd == -1) { @@ -116,23 +183,24 @@ int64_t elapsedRealtime() } } - struct timespec ts; - int result = ioctl(s_fd, + result = ioctl(s_fd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); if (result == 0) { - int64_t when = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; - return (int64_t) nanoseconds_to_milliseconds(when); - } else { - // XXX: there was an error, probably because the driver didn't - // exist ... this should return - // a real error, like an exception! - int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); - return (int64_t) nanoseconds_to_milliseconds(when); + timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, METHOD_IOCTL); + return timestamp; } + + // XXX: there was an error, probably because the driver didn't + // exist ... this should return + // a real error, like an exception! + timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, + METHOD_SYSTEMTIME); + return timestamp; #else - int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); - return (int64_t) nanoseconds_to_milliseconds(when); + return systemTime(SYSTEM_TIME_MONOTONIC); #endif } diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp index 64b4701..d4f8516 100644 --- a/libs/utils/Timers.cpp +++ b/libs/utils/Timers.cpp @@ -39,7 +39,8 @@ nsecs_t systemTime(int clock) CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID, - CLOCK_THREAD_CPUTIME_ID + CLOCK_THREAD_CPUTIME_ID, + CLOCK_BOOTTIME }; struct timespec t; t.tv_sec = t.tv_nsec = 0; diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp index 5cd5731..f5aaea3 100644 --- a/libs/utils/Trace.cpp +++ b/libs/utils/Trace.cpp @@ -25,7 +25,7 @@ namespace android { volatile int32_t Tracer::sIsReady = 0; int Tracer::sTraceFD = -1; -uint64_t Tracer::sEnabledTags = 0; +uint64_t Tracer::sEnabledTags = ATRACE_TAG_NOT_READY; Mutex Tracer::sMutex; void Tracer::changeCallback() { @@ -46,7 +46,7 @@ void Tracer::init() { sTraceFD = open(traceFileName, O_WRONLY); if (sTraceFD == -1) { ALOGE("error opening trace file: %s (%d)", strerror(errno), errno); - // sEnabledTags remains zero indicating that no tracing can occur + sEnabledTags = 0; // no tracing can occur } else { loadSystemProperty(); } diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index e78faa8..c3257bb 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -20,7 +20,8 @@ #include <stdlib.h> #include <stdio.h> -#include <utils/Log.h> +#include <cutils/log.h> + #include <utils/Errors.h> #include <utils/SharedBuffer.h> #include <utils/VectorImpl.h> @@ -50,15 +51,14 @@ VectorImpl::VectorImpl(const VectorImpl& rhs) mFlags(rhs.mFlags), mItemSize(rhs.mItemSize) { if (mStorage) { - SharedBuffer::sharedBuffer(mStorage)->acquire(); + SharedBuffer::bufferFromData(mStorage)->acquire(); } } VectorImpl::~VectorImpl() { - ALOG_ASSERT(!mCount, - "[%p] " - "subclasses of VectorImpl must call finish_vector()" + ALOGW_IF(mCount, + "[%p] subclasses of VectorImpl must call finish_vector()" " in their destructor. Leaking %d bytes.", this, (int)(mCount*mItemSize)); // We can't call _do_destroy() here because the vtable is already gone. @@ -66,14 +66,14 @@ VectorImpl::~VectorImpl() VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) { - ALOG_ASSERT(mItemSize == rhs.mItemSize, + LOG_ALWAYS_FATAL_IF(mItemSize != rhs.mItemSize, "Vector<> have different types (this=%p, rhs=%p)", this, &rhs); if (this != &rhs) { release_storage(); if (rhs.mCount) { mStorage = rhs.mStorage; mCount = rhs.mCount; - SharedBuffer::sharedBuffer(mStorage)->acquire(); + SharedBuffer::bufferFromData(mStorage)->acquire(); } else { mStorage = 0; mCount = 0; @@ -85,7 +85,7 @@ VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) void* VectorImpl::editArrayImpl() { if (mStorage) { - SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit(); + SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage)->attemptEdit(); if (sb == 0) { sb = SharedBuffer::alloc(capacity() * mItemSize); if (sb) { @@ -101,7 +101,7 @@ void* VectorImpl::editArrayImpl() size_t VectorImpl::capacity() const { if (mStorage) { - return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize; + return SharedBuffer::bufferFromData(mStorage)->size() / mItemSize; } return 0; } @@ -251,6 +251,10 @@ ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) ALOG_ASSERT(index<size(), "[%p] replace: index=%d, size=%d", this, (int)index, (int)size()); + if (index >= size()) { + return BAD_INDEX; + } + void* item = editItemLocation(index); if (item != prototype) { if (item == 0) @@ -294,10 +298,13 @@ void* VectorImpl::editItemLocation(size_t index) ALOG_ASSERT(index<capacity(), "[%p] editItemLocation: index=%d, capacity=%d, count=%d", this, (int)index, (int)capacity(), (int)mCount); - - void* buffer = editArrayImpl(); - if (buffer) - return reinterpret_cast<char*>(buffer) + index*mItemSize; + + if (index < capacity()) { + void* buffer = editArrayImpl(); + if (buffer) { + return reinterpret_cast<char*>(buffer) + index*mItemSize; + } + } return 0; } @@ -307,9 +314,12 @@ const void* VectorImpl::itemLocation(size_t index) const "[%p] itemLocation: index=%d, capacity=%d, count=%d", this, (int)index, (int)capacity(), (int)mCount); - const void* buffer = arrayImpl(); - if (buffer) - return reinterpret_cast<const char*>(buffer) + index*mItemSize; + if (index < capacity()) { + const void* buffer = arrayImpl(); + if (buffer) { + return reinterpret_cast<const char*>(buffer) + index*mItemSize; + } + } return 0; } @@ -336,7 +346,7 @@ ssize_t VectorImpl::setCapacity(size_t new_capacity) void VectorImpl::release_storage() { if (mStorage) { - const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage); + const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage); if (sb->release(SharedBuffer::eKeepStorage) == 1) { _do_destroy(mStorage, mCount); SharedBuffer::dealloc(sb); @@ -362,7 +372,7 @@ void* VectorImpl::_grow(size_t where, size_t amount) (mFlags & HAS_TRIVIAL_COPY) && (mFlags & HAS_TRIVIAL_DTOR)) { - const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage); SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); mStorage = sb->data(); } else { @@ -414,7 +424,7 @@ void VectorImpl::_shrink(size_t where, size_t amount) (mFlags & HAS_TRIVIAL_COPY) && (mFlags & HAS_TRIVIAL_DTOR)) { - const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage); SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); mStorage = sb->data(); } else { diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index cad7720..a1bfedb 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -20,6 +20,7 @@ #define LOG_TAG "zipro" //#define LOG_NDEBUG 0 #include <utils/Log.h> +#include <utils/Compat.h> #include <utils/ZipFileRO.h> #include <utils/misc.h> #include <utils/threads.h> @@ -32,14 +33,6 @@ #include <assert.h> #include <unistd.h> -#if HAVE_PRINTF_ZD -# define ZD "%zd" -# define ZD_TYPE ssize_t -#else -# define ZD "%ld" -# define ZD_TYPE long -#endif - /* * We must open binary files using open(path, ... | O_BINARY) under Windows. * Otherwise strange read errors will happen. @@ -48,21 +41,6 @@ # define O_BINARY 0 #endif -/* - * TEMP_FAILURE_RETRY is defined by some, but not all, versions of - * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's - * not already defined, then define it here. - */ -#ifndef TEMP_FAILURE_RETRY -/* Used to retry syscalls that can return EINTR. */ -#define TEMP_FAILURE_RETRY(exp) ({ \ - typeof (exp) _rc; \ - do { \ - _rc = (exp); \ - } while (_rc == -1 && errno == EINTR); \ - _rc; }) -#endif - using namespace android; /* @@ -118,7 +96,7 @@ ZipFileRO::~ZipFileRO() { */ int ZipFileRO::entryToIndex(const ZipEntryRO entry) const { - long ent = ((long) entry) - kZipEntryAdj; + long ent = ((intptr_t) entry) - kZipEntryAdj; if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); return -1; @@ -140,7 +118,7 @@ status_t ZipFileRO::open(const char* zipFileName) /* * Open and map the specified file. */ - fd = ::open(zipFileName, O_RDONLY | O_BINARY); + fd = TEMP_FAILURE_RETRY(::open(zipFileName, O_RDONLY | O_BINARY)); if (fd < 0) { ALOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); return NAME_NOT_FOUND; @@ -320,6 +298,25 @@ bool ZipFileRO::mapCentralDirectory(void) return true; } + +/* + * Round up to the next highest power of 2. + * + * Found on http://graphics.stanford.edu/~seander/bithacks.html. + */ +static unsigned int roundUpPower2(unsigned int val) +{ + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val++; + + return val; +} + bool ZipFileRO::parseZipArchive(void) { bool result = false; @@ -459,7 +456,7 @@ ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const for (int ent = 0; ent < mHashTableSize; ent++) { if (mHashTable[ent].name != NULL) { if (idx-- == 0) - return (ZipEntryRO) (ent + kZipEntryAdj); + return (ZipEntryRO) (intptr_t)(ent + kZipEntryAdj); } } @@ -752,7 +749,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const ptr = (const unsigned char*) file->getDataPtr(); if (method == kCompressStored) { - ssize_t actual = write(fd, ptr, uncompLen); + ssize_t actual = TEMP_FAILURE_RETRY(write(fd, ptr, uncompLen)); if (actual < 0) { ALOGE("Write failed: %s\n", strerror(errno)); goto unmap; @@ -901,9 +898,12 @@ bail: (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) { long writeSize = zstream.next_out - writeBuf; - int cc = write(fd, writeBuf, writeSize); - if (cc != (int) writeSize) { - ALOGW("write failed in inflate (%d vs %ld)\n", cc, writeSize); + int cc = TEMP_FAILURE_RETRY(write(fd, writeBuf, writeSize)); + if (cc < 0) { + ALOGW("write failed in inflate: %s", strerror(errno)); + goto z_bail; + } else if (cc != (int) writeSize) { + ALOGW("write failed in inflate (%d vs %ld)", cc, writeSize); goto z_bail; } diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp index cf5467b..a43bbb0 100644 --- a/libs/utils/ZipUtils.cpp +++ b/libs/utils/ZipUtils.cpp @@ -21,6 +21,7 @@ #define LOG_TAG "ziputil" #include <utils/Log.h> +#include <utils/Compat.h> #include <utils/ZipUtils.h> #include <utils/ZipFileRO.h> @@ -98,10 +99,11 @@ using namespace android; ALOGV("+++ reading %ld bytes (%ld left)\n", getSize, compRemaining); - int cc = read(fd, readBuf, getSize); - if (cc != (int) getSize) { - ALOGD("inflate read failed (%d vs %ld)\n", - cc, getSize); + int cc = TEMP_FAILURE_RETRY(read(fd, readBuf, getSize)); + if (cc < 0) { + ALOGW("inflate read failed: %s", strerror(errno)); + } else if (cc != (int) getSize) { + ALOGW("inflate read failed (%d vs %ld)", cc, getSize); goto z_bail; } diff --git a/libs/utils/misc.cpp b/libs/utils/misc.cpp index b3c99e6..445a23a 100644 --- a/libs/utils/misc.cpp +++ b/libs/utils/misc.cpp @@ -39,90 +39,6 @@ using namespace android; namespace android { /* - * Like strdup(), but uses C++ "new" operator instead of malloc. - */ -char* strdupNew(const char* str) -{ - char* newStr; - int len; - - if (str == NULL) - return NULL; - - len = strlen(str); - newStr = new char[len+1]; - memcpy(newStr, str, len+1); - - return newStr; -} - -/* - * Concatenate an argument vector. - */ -char* concatArgv(int argc, const char* const argv[]) -{ - char* newStr = NULL; - int len, totalLen, posn, idx; - - /* - * First, figure out the total length. - */ - totalLen = idx = 0; - while (1) { - if (idx == argc || argv[idx] == NULL) - break; - if (idx) - totalLen++; // leave a space between args - totalLen += strlen(argv[idx]); - idx++; - } - - /* - * Alloc the string. - */ - newStr = new char[totalLen +1]; - if (newStr == NULL) - return NULL; - - /* - * Finally, allocate the string and copy data over. - */ - idx = posn = 0; - while (1) { - if (idx == argc || argv[idx] == NULL) - break; - if (idx) - newStr[posn++] = ' '; - - len = strlen(argv[idx]); - memcpy(&newStr[posn], argv[idx], len); - posn += len; - - idx++; - } - - assert(posn == totalLen); - newStr[posn] = '\0'; - - return newStr; -} - -/* - * Count the #of args in an argument vector. Don't count the final NULL. - */ -int countArgv(const char* const argv[]) -{ - int count = 0; - - while (argv[count] != NULL) - count++; - - return count; -} - - -#include <stdio.h> -/* * Get a file's type. */ FileType getFileType(const char* fileName) @@ -172,24 +88,6 @@ time_t getFileModDate(const char* fileName) return sb.st_mtime; } -/* - * Round up to the next highest power of 2. - * - * Found on http://graphics.stanford.edu/~seander/bithacks.html. - */ -unsigned int roundUpPower2(unsigned int val) -{ - val--; - val |= val >> 1; - val |= val >> 2; - val |= val >> 4; - val |= val >> 8; - val |= val >> 16; - val++; - - return val; -} - struct sysprop_change_callback_info { sysprop_change_callback callback; int priority; diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 0a5b379..5b2b5b1 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -4,42 +4,30 @@ include $(CLEAR_VARS) # Build the unit tests. test_src_files := \ - BasicHashtable_test.cpp \ - BlobCache_test.cpp \ - Looper_test.cpp \ - String8_test.cpp \ - Unicode_test.cpp \ - Vector_test.cpp \ - ZipFileRO_test.cpp + BasicHashtable_test.cpp \ + BlobCache_test.cpp \ + Looper_test.cpp \ + String8_test.cpp \ + Unicode_test.cpp \ + Vector_test.cpp \ + ZipFileRO_test.cpp shared_libraries := \ - libz \ - liblog \ - libcutils \ - libutils \ - libstlport + libz \ + liblog \ + libcutils \ + libutils \ + libstlport static_libraries := \ - libgtest \ - libgtest_main - -c_includes := \ - external/zlib \ - external/icu4c/common \ - bionic \ - bionic/libstdc++/include \ - external/gtest/include \ - external/stlport/stlport - -module_tags := eng tests + libgtest \ + libgtest_main $(foreach file,$(test_src_files), \ $(eval include $(CLEAR_VARS)) \ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ - $(eval LOCAL_C_INCLUDES := $(c_includes)) \ $(eval LOCAL_SRC_FILES := $(file)) \ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ - $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ - $(eval include $(BUILD_EXECUTABLE)) \ + $(eval include $(BUILD_NATIVE_TEST)) \ ) |