/* * Copyright 2014 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 "BufferQueueCore" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #define EGL_EGLEXT_PROTOTYPES #include #include #include #include #include #include #include #include template static inline T max(T a, T b) { return a > b ? a : b; } namespace android { static String8 getUniqueName() { static volatile int32_t counter = 0; return String8::format("unnamed-%d-%d", getpid(), android_atomic_inc(&counter)); } BufferQueueCore::BufferQueueCore(const sp& allocator) : mAllocator(allocator), mMutex(), mIsAbandoned(false), mConsumerControlledByApp(false), mConsumerName(getUniqueName()), mConsumerListener(), mConsumerUsageBits(0), mConnectedApi(NO_CONNECTED_API), mConnectedProducerListener(), mSlots(), mQueue(), mFreeSlots(), mFreeBuffers(), mOverrideMaxBufferCount(0), mDequeueCondition(), mUseAsyncBuffer(true), mDequeueBufferCannotBlock(false), mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888), mDefaultWidth(1), mDefaultHeight(1), mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN), mDefaultMaxBufferCount(2), mMaxAcquiredBufferCount(1), mBufferHasBeenQueued(false), mFrameCounter(0), mTransformHint(0), mIsAllocating(false), mIsAllocatingCondition(), mAllowAllocation(true), mBufferAge(0), mConsumerHasShadowQueue(false), mConsumerShadowQueueSize(0) { if (allocator == NULL) { sp composer(ComposerService::getComposerService()); mAllocator = composer->createGraphicBufferAlloc(); if (mAllocator == NULL) { BQ_LOGE("createGraphicBufferAlloc failed"); } } for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { mFreeSlots.insert(slot); } } BufferQueueCore::~BufferQueueCore() {} void BufferQueueCore::dump(String8& result, const char* prefix) const { Mutex::Autolock lock(mMutex); String8 fifo; Fifo::const_iterator current(mQueue.begin()); while (current != mQueue.end()) { fifo.appendFormat("%02d:%p crop=[%d,%d,%d,%d], " "xform=0x%02x, time=%#" PRIx64 ", scale=%s\n", current->mSlot, current->mGraphicBuffer.get(), current->mCrop.left, current->mCrop.top, current->mCrop.right, current->mCrop.bottom, current->mTransform, current->mTimestamp, BufferItem::scalingModeName(current->mScalingMode)); ++current; } result.appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, " "mDequeueBufferCannotBlock=%d, default-size=[%dx%d], " "default-format=%d, transform-hint=%02x, FIFO(%zu)={%s}\n", prefix, mMaxAcquiredBufferCount, mDequeueBufferCannotBlock, mDefaultWidth, mDefaultHeight, mDefaultBufferFormat, mTransformHint, mQueue.size(), fifo.string()); // Trim the free buffers so as to not spam the dump int maxBufferCount = 0; for (int s = BufferQueueDefs::NUM_BUFFER_SLOTS - 1; s >= 0; --s) { const BufferSlot& slot(mSlots[s]); if (slot.mBufferState != BufferSlot::FREE || slot.mGraphicBuffer != NULL) { maxBufferCount = s + 1; break; } } for (int s = 0; s < maxBufferCount; ++s) { const BufferSlot& slot(mSlots[s]); const sp& buffer(slot.mGraphicBuffer); result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix, (slot.mBufferState == BufferSlot::ACQUIRED) ? ">" : " ", s, buffer.get(), BufferSlot::bufferStateName(slot.mBufferState)); if (buffer != NULL) { result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle, buffer->width, buffer->height, buffer->stride, buffer->format); } result.append("\n"); } } int BufferQueueCore::getMinUndequeuedBufferCountLocked(bool async) const { // If dequeueBuffer is allowed to error out, we don't have to add an // extra buffer. if (!mUseAsyncBuffer) { return mMaxAcquiredBufferCount; } if (mDequeueBufferCannotBlock || async) { return mMaxAcquiredBufferCount + 1; } return mMaxAcquiredBufferCount; } int BufferQueueCore::getMinMaxBufferCountLocked(bool async) const { return getMinUndequeuedBufferCountLocked(async) + 1; } int BufferQueueCore::getMaxBufferCountLocked(bool async) const { int minMaxBufferCount = getMinMaxBufferCountLocked(async); int maxBufferCount = max(mDefaultMaxBufferCount, minMaxBufferCount); if (mOverrideMaxBufferCount != 0) { assert(mOverrideMaxBufferCount >= minMaxBufferCount); maxBufferCount = mOverrideMaxBufferCount; } // Any buffers that are dequeued by the producer or sitting in the queue // waiting to be consumed need to have their slots preserved. Such buffers // will temporarily keep the max buffer count up until the slots no longer // need to be preserved. for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { BufferSlot::BufferState state = mSlots[s].mBufferState; if (state == BufferSlot::QUEUED || state == BufferSlot::DEQUEUED) { maxBufferCount = s + 1; } } return maxBufferCount; } status_t BufferQueueCore::setDefaultMaxBufferCountLocked(int count) { const int minBufferCount = mUseAsyncBuffer ? 2 : 1; if (count < minBufferCount || count > BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGV("setDefaultMaxBufferCount: invalid count %d, should be in " "[%d, %d]", count, minBufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } BQ_LOGV("setDefaultMaxBufferCount: setting count to %d", count); mDefaultMaxBufferCount = count; mDequeueCondition.broadcast(); return NO_ERROR; } void BufferQueueCore::freeBufferLocked(int slot) { BQ_LOGV("freeBufferLocked: slot %d", slot); bool hadBuffer = mSlots[slot].mGraphicBuffer != NULL; mSlots[slot].mGraphicBuffer.clear(); if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) { mSlots[slot].mNeedsCleanupOnRelease = true; } if (mSlots[slot].mBufferState != BufferSlot::FREE) { mFreeSlots.insert(slot); } else if (hadBuffer) { // If the slot was FREE, but we had a buffer, we need to move this slot // from the free buffers list to the the free slots list mFreeBuffers.remove(slot); mFreeSlots.insert(slot); } mSlots[slot].mBufferState = BufferSlot::FREE; mSlots[slot].mAcquireCalled = false; mSlots[slot].mFrameNumber = 0; // Destroy fence as BufferQueue now takes ownership if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) { eglDestroySyncKHR(mSlots[slot].mEglDisplay, mSlots[slot].mEglFence); mSlots[slot].mEglFence = EGL_NO_SYNC_KHR; } mSlots[slot].mFence = Fence::NO_FENCE; validateConsistencyLocked(); } void BufferQueueCore::freeAllBuffersLocked() { mBufferHasBeenQueued = false; for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { freeBufferLocked(s); } } bool BufferQueueCore::stillTracking(const BufferItem* item) const { const BufferSlot& slot = mSlots[item->mSlot]; BQ_LOGV("stillTracking: item { slot=%d/%" PRIu64 " buffer=%p } " "slot { slot=%d/%" PRIu64 " buffer=%p }", item->mSlot, item->mFrameNumber, (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0), item->mSlot, 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); } void BufferQueueCore::waitWhileAllocatingLocked() const { ATRACE_CALL(); while (mIsAllocating) { mIsAllocatingCondition.wait(mMutex); } } void BufferQueueCore::validateConsistencyLocked() const { static const useconds_t PAUSE_TIME = 0; for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { bool isInFreeSlots = mFreeSlots.count(slot) != 0; bool isInFreeBuffers = std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) != mFreeBuffers.cend(); if (mSlots[slot].mBufferState == BufferSlot::FREE) { if (mSlots[slot].mGraphicBuffer == NULL) { if (!isInFreeSlots) { BQ_LOGE("Slot %d is FREE but is not in mFreeSlots", slot); usleep(PAUSE_TIME); } if (isInFreeBuffers) { BQ_LOGE("Slot %d is in mFreeSlots " "but is also in mFreeBuffers", slot); usleep(PAUSE_TIME); } } else { if (!isInFreeBuffers) { BQ_LOGE("Slot %d is FREE but is not in mFreeBuffers", slot); usleep(PAUSE_TIME); } if (isInFreeSlots) { BQ_LOGE("Slot %d is in mFreeBuffers " "but is also in mFreeSlots", slot); usleep(PAUSE_TIME); } } } else { if (isInFreeSlots) { BQ_LOGE("Slot %d is in mFreeSlots but is not FREE (%d)", slot, mSlots[slot].mBufferState); usleep(PAUSE_TIME); } if (isInFreeBuffers) { BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE (%d)", slot, mSlots[slot].mBufferState); usleep(PAUSE_TIME); } } } } } // namespace android