diff options
Diffstat (limited to 'libs/gui')
-rw-r--r-- | libs/gui/Android.mk | 3 | ||||
-rw-r--r-- | libs/gui/BitTube.cpp | 100 | ||||
-rw-r--r-- | libs/gui/BufferItemConsumer.cpp | 26 | ||||
-rw-r--r-- | libs/gui/BufferQueue.cpp | 648 | ||||
-rw-r--r-- | libs/gui/ConsumerBase.cpp | 94 | ||||
-rw-r--r-- | libs/gui/CpuConsumer.cpp | 34 | ||||
-rw-r--r-- | libs/gui/DummyConsumer.cpp | 43 | ||||
-rw-r--r-- | libs/gui/GLConsumer.cpp | 419 | ||||
-rw-r--r-- | libs/gui/IConsumerListener.cpp | 77 | ||||
-rw-r--r-- | libs/gui/IGraphicBufferConsumer.cpp | 485 | ||||
-rw-r--r-- | libs/gui/IGraphicBufferProducer.cpp | 109 | ||||
-rw-r--r-- | libs/gui/ISensorEventConnection.cpp | 28 | ||||
-rw-r--r-- | libs/gui/ISurfaceComposer.cpp | 59 | ||||
-rw-r--r-- | libs/gui/Sensor.cpp | 161 | ||||
-rw-r--r-- | libs/gui/SensorEventQueue.cpp | 46 | ||||
-rw-r--r-- | libs/gui/Surface.cpp | 34 | ||||
-rw-r--r-- | libs/gui/SurfaceComposerClient.cpp | 19 | ||||
-rw-r--r-- | libs/gui/SurfaceControl.cpp | 12 | ||||
-rw-r--r-- | libs/gui/tests/BufferQueue_test.cpp | 22 | ||||
-rw-r--r-- | libs/gui/tests/CpuConsumer_test.cpp | 5 | ||||
-rw-r--r-- | libs/gui/tests/SurfaceTextureClient_test.cpp | 25 | ||||
-rw-r--r-- | libs/gui/tests/SurfaceTexture_test.cpp | 56 | ||||
-rw-r--r-- | libs/gui/tests/Surface_test.cpp | 29 |
23 files changed, 1739 insertions, 795 deletions
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index c080f47..c14c950 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -2,13 +2,14 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + IGraphicBufferConsumer.cpp \ + IConsumerListener.cpp \ BitTube.cpp \ BufferItemConsumer.cpp \ BufferQueue.cpp \ ConsumerBase.cpp \ CpuConsumer.cpp \ DisplayEventReceiver.cpp \ - DummyConsumer.cpp \ GLConsumer.cpp \ GraphicBufferAlloc.cpp \ GuiConfig.cpp \ diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp index cf44bb9..85a7de7 100644 --- a/libs/gui/BitTube.cpp +++ b/libs/gui/BitTube.cpp @@ -32,39 +32,26 @@ namespace android { // Socket buffer size. The default is typically about 128KB, which is much larger than // we really need. So we make it smaller. -static const size_t SOCKET_BUFFER_SIZE = 4 * 1024; +static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024; BitTube::BitTube() : mSendFd(-1), mReceiveFd(-1) { - int sockets[2]; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) { - int size = SOCKET_BUFFER_SIZE; - setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); - setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); - setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); - setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); - fcntl(sockets[0], F_SETFL, O_NONBLOCK); - fcntl(sockets[1], F_SETFL, O_NONBLOCK); - mReceiveFd = sockets[0]; - mSendFd = sockets[1]; - } else { - mReceiveFd = -errno; - ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd)); - } + init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE); +} + +BitTube::BitTube(size_t bufsize) + : mSendFd(-1), mReceiveFd(-1) +{ + init(bufsize, bufsize); } BitTube::BitTube(const Parcel& data) : mSendFd(-1), mReceiveFd(-1) { mReceiveFd = dup(data.readFileDescriptor()); - if (mReceiveFd >= 0) { - int size = SOCKET_BUFFER_SIZE; - setsockopt(mReceiveFd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); - setsockopt(mReceiveFd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); - fcntl(mReceiveFd, F_SETFL, O_NONBLOCK); - } else { + if (mReceiveFd < 0) { mReceiveFd = -errno; ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)", strerror(-mReceiveFd)); @@ -80,6 +67,25 @@ BitTube::~BitTube() close(mReceiveFd); } +void BitTube::init(size_t rcvbuf, size_t sndbuf) { + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) { + size_t size = DEFAULT_SOCKET_BUFFER_SIZE; + setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); + setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)); + // sine we don't use the "return channel", we keep it small... + setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); + setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); + fcntl(sockets[0], F_SETFL, O_NONBLOCK); + fcntl(sockets[1], F_SETFL, O_NONBLOCK); + mReceiveFd = sockets[0]; + mSendFd = sockets[1]; + } else { + mReceiveFd = -errno; + ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd)); + } +} + status_t BitTube::initCheck() const { if (mReceiveFd < 0) { @@ -98,10 +104,10 @@ ssize_t BitTube::write(void const* vaddr, size_t size) ssize_t err, len; do { len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL); + // cannot return less than size, since we're using SOCK_SEQPACKET err = len < 0 ? errno : 0; } while (err == EINTR); return err == 0 ? len : -err; - } ssize_t BitTube::read(void* vaddr, size_t size) @@ -134,39 +140,31 @@ status_t BitTube::writeToParcel(Parcel* reply) const ssize_t BitTube::sendObjects(const sp<BitTube>& tube, void const* events, size_t count, size_t objSize) { - ssize_t numObjects = 0; - for (size_t i=0 ; i<count ; i++) { - const char* vaddr = reinterpret_cast<const char*>(events) + objSize * i; - ssize_t size = tube->write(vaddr, objSize); - if (size < 0) { - // error occurred - return size; - } else if (size == 0) { - // no more space - break; - } - numObjects++; - } - return numObjects; + const char* vaddr = reinterpret_cast<const char*>(events); + ssize_t size = tube->write(vaddr, count*objSize); + + // should never happen because of SOCK_SEQPACKET + LOG_ALWAYS_FATAL_IF((size >= 0) && (size % objSize), + "BitTube::sendObjects(count=%d, size=%d), res=%d (partial events were sent!)", + count, objSize, size); + + //ALOGE_IF(size<0, "error %d sending %d events", size, count); + return size < 0 ? size : size / objSize; } ssize_t BitTube::recvObjects(const sp<BitTube>& tube, void* events, size_t count, size_t objSize) { - ssize_t numObjects = 0; - for (size_t i=0 ; i<count ; i++) { - char* vaddr = reinterpret_cast<char*>(events) + objSize * i; - ssize_t size = tube->read(vaddr, objSize); - if (size < 0) { - // error occurred - return size; - } else if (size == 0) { - // no more messages - break; - } - numObjects++; - } - return numObjects; + char* vaddr = reinterpret_cast<char*>(events); + ssize_t size = tube->read(vaddr, count*objSize); + + // should never happen because of SOCK_SEQPACKET + LOG_ALWAYS_FATAL_IF((size >= 0) && (size % objSize), + "BitTube::recvObjects(count=%d, size=%d), res=%d (partial events were received!)", + count, objSize, size); + + //ALOGE_IF(size<0, "error %d receiving %d events", size, count); + return size < 0 ? size : size / objSize; } // ---------------------------------------------------------------------------- diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index 7db1b84..350887a 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -29,13 +29,12 @@ namespace android { -BufferItemConsumer::BufferItemConsumer(uint32_t consumerUsage, - int bufferCount, bool synchronousMode) : - ConsumerBase(new BufferQueue(true) ) +BufferItemConsumer::BufferItemConsumer(const sp<BufferQueue>& bq, + uint32_t consumerUsage, int bufferCount, bool controlledByApp) : + ConsumerBase(bq, controlledByApp) { - mBufferQueue->setConsumerUsageBits(consumerUsage); - mBufferQueue->setSynchronousMode(synchronousMode); - mBufferQueue->setMaxAcquiredBufferCount(bufferCount); + mConsumer->setConsumerUsageBits(consumerUsage); + mConsumer->setMaxAcquiredBufferCount(bufferCount); } BufferItemConsumer::~BufferItemConsumer() { @@ -44,17 +43,18 @@ BufferItemConsumer::~BufferItemConsumer() { void BufferItemConsumer::setName(const String8& name) { Mutex::Autolock _l(mMutex); mName = name; - mBufferQueue->setConsumerName(name); + mConsumer->setConsumerName(name); } -status_t BufferItemConsumer::acquireBuffer(BufferItem *item, bool waitForFence) { +status_t BufferItemConsumer::acquireBuffer(BufferItem *item, + nsecs_t presentWhen, bool waitForFence) { status_t err; if (!item) return BAD_VALUE; Mutex::Autolock _l(mMutex); - err = acquireBufferLocked(item); + err = acquireBufferLocked(item, presentWhen); if (err != OK) { if (err != NO_BUFFER_AVAILABLE) { BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); @@ -82,9 +82,9 @@ status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, Mutex::Autolock _l(mMutex); - err = addReleaseFenceLocked(item.mBuf, releaseFence); + err = addReleaseFenceLocked(item.mBuf, item.mGraphicBuffer, releaseFence); - err = releaseBufferLocked(item.mBuf, EGL_NO_DISPLAY, + err = releaseBufferLocked(item.mBuf, item.mGraphicBuffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); if (err != OK) { BI_LOGE("Failed to release buffer: %s (%d)", @@ -95,12 +95,12 @@ status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, status_t BufferItemConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) { Mutex::Autolock _l(mMutex); - return mBufferQueue->setDefaultBufferSize(w, h); + return mConsumer->setDefaultBufferSize(w, h); } status_t BufferItemConsumer::setDefaultBufferFormat(uint32_t defaultFormat) { Mutex::Autolock _l(mMutex); - return mBufferQueue->setDefaultBufferFormat(defaultFormat); + return mConsumer->setDefaultBufferFormat(defaultFormat); } } // namespace android diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index b4c7231..c165a68 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -25,11 +25,13 @@ #include <EGL/eglext.h> #include <gui/BufferQueue.h> +#include <gui/IConsumerListener.h> #include <gui/ISurfaceComposer.h> #include <private/gui/ComposerService.h> #include <utils/Log.h> #include <utils/Trace.h> +#include <utils/CallStack.h> // Macros for including the BufferQueue name in log messages #define ST_LOGV(x, ...) ALOGV("[%s] "x, mConsumerName.string(), ##__VA_ARGS__) @@ -63,15 +65,15 @@ static const char* scalingModeName(int scalingMode) { } } -BufferQueue::BufferQueue(bool allowSynchronousMode, - const sp<IGraphicBufferAlloc>& allocator) : +BufferQueue::BufferQueue(const sp<IGraphicBufferAlloc>& allocator) : mDefaultWidth(1), mDefaultHeight(1), mMaxAcquiredBufferCount(1), mDefaultMaxBufferCount(2), mOverrideMaxBufferCount(0), - mSynchronousMode(false), - mAllowSynchronousMode(allowSynchronousMode), + mConsumerControlledByApp(false), + mDequeueBufferCannotBlock(false), + mUseAsyncBuffer(true), mConnectedApi(NO_CONNECTED_API), mAbandoned(false), mFrameCounter(0), @@ -100,7 +102,8 @@ BufferQueue::~BufferQueue() { } status_t BufferQueue::setDefaultMaxBufferCountLocked(int count) { - if (count < 2 || count > NUM_BUFFER_SLOTS) + const int minBufferCount = mUseAsyncBuffer ? 2 : 1; + if (count < minBufferCount || count > NUM_BUFFER_SLOTS) return BAD_VALUE; mDefaultMaxBufferCount = count; @@ -109,11 +112,6 @@ status_t BufferQueue::setDefaultMaxBufferCountLocked(int count) { return NO_ERROR; } -bool BufferQueue::isSynchronousMode() const { - Mutex::Autolock lock(mMutex); - return mSynchronousMode; -} - void BufferQueue::setConsumerName(const String8& name) { Mutex::Autolock lock(mMutex); mConsumerName = name; @@ -141,7 +139,7 @@ status_t BufferQueue::setTransformHint(uint32_t hint) { status_t BufferQueue::setBufferCount(int bufferCount) { ST_LOGV("setBufferCount: count=%d", bufferCount); - sp<ConsumerListener> listener; + sp<IConsumerListener> listener; { Mutex::Autolock lock(mMutex); @@ -156,21 +154,21 @@ status_t BufferQueue::setBufferCount(int bufferCount) { } // Error out if the user has dequeued buffers - int maxBufferCount = getMaxBufferCountLocked(); - for (int i=0 ; i<maxBufferCount; i++) { + for (int i=0 ; i<NUM_BUFFER_SLOTS; i++) { if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { ST_LOGE("setBufferCount: client owns some buffers"); return -EINVAL; } } - const int minBufferSlots = getMinMaxBufferCountLocked(); if (bufferCount == 0) { mOverrideMaxBufferCount = 0; mDequeueCondition.broadcast(); return NO_ERROR; } + // fine to assume async to false before we're setting the buffer count + const int minBufferSlots = getMinMaxBufferCountLocked(false); if (bufferCount < minBufferSlots) { ST_LOGE("setBufferCount: requested buffer count (%d) is less than " "minimum (%d)", bufferCount, minBufferSlots); @@ -178,12 +176,10 @@ 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? + // and will release all of its buffer references. We don't clear the + // queue, however, so currently queued buffers still get displayed. freeAllBuffersLocked(); mOverrideMaxBufferCount = bufferCount; - mBufferHasBeenQueued = false; mDequeueCondition.broadcast(); listener = mConsumerListener; } // scope for lock @@ -217,11 +213,14 @@ int BufferQueue::query(int what, int* outValue) value = mDefaultBufferFormat; break; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: - value = getMinUndequeuedBufferCountLocked(); + value = getMinUndequeuedBufferCount(false); break; case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: value = (mQueue.size() >= 2); break; + case NATIVE_WINDOW_CONSUMER_USAGE_BITS: + value = mConsumerUsageBits; + break; default: return BAD_VALUE; } @@ -237,15 +236,11 @@ status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) { ST_LOGE("requestBuffer: BufferQueue has been abandoned!"); return NO_INIT; } - int maxBufferCount = getMaxBufferCountLocked(); - if (slot < 0 || maxBufferCount <= slot) { + if (slot < 0 || slot >= NUM_BUFFER_SLOTS) { ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d", - maxBufferCount, slot); + NUM_BUFFER_SLOTS, 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; @@ -255,7 +250,7 @@ status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) { return NO_ERROR; } -status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, +status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, bool async, 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); @@ -279,7 +274,6 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, usage |= mConsumerUsageBits; int found = -1; - int dequeuedCount = 0; bool tryAgain = true; while (tryAgain) { if (mAbandoned) { @@ -287,7 +281,16 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, return NO_INIT; } - const int maxBufferCount = getMaxBufferCountLocked(); + const int maxBufferCount = getMaxBufferCountLocked(async); + if (async && mOverrideMaxBufferCount) { + // FIXME: some drivers are manually setting the buffer-count (which they + // shouldn't), so we do this extra test here to handle that case. + // This is TEMPORARY, until we get this fixed. + if (mOverrideMaxBufferCount < maxBufferCount) { + ST_LOGE("dequeueBuffer: async mode is invalid with buffercount override"); + return BAD_VALUE; + } + } // Free up any buffers that are in slots beyond the max buffer // count. @@ -301,23 +304,28 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, // look for a free buffer to give to the client found = INVALID_BUFFER_SLOT; - dequeuedCount = 0; + int dequeuedCount = 0; + int acquiredCount = 0; for (int i = 0; i < maxBufferCount; i++) { const int state = mSlots[i].mBufferState; - if (state == BufferSlot::DEQUEUED) { - dequeuedCount++; - } - - 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; - } + switch (state) { + case BufferSlot::DEQUEUED: + dequeuedCount++; + break; + case BufferSlot::ACQUIRED: + acquiredCount++; + break; + case 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; + } + break; } } @@ -336,7 +344,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, // make sure the client is not trying to dequeue more buffers // than allowed. const int newUndequeuedCount = maxBufferCount - (dequeuedCount+1); - const int minUndequeuedCount = getMinUndequeuedBufferCountLocked(); + const int minUndequeuedCount = getMinUndequeuedBufferCount(async); if (newUndequeuedCount < minUndequeuedCount) { ST_LOGE("dequeueBuffer: min undequeued buffer count (%d) " "exceeded (dequeued=%d undequeudCount=%d)", @@ -350,6 +358,16 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, // the max buffer count to change. tryAgain = found == INVALID_BUFFER_SLOT; if (tryAgain) { + // return an error if we're in "cannot block" mode (producer and consumer + // are controlled by the application) -- however, the consumer is allowed + // to acquire briefly an extra buffer (which could cause us to have to wait here) + // and that's okay because we know the wait will be brief (it happens + // if we dequeue a buffer while the consumer has acquired one but not released + // the old one yet -- for e.g.: see GLConsumer::updateTexImage()). + if (mDequeueBufferCannotBlock && (acquiredCount <= mMaxAcquiredBufferCount)) { + ST_LOGE("dequeueBuffer: would block! returning an error instead."); + return WOULD_BLOCK; + } mDequeueCondition.wait(mMutex); } } @@ -392,6 +410,13 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, returnFlags |= IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION; } + + if (CC_UNLIKELY(mSlots[buf].mFence == NULL)) { + ST_LOGE("dequeueBuffer: about to return a NULL fence from mSlot. " + "buf=%d, w=%d, h=%d, format=%d", + buf, buffer->width, buffer->height, buffer->format); + } + dpy = mSlots[buf].mEglDisplay; eglFence = mSlots[buf].mEglFence; *outFence = mSlots[buf].mFence; @@ -402,11 +427,9 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, if (returnFlags & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { status_t error; sp<GraphicBuffer> graphicBuffer( - mGraphicBufferAlloc->createGraphicBuffer( - w, h, format, usage, &error)); + mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage, &error)); if (graphicBuffer == 0) { - ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " - "failed"); + ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer failed"); return error; } @@ -418,6 +441,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, return NO_INIT; } + mSlots[*outBuf].mFrameNumber = ~0; mSlots[*outBuf].mGraphicBuffer = graphicBuffer; } } @@ -435,44 +459,13 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, eglDestroySyncKHR(dpy, eglFence); } - ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, + ST_LOGV("dequeueBuffer: returning slot=%d/%llu buf=%p flags=%#x", *outBuf, + mSlots[*outBuf].mFrameNumber, mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); return returnFlags; } -status_t BufferQueue::setSynchronousMode(bool enabled) { - ATRACE_CALL(); - ST_LOGV("setSynchronousMode: enabled=%d", enabled); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("setSynchronousMode: BufferQueue has been abandoned!"); - return NO_INIT; - } - - status_t err = OK; - if (!mAllowSynchronousMode && enabled) - return err; - - if (!enabled) { - // going to asynchronous mode, drain the queue - err = drainQueueLocked(); - if (err != NO_ERROR) - return err; - } - - if (mSynchronousMode != enabled) { - // - if we're going to asynchronous mode, the queue is guaranteed to be - // empty here - // - if the client set the number of buffers, we're guaranteed that - // we have at least 3 (because we don't allow less) - mSynchronousMode = enabled; - mDequeueCondition.broadcast(); - } - return err; -} - status_t BufferQueue::queueBuffer(int buf, const QueueBufferInput& input, QueueBufferOutput* output) { ATRACE_CALL(); @@ -482,29 +475,49 @@ status_t BufferQueue::queueBuffer(int buf, uint32_t transform; int scalingMode; int64_t timestamp; + bool isAutoTimestamp; + bool async; sp<Fence> fence; - input.deflate(×tamp, &crop, &scalingMode, &transform, &fence); + input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode, &transform, + &async, &fence); if (fence == NULL) { ST_LOGE("queueBuffer: fence is NULL"); return BAD_VALUE; } - ST_LOGV("queueBuffer: slot=%d time=%#llx crop=[%d,%d,%d,%d] tr=%#x " - "scale=%s", - buf, timestamp, crop.left, crop.top, crop.right, crop.bottom, - transform, scalingModeName(scalingMode)); + switch (scalingMode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: + case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: + case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: + break; + default: + ST_LOGE("unknown scaling mode: %d", scalingMode); + return -EINVAL; + } - sp<ConsumerListener> listener; + sp<IConsumerListener> listener; { // scope for the lock Mutex::Autolock lock(mMutex); + if (mAbandoned) { ST_LOGE("queueBuffer: BufferQueue has been abandoned!"); return NO_INIT; } - int maxBufferCount = getMaxBufferCountLocked(); + + const int maxBufferCount = getMaxBufferCountLocked(async); + if (async && mOverrideMaxBufferCount) { + // FIXME: some drivers are manually setting the buffer-count (which they + // shouldn't), so we do this extra test here to handle that case. + // This is TEMPORARY, until we get this fixed. + if (mOverrideMaxBufferCount < maxBufferCount) { + ST_LOGE("queueBuffer: async mode is invalid with buffercount override"); + return BAD_VALUE; + } + } if (buf < 0 || buf >= maxBufferCount) { ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d", maxBufferCount, buf); @@ -519,6 +532,12 @@ status_t BufferQueue::queueBuffer(int buf, return -EINVAL; } + ST_LOGV("queueBuffer: slot=%d/%llu time=%#llx crop=[%d,%d,%d,%d] " + "tr=%#x scale=%s", + buf, mFrameCounter + 1, timestamp, + crop.left, crop.top, crop.right, crop.bottom, + transform, scalingModeName(scalingMode)); + const sp<GraphicBuffer>& graphicBuffer(mSlots[buf].mGraphicBuffer); Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); Rect croppedCrop; @@ -529,52 +548,50 @@ status_t BufferQueue::queueBuffer(int buf, return -EINVAL; } - if (mSynchronousMode) { - // In synchronous mode we queue all buffers in a FIFO. - mQueue.push_back(buf); + mSlots[buf].mFence = fence; + mSlots[buf].mBufferState = BufferSlot::QUEUED; + mFrameCounter++; + mSlots[buf].mFrameNumber = mFrameCounter; + + BufferItem item; + item.mAcquireCalled = mSlots[buf].mAcquireCalled; + item.mGraphicBuffer = mSlots[buf].mGraphicBuffer; + item.mCrop = crop; + item.mTransform = transform & ~NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; + item.mTransformToDisplayInverse = bool(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY); + item.mScalingMode = scalingMode; + item.mTimestamp = timestamp; + item.mIsAutoTimestamp = isAutoTimestamp; + item.mFrameNumber = mFrameCounter; + item.mBuf = buf; + item.mFence = fence; + item.mIsDroppable = mDequeueBufferCannotBlock || async; - // Synchronous mode always signals that an additional frame should - // be consumed. + if (mQueue.empty()) { + // when the queue is empty, we can ignore "mDequeueBufferCannotBlock", and + // simply queue this buffer. + mQueue.push_back(item); listener = mConsumerListener; } else { - // In asynchronous mode we only keep the most recent buffer. - if (mQueue.empty()) { - mQueue.push_back(buf); - - // Asynchronous mode only signals that a frame should be - // consumed if no previous frame was pending. If a frame were - // pending then the consumer would have already been notified. - listener = mConsumerListener; + // when the queue is not empty, we need to look at the front buffer + // state and see if we need to replace it. + Fifo::iterator front(mQueue.begin()); + if (front->mIsDroppable) { + // buffer slot currently queued is marked free if still tracked + if (stillTracking(front)) { + mSlots[front->mBuf].mBufferState = BufferSlot::FREE; + // reset the frame number of the freed buffer so that it is the first in + // line to be dequeued again. + mSlots[front->mBuf].mFrameNumber = 0; + } + // and we record the new buffer in the queued list + *front = item; } else { - Fifo::iterator front(mQueue.begin()); - // buffer currently queued is freed - mSlots[*front].mBufferState = BufferSlot::FREE; - // and we record the new buffer index in the queued list - *front = buf; + mQueue.push_back(item); + listener = mConsumerListener; } } - mSlots[buf].mTimestamp = timestamp; - mSlots[buf].mCrop = crop; - mSlots[buf].mTransform = transform; - mSlots[buf].mFence = fence; - - switch (scalingMode) { - case NATIVE_WINDOW_SCALING_MODE_FREEZE: - case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: - case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: - break; - default: - ST_LOGE("unknown scaling mode: %d (ignoring)", scalingMode); - scalingMode = mSlots[buf].mScalingMode; - break; - } - - mSlots[buf].mBufferState = BufferSlot::QUEUED; - mSlots[buf].mScalingMode = scalingMode; - mFrameCounter++; - mSlots[buf].mFrameNumber = mFrameCounter; - mBufferHasBeenQueued = true; mDequeueCondition.broadcast(); @@ -601,10 +618,9 @@ void BufferQueue::cancelBuffer(int buf, const sp<Fence>& fence) { return; } - int maxBufferCount = getMaxBufferCountLocked(); - if (buf < 0 || buf >= maxBufferCount) { + if (buf < 0 || buf >= NUM_BUFFER_SLOTS) { ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", - maxBufferCount, buf); + NUM_BUFFER_SLOTS, buf); return; } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", @@ -620,9 +636,12 @@ void BufferQueue::cancelBuffer(int buf, const sp<Fence>& fence) { mDequeueCondition.broadcast(); } -status_t BufferQueue::connect(int api, QueueBufferOutput* output) { + +status_t BufferQueue::connect(const sp<IBinder>& token, + int api, bool producerControlledByApp, QueueBufferOutput* output) { ATRACE_CALL(); - ST_LOGV("connect: api=%d", api); + ST_LOGV("connect: api=%d producerControlledByApp=%s", api, + producerControlledByApp ? "true" : "false"); Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -647,8 +666,18 @@ status_t BufferQueue::connect(int api, QueueBufferOutput* output) { err = -EINVAL; } else { mConnectedApi = api; - output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, - mQueue.size()); + output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, mQueue.size()); + + // set-up a death notification so that we can disconnect + // automatically when/if the remote producer dies. + if (token != NULL && token->remoteBinder() != NULL) { + status_t err = token->linkToDeath(static_cast<IBinder::DeathRecipient*>(this)); + if (err == NO_ERROR) { + mConnectedProducerToken = token; + } else { + ALOGE("linkToDeath failed: %s (%d)", strerror(-err), err); + } + } } break; default: @@ -657,16 +686,27 @@ status_t BufferQueue::connect(int api, QueueBufferOutput* output) { } mBufferHasBeenQueued = false; + mDequeueBufferCannotBlock = mConsumerControlledByApp && producerControlledByApp; return err; } +void BufferQueue::binderDied(const wp<IBinder>& who) { + // If we're here, it means that a producer we were connected to died. + // We're GUARANTEED that we still are connected to it because it has no other way + // to get disconnected -- or -- we wouldn't be here because we're removing this + // callback upon disconnect. Therefore, it's okay to read mConnectedApi without + // synchronization here. + int api = mConnectedApi; + this->disconnect(api); +} + status_t BufferQueue::disconnect(int api) { ATRACE_CALL(); ST_LOGV("disconnect: api=%d", api); int err = NO_ERROR; - sp<ConsumerListener> listener; + sp<IConsumerListener> listener; { // Scope for the lock Mutex::Autolock lock(mMutex); @@ -683,7 +723,15 @@ status_t BufferQueue::disconnect(int api) { case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: if (mConnectedApi == api) { - drainQueueAndFreeBuffersLocked(); + freeAllBuffersLocked(); + // remove our death notification callback if we have one + sp<IBinder> token = mConnectedProducerToken; + if (token != NULL) { + // this can fail if we're here because of the death notification + // either way, we just ignore. + token->unlinkToDeath(static_cast<IBinder::DeathRecipient*>(this)); + } + mConnectedProducerToken = NULL; mConnectedApi = NO_CONNECTED_API; mDequeueCondition.broadcast(); listener = mConsumerListener; @@ -707,36 +755,31 @@ status_t BufferQueue::disconnect(int api) { return err; } -void BufferQueue::dump(String8& result) const -{ - char buffer[1024]; - BufferQueue::dump(result, "", buffer, 1024); -} - -void BufferQueue::dump(String8& result, const char* prefix, - char* buffer, size_t SIZE) const -{ +void BufferQueue::dump(String8& result, const char* prefix) const { Mutex::Autolock _l(mMutex); String8 fifo; int fifoSize = 0; Fifo::const_iterator i(mQueue.begin()); while (i != mQueue.end()) { - snprintf(buffer, SIZE, "%02d ", *i++); - fifoSize++; - fifo.append(buffer); + fifo.appendFormat("%02d:%p crop=[%d,%d,%d,%d], " + "xform=0x%02x, time=%#llx, scale=%s\n", + i->mBuf, i->mGraphicBuffer.get(), + i->mCrop.left, i->mCrop.top, i->mCrop.right, + i->mCrop.bottom, i->mTransform, i->mTimestamp, + scalingModeName(i->mScalingMode) + ); + i++; + fifoSize++; } - int maxBufferCount = getMaxBufferCountLocked(); - snprintf(buffer, SIZE, - "%s-BufferQueue maxBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], " + result.appendFormat( + "%s-BufferQueue mMaxAcquiredBufferCount=%d, mDequeueBufferCannotBlock=%d, default-size=[%dx%d], " "default-format=%d, transform-hint=%02x, FIFO(%d)={%s}\n", - prefix, maxBufferCount, mSynchronousMode, mDefaultWidth, + prefix, mMaxAcquiredBufferCount, mDequeueBufferCannotBlock, mDefaultWidth, mDefaultHeight, mDefaultBufferFormat, mTransformHint, fifoSize, fifo.string()); - result.append(buffer); - struct { const char * operator()(int state) const { @@ -750,27 +793,30 @@ void BufferQueue::dump(String8& result, const char* prefix, } } stateName; + // just trim the free buffers to not spam the dump + int maxBufferCount = 0; + for (int i=NUM_BUFFER_SLOTS-1 ; i>=0 ; i--) { + const BufferSlot& slot(mSlots[i]); + if ((slot.mBufferState != BufferSlot::FREE) || (slot.mGraphicBuffer != NULL)) { + maxBufferCount = i+1; + break; + } + } + for (int i=0 ; i<maxBufferCount ; i++) { const BufferSlot& slot(mSlots[i]); - snprintf(buffer, SIZE, - "%s%s[%02d] " - "state=%-8s, crop=[%d,%d,%d,%d], " - "xform=0x%02x, time=%#llx, scale=%s", - prefix, (slot.mBufferState == BufferSlot::ACQUIRED)?">":" ", i, - stateName(slot.mBufferState), - slot.mCrop.left, slot.mCrop.top, slot.mCrop.right, - slot.mCrop.bottom, slot.mTransform, slot.mTimestamp, - scalingModeName(slot.mScalingMode) + const sp<GraphicBuffer>& buf(slot.mGraphicBuffer); + result.appendFormat( + "%s%s[%02d:%p] state=%-8s", + prefix, (slot.mBufferState == BufferSlot::ACQUIRED)?">":" ", i, buf.get(), + stateName(slot.mBufferState) ); - result.append(buffer); - const sp<GraphicBuffer>& buf(slot.mGraphicBuffer); if (buf != NULL) { - snprintf(buffer, SIZE, + result.appendFormat( ", %p [%4ux%4u:%4u,%3X]", buf->handle, buf->width, buf->height, buf->stride, buf->format); - result.append(buffer); } result.append("\n"); } @@ -795,16 +841,13 @@ void BufferQueue::freeBufferLocked(int slot) { } void BufferQueue::freeAllBuffersLocked() { - ALOGW_IF(!mQueue.isEmpty(), - "freeAllBuffersLocked called but mQueue is not empty"); - mQueue.clear(); mBufferHasBeenQueued = false; for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { freeBufferLocked(i); } } -status_t BufferQueue::acquireBuffer(BufferItem *buffer) { +status_t BufferQueue::acquireBuffer(BufferItem *buffer, nsecs_t expectedPresent) { ATRACE_CALL(); Mutex::Autolock _l(mMutex); @@ -827,58 +870,153 @@ status_t BufferQueue::acquireBuffer(BufferItem *buffer) { // 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. - if (!mQueue.empty()) { - Fifo::iterator front(mQueue.begin()); - int buf = *front; + if (mQueue.empty()) { + return NO_BUFFER_AVAILABLE; + } - ATRACE_BUFFER_INDEX(buf); + Fifo::iterator front(mQueue.begin()); - if (mSlots[buf].mAcquireCalled) { - buffer->mGraphicBuffer = NULL; - } else { - buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer; + // If expectedPresent is specified, we may not want to return a buffer yet. + // If it's specified and there's more than one buffer queued, we may + // want to drop a buffer. + if (expectedPresent != 0) { + const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second + + // The "expectedPresent" argument indicates when the buffer is expected + // to be presented on-screen. If the buffer's desired-present time + // is earlier (less) than expectedPresent, meaning it'll be displayed + // on time or possibly late if we show it ASAP, we acquire and return + // it. If we don't want to display it until after the expectedPresent + // time, we return PRESENT_LATER without acquiring it. + // + // To be safe, we don't defer acquisition if expectedPresent is + // more than one second in the future beyond the desired present time + // (i.e. we'd be holding the buffer for a long time). + // + // NOTE: code assumes monotonic time values from the system clock are + // positive. + + // Start by checking to see if we can drop frames. We skip this check + // if the timestamps are being auto-generated by Surface -- if the + // app isn't generating timestamps explicitly, they probably don't + // want frames to be discarded based on them. + while (mQueue.size() > 1 && !mQueue[0].mIsAutoTimestamp) { + // If entry[1] is timely, drop entry[0] (and repeat). We apply + // an additional criteria here: we only drop the earlier buffer if + // our desiredPresent falls within +/- 1 second of the expected + // present. Otherwise, bogus desiredPresent times (e.g. 0 or + // a small relative timestamp), which normally mean "ignore the + // timestamp and acquire immediately", would cause us to drop + // frames. + // + // We may want to add an additional criteria: don't drop the + // earlier buffer if entry[1]'s fence hasn't signaled yet. + // + // (Vector front is [0], back is [size()-1]) + const BufferItem& bi(mQueue[1]); + nsecs_t desiredPresent = bi.mTimestamp; + if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC || + desiredPresent > expectedPresent) { + // This buffer is set to display in the near future, or + // desiredPresent is garbage. Either way we don't want to + // drop the previous buffer just to get this on screen sooner. + ST_LOGV("pts nodrop: des=%lld expect=%lld (%lld) now=%lld", + desiredPresent, expectedPresent, desiredPresent - expectedPresent, + systemTime(CLOCK_MONOTONIC)); + break; + } + ST_LOGV("pts drop: queue1des=%lld expect=%lld size=%d", + desiredPresent, expectedPresent, mQueue.size()); + if (stillTracking(front)) { + // front buffer is still in mSlots, so mark the slot as free + mSlots[front->mBuf].mBufferState = BufferSlot::FREE; + } + mQueue.erase(front); + front = mQueue.begin(); + } + + // See if the front buffer is due. + nsecs_t desiredPresent = front->mTimestamp; + if (desiredPresent > expectedPresent && + desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) { + ST_LOGV("pts defer: des=%lld expect=%lld (%lld) now=%lld", + desiredPresent, expectedPresent, desiredPresent - expectedPresent, + systemTime(CLOCK_MONOTONIC)); + return PRESENT_LATER; } - buffer->mCrop = mSlots[buf].mCrop; - buffer->mTransform = mSlots[buf].mTransform; - buffer->mScalingMode = mSlots[buf].mScalingMode; - buffer->mFrameNumber = mSlots[buf].mFrameNumber; - buffer->mTimestamp = mSlots[buf].mTimestamp; - buffer->mBuf = buf; - buffer->mFence = mSlots[buf].mFence; + ST_LOGV("pts accept: des=%lld expect=%lld (%lld) now=%lld", + desiredPresent, expectedPresent, desiredPresent - expectedPresent, + systemTime(CLOCK_MONOTONIC)); + } + + int buf = front->mBuf; + *buffer = *front; + ATRACE_BUFFER_INDEX(buf); + + ST_LOGV("acquireBuffer: acquiring { slot=%d/%llu, buffer=%p }", + front->mBuf, front->mFrameNumber, + front->mGraphicBuffer->handle); + // if front buffer still being tracked update slot state + if (stillTracking(front)) { mSlots[buf].mAcquireCalled = true; mSlots[buf].mNeedsCleanupOnRelease = false; mSlots[buf].mBufferState = BufferSlot::ACQUIRED; mSlots[buf].mFence = Fence::NO_FENCE; + } - mQueue.erase(front); - mDequeueCondition.broadcast(); - - ATRACE_INT(mConsumerName.string(), mQueue.size()); - } else { - return NO_BUFFER_AVAILABLE; + // If the buffer has previously been acquired by the consumer, set + // mGraphicBuffer to NULL to avoid unnecessarily remapping this + // buffer on the consumer side. + if (buffer->mAcquireCalled) { + buffer->mGraphicBuffer = NULL; } + mQueue.erase(front); + mDequeueCondition.broadcast(); + + ATRACE_INT(mConsumerName.string(), mQueue.size()); + return NO_ERROR; } -status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display, +status_t BufferQueue::releaseBuffer( + int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR eglFence, const sp<Fence>& fence) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(buf); - Mutex::Autolock _l(mMutex); - if (buf == INVALID_BUFFER_SLOT || fence == NULL) { return BAD_VALUE; } - mSlots[buf].mEglDisplay = display; - mSlots[buf].mEglFence = eglFence; - mSlots[buf].mFence = fence; + Mutex::Autolock _l(mMutex); + + // If the frame number has changed because buffer has been reallocated, + // we can ignore this releaseBuffer for the old buffer. + if (frameNumber != mSlots[buf].mFrameNumber) { + return STALE_BUFFER_SLOT; + } + + + // Internal state consistency checks: + // Make sure this buffers hasn't been queued while we were owning it (acquired) + Fifo::iterator front(mQueue.begin()); + Fifo::const_iterator const end(mQueue.end()); + while (front != end) { + if (front->mBuf == buf) { + LOG_ALWAYS_FATAL("[%s] received new buffer(#%lld) on slot #%d that has not yet been " + "acquired", mConsumerName.string(), frameNumber, buf); + break; // never reached + } + front++; + } // The buffer can now only be released if its in the acquired state if (mSlots[buf].mBufferState == BufferSlot::ACQUIRED) { + mSlots[buf].mEglDisplay = display; + mSlots[buf].mEglFence = eglFence; + mSlots[buf].mFence = fence; mSlots[buf].mBufferState = BufferSlot::FREE; } else if (mSlots[buf].mNeedsCleanupOnRelease) { ST_LOGV("releasing a stale buf %d its state was %d", buf, mSlots[buf].mBufferState); @@ -893,8 +1031,10 @@ status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display, return NO_ERROR; } -status_t BufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListener) { - ST_LOGV("consumerConnect"); +status_t BufferQueue::consumerConnect(const sp<IConsumerListener>& consumerListener, + bool controlledByApp) { + ST_LOGV("consumerConnect controlledByApp=%s", + controlledByApp ? "true" : "false"); Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -907,6 +1047,7 @@ status_t BufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListen } mConsumerListener = consumerListener; + mConsumerControlledByApp = controlledByApp; return NO_ERROR; } @@ -943,14 +1084,24 @@ status_t BufferQueue::getReleasedBuffers(uint32_t* slotMask) { mask |= 1 << i; } } + + // Remove buffers in flight (on the queue) from the mask where acquire has + // been called, as the consumer will not receive the buffer address, so + // it should not free these slots. + Fifo::iterator front(mQueue.begin()); + while (front != mQueue.end()) { + if (front->mAcquireCalled) + mask &= ~(1 << front->mBuf); + front++; + } + *slotMask = mask; ST_LOGV("getReleasedBuffers: returning mask %#x", mask); return NO_ERROR; } -status_t BufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h) -{ +status_t BufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h) { ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h); if (!w || !h) { ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)", @@ -970,6 +1121,17 @@ status_t BufferQueue::setDefaultMaxBufferCount(int bufferCount) { return setDefaultMaxBufferCountLocked(bufferCount); } +status_t BufferQueue::disableAsyncBuffer() { + ATRACE_CALL(); + Mutex::Autolock lock(mMutex); + if (mConsumerListener != NULL) { + ST_LOGE("disableAsyncBuffer: consumer already connected!"); + return INVALID_OPERATION; + } + mUseAsyncBuffer = false; + return NO_ERROR; +} + status_t BufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { ATRACE_CALL(); Mutex::Autolock lock(mMutex); @@ -985,58 +1147,26 @@ status_t BufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { return NO_ERROR; } -void BufferQueue::freeAllBuffersExceptHeadLocked() { - int head = -1; - if (!mQueue.empty()) { - Fifo::iterator front(mQueue.begin()); - head = *front; - } - mBufferHasBeenQueued = false; - for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - if (i != head) { - freeBufferLocked(i); - } - } -} - -status_t BufferQueue::drainQueueLocked() { - while (mSynchronousMode && mQueue.size() > 1) { - mDequeueCondition.wait(mMutex); - if (mAbandoned) { - ST_LOGE("drainQueueLocked: BufferQueue has been abandoned!"); - return NO_INIT; - } - if (mConnectedApi == NO_CONNECTED_API) { - ST_LOGE("drainQueueLocked: BufferQueue is not connected!"); - return NO_INIT; - } - } - return NO_ERROR; -} +int BufferQueue::getMinUndequeuedBufferCount(bool async) const { + // if dequeueBuffer is allowed to error out, we don't have to + // add an extra buffer. + if (!mUseAsyncBuffer) + return mMaxAcquiredBufferCount; -status_t BufferQueue::drainQueueAndFreeBuffersLocked() { - status_t err = drainQueueLocked(); - if (err == NO_ERROR) { - if (mQueue.empty()) { - freeAllBuffersLocked(); - } else { - freeAllBuffersExceptHeadLocked(); - } - } - return err; -} + // we're in async mode, or we want to prevent the app to + // deadlock itself, we throw-in an extra buffer to guarantee it. + if (mDequeueBufferCannotBlock || async) + return mMaxAcquiredBufferCount+1; -int BufferQueue::getMinMaxBufferCountLocked() const { - return getMinUndequeuedBufferCountLocked() + 1; + return mMaxAcquiredBufferCount; } -int BufferQueue::getMinUndequeuedBufferCountLocked() const { - return mSynchronousMode ? mMaxAcquiredBufferCount : - mMaxAcquiredBufferCount + 1; +int BufferQueue::getMinMaxBufferCountLocked(bool async) const { + return getMinUndequeuedBufferCount(async) + 1; } -int BufferQueue::getMaxBufferCountLocked() const { - int minMaxBufferCount = getMinMaxBufferCountLocked(); +int BufferQueue::getMaxBufferCountLocked(bool async) const { + int minMaxBufferCount = getMinMaxBufferCountLocked(async); int maxBufferCount = mDefaultMaxBufferCount; if (maxBufferCount < minMaxBufferCount) { @@ -1061,21 +1191,37 @@ int BufferQueue::getMaxBufferCountLocked() const { return maxBufferCount; } +bool BufferQueue::stillTracking(const BufferItem *item) const { + const BufferSlot &slot = mSlots[item->mBuf]; + + ST_LOGV("stillTracking?: item: { slot=%d/%llu, buffer=%p }, " + "slot: { slot=%d/%llu, buffer=%p }", + item->mBuf, item->mFrameNumber, + (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0), + item->mBuf, slot.mFrameNumber, + (slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0)); + + // Compare item with its original buffer slot. We can check the slot + // as the buffer would not be moved to a different slot by the producer. + return (slot.mGraphicBuffer != NULL && + item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle); +} + BufferQueue::ProxyConsumerListener::ProxyConsumerListener( - const wp<BufferQueue::ConsumerListener>& consumerListener): + const wp<ConsumerListener>& consumerListener): mConsumerListener(consumerListener) {} BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {} void BufferQueue::ProxyConsumerListener::onFrameAvailable() { - sp<BufferQueue::ConsumerListener> listener(mConsumerListener.promote()); + sp<ConsumerListener> listener(mConsumerListener.promote()); if (listener != NULL) { listener->onFrameAvailable(); } } void BufferQueue::ProxyConsumerListener::onBuffersReleased() { - sp<BufferQueue::ConsumerListener> listener(mConsumerListener.promote()); + sp<ConsumerListener> listener(mConsumerListener.promote()); if (listener != NULL) { listener->onBuffersReleased(); } diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 4937b17..c4ec857 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -51,9 +51,9 @@ static int32_t createProcessUniqueId() { return android_atomic_inc(&globalCounter); } -ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) : +ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) : mAbandoned(false), - mBufferQueue(bufferQueue) { + mConsumer(bufferQueue) { // Choose a name using the PID and a process-unique ID. mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); @@ -61,17 +61,15 @@ ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) : // 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); + wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this); + sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener); - status_t err = mBufferQueue->consumerConnect(proxy); + status_t err = mConsumer->consumerConnect(proxy, controlledByApp); if (err != NO_ERROR) { CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)", strerror(-err), err); } else { - mBufferQueue->setConsumerName(mName); + mConsumer->setConsumerName(mName); } } @@ -95,12 +93,7 @@ void ConsumerBase::freeBufferLocked(int slotIndex) { CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); mSlots[slotIndex].mGraphicBuffer = 0; mSlots[slotIndex].mFence = Fence::NO_FENCE; -} - -// Used for refactoring, should not be in final interface -sp<BufferQueue> ConsumerBase::getBufferQueue() const { - Mutex::Autolock lock(mMutex); - return mBufferQueue; + mSlots[slotIndex].mFrameNumber = 0; } void ConsumerBase::onFrameAvailable() { @@ -129,7 +122,7 @@ void ConsumerBase::onBuffersReleased() { } uint32_t mask = 0; - mBufferQueue->getReleasedBuffers(&mask); + mConsumer->getReleasedBuffers(&mask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { if (mask & (1 << i)) { freeBufferLocked(i); @@ -153,8 +146,8 @@ void ConsumerBase::abandonLocked() { freeBufferLocked(i); } // disconnect from the BufferQueue - mBufferQueue->consumerDisconnect(); - mBufferQueue.clear(); + mConsumer->consumerDisconnect(); + mConsumer.clear(); } void ConsumerBase::setFrameAvailableListener( @@ -165,28 +158,25 @@ void ConsumerBase::setFrameAvailableListener( } void ConsumerBase::dump(String8& result) const { - char buffer[1024]; - dump(result, "", buffer, 1024); + dump(result, ""); } -void ConsumerBase::dump(String8& result, const char* prefix, - char* buffer, size_t size) const { +void ConsumerBase::dump(String8& result, const char* prefix) const { Mutex::Autolock _l(mMutex); - dumpLocked(result, prefix, buffer, size); + dumpLocked(result, prefix); } -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); +void ConsumerBase::dumpLocked(String8& result, const char* prefix) const { + result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned)); if (!mAbandoned) { - mBufferQueue->dump(result, prefix, buffer, SIZE); + mConsumer->dump(result, prefix); } } -status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item) { - status_t err = mBufferQueue->acquireBuffer(item); +status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item, + nsecs_t presentWhen) { + status_t err = mConsumer->acquireBuffer(item, presentWhen); if (err != NO_ERROR) { return err; } @@ -195,21 +185,31 @@ status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item) { mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer; } + mSlots[item->mBuf].mFrameNumber = item->mFrameNumber; mSlots[item->mBuf].mFence = item->mFence; - CB_LOGV("acquireBufferLocked: -> slot=%d", item->mBuf); + CB_LOGV("acquireBufferLocked: -> slot=%d/%llu", + item->mBuf, item->mFrameNumber); return OK; } -status_t ConsumerBase::addReleaseFence(int slot, const sp<Fence>& fence) { +status_t ConsumerBase::addReleaseFence(int slot, + const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) { Mutex::Autolock lock(mMutex); - return addReleaseFenceLocked(slot, fence); + return addReleaseFenceLocked(slot, graphicBuffer, fence); } -status_t ConsumerBase::addReleaseFenceLocked(int slot, const sp<Fence>& fence) { +status_t ConsumerBase::addReleaseFenceLocked(int slot, + const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) { CB_LOGV("addReleaseFenceLocked: slot=%d", slot); + // If consumer no longer tracks this graphicBuffer, we can safely + // drop this fence, as it will never be received by the producer. + if (!stillTracking(slot, graphicBuffer)) { + return OK; + } + if (!mSlots[slot].mFence.get()) { mSlots[slot].mFence = fence; } else { @@ -229,11 +229,20 @@ status_t ConsumerBase::addReleaseFenceLocked(int slot, const sp<Fence>& fence) { 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); +status_t ConsumerBase::releaseBufferLocked( + int slot, const sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { + // If consumer no longer tracks this graphicBuffer (we received a new + // buffer on the same slot), the buffer producer is definitely no longer + // tracking it. + if (!stillTracking(slot, graphicBuffer)) { + return OK; + } + + CB_LOGV("releaseBufferLocked: slot=%d/%llu", + slot, mSlots[slot].mFrameNumber); + status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, + display, eglFence, mSlots[slot].mFence); if (err == BufferQueue::STALE_BUFFER_SLOT) { freeBufferLocked(slot); } @@ -243,4 +252,13 @@ status_t ConsumerBase::releaseBufferLocked(int slot, EGLDisplay display, return err; } +bool ConsumerBase::stillTracking(int slot, + const sp<GraphicBuffer> graphicBuffer) { + if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) { + return false; + } + return (mSlots[slot].mGraphicBuffer != NULL && + mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle); +} + } // namespace android diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index 0543649..bff55d1 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -30,17 +30,17 @@ namespace android { -CpuConsumer::CpuConsumer(uint32_t maxLockedBuffers, bool synchronousMode) : - ConsumerBase(new BufferQueue(true) ), +CpuConsumer::CpuConsumer(const sp<IGraphicBufferConsumer>& bq, + uint32_t maxLockedBuffers, bool controlledByApp) : + ConsumerBase(bq, controlledByApp), mMaxLockedBuffers(maxLockedBuffers), mCurrentLockedBuffers(0) { // Create tracking entries for locked buffers mAcquiredBuffers.insertAt(0, maxLockedBuffers); - mBufferQueue->setSynchronousMode(synchronousMode); - mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN); - mBufferQueue->setMaxAcquiredBufferCount(maxLockedBuffers); + mConsumer->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN); + mConsumer->setMaxAcquiredBufferCount(maxLockedBuffers); } CpuConsumer::~CpuConsumer() { @@ -52,7 +52,19 @@ CpuConsumer::~CpuConsumer() { void CpuConsumer::setName(const String8& name) { Mutex::Autolock _l(mMutex); mName = name; - mBufferQueue->setConsumerName(name); + mConsumer->setConsumerName(name); +} + +status_t CpuConsumer::setDefaultBufferSize(uint32_t width, uint32_t height) +{ + Mutex::Autolock _l(mMutex); + return mConsumer->setDefaultBufferSize(width, height); +} + +status_t CpuConsumer::setDefaultBufferFormat(uint32_t defaultFormat) +{ + Mutex::Autolock _l(mMutex); + return mConsumer->setDefaultBufferFormat(defaultFormat); } status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) { @@ -60,14 +72,16 @@ status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) { if (!nativeBuffer) return BAD_VALUE; if (mCurrentLockedBuffers == mMaxLockedBuffers) { - return INVALID_OPERATION; + CC_LOGW("Max buffers have been locked (%d), cannot lock anymore.", + mMaxLockedBuffers); + return NOT_ENOUGH_DATA; } BufferQueue::BufferItem b; Mutex::Autolock _l(mMutex); - err = acquireBufferLocked(&b); + err = acquireBufferLocked(&b, 0); if (err != OK) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { return BAD_VALUE; @@ -189,7 +203,9 @@ status_t CpuConsumer::releaseAcquiredBufferLocked(int lockedIdx) { // disconnected after this buffer was acquired. if (CC_LIKELY(mAcquiredBuffers[lockedIdx].mGraphicBuffer == mSlots[buf].mGraphicBuffer)) { - releaseBufferLocked(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + releaseBufferLocked( + buf, mAcquiredBuffers[lockedIdx].mGraphicBuffer, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); } AcquiredBuffer &ab = mAcquiredBuffers.editItemAt(lockedIdx); diff --git a/libs/gui/DummyConsumer.cpp b/libs/gui/DummyConsumer.cpp deleted file mode 100644 index be47e0e..0000000 --- a/libs/gui/DummyConsumer.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 "DummyConsumer" -// #define LOG_NDEBUG 0 - -#include <gui/DummyConsumer.h> - -#include <utils/Log.h> -#include <utils/String8.h> - -namespace android { - -DummyConsumer::DummyConsumer() { - ALOGV("DummyConsumer"); -} - -DummyConsumer::~DummyConsumer() { - ALOGV("~DummyConsumer"); -} - -void DummyConsumer::onFrameAvailable() { - ALOGV("onFrameAvailable"); -} - -void DummyConsumer::onBuffersReleased() { - ALOGV("onBuffersReleased"); -} - -}; // namespace android diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index dc46a51..7ee3081 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -25,6 +25,7 @@ #include <EGL/eglext.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#include <cutils/compiler.h> #include <hardware/hardware.h> @@ -40,6 +41,9 @@ #include <utils/String8.h> #include <utils/Trace.h> +EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); +#define CROP_EXT_STR "EGL_ANDROID_image_crop" + namespace android { // Macros for including the GLConsumer name in log messages @@ -49,6 +53,14 @@ namespace android { #define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) #define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) +static const struct { + size_t width, height; + char const* bits; +} kDebugData = { 15, 12, + "___________________________________XX_XX_______X_X_____X_X____X_XXXXXXX_X____XXXXXXXXXXX__" + "___XX_XXX_XX_______XXXXXXX_________X___X_________X_____X__________________________________" +}; + // Transform matrices static float mtxIdentity[16] = { 1, 0, 0, 0, @@ -77,21 +89,41 @@ static float mtxRot90[16] = { static void mtxMul(float out[16], const float a[16], const float b[16]); +Mutex GLConsumer::sStaticInitLock; +sp<GraphicBuffer> GLConsumer::sReleasedTexImageBuffer; + +static bool hasEglAndroidImageCropImpl() { + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); + size_t cropExtLen = strlen(CROP_EXT_STR); + size_t extsLen = strlen(exts); + bool equal = !strcmp(CROP_EXT_STR, exts); + bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1); + bool atEnd = (cropExtLen+1) < extsLen && + !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1)); + bool inMiddle = strstr(exts, " " CROP_EXT_STR " "); + return equal || atStart || atEnd || inMiddle; +} -GLConsumer::GLConsumer(const sp<BufferQueue>& bq, GLuint tex, - GLenum texTarget, bool useFenceSync) : - ConsumerBase(bq), - mUseFenceSync(useFenceSync), - mTexTarget(texTarget) {} +static bool hasEglAndroidImageCrop() { + // Only compute whether the extension is present once the first time this + // function is called. + static bool hasIt = hasEglAndroidImageCropImpl(); + return hasIt; +} +static bool isEglImageCroppable(const Rect& crop) { + return hasEglAndroidImageCrop() && (crop.left == 0 && crop.top == 0); +} -GLConsumer::GLConsumer(GLuint tex, bool allowSynchronousMode, - GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) : - ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue), +GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, + uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : + ConsumerBase(bq, isControlledByApp), mCurrentTransform(0), mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mCurrentFence(Fence::NO_FENCE), mCurrentTimestamp(0), + mCurrentFrameNumber(0), mDefaultWidth(1), mDefaultHeight(1), mFilteringEnabled(true), @@ -108,12 +140,12 @@ GLConsumer::GLConsumer(GLuint tex, bool allowSynchronousMode, memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix)); - mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); } status_t GLConsumer::setDefaultMaxBufferCount(int bufferCount) { Mutex::Autolock lock(mMutex); - return mBufferQueue->setDefaultMaxBufferCount(bufferCount); + return mConsumer->setDefaultMaxBufferCount(bufferCount); } @@ -122,7 +154,7 @@ status_t GLConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) Mutex::Autolock lock(mMutex); mDefaultWidth = w; mDefaultHeight = h; - return mBufferQueue->setDefaultBufferSize(w, h); + return mConsumer->setDefaultBufferSize(w, h); } status_t GLConsumer::updateTexImage() { @@ -146,7 +178,7 @@ status_t GLConsumer::updateTexImage() { // Acquire the next buffer. // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. - err = acquireBufferLocked(&item); + err = acquireBufferLocked(&item, 0); if (err != NO_ERROR) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // We always bind the texture even if we don't update its contents. @@ -161,7 +193,7 @@ status_t GLConsumer::updateTexImage() { } // Release the previous buffer. - err = releaseAndUpdateLocked(item); + err = updateAndReleaseLocked(item); if (err != NO_ERROR) { // We always bind the texture. glBindTexture(mTexTarget, mTexName); @@ -172,44 +204,154 @@ status_t GLConsumer::updateTexImage() { return bindTextureImageLocked(); } -status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item) { - status_t err = ConsumerBase::acquireBufferLocked(item); + +status_t GLConsumer::releaseTexImage() { + ATRACE_CALL(); + ST_LOGV("releaseTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("releaseTexImage: GLConsumer is abandoned!"); + return NO_INIT; + } + + // Make sure the EGL state is the same as in previous calls. + status_t err = NO_ERROR; + + if (mAttached) { + err = checkAndUpdateEglStateLocked(true); + if (err != NO_ERROR) { + return err; + } + } else { + // if we're detached, no need to validate EGL's state -- we won't use it. + } + + // Update the GLConsumer state. + int buf = mCurrentTexture; + if (buf != BufferQueue::INVALID_BUFFER_SLOT) { + + ST_LOGV("releaseTexImage: (slot=%d, mAttached=%d)", buf, mAttached); + + if (mAttached) { + // Do whatever sync ops we need to do before releasing the slot. + err = syncForReleaseLocked(mEglDisplay); + if (err != NO_ERROR) { + ST_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); + return err; + } + } else { + // if we're detached, we just use the fence that was created in detachFromContext() + // so... basically, nothing more to do here. + } + + err = releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + if (err < NO_ERROR) { + ST_LOGE("releaseTexImage: failed to release buffer: %s (%d)", + strerror(-err), err); + return err; + } + + mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + mCurrentTextureBuf = getDebugTexImageBuffer(); + mCurrentCrop.makeInvalid(); + mCurrentTransform = 0; + mCurrentScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; + mCurrentTimestamp = 0; + mCurrentFence = Fence::NO_FENCE; + + if (mAttached) { + // bind a dummy texture + glBindTexture(mTexTarget, mTexName); + bindUnslottedBufferLocked(mEglDisplay); + } else { + // detached, don't touch the texture (and we may not even have an + // EGLDisplay here. + } + } + + return NO_ERROR; +} + +sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() { + Mutex::Autolock _l(sStaticInitLock); + if (CC_UNLIKELY(sReleasedTexImageBuffer == NULL)) { + // The first time, create the debug texture in case the application + // continues to use it. + sp<GraphicBuffer> buffer = new GraphicBuffer( + kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, + GraphicBuffer::USAGE_SW_WRITE_RARELY); + uint32_t* bits; + buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits)); + size_t w = buffer->getStride(); + size_t h = buffer->getHeight(); + memset(bits, 0, w*h*4); + for (size_t y=0 ; y<kDebugData.height ; y++) { + for (size_t x=0 ; x<kDebugData.width ; x++) { + bits[x] = (kDebugData.bits[y*kDebugData.width+x] == 'X') ? 0xFF000000 : 0xFFFFFFFF; + } + bits += w; + } + buffer->unlock(); + sReleasedTexImageBuffer = buffer; + } + return sReleasedTexImageBuffer; +} + +status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item, + nsecs_t presentWhen) { + status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen); if (err != NO_ERROR) { return err; } int slot = item->mBuf; - if (item->mGraphicBuffer != NULL) { - // This buffer has not been acquired before, so we must assume - // that any EGLImage in mEglSlots is stale. - if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) { - if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) { - ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", - slot); - // keep going - } - mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; + bool destroyEglImage = false; + + if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) { + if (item->mGraphicBuffer != NULL) { + // This buffer has not been acquired before, so we must assume + // that any EGLImage in mEglSlots is stale. + destroyEglImage = true; + } else if (mEglSlots[slot].mCropRect != item->mCrop) { + // We've already seen this buffer before, but it now has a + // different crop rect, so we'll need to recreate the EGLImage if + // we're using the EGL_ANDROID_image_crop extension. + destroyEglImage = hasEglAndroidImageCrop(); } } + if (destroyEglImage) { + if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) { + ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", + slot); + // keep going + } + mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; + } + return NO_ERROR; } -status_t GLConsumer::releaseBufferLocked(int buf, EGLDisplay display, - EGLSyncKHR eglFence) { - status_t err = ConsumerBase::releaseBufferLocked(buf, display, eglFence); - +status_t GLConsumer::releaseBufferLocked(int buf, + sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { + // release the buffer if it hasn't already been discarded by the + // BufferQueue. This can happen, for example, when the producer of this + // buffer has reallocated the original buffer slot after this buffer + // was acquired. + status_t err = ConsumerBase::releaseBufferLocked( + buf, graphicBuffer, display, eglFence); mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; - return err; } -status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) +status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item) { status_t err = NO_ERROR; if (!mAttached) { - ST_LOGE("releaseAndUpdate: GLConsumer is not attached to an OpenGL " + ST_LOGE("updateAndRelease: GLConsumer is not attached to an OpenGL " "ES context"); return INVALID_OPERATION; } @@ -230,13 +372,15 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) // EGLImage when detaching from a context but the buffer has not been // re-allocated. if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) { - EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer); + EGLImageKHR image = createImage(mEglDisplay, + mSlots[buf].mGraphicBuffer, item.mCrop); if (image == EGL_NO_IMAGE_KHR) { - ST_LOGW("releaseAndUpdate: unable to createImage on display=%p slot=%d", + ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, buf); return UNKNOWN_ERROR; } mEglSlots[buf].mEglImage = image; + mEglSlots[buf].mCropRect = item.mCrop; } // Do whatever sync ops we need to do before releasing the old slot. @@ -244,21 +388,25 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) 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. - releaseBufferLocked(buf, mEglDisplay, EGL_NO_SYNC_KHR); + // As we are still under lock since acquireBuffer, it is safe to + // release by slot. + releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, + mEglDisplay, EGL_NO_SYNC_KHR); return err; } - ST_LOGV("releaseAndUpdate: (slot=%d buf=%p) -> (slot=%d buf=%p)", + ST_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf, mSlots[buf].mGraphicBuffer->handle); // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - status_t status = releaseBufferLocked(mCurrentTexture, mEglDisplay, + status_t status = releaseBufferLocked( + mCurrentTexture, mCurrentTextureBuf, mEglDisplay, mEglSlots[mCurrentTexture].mEglFence); - if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) { - ST_LOGE("releaseAndUpdate: failed to release buffer: %s (%d)", + if (status < NO_ERROR) { + ST_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), status); err = status; // keep going, with error raised [?] @@ -273,6 +421,7 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; mCurrentFence = item.mFence; + mCurrentFrameNumber = item.mFrameNumber; computeCurrentTransformMatrixLocked(); @@ -317,18 +466,27 @@ status_t GLConsumer::bindTextureImageLocked() { } -status_t GLConsumer::checkAndUpdateEglStateLocked() { +status_t GLConsumer::checkAndUpdateEglStateLocked(bool contextCheck) { EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); - if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || - dpy == EGL_NO_DISPLAY) { + if (!contextCheck) { + // if this is the first time we're called, mEglDisplay/mEglContext have + // never been set, so don't error out (below). + if (mEglDisplay == EGL_NO_DISPLAY) { + mEglDisplay = dpy; + } + if (mEglContext == EGL_NO_DISPLAY) { + mEglContext = ctx; + } + } + + if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) { ST_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); return INVALID_OPERATION; } - if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || - ctx == EGL_NO_CONTEXT) { + if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) { ST_LOGE("checkAndUpdateEglState: invalid current EGLContext"); return INVALID_OPERATION; } @@ -341,7 +499,8 @@ status_t GLConsumer::checkAndUpdateEglStateLocked() { void GLConsumer::setReleaseFence(const sp<Fence>& fence) { if (fence->isValid() && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - status_t err = addReleaseFence(mCurrentTexture, fence); + status_t err = addReleaseFence(mCurrentTexture, + mCurrentTextureBuf, fence); if (err != OK) { ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err); @@ -406,7 +565,7 @@ status_t GLConsumer::detachFromContext() { return OK; } -status_t GLConsumer::attachToContext(GLuint tex) { +status_t GLConsumer::attachToContext(uint32_t tex) { ATRACE_CALL(); ST_LOGV("attachToContext"); Mutex::Autolock lock(mMutex); @@ -437,7 +596,7 @@ status_t GLConsumer::attachToContext(GLuint tex) { // We need to bind the texture regardless of whether there's a current // buffer. - glBindTexture(mTexTarget, tex); + glBindTexture(mTexTarget, GLuint(tex)); if (mCurrentTextureBuf != NULL) { // The EGLImageKHR that was associated with the slot was destroyed when @@ -462,7 +621,8 @@ status_t GLConsumer::bindUnslottedBufferLocked(EGLDisplay dpy) { mCurrentTexture, mCurrentTextureBuf.get()); // Create a temporary EGLImageKHR. - EGLImageKHR image = createImage(dpy, mCurrentTextureBuf); + Rect crop; + EGLImageKHR image = createImage(dpy, mCurrentTextureBuf, mCurrentCrop); if (image == EGL_NO_IMAGE_KHR) { return UNKNOWN_ERROR; } @@ -510,7 +670,8 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { return UNKNOWN_ERROR; } sp<Fence> fence(new Fence(fenceFd)); - status_t err = addReleaseFenceLocked(mCurrentTexture, fence); + status_t err = addReleaseFenceLocked(mCurrentTexture, + mCurrentTextureBuf, fence); if (err != OK) { ST_LOGE("syncForReleaseLocked: error adding release fence: " "%s (%d)", strerror(-err), err); @@ -571,7 +732,7 @@ bool GLConsumer::isExternalFormat(uint32_t format) return false; } -GLenum GLConsumer::getCurrentTextureTarget() const { +uint32_t GLConsumer::getCurrentTextureTarget() const { return mTexTarget; } @@ -633,62 +794,66 @@ void GLConsumer::computeCurrentTransformMatrixLocked() { 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(); - float bufferHeight = buf->getHeight(); - if (!cropRect.isEmpty()) { - float shrinkAmount = 0.0f; - if (mFilteringEnabled) { - // In order to prevent bilinear sampling beyond the edge of the - // crop rectangle we may need to shrink it by 2 texels in each - // dimension. Normally this would just need to take 1/2 a texel - // off each end, but because the chroma channels of YUV420 images - // are subsampled we may need to shrink the crop region by a whole - // texel on each side. - switch (buf->getPixelFormat()) { - case PIXEL_FORMAT_RGBA_8888: - case PIXEL_FORMAT_RGBX_8888: - case PIXEL_FORMAT_RGB_888: - case PIXEL_FORMAT_RGB_565: - case PIXEL_FORMAT_BGRA_8888: - case PIXEL_FORMAT_RGBA_5551: - case PIXEL_FORMAT_RGBA_4444: - // 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; + float mtxBeforeFlipV[16]; + if (!isEglImageCroppable(mCurrentCrop)) { + Rect cropRect = mCurrentCrop; + float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; + float bufferWidth = buf->getWidth(); + float bufferHeight = buf->getHeight(); + if (!cropRect.isEmpty()) { + float shrinkAmount = 0.0f; + if (mFilteringEnabled) { + // In order to prevent bilinear sampling beyond the edge of the + // crop rectangle we may need to shrink it by 2 texels in each + // dimension. Normally this would just need to take 1/2 a texel + // off each end, but because the chroma channels of YUV420 images + // are subsampled we may need to shrink the crop region by a whole + // texel on each side. + switch (buf->getPixelFormat()) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_RGB_888: + case PIXEL_FORMAT_RGB_565: + case PIXEL_FORMAT_BGRA_8888: + // 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; + } } - } - // Only shrink the dimensions that are not the size of the buffer. - if (cropRect.width() < bufferWidth) { - tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; - sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / - bufferWidth; + // Only shrink the dimensions that are not the size of the buffer. + if (cropRect.width() < bufferWidth) { + tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; + sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / + bufferWidth; + } + if (cropRect.height() < bufferHeight) { + ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / + bufferHeight; + sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / + bufferHeight; + } } - if (cropRect.height() < bufferHeight) { - ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / - bufferHeight; - sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / - bufferHeight; + float crop[16] = { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, 1, 0, + tx, ty, 0, 1, + }; + + mtxMul(mtxBeforeFlipV, crop, xform); + } else { + for (int i = 0; i < 16; i++) { + mtxBeforeFlipV[i] = xform[i]; } } - float crop[16] = { - sx, 0, 0, 0, - 0, sy, 0, 0, - 0, 0, 1, 0, - tx, ty, 0, 1, - }; - - float mtxBeforeFlipV[16]; - mtxMul(mtxBeforeFlipV, crop, xform); // SurfaceFlinger expects the top of its window textures to be at a Y // coordinate of 0, so GLConsumer must behave the same way. We don't @@ -703,13 +868,33 @@ nsecs_t GLConsumer::getTimestamp() { return mCurrentTimestamp; } +nsecs_t GLConsumer::getFrameNumber() { + ST_LOGV("getFrameNumber"); + Mutex::Autolock lock(mMutex); + return mCurrentFrameNumber; +} + EGLImageKHR GLConsumer::createImage(EGLDisplay dpy, - const sp<GraphicBuffer>& graphicBuffer) { + const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) { EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_IMAGE_CROP_LEFT_ANDROID, crop.left, + EGL_IMAGE_CROP_TOP_ANDROID, crop.top, + EGL_IMAGE_CROP_RIGHT_ANDROID, crop.right, + EGL_IMAGE_CROP_BOTTOM_ANDROID, crop.bottom, EGL_NONE, }; + if (!crop.isValid()) { + // No crop rect to set, so terminate the attrib array before the crop. + attrs[2] = EGL_NONE; + } else if (!isEglImageCroppable(crop)) { + // The crop rect is not at the origin, so we can't set the crop on the + // EGLImage because that's not allowed by the EGL_ANDROID_image_crop + // extension. In the future we can add a layered extension that + // removes this restriction if there is hardware that can support it. + attrs[2] = EGL_NONE; + } EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); if (image == EGL_NO_IMAGE_KHR) { @@ -840,11 +1025,6 @@ status_t GLConsumer::doGLFenceWaitLocked() const { return NO_ERROR; } -bool GLConsumer::isSynchronousMode() const { - Mutex::Autolock lock(mMutex); - return mBufferQueue->isSynchronousMode(); -} - void GLConsumer::freeBufferLocked(int slotIndex) { ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); if (slotIndex == mCurrentTexture) { @@ -868,44 +1048,35 @@ void GLConsumer::abandonLocked() { void GLConsumer::setName(const String8& name) { Mutex::Autolock _l(mMutex); mName = name; - mBufferQueue->setConsumerName(name); + mConsumer->setConsumerName(name); } status_t GLConsumer::setDefaultBufferFormat(uint32_t defaultFormat) { Mutex::Autolock lock(mMutex); - return mBufferQueue->setDefaultBufferFormat(defaultFormat); + return mConsumer->setDefaultBufferFormat(defaultFormat); } status_t GLConsumer::setConsumerUsageBits(uint32_t usage) { Mutex::Autolock lock(mMutex); usage |= DEFAULT_USAGE_FLAGS; - return mBufferQueue->setConsumerUsageBits(usage); + return mConsumer->setConsumerUsageBits(usage); } status_t GLConsumer::setTransformHint(uint32_t hint) { Mutex::Autolock lock(mMutex); - return mBufferQueue->setTransformHint(hint); -} - -// Used for refactoring BufferQueue from GLConsumer -// Should not be in final interface once users of GLConsumer are clean up. -status_t GLConsumer::setSynchronousMode(bool enabled) { - Mutex::Autolock lock(mMutex); - return mBufferQueue->setSynchronousMode(enabled); + return mConsumer->setTransformHint(hint); } -void GLConsumer::dumpLocked(String8& result, const char* prefix, - char* buffer, size_t size) const +void GLConsumer::dumpLocked(String8& result, const char* prefix) const { - snprintf(buffer, size, + result.appendFormat( "%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); - ConsumerBase::dumpLocked(result, prefix, buffer, size); + ConsumerBase::dumpLocked(result, prefix); } static void mtxMul(float out[16], const float a[16], const float b[16]) { diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp new file mode 100644 index 0000000..5304462 --- /dev/null +++ b/libs/gui/IConsumerListener.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> + +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +#include <gui/IConsumerListener.h> + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +enum { + ON_FRAME_AVAILABLE = IBinder::FIRST_CALL_TRANSACTION, + ON_BUFFER_RELEASED +}; + +class BpConsumerListener : public BpInterface<IConsumerListener> +{ +public: + BpConsumerListener(const sp<IBinder>& impl) + : BpInterface<IConsumerListener>(impl) { + } + + virtual void onFrameAvailable() { + Parcel data, reply; + data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor()); + remote()->transact(ON_FRAME_AVAILABLE, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual void onBuffersReleased() { + Parcel data, reply; + data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor()); + remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener"); + +// ---------------------------------------------------------------------- + +status_t BnConsumerListener::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case ON_FRAME_AVAILABLE: + CHECK_INTERFACE(IConsumerListener, data, reply); + onFrameAvailable(); + return NO_ERROR; + case ON_BUFFER_RELEASED: + CHECK_INTERFACE(IConsumerListener, data, reply); + onBuffersReleased(); + return NO_ERROR; + } + return BBinder::onTransact(code, data, reply, flags); +} + + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp new file mode 100644 index 0000000..9574b61 --- /dev/null +++ b/libs/gui/IGraphicBufferConsumer.cpp @@ -0,0 +1,485 @@ +/* + * Copyright (C) 2013 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 EGL_EGLEXT_PROTOTYPES + +#include <EGL/egl.h> +#include <EGL/eglext.h> + + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> + +#include <binder/Parcel.h> +#include <binder/IInterface.h> + +#include <gui/IConsumerListener.h> +#include <gui/IGraphicBufferConsumer.h> + +#include <ui/GraphicBuffer.h> +#include <ui/Fence.h> + +#include <system/window.h> + +namespace android { +// --------------------------------------------------------------------------- + +IGraphicBufferConsumer::BufferItem::BufferItem() : + mTransform(0), + mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mTimestamp(0), + mIsAutoTimestamp(false), + mFrameNumber(0), + mBuf(INVALID_BUFFER_SLOT), + mIsDroppable(false), + mAcquireCalled(false), + mTransformToDisplayInverse(false) { + mCrop.makeInvalid(); +} + +size_t IGraphicBufferConsumer::BufferItem::getPodSize() const { + size_t c = sizeof(mCrop) + + sizeof(mTransform) + + sizeof(mScalingMode) + + sizeof(mTimestamp) + + sizeof(mIsAutoTimestamp) + + sizeof(mFrameNumber) + + sizeof(mBuf) + + sizeof(mIsDroppable) + + sizeof(mAcquireCalled) + + sizeof(mTransformToDisplayInverse); + return c; +} + +size_t IGraphicBufferConsumer::BufferItem::getFlattenedSize() const { + size_t c = 0; + if (mGraphicBuffer != 0) { + c += mGraphicBuffer->getFlattenedSize(); + FlattenableUtils::align<4>(c); + } + if (mFence != 0) { + c += mFence->getFlattenedSize(); + FlattenableUtils::align<4>(c); + } + return sizeof(int32_t) + c + getPodSize(); +} + +size_t IGraphicBufferConsumer::BufferItem::getFdCount() const { + size_t c = 0; + if (mGraphicBuffer != 0) { + c += mGraphicBuffer->getFdCount(); + } + if (mFence != 0) { + c += mFence->getFdCount(); + } + return c; +} + +status_t IGraphicBufferConsumer::BufferItem::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + + // make sure we have enough space + if (count < BufferItem::getFlattenedSize()) { + return NO_MEMORY; + } + + // content flags are stored first + uint32_t& flags = *static_cast<uint32_t*>(buffer); + + // advance the pointer + FlattenableUtils::advance(buffer, size, sizeof(uint32_t)); + + flags = 0; + if (mGraphicBuffer != 0) { + status_t err = mGraphicBuffer->flatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + flags |= 1; + } + if (mFence != 0) { + status_t err = mFence->flatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + flags |= 2; + } + + // check we have enough space (in case flattening the fence/graphicbuffer lied to us) + if (size < getPodSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, mCrop); + FlattenableUtils::write(buffer, size, mTransform); + FlattenableUtils::write(buffer, size, mScalingMode); + FlattenableUtils::write(buffer, size, mTimestamp); + FlattenableUtils::write(buffer, size, mIsAutoTimestamp); + FlattenableUtils::write(buffer, size, mFrameNumber); + FlattenableUtils::write(buffer, size, mBuf); + FlattenableUtils::write(buffer, size, mIsDroppable); + FlattenableUtils::write(buffer, size, mAcquireCalled); + FlattenableUtils::write(buffer, size, mTransformToDisplayInverse); + + return NO_ERROR; +} + +status_t IGraphicBufferConsumer::BufferItem::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + + if (size < sizeof(uint32_t)) + return NO_MEMORY; + + uint32_t flags = 0; + FlattenableUtils::read(buffer, size, flags); + + if (flags & 1) { + mGraphicBuffer = new GraphicBuffer(); + status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + } + + if (flags & 2) { + mFence = new Fence(); + status_t err = mFence->unflatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + } + + // check we have enough space + if (size < getPodSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, mCrop); + FlattenableUtils::read(buffer, size, mTransform); + FlattenableUtils::read(buffer, size, mScalingMode); + FlattenableUtils::read(buffer, size, mTimestamp); + FlattenableUtils::read(buffer, size, mIsAutoTimestamp); + FlattenableUtils::read(buffer, size, mFrameNumber); + FlattenableUtils::read(buffer, size, mBuf); + FlattenableUtils::read(buffer, size, mIsDroppable); + FlattenableUtils::read(buffer, size, mAcquireCalled); + FlattenableUtils::read(buffer, size, mTransformToDisplayInverse); + + return NO_ERROR; +} + +// --------------------------------------------------------------------------- + +enum { + ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + RELEASE_BUFFER, + CONSUMER_CONNECT, + CONSUMER_DISCONNECT, + GET_RELEASED_BUFFERS, + SET_DEFAULT_BUFFER_SIZE, + SET_DEFAULT_MAX_BUFFER_COUNT, + DISABLE_ASYNC_BUFFER, + SET_MAX_ACQUIRED_BUFFER_COUNT, + SET_CONSUMER_NAME, + SET_DEFAULT_BUFFER_FORMAT, + SET_CONSUMER_USAGE_BITS, + SET_TRANSFORM_HINT, + DUMP, +}; + + +class BpGraphicBufferConsumer : public BpInterface<IGraphicBufferConsumer> +{ +public: + BpGraphicBufferConsumer(const sp<IBinder>& impl) + : BpInterface<IGraphicBufferConsumer>(impl) + { + } + + virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt64(presentWhen); + status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.read(*buffer); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, + EGLDisplay display, EGLSyncKHR fence, + const sp<Fence>& releaseFence) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(buf); + data.writeInt64(frameNumber); + data.write(*releaseFence); + status_t result = remote()->transact(RELEASE_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeStrongBinder(consumer->asBinder()); + data.writeInt32(controlledByApp); + status_t result = remote()->transact(CONSUMER_CONNECT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t consumerDisconnect() { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + status_t result = remote()->transact(CONSUMER_DISCONNECT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t getReleasedBuffers(uint32_t* slotMask) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + status_t result = remote()->transact(GET_RELEASED_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + *slotMask = reply.readInt32(); + return reply.readInt32(); + } + + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(w); + data.writeInt32(h); + status_t result = remote()->transact(SET_DEFAULT_BUFFER_SIZE, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setDefaultMaxBufferCount(int bufferCount) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(bufferCount); + status_t result = remote()->transact(SET_DEFAULT_MAX_BUFFER_COUNT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t disableAsyncBuffer() { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + status_t result = remote()->transact(DISABLE_ASYNC_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(maxAcquiredBuffers); + status_t result = remote()->transact(SET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual void setConsumerName(const String8& name) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeString8(name); + remote()->transact(SET_CONSUMER_NAME, data, &reply); + } + + virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(defaultFormat); + status_t result = remote()->transact(SET_DEFAULT_BUFFER_FORMAT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setConsumerUsageBits(uint32_t usage) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(usage); + status_t result = remote()->transact(SET_CONSUMER_USAGE_BITS, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setTransformHint(uint32_t hint) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(hint); + status_t result = remote()->transact(SET_TRANSFORM_HINT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual void dump(String8& result, const char* prefix) const { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeString8(result); + data.writeString8(String8(prefix ? prefix : "")); + remote()->transact(DUMP, data, &reply); + reply.readString8(); + } +}; + +IMPLEMENT_META_INTERFACE(GraphicBufferConsumer, "android.gui.IGraphicBufferConsumer"); + +// ---------------------------------------------------------------------- + +status_t BnGraphicBufferConsumer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case ACQUIRE_BUFFER: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + BufferItem item; + int64_t presentWhen = data.readInt64(); + status_t result = acquireBuffer(&item, presentWhen); + status_t err = reply->write(item); + if (err) return err; + reply->writeInt32(result); + return NO_ERROR; + } break; + case RELEASE_BUFFER: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + int buf = data.readInt32(); + uint64_t frameNumber = data.readInt64(); + sp<Fence> releaseFence = new Fence(); + status_t err = data.read(*releaseFence); + if (err) return err; + status_t result = releaseBuffer(buf, frameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence); + reply->writeInt32(result); + return NO_ERROR; + } break; + case CONSUMER_CONNECT: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + sp<IConsumerListener> consumer = IConsumerListener::asInterface( data.readStrongBinder() ); + bool controlledByApp = data.readInt32(); + status_t result = consumerConnect(consumer, controlledByApp); + reply->writeInt32(result); + return NO_ERROR; + } break; + case CONSUMER_DISCONNECT: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + status_t result = consumerDisconnect(); + reply->writeInt32(result); + return NO_ERROR; + } break; + case GET_RELEASED_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t slotMask; + status_t result = getReleasedBuffers(&slotMask); + reply->writeInt32(slotMask); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_DEFAULT_BUFFER_SIZE: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + status_t result = setDefaultBufferSize(w, h); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_DEFAULT_MAX_BUFFER_COUNT: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t bufferCount = data.readInt32(); + status_t result = setDefaultMaxBufferCount(bufferCount); + reply->writeInt32(result); + return NO_ERROR; + } break; + case DISABLE_ASYNC_BUFFER: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + status_t result = disableAsyncBuffer(); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_MAX_ACQUIRED_BUFFER_COUNT: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t maxAcquiredBuffers = data.readInt32(); + status_t result = setMaxAcquiredBufferCount(maxAcquiredBuffers); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_CONSUMER_NAME: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + setConsumerName( data.readString8() ); + return NO_ERROR; + } break; + case SET_DEFAULT_BUFFER_FORMAT: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t defaultFormat = data.readInt32(); + status_t result = setDefaultBufferFormat(defaultFormat); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_CONSUMER_USAGE_BITS: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t usage = data.readInt32(); + status_t result = setConsumerUsageBits(usage); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_TRANSFORM_HINT: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t hint = data.readInt32(); + status_t result = setTransformHint(hint); + reply->writeInt32(result); + return NO_ERROR; + } break; + case DUMP: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + String8 result = data.readString8(); + String8 prefix = data.readString8(); + static_cast<IGraphicBufferConsumer*>(this)->dump(result, prefix); + reply->writeString8(result); + return NO_ERROR; + } + } + return BBinder::onTransact(code, data, reply, flags); +} + +}; // namespace android diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index c335439..0f461e5 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -37,12 +37,10 @@ enum { QUEUE_BUFFER, CANCEL_BUFFER, QUERY, - SET_SYNCHRONOUS_MODE, CONNECT, DISCONNECT, }; - class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> { public: @@ -85,10 +83,11 @@ public: return result; } - virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, + virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, bool async, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(async); data.writeInt32(w); data.writeInt32(h); data.writeInt32(format); @@ -98,13 +97,10 @@ public: return result; } *buf = reply.readInt32(); - bool fenceWasWritten = reply.readInt32(); - if (fenceWasWritten) { - // If the fence was written by the callee, then overwrite the - // caller's fence here. If it wasn't written then don't touch the - // caller's fence. + bool nonNull = reply.readInt32(); + if (nonNull) { *fence = new Fence(); - reply.read(*(fence->get())); + reply.read(**fence); } result = reply.readInt32(); return result; @@ -146,22 +142,13 @@ public: return result; } - virtual status_t setSynchronousMode(bool enabled) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); - data.writeInt32(enabled); - status_t result = remote()->transact(SET_SYNCHRONOUS_MODE, data, &reply); - if (result != NO_ERROR) { - return result; - } - result = reply.readInt32(); - return result; - } - - virtual status_t connect(int api, QueueBufferOutput* output) { + virtual status_t connect(const sp<IBinder>& token, + int api, bool producerControlledByApp, QueueBufferOutput* output) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeStrongBinder(token); data.writeInt32(api); + data.writeInt32(producerControlledByApp); status_t result = remote()->transact(CONNECT, data, &reply); if (result != NO_ERROR) { return result; @@ -213,17 +200,18 @@ status_t BnGraphicBufferProducer::onTransact( } break; case DEQUEUE_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + bool async = data.readInt32(); uint32_t w = data.readInt32(); uint32_t h = data.readInt32(); uint32_t format = data.readInt32(); uint32_t usage = data.readInt32(); int buf; sp<Fence> fence; - int result = dequeueBuffer(&buf, &fence, w, h, format, usage); + int result = dequeueBuffer(&buf, &fence, async, w, h, format, usage); reply->writeInt32(buf); reply->writeInt32(fence != NULL); if (fence != NULL) { - reply->write(*fence.get()); + reply->write(*fence); } reply->writeInt32(result); return NO_ERROR; @@ -256,20 +244,15 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(res); return NO_ERROR; } break; - case SET_SYNCHRONOUS_MODE: { - CHECK_INTERFACE(IGraphicBufferProducer, data, reply); - bool enabled = data.readInt32(); - status_t res = setSynchronousMode(enabled); - reply->writeInt32(res); - return NO_ERROR; - } break; case CONNECT: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + sp<IBinder> token = data.readStrongBinder(); int api = data.readInt32(); + bool producerControlledByApp = data.readInt32(); QueueBufferOutput* const output = reinterpret_cast<QueueBufferOutput *>( reply->writeInplace(sizeof(QueueBufferOutput))); - status_t res = connect(api, output); + status_t res = connect(token, api, producerControlledByApp, output); reply->writeInt32(res); return NO_ERROR; } break; @@ -290,45 +273,59 @@ IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) parcel.read(*this); } -size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const -{ +size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const { return sizeof(timestamp) + + sizeof(isAutoTimestamp) + sizeof(crop) + sizeof(scalingMode) + sizeof(transform) + + sizeof(async) + fence->getFlattenedSize(); } -size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const -{ +size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const { return fence->getFdCount(); } -status_t IGraphicBufferProducer::QueueBufferInput::flatten(void* buffer, size_t size, - int fds[], size_t count) const +status_t IGraphicBufferProducer::QueueBufferInput::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { - status_t err = NO_ERROR; - 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); - err = fence->flatten(p, size - (p - (char*)buffer), fds, count); - return err; + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + FlattenableUtils::write(buffer, size, timestamp); + FlattenableUtils::write(buffer, size, isAutoTimestamp); + FlattenableUtils::write(buffer, size, crop); + FlattenableUtils::write(buffer, size, scalingMode); + FlattenableUtils::write(buffer, size, transform); + FlattenableUtils::write(buffer, size, async); + return fence->flatten(buffer, size, fds, count); } -status_t IGraphicBufferProducer::QueueBufferInput::unflatten(void const* buffer, - size_t size, int fds[], size_t count) +status_t IGraphicBufferProducer::QueueBufferInput::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { - status_t err = NO_ERROR; - 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); + size_t minNeeded = + sizeof(timestamp) + + sizeof(isAutoTimestamp) + + sizeof(crop) + + sizeof(scalingMode) + + sizeof(transform) + + sizeof(async); + + if (size < minNeeded) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, timestamp); + FlattenableUtils::read(buffer, size, isAutoTimestamp); + FlattenableUtils::read(buffer, size, crop); + FlattenableUtils::read(buffer, size, scalingMode); + FlattenableUtils::read(buffer, size, transform); + FlattenableUtils::read(buffer, size, async); + fence = new Fence(); - err = fence->unflatten(p, size - (p - (const char*)buffer), fds, count); - return err; + return fence->unflatten(buffer, size, fds, count); } }; // namespace android diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/gui/ISensorEventConnection.cpp index 0e51e8e..28fcb53 100644 --- a/libs/gui/ISensorEventConnection.cpp +++ b/libs/gui/ISensorEventConnection.cpp @@ -33,7 +33,8 @@ namespace android { enum { GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, ENABLE_DISABLE, - SET_EVENT_RATE + SET_EVENT_RATE, + FLUSH_SENSOR }; class BpSensorEventConnection : public BpInterface<ISensorEventConnection> @@ -52,12 +53,16 @@ public: return new BitTube(reply); } - virtual status_t enableDisable(int handle, bool enabled) + virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs, + nsecs_t maxBatchReportLatencyNs, int reservedFlags) { Parcel data, reply; data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); data.writeInt32(handle); data.writeInt32(enabled); + data.writeInt64(samplingPeriodNs); + data.writeInt64(maxBatchReportLatencyNs); + data.writeInt32(reservedFlags); remote()->transact(ENABLE_DISABLE, data, &reply); return reply.readInt32(); } @@ -71,6 +76,13 @@ public: remote()->transact(SET_EVENT_RATE, data, &reply); return reply.readInt32(); } + + virtual status_t flush() { + Parcel data, reply; + data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); + remote()->transact(FLUSH_SENSOR, data, &reply); + return reply.readInt32(); + } }; IMPLEMENT_META_INTERFACE(SensorEventConnection, "android.gui.SensorEventConnection"); @@ -91,7 +103,11 @@ status_t BnSensorEventConnection::onTransact( CHECK_INTERFACE(ISensorEventConnection, data, reply); int handle = data.readInt32(); int enabled = data.readInt32(); - status_t result = enableDisable(handle, enabled); + nsecs_t samplingPeriodNs = data.readInt64(); + nsecs_t maxBatchReportLatencyNs = data.readInt64(); + int reservedFlags = data.readInt32(); + status_t result = enableDisable(handle, enabled, samplingPeriodNs, + maxBatchReportLatencyNs, reservedFlags); reply->writeInt32(result); return NO_ERROR; } break; @@ -103,6 +119,12 @@ status_t BnSensorEventConnection::onTransact( reply->writeInt32(result); return NO_ERROR; } break; + case FLUSH_SENSOR: { + CHECK_INTERFACE(ISensorEventConnection, data, reply); + status_t result = flush(); + reply->writeInt32(result); + return NO_ERROR; + } break; } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 6442a86..aab0604 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -105,8 +105,7 @@ public: virtual status_t captureScreen(const sp<IBinder>& display, const sp<IGraphicBufferProducer>& producer, uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ, - bool isCpuConsumer) + uint32_t minLayerZ, uint32_t maxLayerZ) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -116,7 +115,6 @@ public: data.writeInt32(reqHeight); data.writeInt32(minLayerZ); data.writeInt32(maxLayerZ); - data.writeInt32(isCpuConsumer); remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); return reply.readInt32(); } @@ -187,6 +185,14 @@ public: return reply.readStrongBinder(); } + virtual void destroyDisplay(const sp<IBinder>& display) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + remote()->transact(BnSurfaceComposer::DESTROY_DISPLAY, data, &reply); + } + virtual sp<IBinder> getBuiltInDisplay(int32_t id) { Parcel data, reply; @@ -235,12 +241,14 @@ status_t BnSurfaceComposer::onTransact( CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> b = createConnection()->asBinder(); reply->writeStrongBinder(b); - } break; + return NO_ERROR; + } case CREATE_GRAPHIC_BUFFER_ALLOC: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> b = createGraphicBufferAlloc()->asBinder(); reply->writeStrongBinder(b); - } break; + return NO_ERROR; + } case SET_TRANSACTION_STATE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); size_t count = data.readInt32(); @@ -261,11 +269,13 @@ status_t BnSurfaceComposer::onTransact( } uint32_t flags = data.readInt32(); setTransactionState(state, displays, flags); - } break; + return NO_ERROR; + } case BOOT_FINISHED: { CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); - } break; + return NO_ERROR; + } case CAPTURE_SCREEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = data.readStrongBinder(); @@ -275,25 +285,25 @@ status_t BnSurfaceComposer::onTransact( uint32_t reqHeight = data.readInt32(); uint32_t minLayerZ = data.readInt32(); uint32_t maxLayerZ = data.readInt32(); - bool isCpuConsumer = data.readInt32(); status_t res = captureScreen(display, producer, - reqWidth, reqHeight, minLayerZ, maxLayerZ, - isCpuConsumer); + reqWidth, reqHeight, minLayerZ, maxLayerZ); reply->writeInt32(res); - } break; + return NO_ERROR; + } case AUTHENTICATE_SURFACE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IGraphicBufferProducer> bufferProducer = interface_cast<IGraphicBufferProducer>(data.readStrongBinder()); int32_t result = authenticateSurfaceTexture(bufferProducer) ? 1 : 0; reply->writeInt32(result); - } break; + return NO_ERROR; + } case CREATE_DISPLAY_EVENT_CONNECTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IDisplayEventConnection> connection(createDisplayEventConnection()); reply->writeStrongBinder(connection->asBinder()); return NO_ERROR; - } break; + } case CREATE_DISPLAY: { CHECK_INTERFACE(ISurfaceComposer, data, reply); String8 displayName = data.readString8(); @@ -301,24 +311,32 @@ status_t BnSurfaceComposer::onTransact( sp<IBinder> display(createDisplay(displayName, secure)); reply->writeStrongBinder(display); return NO_ERROR; - } break; + } + case DESTROY_DISPLAY: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> display = data.readStrongBinder(); + destroyDisplay(display); + return NO_ERROR; + } 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; + return NO_ERROR; + } case UNBLANK: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = data.readStrongBinder(); unblank(display); - } break; + return NO_ERROR; + } case GET_DISPLAY_INFO: { CHECK_INTERFACE(ISurfaceComposer, data, reply); DisplayInfo info; @@ -326,10 +344,13 @@ status_t BnSurfaceComposer::onTransact( status_t result = getDisplayInfo(display, &info); memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo)); reply->writeInt32(result); - } break; - default: + return NO_ERROR; + } + default: { return BBinder::onTransact(code, data, reply, flags); + } } + // should be unreachable return NO_ERROR; } diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp index c52a88f..da6b0f9 100644 --- a/libs/gui/Sensor.cpp +++ b/libs/gui/Sensor.cpp @@ -32,11 +32,11 @@ namespace android { Sensor::Sensor() : mHandle(0), mType(0), mMinValue(0), mMaxValue(0), mResolution(0), - mPower(0), mMinDelay(0) + mPower(0), mMinDelay(0), mFifoReservedEventCount(0), mFifoMaxEventCount(0) { } -Sensor::Sensor(struct sensor_t const* hwSensor) +Sensor::Sensor(struct sensor_t const* hwSensor, int halVersion) { mName = hwSensor->name; mVendor = hwSensor->vendor; @@ -48,6 +48,15 @@ Sensor::Sensor(struct sensor_t const* hwSensor) mResolution = hwSensor->resolution; mPower = hwSensor->power; mMinDelay = hwSensor->minDelay; + // Set fifo event count zero for older devices which do not support batching. Fused + // sensors also have their fifo counts set to zero. + if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) { + mFifoReservedEventCount = hwSensor->fifoReservedEventCount; + mFifoMaxEventCount = hwSensor->fifoMaxEventCount; + } else { + mFifoReservedEventCount = 0; + mFifoMaxEventCount = 0; + } } Sensor::~Sensor() @@ -98,85 +107,97 @@ int32_t Sensor::getVersion() const { return mVersion; } -size_t Sensor::getSize() const -{ - return sizeof(int32_t) + ((mName.length() + 3) & ~3) + - sizeof(int32_t) + ((mVendor.length() + 3) & ~3) + - sizeof(int32_t) * 3 + - sizeof(float) * 4 + - sizeof(int32_t); -} - -static inline -size_t write(void* buffer, size_t offset, const String8& value) { - memcpy(static_cast<char*>(buffer) + offset, value.string(), value.length()); - return (value.length() + 3) & ~3; -} - -static inline -size_t write(void* buffer, size_t offset, float value) { - *reinterpret_cast<float*>(static_cast<char*>(buffer) + offset) = value; - return sizeof(float); +int32_t Sensor::getFifoReservedEventCount() const { + return mFifoReservedEventCount; } -static inline -size_t write(void* buffer, size_t offset, int32_t value) { - *reinterpret_cast<int32_t*>(static_cast<char*>(buffer) + offset) = value; - return sizeof(int32_t); +int32_t Sensor::getFifoMaxEventCount() const { + return mFifoMaxEventCount; } -status_t Sensor::flatten(void* buffer) const +size_t Sensor::getFlattenedSize() const { - size_t offset = 0; - offset += write(buffer, offset, int32_t(mName.length())); - offset += write(buffer, offset, mName); - offset += write(buffer, offset, int32_t(mVendor.length())); - offset += write(buffer, offset, mVendor); - offset += write(buffer, offset, mVersion); - offset += write(buffer, offset, mHandle); - offset += write(buffer, offset, mType); - offset += write(buffer, offset, mMinValue); - offset += write(buffer, offset, mMaxValue); - offset += write(buffer, offset, mResolution); - offset += write(buffer, offset, mPower); - offset += write(buffer, offset, mMinDelay); + size_t fixedSize = + sizeof(int32_t) * 3 + + sizeof(float) * 4 + + sizeof(int32_t) * 3; + + size_t variableSize = + sizeof(int32_t) + FlattenableUtils::align<4>(mName.length()) + + sizeof(int32_t) + FlattenableUtils::align<4>(mVendor.length()); + + return fixedSize + variableSize; +} + +status_t Sensor::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, mName.length()); + memcpy(static_cast<char*>(buffer), mName.string(), mName.length()); + FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(mName.length())); + + FlattenableUtils::write(buffer, size, mVendor.length()); + memcpy(static_cast<char*>(buffer), mVendor.string(), mVendor.length()); + FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(mVendor.length())); + + FlattenableUtils::write(buffer, size, mVersion); + FlattenableUtils::write(buffer, size, mHandle); + FlattenableUtils::write(buffer, size, mType); + FlattenableUtils::write(buffer, size, mMinValue); + FlattenableUtils::write(buffer, size, mMaxValue); + FlattenableUtils::write(buffer, size, mResolution); + FlattenableUtils::write(buffer, size, mPower); + FlattenableUtils::write(buffer, size, mMinDelay); + FlattenableUtils::write(buffer, size, mFifoReservedEventCount); + FlattenableUtils::write(buffer, size, mFifoMaxEventCount); return NO_ERROR; } -static inline -size_t read(void const* buffer, size_t offset, String8* value, int32_t len) { - value->setTo(static_cast<char const*>(buffer) + offset, len); - return (len + 3) & ~3; -} +status_t Sensor::unflatten(void const* buffer, size_t size) { + size_t len; -static inline -size_t read(void const* buffer, size_t offset, float* value) { - *value = *reinterpret_cast<float const*>(static_cast<char const*>(buffer) + offset); - return sizeof(float); -} + if (size < sizeof(size_t)) { + return NO_MEMORY; + } + FlattenableUtils::read(buffer, size, len); + if (size < len) { + return NO_MEMORY; + } + mName.setTo(static_cast<char const*>(buffer), len); + FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len)); -static inline -size_t read(void const* buffer, size_t offset, int32_t* value) { - *value = *reinterpret_cast<int32_t const*>(static_cast<char const*>(buffer) + offset); - return sizeof(int32_t); -} -status_t Sensor::unflatten(void const* buffer, size_t size) -{ - int32_t len; - size_t offset = 0; - offset += read(buffer, offset, &len); - offset += read(buffer, offset, &mName, len); - offset += read(buffer, offset, &len); - offset += read(buffer, offset, &mVendor, len); - offset += read(buffer, offset, &mVersion); - offset += read(buffer, offset, &mHandle); - offset += read(buffer, offset, &mType); - offset += read(buffer, offset, &mMinValue); - offset += read(buffer, offset, &mMaxValue); - offset += read(buffer, offset, &mResolution); - offset += read(buffer, offset, &mPower); - offset += read(buffer, offset, &mMinDelay); + if (size < sizeof(size_t)) { + return NO_MEMORY; + } + FlattenableUtils::read(buffer, size, len); + if (size < len) { + return NO_MEMORY; + } + mVendor.setTo(static_cast<char const*>(buffer), len); + FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len)); + + size_t fixedSize = + sizeof(int32_t) * 3 + + sizeof(float) * 4 + + sizeof(int32_t) * 3; + + if (size < fixedSize) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, mVersion); + FlattenableUtils::read(buffer, size, mHandle); + FlattenableUtils::read(buffer, size, mType); + FlattenableUtils::read(buffer, size, mMinValue); + FlattenableUtils::read(buffer, size, mMaxValue); + FlattenableUtils::read(buffer, size, mResolution); + FlattenableUtils::read(buffer, size, mPower); + FlattenableUtils::read(buffer, size, mMinDelay); + FlattenableUtils::read(buffer, size, mFifoReservedEventCount); + FlattenableUtils::read(buffer, size, mFifoMaxEventCount); return NO_ERROR; } diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp index 8a1bf41..c365671 100644 --- a/libs/gui/SensorEventQueue.cpp +++ b/libs/gui/SensorEventQueue.cpp @@ -35,12 +35,12 @@ namespace android { // ---------------------------------------------------------------------------- SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection) - : mSensorEventConnection(connection) -{ + : mSensorEventConnection(connection), mRecBuffer(NULL), mAvailable(0), mConsumed(0) { + mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT]; } -SensorEventQueue::~SensorEventQueue() -{ +SensorEventQueue::~SensorEventQueue() { + delete [] mRecBuffer; } void SensorEventQueue::onFirstRef() @@ -59,9 +59,21 @@ ssize_t SensorEventQueue::write(const sp<BitTube>& tube, return BitTube::sendObjects(tube, events, numEvents); } -ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) -{ - return BitTube::recvObjects(mSensorChannel, events, numEvents); +ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) { + if (mAvailable == 0) { + ssize_t err = BitTube::recvObjects(mSensorChannel, + mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT); + if (err < 0) { + return err; + } + mAvailable = err; + mConsumed = 0; + } + size_t count = numEvents < mAvailable ? numEvents : mAvailable; + memcpy(events, mRecBuffer + mConsumed, count*sizeof(ASensorEvent)); + mAvailable -= count; + mConsumed += count; + return count; } sp<Looper> SensorEventQueue::getLooper() const @@ -107,23 +119,25 @@ status_t SensorEventQueue::wake() const } status_t SensorEventQueue::enableSensor(Sensor const* sensor) const { - return mSensorEventConnection->enableDisable(sensor->getHandle(), true); + return mSensorEventConnection->enableDisable(sensor->getHandle(), true, 0, 0, false); } status_t SensorEventQueue::disableSensor(Sensor const* sensor) const { - return mSensorEventConnection->enableDisable(sensor->getHandle(), false); + return mSensorEventConnection->enableDisable(sensor->getHandle(), false, 0, 0, false); } -status_t SensorEventQueue::enableSensor(int32_t handle, int32_t us) const { - status_t err = mSensorEventConnection->enableDisable(handle, true); - if (err == NO_ERROR) { - mSensorEventConnection->setEventRate(handle, us2ns(us)); - } - return err; +status_t SensorEventQueue::enableSensor(int32_t handle, int32_t samplingPeriodUs, + int maxBatchReportLatencyUs, int reservedFlags) const { + return mSensorEventConnection->enableDisable(handle, true, us2ns(samplingPeriodUs), + us2ns(maxBatchReportLatencyUs), reservedFlags); +} + +status_t SensorEventQueue::flush() const { + return mSensorEventConnection->flush(); } status_t SensorEventQueue::disableSensor(int32_t handle) const { - return mSensorEventConnection->enableDisable(handle, false); + return mSensorEventConnection->enableDisable(handle, false, 0, 0, false); } status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const { diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index a616c1e..27dbc4e 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -37,7 +37,8 @@ namespace android { Surface::Surface( - const sp<IGraphicBufferProducer>& bufferProducer) + const sp<IGraphicBufferProducer>& bufferProducer, + bool controlledByApp) : mGraphicBufferProducer(bufferProducer) { // Initialize the ANativeWindow function pointers. @@ -71,6 +72,8 @@ Surface::Surface( mTransformHint = 0; mConsumerRunningBehind = false; mConnectedToCpu = false; + mProducerControlledByApp = controlledByApp; + mSwapIntervalZero = false; } Surface::~Surface() { @@ -160,7 +163,6 @@ int Surface::setSwapInterval(int interval) { // EGL specification states: // interval is silently clamped to minimum and maximum implementation // dependent values before being stored. - // Although we don't have to, we apply the same logic here. if (interval < minSwapInterval) interval = minSwapInterval; @@ -168,13 +170,12 @@ int Surface::setSwapInterval(int interval) { if (interval > maxSwapInterval) interval = maxSwapInterval; - status_t res = mGraphicBufferProducer->setSynchronousMode(interval ? true : false); + mSwapIntervalZero = (interval == 0); - return res; + return NO_ERROR; } -int Surface::dequeueBuffer(android_native_buffer_t** buffer, - int* fenceFd) { +int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { ATRACE_CALL(); ALOGV("Surface::dequeueBuffer"); Mutex::Autolock lock(mMutex); @@ -182,7 +183,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int reqW = mReqWidth ? mReqWidth : mUserWidth; int reqH = mReqHeight ? mReqHeight : mUserHeight; sp<Fence> fence; - status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, + status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, mSwapIntervalZero, reqW, reqH, mReqFormat, mReqUsage); if (result < 0) { ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer(%d, %d, %d, %d)" @@ -191,6 +192,10 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, return result; } sp<GraphicBuffer>& gbuf(mSlots[buf].buffer); + + // this should never happen + ALOGE_IF(fence == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf); + if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { freeAllBuffers(); } @@ -198,8 +203,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) { result = mGraphicBufferProducer->requestBuffer(buf, &gbuf); if (result != NO_ERROR) { - ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", - result); + ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result); return result; } } @@ -258,10 +262,12 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { ALOGV("Surface::queueBuffer"); Mutex::Autolock lock(mMutex); int64_t timestamp; + bool isAutoTimestamp = false; if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + isAutoTimestamp = true; ALOGV("Surface::queueBuffer making up timestamp: %.2f ms", - timestamp / 1000000.f); + timestamp / 1000000.f); } else { timestamp = mTimestamp; } @@ -277,8 +283,8 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); IGraphicBufferProducer::QueueBufferOutput output; - IGraphicBufferProducer::QueueBufferInput input(timestamp, crop, mScalingMode, - mTransform, fence); + IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp, + crop, mScalingMode, mTransform, mSwapIntervalZero, fence); status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); if (err != OK) { ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err); @@ -484,9 +490,10 @@ int Surface::dispatchUnlockAndPost(va_list args) { int Surface::connect(int api) { ATRACE_CALL(); ALOGV("Surface::connect"); + static sp<BBinder> sLife = new BBinder(); Mutex::Autolock lock(mMutex); IGraphicBufferProducer::QueueBufferOutput output; - int err = mGraphicBufferProducer->connect(api, &output); + int err = mGraphicBufferProducer->connect(sLife, api, mProducerControlledByApp, &output); if (err == NO_ERROR) { uint32_t numPendingBuffers = 0; output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint, @@ -499,6 +506,7 @@ int Surface::connect(int api) { return err; } + int Surface::disconnect(int api) { ATRACE_CALL(); ALOGV("Surface::disconnect"); diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index f345df8..aafc4d2 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -135,6 +135,7 @@ class Composer : public Singleton<Composer> public: sp<IBinder> createDisplay(const String8& displayName, bool secure); + void destroyDisplay(const sp<IBinder>& display); sp<IBinder> getBuiltInDisplay(int32_t id); status_t setPosition(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, @@ -188,6 +189,10 @@ sp<IBinder> Composer::createDisplay(const String8& displayName, bool secure) { secure); } +void Composer::destroyDisplay(const sp<IBinder>& display) { + return ComposerService::getComposerService()->destroyDisplay(display); +} + sp<IBinder> Composer::getBuiltInDisplay(int32_t id) { return ComposerService::getComposerService()->getBuiltInDisplay(id); } @@ -490,6 +495,10 @@ sp<IBinder> SurfaceComposerClient::createDisplay(const String8& displayName, return Composer::getInstance().createDisplay(displayName, secure); } +void SurfaceComposerClient::destroyDisplay(const sp<IBinder>& display) { + Composer::getInstance().destroyDisplay(display); +} + sp<IBinder> SurfaceComposerClient::getBuiltInDisplay(int32_t id) { return Composer::getInstance().getBuiltInDisplay(id); } @@ -618,8 +627,7 @@ status_t ScreenshotClient::capture( sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == NULL) return NO_INIT; return s->captureScreen(display, producer, - reqWidth, reqHeight, minLayerZ, maxLayerZ, - false); + reqWidth, reqHeight, minLayerZ, maxLayerZ); } ScreenshotClient::ScreenshotClient() @@ -633,7 +641,8 @@ ScreenshotClient::~ScreenshotClient() { sp<CpuConsumer> ScreenshotClient::getCpuConsumer() const { if (mCpuConsumer == NULL) { - mCpuConsumer = new CpuConsumer(1); + mBufferQueue = new BufferQueue(); + mCpuConsumer = new CpuConsumer(mBufferQueue, 1); mCpuConsumer->setName(String8("ScreenshotClient")); } return mCpuConsumer; @@ -652,8 +661,8 @@ status_t ScreenshotClient::update(const sp<IBinder>& display, mHaveBuffer = false; } - status_t err = s->captureScreen(display,cpuConsumer->getBufferQueue(), - reqWidth, reqHeight, minLayerZ, maxLayerZ, true); + status_t err = s->captureScreen(display, mBufferQueue, + reqWidth, reqHeight, minLayerZ, maxLayerZ); if (err == NO_ERROR) { err = mCpuConsumer->lockNextBuffer(&mBuffer); diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index f4e88f5..16e533c 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -46,13 +46,13 @@ namespace android { // ============================================================================ SurfaceControl::SurfaceControl( - const sp<SurfaceComposerClient>& client, + const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle, const sp<IGraphicBufferProducer>& gbp) : mClient(client), mHandle(handle), mGraphicBufferProducer(gbp) { } - + SurfaceControl::~SurfaceControl() { destroy(); @@ -71,7 +71,7 @@ void SurfaceControl::destroy() IPCThreadState::self()->flushCommands(); } -void SurfaceControl::clear() +void SurfaceControl::clear() { // here, the window manager tells us explicitly that we should destroy // the surface's resource. Soon after this call, it will also release @@ -83,7 +83,7 @@ void SurfaceControl::clear() } bool SurfaceControl::isSameSurface( - const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) + const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) { if (lhs == 0 || rhs == 0) return false; @@ -181,7 +181,9 @@ sp<Surface> SurfaceControl::getSurface() const { Mutex::Autolock _l(mLock); if (mSurfaceData == 0) { - mSurfaceData = new Surface(mGraphicBufferProducer); + // This surface is always consumed by SurfaceFlinger, so the + // producerControlledByApp value doesn't matter; using false. + mSurfaceData = new Surface(mGraphicBufferProducer, false); } return mSurfaceData; } diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 62d215b..03c1a29 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -55,47 +55,47 @@ protected: sp<BufferQueue> mBQ; }; -struct DummyConsumer : public BufferQueue::ConsumerListener { +struct DummyConsumer : public BnConsumerListener { virtual void onFrameAvailable() {} virtual void onBuffersReleased() {} }; TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { sp<DummyConsumer> dc(new DummyConsumer); - mBQ->consumerConnect(dc); + mBQ->consumerConnect(dc, false); IGraphicBufferProducer::QueueBufferOutput qbo; - mBQ->connect(NATIVE_WINDOW_API_CPU, &qbo); + mBQ->connect(NULL, NATIVE_WINDOW_API_CPU, false, &qbo); mBQ->setBufferCount(4); int slot; sp<Fence> fence; sp<GraphicBuffer> buf; - IGraphicBufferProducer::QueueBufferInput qbi(0, Rect(0, 0, 1, 1), - NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); + IGraphicBufferProducer::QueueBufferInput qbi(0, false, Rect(0, 0, 1, 1), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false, Fence::NO_FENCE); BufferQueue::BufferItem item; for (int i = 0; i < 2; i++) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mBQ->dequeueBuffer(&slot, &fence, 1, 1, 0, + mBQ->dequeueBuffer(&slot, &fence, false, 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(OK, mBQ->acquireBuffer(&item, 0)); } ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mBQ->dequeueBuffer(&slot, &fence, 1, 1, 0, + mBQ->dequeueBuffer(&slot, &fence, false, 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)); + ASSERT_EQ(INVALID_OPERATION, mBQ->acquireBuffer(&item, 0)); } TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) { sp<DummyConsumer> dc(new DummyConsumer); - mBQ->consumerConnect(dc); + mBQ->consumerConnect(dc, false); ASSERT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(0)); ASSERT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(-3)); @@ -106,7 +106,7 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { sp<DummyConsumer> dc(new DummyConsumer); - mBQ->consumerConnect(dc); + mBQ->consumerConnect(dc, false); ASSERT_EQ(OK, mBQ->setMaxAcquiredBufferCount(1)); ASSERT_EQ(OK, mBQ->setMaxAcquiredBufferCount(2)); diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp index 73fdd04..afbc026 100644 --- a/libs/gui/tests/CpuConsumer_test.cpp +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -66,10 +66,11 @@ protected: test_info->name(), params.width, params.height, params.maxLockedBuffers, params.format); - mCC = new CpuConsumer(params.maxLockedBuffers); + sp<BufferQueue> bq = new BufferQueue(); + mCC = new CpuConsumer(bq, params.maxLockedBuffers); String8 name("CpuConsumer_Under_Test"); mCC->setName(name); - mSTC = new Surface(mCC->getProducerInterface()); + mSTC = new Surface(bq); mANW = mSTC; } diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index 7376b4c..989fcef 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -18,7 +18,10 @@ //#define LOG_NDEBUG 0 #include <EGL/egl.h> +#include <GLES2/gl2.h> + #include <gtest/gtest.h> +#include <gui/GLConsumer.h> #include <gui/Surface.h> #include <system/graphics.h> #include <utils/Log.h> @@ -40,8 +43,9 @@ protected: ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); - mST = new GLConsumer(123); - mSTC = new Surface(mST->getBufferQueue()); + sp<BufferQueue> bq = new BufferQueue(); + mST = new GLConsumer(bq, 123); + mSTC = new Surface(bq); mANW = mSTC; // We need a valid GL context so we can test updateTexImage() @@ -337,7 +341,7 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeVsGeometry) { TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) { android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, mST->setSynchronousMode(false)); + ASSERT_EQ(OK, mANW->setSwapInterval(mANW.get(), 0)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); @@ -345,7 +349,7 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) { EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(OK, mST->updateTexImage()); - ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, mANW->setSwapInterval(mANW.get(), 1)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); @@ -360,7 +364,6 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) { 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, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); @@ -381,7 +384,6 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeSlowRetire) { 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, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); @@ -402,7 +404,6 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeFastRetire) { TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) { android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); @@ -428,7 +429,6 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) { TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeDequeueCurrent) { android_native_buffer_t* buf[3]; 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, native_window_dequeue_buffer_and_wait(mANW.get(), &firstBuf)); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), firstBuf, -1)); @@ -448,7 +448,6 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeDequeueCurrent) TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) { android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); 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. @@ -527,7 +526,6 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) { }; android_native_buffer_t* buf[3]; - 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, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); @@ -661,8 +659,6 @@ TEST_F(SurfaceTextureClientTest, QueryFormatAfterSettingWorks) { HAL_PIXEL_FORMAT_RGB_888, HAL_PIXEL_FORMAT_RGB_565, HAL_PIXEL_FORMAT_BGRA_8888, - HAL_PIXEL_FORMAT_RGBA_5551, - HAL_PIXEL_FORMAT_RGBA_4444, HAL_PIXEL_FORMAT_YV12, }; @@ -715,8 +711,9 @@ protected: ASSERT_NE(EGL_NO_CONTEXT, mEglContext); for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { - sp<GLConsumer> st(new GLConsumer(i)); - sp<Surface> stc(new Surface(st->getBufferQueue())); + sp<BufferQueue> bq = new BufferQueue(); + sp<GLConsumer> st(new GLConsumer(bq, i)); + sp<Surface> stc(new Surface(bq)); mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig, static_cast<ANativeWindow*>(stc.get()), NULL); ASSERT_EQ(EGL_SUCCESS, eglGetError()); diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index de64e9a..e4fba15 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -384,8 +384,9 @@ protected: virtual void SetUp() { GLTest::SetUp(); - mGlConsumer = new GLConsumer(TEX_ID); - mSurface = new Surface(mGlConsumer->getBufferQueue()); + sp<BufferQueue> bq = new BufferQueue(); + mGlConsumer = new GLConsumer(bq, TEX_ID); + mSurface = new Surface(bq); mANW = mSurface.get(); } @@ -478,8 +479,10 @@ protected: virtual void SetUp() { GLTest::SetUp(); - mST = new GLConsumer(TEX_ID); - mSTC = new Surface(mST->getBufferQueue()); + sp<BufferQueue> bq = new BufferQueue(); + mBQ = bq; + mST = new GLConsumer(bq, TEX_ID); + mSTC = new Surface(bq); mANW = mSTC; mTextureRenderer = new TextureRenderer(TEX_ID, mST); ASSERT_NO_FATAL_FAILURE(mTextureRenderer->SetUp()); @@ -625,7 +628,7 @@ protected: // 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 { + class DisconnectWaiter : public BnConsumerListener { public: DisconnectWaiter () : mWaitForDisconnect(false), @@ -669,6 +672,7 @@ protected: Condition mFrameCondition; }; + sp<BufferQueue> mBQ; sp<GLConsumer> mST; sp<Surface> mSTC; sp<ANativeWindow> mANW; @@ -941,7 +945,6 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { enum { texHeight = 16 }; enum { numFrames = 1024 }; - ASSERT_EQ(NO_ERROR, mST->setSynchronousMode(true)); ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); @@ -1208,10 +1211,8 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { sp<ANativeWindow> mANW; }; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - sp<DisconnectWaiter> dw(new DisconnectWaiter()); - mST->getBufferQueue()->consumerConnect(dw); + mBQ->consumerConnect(dw, false); sp<Thread> pt(new ProducerThread(mANW)); @@ -1234,8 +1235,6 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { // when it is disconnected and reconnected. Otherwise it will // attempt to release a buffer that it does not owned TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) { - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_EGL)); @@ -1255,8 +1254,6 @@ TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_EGL)); - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); @@ -1269,8 +1266,6 @@ TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) { } TEST_F(SurfaceTextureGLTest, ScaleToWindowMode) { - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_set_scaling_mode(mANW.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)); @@ -1303,8 +1298,6 @@ TEST_F(SurfaceTextureGLTest, ScaleToWindowMode) { // the image such that it has the same aspect ratio as the // default buffer size TEST_F(SurfaceTextureGLTest, CroppedScalingMode) { - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_set_scaling_mode(mANW.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)); @@ -1414,7 +1407,6 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { Mutex mMutex; }; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2)); sp<Thread> pt(new ProducerThread(mANW)); @@ -1807,32 +1799,6 @@ TEST_F(SurfaceTextureGLToGLTest, EglMakeCurrentAfterConsumerDeathUnrefsBuffers) EXPECT_EQ(1, buffer->getStrongCount()); } - -TEST_F(SurfaceTextureGLToGLTest, EglSurfaceDefaultsToSynchronousMode) { - // This test requires 3 buffers to run on a single thread. - mST->setDefaultMaxBufferCount(3); - - ASSERT_TRUE(mST->isSynchronousMode()); - - for (int i = 0; i < 10; i++) { - // Produce a frame - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - glClear(GL_COLOR_BUFFER_BIT); - EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Consume a frame - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - } - - ASSERT_TRUE(mST->isSynchronousMode()); -} - TEST_F(SurfaceTextureGLToGLTest, TexturingFromUserSizedGLFilledBuffer) { enum { texWidth = 64 }; enum { texHeight = 64 }; @@ -2282,7 +2248,6 @@ TEST_F(SurfaceTextureGLThreadToGLTest, } }; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2)); runProducerThread(new PT()); @@ -2823,7 +2788,6 @@ TEST_F(SurfaceTextureMultiContextGLTest, TEST_F(SurfaceTextureMultiContextGLTest, UpdateTexImageSucceedsForBufferConsumedBeforeDetach) { - ASSERT_EQ(NO_ERROR, mST->setSynchronousMode(true)); ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2)); // produce two frames and consume them both on the primary context diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 429becf..e0272ba 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -20,6 +20,7 @@ #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> +#include <gui/BufferItemConsumer.h> #include <utils/String8.h> #include <private/gui/ComposerService.h> @@ -87,11 +88,12 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { sp<ANativeWindow> anw(mSurface); // Verify the screenshot works with no protected buffers. - sp<CpuConsumer> consumer = new CpuConsumer(1); + sp<BufferQueue> bq = new BufferQueue(); + sp<CpuConsumer> consumer = new CpuConsumer(bq, 1); sp<ISurfaceComposer> sf(ComposerService::getComposerService()); sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); - ASSERT_EQ(NO_ERROR, sf->captureScreen(display, consumer->getBufferQueue(), - 64, 64, 0, 0x7fffffff, true)); + ASSERT_EQ(NO_ERROR, sf->captureScreen(display, bq, + 64, 64, 0, 0x7fffffff)); // Set the PROTECTED usage bit and verify that the screenshot fails. Note // that we need to dequeue a buffer in order for it to actually get @@ -119,8 +121,8 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { &buf)); ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1)); } - ASSERT_EQ(NO_ERROR, sf->captureScreen(display, consumer->getBufferQueue(), - 64, 64, 0, 0x7fffffff, true)); + ASSERT_EQ(NO_ERROR, sf->captureScreen(display, bq, + 64, 64, 0, 0x7fffffff)); } TEST_F(SurfaceTest, ConcreteTypeIsSurface) { @@ -131,4 +133,21 @@ TEST_F(SurfaceTest, ConcreteTypeIsSurface) { EXPECT_EQ(NATIVE_WINDOW_SURFACE, result); } +TEST_F(SurfaceTest, QueryConsumerUsage) { + const int TEST_USAGE_FLAGS = + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER; + sp<BufferQueue> bq = new BufferQueue(); + sp<BufferItemConsumer> c = new BufferItemConsumer(bq, + TEST_USAGE_FLAGS); + sp<Surface> s = new Surface(bq); + + sp<ANativeWindow> anw(s); + + int flags = -1; + int err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &flags); + + ASSERT_EQ(NO_ERROR, err); + ASSERT_EQ(TEST_USAGE_FLAGS, flags); +} + } |