/* * 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. */ #include #define LOG_TAG "BufferQueueProducer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #if DEBUG_ONLY_CODE #define VALIDATE_CONSISTENCY() do { mCore->validateConsistencyLocked(); } while (0) #else #define VALIDATE_CONSISTENCY() #endif #define EGL_EGLEXT_PROTOTYPES #include #include #include #include #include #include #include #include #include #include #include namespace android { // Macros for include BufferQueueCore information in log messages #define BQ_LOGV(x, ...) \ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGD(x, ...) \ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGI(x, ...) \ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGW(x, ...) \ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGE(x, ...) \ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) static constexpr uint32_t BQ_LAYER_COUNT = 1; ProducerListener::~ProducerListener() = default; BufferQueueProducer::BufferQueueProducer(const sp& core, bool consumerIsSurfaceFlinger) : mCore(core), mSlots(core->mSlots), mConsumerName(), mStickyTransform(0), mConsumerIsSurfaceFlinger(consumerIsSurfaceFlinger), mLastQueueBufferFence(Fence::NO_FENCE), mLastQueuedTransform(0), mCallbackMutex(), mNextCallbackTicket(0), mCurrentCallbackTicket(0), mCallbackCondition(), mDequeueTimeout(-1), mDequeueWaitingForAllocation(false) {} BufferQueueProducer::~BufferQueueProducer() {} status_t BufferQueueProducer::requestBuffer(int slot, sp* buf) { ATRACE_CALL(); BQ_LOGV("requestBuffer: slot %d", slot); std::lock_guard lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("requestBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("requestBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("requestBuffer: slot %d is not owned by the producer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } mSlots[slot].mRequestBufferCalled = true; *buf = mSlots[slot].mGraphicBuffer; return NO_ERROR; } status_t BufferQueueProducer::setMaxDequeuedBufferCount( int maxDequeuedBuffers) { ATRACE_CALL(); BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d", maxDequeuedBuffers); sp listener; { // Autolock scope std::unique_lock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { BQ_LOGE("setMaxDequeuedBufferCount: BufferQueue has been " "abandoned"); return NO_INIT; } if (maxDequeuedBuffers == mCore->mMaxDequeuedBufferCount) { return NO_ERROR; } // The new maxDequeuedBuffer count should not be violated by the number // of currently dequeued buffers int dequeuedCount = 0; for (int s : mCore->mActiveBuffers) { if (mSlots[s].mBufferState.isDequeued()) { dequeuedCount++; } } if (dequeuedCount > maxDequeuedBuffers) { BQ_LOGE("setMaxDequeuedBufferCount: the requested maxDequeuedBuffer" "count (%d) exceeds the current dequeued buffer count (%d)", maxDequeuedBuffers, dequeuedCount); return BAD_VALUE; } int bufferCount = mCore->getMinUndequeuedBufferCountLocked(); bufferCount += maxDequeuedBuffers; if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("setMaxDequeuedBufferCount: bufferCount %d too large " "(max %d)", bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } const int minBufferSlots = mCore->getMinMaxBufferCountLocked(); if (bufferCount < minBufferSlots) { BQ_LOGE("setMaxDequeuedBufferCount: requested buffer count %d is " "less than minimum %d", bufferCount, minBufferSlots); return BAD_VALUE; } if (bufferCount > mCore->mMaxBufferCount) { BQ_LOGE("setMaxDequeuedBufferCount: %d dequeued buffers would " "exceed the maxBufferCount (%d) (maxAcquired %d async %d " "mDequeuedBufferCannotBlock %d)", maxDequeuedBuffers, mCore->mMaxBufferCount, mCore->mMaxAcquiredBufferCount, mCore->mAsyncMode, mCore->mDequeueBufferCannotBlock); return BAD_VALUE; } int delta = maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount; if (!mCore->adjustAvailableSlotsLocked(delta)) { return BAD_VALUE; } mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers; VALIDATE_CONSISTENCY(); if (delta < 0) { listener = mCore->mConsumerListener; } mCore->mDequeueCondition.notify_all(); } // Autolock scope // Call back without lock held if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; } status_t BufferQueueProducer::setAsyncMode(bool async) { ATRACE_CALL(); BQ_LOGV("setAsyncMode: async = %d", async); sp listener; { // Autolock scope std::unique_lock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { BQ_LOGE("setAsyncMode: BufferQueue has been abandoned"); return NO_INIT; } if (async == mCore->mAsyncMode) { return NO_ERROR; } if ((mCore->mMaxAcquiredBufferCount + mCore->mMaxDequeuedBufferCount + (async || mCore->mDequeueBufferCannotBlock ? 1 : 0)) > mCore->mMaxBufferCount) { BQ_LOGE("setAsyncMode(%d): this call would cause the " "maxBufferCount (%d) to be exceeded (maxAcquired %d " "maxDequeued %d mDequeueBufferCannotBlock %d)", async, mCore->mMaxBufferCount, mCore->mMaxAcquiredBufferCount, mCore->mMaxDequeuedBufferCount, mCore->mDequeueBufferCannotBlock); return BAD_VALUE; } int delta = mCore->getMaxBufferCountLocked(async, mCore->mDequeueBufferCannotBlock, mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked(); if (!mCore->adjustAvailableSlotsLocked(delta)) { BQ_LOGE("setAsyncMode: BufferQueue failed to adjust the number of " "available slots. Delta = %d", delta); return BAD_VALUE; } mCore->mAsyncMode = async; VALIDATE_CONSISTENCY(); mCore->mDequeueCondition.notify_all(); if (delta < 0) { listener = mCore->mConsumerListener; } } // Autolock scope // Call back without lock held if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; } int BufferQueueProducer::getFreeBufferLocked() const { if (mCore->mFreeBuffers.empty()) { return BufferQueueCore::INVALID_BUFFER_SLOT; } int slot = mCore->mFreeBuffers.front(); mCore->mFreeBuffers.pop_front(); return slot; } int BufferQueueProducer::getFreeSlotLocked() const { if (mCore->mFreeSlots.empty()) { return BufferQueueCore::INVALID_BUFFER_SLOT; } int slot = *(mCore->mFreeSlots.begin()); mCore->mFreeSlots.erase(slot); return slot; } status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, std::unique_lock& lock, int* found) const { auto callerString = (caller == FreeSlotCaller::Dequeue) ? "dequeueBuffer" : "attachBuffer"; bool tryAgain = true; while (tryAgain) { if (mCore->mIsAbandoned) { BQ_LOGE("%s: BufferQueue has been abandoned", callerString); return NO_INIT; } int dequeuedCount = 0; int acquiredCount = 0; for (int s : mCore->mActiveBuffers) { if (mSlots[s].mBufferState.isDequeued()) { ++dequeuedCount; } if (mSlots[s].mBufferState.isAcquired()) { ++acquiredCount; } } // Producers are not allowed to dequeue more than // mMaxDequeuedBufferCount buffers. // This check is only done if a buffer has already been queued if (mCore->mBufferHasBeenQueued && dequeuedCount >= mCore->mMaxDequeuedBufferCount) { // Supress error logs when timeout is non-negative. if (mDequeueTimeout < 0) { BQ_LOGE("%s: attempting to exceed the max dequeued buffer " "count (%d)", callerString, mCore->mMaxDequeuedBufferCount); } return INVALID_OPERATION; } *found = BufferQueueCore::INVALID_BUFFER_SLOT; // If we disconnect and reconnect quickly, we can be in a state where // our slots are empty but we have many buffers in the queue. This can // cause us to run out of memory if we outrun the consumer. Wait here if // it looks like we have too many buffers queued up. const int maxBufferCount = mCore->getMaxBufferCountLocked(); bool tooManyBuffers = mCore->mQueue.size() > static_cast(maxBufferCount); if (tooManyBuffers) { BQ_LOGV("%s: queue size is %zu, waiting", callerString, mCore->mQueue.size()); } else { // If in shared buffer mode and a shared buffer exists, always // return it. if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot != BufferQueueCore::INVALID_BUFFER_SLOT) { *found = mCore->mSharedBufferSlot; } else { if (caller == FreeSlotCaller::Dequeue) { // If we're calling this from dequeue, prefer free buffers int slot = getFreeBufferLocked(); if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) { *found = slot; } else if (mCore->mAllowAllocation) { *found = getFreeSlotLocked(); } } else { // If we're calling this from attach, prefer free slots int slot = getFreeSlotLocked(); if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) { *found = slot; } else { *found = getFreeBufferLocked(); } } } } // If no buffer is found, or if the queue has too many buffers // outstanding, wait for a buffer to be acquired or released, or for the // max buffer count to change. tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || tooManyBuffers; if (tryAgain) { // Return an error if we're in non-blocking mode (producer and // consumer are controlled by the application). // However, the consumer is allowed to briefly acquire an extra // buffer (which could cause us to have to wait here), which is // okay, since it is only used to implement an atomic acquire + // release (e.g., in GLConsumer::updateTexImage()) if ((mCore->mDequeueBufferCannotBlock || mCore->mAsyncMode) && (acquiredCount <= mCore->mMaxAcquiredBufferCount)) { return WOULD_BLOCK; } if (mDequeueTimeout >= 0) { std::cv_status result = mCore->mDequeueCondition.wait_for(lock, std::chrono::nanoseconds(mDequeueTimeout)); if (result == std::cv_status::timeout) { return TIMED_OUT; } } else { mCore->mDequeueCondition.wait(lock); } } } // while (tryAgain) return NO_ERROR; } status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* outFence, uint32_t width, uint32_t height, PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) { ATRACE_CALL(); { // Autolock scope std::lock_guard lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; if (mCore->mIsAbandoned) { BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("dequeueBuffer: BufferQueue has no connected producer"); return NO_INIT; } } // Autolock scope BQ_LOGV("dequeueBuffer: w=%u h=%u format=%#x, usage=%#" PRIx64, width, height, format, usage); if ((width && !height) || (!width && height)) { BQ_LOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height); return BAD_VALUE; } status_t returnFlags = NO_ERROR; EGLDisplay eglDisplay = EGL_NO_DISPLAY; EGLSyncKHR eglFence = EGL_NO_SYNC_KHR; bool attachedByConsumer = false; { // Autolock scope std::unique_lock lock(mCore->mMutex); // If we don't have a free buffer, but we are currently allocating, we wait until allocation // is finished such that we don't allocate in parallel. if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) { mDequeueWaitingForAllocation = true; mCore->waitWhileAllocatingLocked(lock); mDequeueWaitingForAllocation = false; mDequeueWaitingForAllocationCondition.notify_all(); } if (format == 0) { format = mCore->mDefaultBufferFormat; } // Enable the usage bits the consumer requested usage |= mCore->mConsumerUsageBits; const bool useDefaultSize = !width && !height; if (useDefaultSize) { width = mCore->mDefaultWidth; height = mCore->mDefaultHeight; if (mCore->mAutoPrerotation && (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) { std::swap(width, height); } } int found = BufferItem::INVALID_BUFFER_SLOT; while (found == BufferItem::INVALID_BUFFER_SLOT) { status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found); if (status != NO_ERROR) { return status; } // This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { BQ_LOGE("dequeueBuffer: no available buffer slots"); return -EBUSY; } const sp& buffer(mSlots[found].mGraphicBuffer); // If we are not allowed to allocate new buffers, // waitForFreeSlotThenRelock must have returned a slot containing a // buffer. If this buffer would require reallocation to meet the // requested attributes, we free it and attempt to get another one. if (!mCore->mAllowAllocation) { if (buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) { if (mCore->mSharedBufferSlot == found) { BQ_LOGE("dequeueBuffer: cannot re-allocate a sharedbuffer"); return BAD_VALUE; } mCore->mFreeSlots.insert(found); mCore->clearBufferSlotLocked(found); found = BufferItem::INVALID_BUFFER_SLOT; continue; } } } const sp& buffer(mSlots[found].mGraphicBuffer); if (mCore->mSharedBufferSlot == found && buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) { BQ_LOGE("dequeueBuffer: cannot re-allocate a shared" "buffer"); return BAD_VALUE; } if (mCore->mSharedBufferSlot != found) { mCore->mActiveBuffers.insert(found); } *outSlot = found; ATRACE_BUFFER_INDEX(found); attachedByConsumer = mSlots[found].mNeedsReallocation; mSlots[found].mNeedsReallocation = false; mSlots[found].mBufferState.dequeue(); if ((buffer == nullptr) || buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) { mSlots[found].mAcquireCalled = false; mSlots[found].mGraphicBuffer = nullptr; mSlots[found].mRequestBufferCalled = false; mSlots[found].mEglDisplay = EGL_NO_DISPLAY; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; mSlots[found].mFence = Fence::NO_FENCE; mCore->mBufferAge = 0; mCore->mIsAllocating = true; returnFlags |= BUFFER_NEEDS_REALLOCATION; } else { // We add 1 because that will be the frame number when this buffer // is queued mCore->mBufferAge = mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber; } BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64, mCore->mBufferAge); if (CC_UNLIKELY(mSlots[found].mFence == nullptr)) { BQ_LOGE("dequeueBuffer: about to return a NULL fence - " "slot=%d w=%d h=%d format=%u", found, buffer->width, buffer->height, buffer->format); } eglDisplay = mSlots[found].mEglDisplay; eglFence = mSlots[found].mEglFence; // Don't return a fence in shared buffer mode, except for the first // frame. *outFence = (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == found) ? Fence::NO_FENCE : mSlots[found].mFence; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; mSlots[found].mFence = Fence::NO_FENCE; // If shared buffer mode has just been enabled, cache the slot of the // first buffer that is dequeued and mark it as the shared buffer. if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == BufferQueueCore::INVALID_BUFFER_SLOT) { mCore->mSharedBufferSlot = found; mSlots[found].mBufferState.mShared = true; } if (!(returnFlags & BUFFER_NEEDS_REALLOCATION)) { if (mCore->mConsumerListener != nullptr) { mCore->mConsumerListener->onFrameDequeued(mSlots[*outSlot].mGraphicBuffer->getId()); } } } // Autolock scope if (returnFlags & BUFFER_NEEDS_REALLOCATION) { BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); sp graphicBuffer = new GraphicBuffer( width, height, format, BQ_LAYER_COUNT, usage, {mConsumerName.string(), mConsumerName.size()}); status_t error = graphicBuffer->initCheck(); { // Autolock scope std::lock_guard lock(mCore->mMutex); if (error == NO_ERROR && !mCore->mIsAbandoned) { graphicBuffer->setGenerationNumber(mCore->mGenerationNumber); mSlots[*outSlot].mGraphicBuffer = graphicBuffer; if (mCore->mConsumerListener != nullptr) { mCore->mConsumerListener->onFrameDequeued( mSlots[*outSlot].mGraphicBuffer->getId()); } } mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.notify_all(); if (error != NO_ERROR) { mCore->mFreeSlots.insert(*outSlot); mCore->clearBufferSlotLocked(*outSlot); BQ_LOGE("dequeueBuffer: createGraphicBuffer failed"); return error; } if (mCore->mIsAbandoned) { mCore->mFreeSlots.insert(*outSlot); mCore->clearBufferSlotLocked(*outSlot); BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); return NO_INIT; } VALIDATE_CONSISTENCY(); } // Autolock scope } if (attachedByConsumer) { returnFlags |= BUFFER_NEEDS_REALLOCATION; } if (eglFence != EGL_NO_SYNC_KHR) { EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0, 1000000000); // If something goes wrong, log the error, but return the buffer without // synchronizing access to it. It's too late at this point to abort the // dequeue operation. if (result == EGL_FALSE) { BQ_LOGE("dequeueBuffer: error %#x waiting for fence", eglGetError()); } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { BQ_LOGE("dequeueBuffer: timeout waiting for fence"); } eglDestroySyncKHR(eglDisplay, eglFence); } BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x", *outSlot, mSlots[*outSlot].mFrameNumber, mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); if (outBufferAge) { *outBufferAge = mCore->mBufferAge; } addAndGetFrameTimestamps(nullptr, outTimestamps); return returnFlags; } status_t BufferQueueProducer::detachBuffer(int slot) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); BQ_LOGV("detachBuffer: slot %d", slot); sp listener; { std::lock_guard lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("detachBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("detachBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (mCore->mSharedBufferMode || mCore->mSharedBufferSlot == slot) { BQ_LOGE("detachBuffer: cannot detach a buffer in shared buffer mode"); return BAD_VALUE; } if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("detachBuffer: slot %d is not owned by the producer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } else if (!mSlots[slot].mRequestBufferCalled) { BQ_LOGE("detachBuffer: buffer in slot %d has not been requested", slot); return BAD_VALUE; } listener = mCore->mConsumerListener; auto gb = mSlots[slot].mGraphicBuffer; if (listener != nullptr && gb != nullptr) { listener->onFrameDetached(gb->getId()); } mSlots[slot].mBufferState.detachProducer(); mCore->mActiveBuffers.erase(slot); mCore->mFreeSlots.insert(slot); mCore->clearBufferSlotLocked(slot); mCore->mDequeueCondition.notify_all(); VALIDATE_CONSISTENCY(); } if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; } status_t BufferQueueProducer::detachNextBuffer(sp* outBuffer, sp* outFence) { ATRACE_CALL(); if (outBuffer == nullptr) { BQ_LOGE("detachNextBuffer: outBuffer must not be NULL"); return BAD_VALUE; } else if (outFence == nullptr) { BQ_LOGE("detachNextBuffer: outFence must not be NULL"); return BAD_VALUE; } sp listener; { std::unique_lock lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (mCore->mSharedBufferMode) { BQ_LOGE("detachNextBuffer: cannot detach a buffer in shared buffer " "mode"); return BAD_VALUE; } mCore->waitWhileAllocatingLocked(lock); if (mCore->mFreeBuffers.empty()) { return NO_MEMORY; } int found = mCore->mFreeBuffers.front(); mCore->mFreeBuffers.remove(found); mCore->mFreeSlots.insert(found); BQ_LOGV("detachNextBuffer detached slot %d", found); *outBuffer = mSlots[found].mGraphicBuffer; *outFence = mSlots[found].mFence; mCore->clearBufferSlotLocked(found); VALIDATE_CONSISTENCY(); listener = mCore->mConsumerListener; } if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; } status_t BufferQueueProducer::attachBuffer(int* outSlot, const sp& buffer) { ATRACE_CALL(); if (outSlot == nullptr) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; } else if (buffer == nullptr) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } std::unique_lock lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("attachBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("attachBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (mCore->mSharedBufferMode) { BQ_LOGE("attachBuffer: cannot attach a buffer in shared buffer mode"); return BAD_VALUE; } if (buffer->getGenerationNumber() != mCore->mGenerationNumber) { BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] " "[queue %u]", buffer->getGenerationNumber(), mCore->mGenerationNumber); return BAD_VALUE; } mCore->waitWhileAllocatingLocked(lock); status_t returnFlags = NO_ERROR; int found; status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, lock, &found); if (status != NO_ERROR) { return status; } // This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { BQ_LOGE("attachBuffer: no available buffer slots"); return -EBUSY; } *outSlot = found; ATRACE_BUFFER_INDEX(*outSlot); BQ_LOGV("attachBuffer: returning slot %d flags=%#x", *outSlot, returnFlags); mSlots[*outSlot].mGraphicBuffer = buffer; mSlots[*outSlot].mBufferState.attachProducer(); mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR; mSlots[*outSlot].mFence = Fence::NO_FENCE; mSlots[*outSlot].mRequestBufferCalled = true; mSlots[*outSlot].mAcquireCalled = false; mSlots[*outSlot].mNeedsReallocation = false; mCore->mActiveBuffers.insert(found); VALIDATE_CONSISTENCY(); return returnFlags; } status_t BufferQueueProducer::queueBuffer(int slot, const QueueBufferInput &input, QueueBufferOutput *output) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); int64_t requestedPresentTimestamp; bool isAutoTimestamp; android_dataspace dataSpace; Rect crop(Rect::EMPTY_RECT); int scalingMode; uint32_t transform; uint32_t stickyTransform; sp acquireFence; bool getFrameTimestamps = false; input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode, &transform, &acquireFence, &stickyTransform, &getFrameTimestamps); const Region& surfaceDamage = input.getSurfaceDamage(); const HdrMetadata& hdrMetadata = input.getHdrMetadata(); if (acquireFence == nullptr) { BQ_LOGE("queueBuffer: fence is NULL"); return BAD_VALUE; } auto acquireFenceTime = std::make_shared(acquireFence); 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: BQ_LOGE("queueBuffer: unknown scaling mode %d", scalingMode); return BAD_VALUE; } sp frameAvailableListener; sp frameReplacedListener; int callbackTicket = 0; uint64_t currentFrameNumber = 0; BufferItem item; { // Autolock scope std::lock_guard lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("queueBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("queueBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("queueBuffer: slot %d is not owned by the producer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } else if (!mSlots[slot].mRequestBufferCalled) { BQ_LOGE("queueBuffer: slot %d was queued without requesting " "a buffer", slot); return BAD_VALUE; } // If shared buffer mode has just been enabled, cache the slot of the // first buffer that is queued and mark it as the shared buffer. if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == BufferQueueCore::INVALID_BUFFER_SLOT) { mCore->mSharedBufferSlot = slot; mSlots[slot].mBufferState.mShared = true; } BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d" " validHdrMetadataTypes=0x%x crop=[%d,%d,%d,%d] transform=%#x scale=%s", slot, mCore->mFrameCounter + 1, requestedPresentTimestamp, dataSpace, hdrMetadata.validTypes, crop.left, crop.top, crop.right, crop.bottom, transform, BufferItem::scalingModeName(static_cast(scalingMode))); const sp& graphicBuffer(mSlots[slot].mGraphicBuffer); Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); Rect croppedRect(Rect::EMPTY_RECT); crop.intersect(bufferRect, &croppedRect); if (croppedRect != crop) { BQ_LOGE("queueBuffer: crop rect is not contained within the " "buffer in slot %d", slot); return BAD_VALUE; } // Override UNKNOWN dataspace with consumer default if (dataSpace == HAL_DATASPACE_UNKNOWN) { dataSpace = mCore->mDefaultBufferDataSpace; } mSlots[slot].mFence = acquireFence; mSlots[slot].mBufferState.queue(); // Increment the frame counter and store a local version of it // for use outside the lock on mCore->mMutex. ++mCore->mFrameCounter; currentFrameNumber = mCore->mFrameCounter; mSlots[slot].mFrameNumber = currentFrameNumber; item.mAcquireCalled = mSlots[slot].mAcquireCalled; item.mGraphicBuffer = mSlots[slot].mGraphicBuffer; item.mCrop = crop; item.mTransform = transform & ~static_cast(NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY); item.mTransformToDisplayInverse = (transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0; item.mScalingMode = static_cast(scalingMode); item.mTimestamp = requestedPresentTimestamp; item.mIsAutoTimestamp = isAutoTimestamp; item.mDataSpace = dataSpace; item.mHdrMetadata = hdrMetadata; item.mFrameNumber = currentFrameNumber; item.mSlot = slot; item.mFence = acquireFence; item.mFenceTime = acquireFenceTime; item.mIsDroppable = mCore->mAsyncMode || (mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) || (mCore->mLegacyBufferDrop && mCore->mQueueBufferCanDrop) || (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot); item.mSurfaceDamage = surfaceDamage; item.mQueuedBuffer = true; item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh; item.mApi = mCore->mConnectedApi; mStickyTransform = stickyTransform; // Cache the shared buffer data so that the BufferItem can be recreated. if (mCore->mSharedBufferMode) { mCore->mSharedBufferCache.crop = crop; mCore->mSharedBufferCache.transform = transform; mCore->mSharedBufferCache.scalingMode = static_cast( scalingMode); mCore->mSharedBufferCache.dataspace = dataSpace; } output->bufferReplaced = false; if (mCore->mQueue.empty()) { // When the queue is empty, we can ignore mDequeueBufferCannotBlock // and simply queue this buffer mCore->mQueue.push_back(item); frameAvailableListener = mCore->mConsumerListener; } else { // When the queue is not empty, we need to look at the last buffer // in the queue to see if we need to replace it const BufferItem& last = mCore->mQueue.itemAt( mCore->mQueue.size() - 1); if (last.mIsDroppable) { if (!last.mIsStale) { mSlots[last.mSlot].mBufferState.freeQueued(); // After leaving shared buffer mode, the shared buffer will // still be around. Mark it as no longer shared if this // operation causes it to be free. if (!mCore->mSharedBufferMode && mSlots[last.mSlot].mBufferState.isFree()) { mSlots[last.mSlot].mBufferState.mShared = false; } // Don't put the shared buffer on the free list. if (!mSlots[last.mSlot].mBufferState.isShared()) { mCore->mActiveBuffers.erase(last.mSlot); mCore->mFreeBuffers.push_back(last.mSlot); output->bufferReplaced = true; } } // Make sure to merge the damage rect from the frame we're about // to drop into the new frame's damage rect. if (last.mSurfaceDamage.bounds() == Rect::INVALID_RECT || item.mSurfaceDamage.bounds() == Rect::INVALID_RECT) { item.mSurfaceDamage = Region::INVALID_REGION; } else { item.mSurfaceDamage |= last.mSurfaceDamage; } // Overwrite the droppable buffer with the incoming one mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item; frameReplacedListener = mCore->mConsumerListener; } else { mCore->mQueue.push_back(item); frameAvailableListener = mCore->mConsumerListener; } } mCore->mBufferHasBeenQueued = true; mCore->mDequeueCondition.notify_all(); mCore->mLastQueuedSlot = slot; output->width = mCore->mDefaultWidth; output->height = mCore->mDefaultHeight; output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint; output->numPendingBuffers = static_cast(mCore->mQueue.size()); output->nextFrameNumber = mCore->mFrameCounter + 1; ATRACE_INT(mCore->mConsumerName.string(), static_cast(mCore->mQueue.size())); #ifndef NO_BINDER mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); #endif // Take a ticket for the callback functions callbackTicket = mNextCallbackTicket++; VALIDATE_CONSISTENCY(); } // Autolock scope // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because // it is guaranteed that the BufferQueue is inside SurfaceFlinger's process and // there will be no Binder call if (!mConsumerIsSurfaceFlinger) { item.mGraphicBuffer.clear(); } // Update and get FrameEventHistory. nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC); NewFrameEventsEntry newFrameEventsEntry = { currentFrameNumber, postedTime, requestedPresentTimestamp, std::move(acquireFenceTime) }; addAndGetFrameTimestamps(&newFrameEventsEntry, getFrameTimestamps ? &output->frameTimestamps : nullptr); // Call back without the main BufferQueue lock held, but with the callback // lock held so we can ensure that callbacks occur in order int connectedApi; sp lastQueuedFence; { // scope for the lock std::unique_lock lock(mCallbackMutex); while (callbackTicket != mCurrentCallbackTicket) { mCallbackCondition.wait(lock); } if (frameAvailableListener != nullptr) { frameAvailableListener->onFrameAvailable(item); } else if (frameReplacedListener != nullptr) { frameReplacedListener->onFrameReplaced(item); } connectedApi = mCore->mConnectedApi; lastQueuedFence = std::move(mLastQueueBufferFence); mLastQueueBufferFence = std::move(acquireFence); mLastQueuedCrop = item.mCrop; mLastQueuedTransform = item.mTransform; ++mCurrentCallbackTicket; mCallbackCondition.notify_all(); } // Wait without lock held if (connectedApi == NATIVE_WINDOW_API_EGL) { // Waiting here allows for two full buffers to be queued but not a // third. In the event that frames take varying time, this makes a // small trade-off in favor of latency rather than throughput. lastQueuedFence->waitForever("Throttling EGL Production"); } return NO_ERROR; } status_t BufferQueueProducer::cancelBuffer(int slot, const sp& fence) { ATRACE_CALL(); BQ_LOGV("cancelBuffer: slot %d", slot); std::lock_guard lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("cancelBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("cancelBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (mCore->mSharedBufferMode) { BQ_LOGE("cancelBuffer: cannot cancel a buffer in shared buffer mode"); return BAD_VALUE; } if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("cancelBuffer: slot %d is not owned by the producer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } else if (fence == nullptr) { BQ_LOGE("cancelBuffer: fence is NULL"); return BAD_VALUE; } mSlots[slot].mBufferState.cancel(); // After leaving shared buffer mode, the shared buffer will still be around. // Mark it as no longer shared if this operation causes it to be free. if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) { mSlots[slot].mBufferState.mShared = false; } // Don't put the shared buffer on the free list. if (!mSlots[slot].mBufferState.isShared()) { mCore->mActiveBuffers.erase(slot); mCore->mFreeBuffers.push_back(slot); } auto gb = mSlots[slot].mGraphicBuffer; if (mCore->mConsumerListener != nullptr && gb != nullptr) { mCore->mConsumerListener->onFrameCancelled(gb->getId()); } mSlots[slot].mFence = fence; mCore->mDequeueCondition.notify_all(); VALIDATE_CONSISTENCY(); return NO_ERROR; } int BufferQueueProducer::query(int what, int *outValue) { ATRACE_CALL(); std::lock_guard lock(mCore->mMutex); if (outValue == nullptr) { BQ_LOGE("query: outValue was NULL"); return BAD_VALUE; } if (mCore->mIsAbandoned) { BQ_LOGE("query: BufferQueue has been abandoned"); return NO_INIT; } int value; switch (what) { case NATIVE_WINDOW_WIDTH: value = static_cast(mCore->mDefaultWidth); break; case NATIVE_WINDOW_HEIGHT: value = static_cast(mCore->mDefaultHeight); break; case NATIVE_WINDOW_FORMAT: value = static_cast(mCore->mDefaultBufferFormat); break; case NATIVE_WINDOW_LAYER_COUNT: // All BufferQueue buffers have a single layer. value = BQ_LAYER_COUNT; break; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: value = mCore->getMinUndequeuedBufferCountLocked(); break; case NATIVE_WINDOW_STICKY_TRANSFORM: value = static_cast(mStickyTransform); break; case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: value = (mCore->mQueue.size() > 1); break; case NATIVE_WINDOW_CONSUMER_USAGE_BITS: // deprecated; higher 32 bits are truncated value = static_cast(mCore->mConsumerUsageBits); break; case NATIVE_WINDOW_DEFAULT_DATASPACE: value = static_cast(mCore->mDefaultBufferDataSpace); break; case NATIVE_WINDOW_BUFFER_AGE: if (mCore->mBufferAge > INT32_MAX) { value = 0; } else { value = static_cast(mCore->mBufferAge); } break; case NATIVE_WINDOW_CONSUMER_IS_PROTECTED: value = static_cast(mCore->mConsumerIsProtected); break; default: return BAD_VALUE; } BQ_LOGV("query: %d? %d", what, value); *outValue = value; return NO_ERROR; } status_t BufferQueueProducer::connect(const sp& listener, int api, bool producerControlledByApp, QueueBufferOutput *output) { ATRACE_CALL(); std::lock_guard lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; BQ_LOGV("connect: api=%d producerControlledByApp=%s", api, producerControlledByApp ? "true" : "false"); if (mCore->mIsAbandoned) { BQ_LOGE("connect: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConsumerListener == nullptr) { BQ_LOGE("connect: BufferQueue has no consumer"); return NO_INIT; } if (output == nullptr) { BQ_LOGE("connect: output was NULL"); return BAD_VALUE; } if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("connect: already connected (cur=%d req=%d)", mCore->mConnectedApi, api); return BAD_VALUE; } int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, mDequeueTimeout < 0 ? mCore->mConsumerControlledByApp && producerControlledByApp : false, mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked(); if (!mCore->adjustAvailableSlotsLocked(delta)) { BQ_LOGE("connect: BufferQueue failed to adjust the number of available " "slots. Delta = %d", delta); return BAD_VALUE; } int status = NO_ERROR; switch (api) { case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_CPU: case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: mCore->mConnectedApi = api; output->width = mCore->mDefaultWidth; output->height = mCore->mDefaultHeight; output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint; output->numPendingBuffers = static_cast(mCore->mQueue.size()); output->nextFrameNumber = mCore->mFrameCounter + 1; output->bufferReplaced = false; output->maxBufferCount = mCore->mMaxBufferCount; if (listener != nullptr) { // Set up a death notification so that we can disconnect // automatically if the remote producer dies #ifndef NO_BINDER if (IInterface::asBinder(listener)->remoteBinder() != nullptr) { status = IInterface::asBinder(listener)->linkToDeath( static_cast(this)); if (status != NO_ERROR) { BQ_LOGE("connect: linkToDeath failed: %s (%d)", strerror(-status), status); } mCore->mLinkedToDeath = listener; } #endif mCore->mConnectedProducerListener = listener; mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify(); } break; default: BQ_LOGE("connect: unknown API %d", api); status = BAD_VALUE; break; } mCore->mConnectedPid = BufferQueueThreadState::getCallingPid(); mCore->mBufferHasBeenQueued = false; mCore->mDequeueBufferCannotBlock = false; mCore->mQueueBufferCanDrop = false; mCore->mLegacyBufferDrop = true; if (mCore->mConsumerControlledByApp && producerControlledByApp) { mCore->mDequeueBufferCannotBlock = mDequeueTimeout < 0; mCore->mQueueBufferCanDrop = mDequeueTimeout <= 0; } mCore->mAllowAllocation = true; VALIDATE_CONSISTENCY(); return status; } status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { ATRACE_CALL(); BQ_LOGV("disconnect: api %d", api); int status = NO_ERROR; sp listener; { // Autolock scope std::unique_lock lock(mCore->mMutex); if (mode == DisconnectMode::AllLocal) { if (BufferQueueThreadState::getCallingPid() != mCore->mConnectedPid) { return NO_ERROR; } api = BufferQueueCore::CURRENTLY_CONNECTED_API; } mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { // It's not really an error to disconnect after the surface has // been abandoned; it should just be a no-op. return NO_ERROR; } if (api == BufferQueueCore::CURRENTLY_CONNECTED_API) { if (mCore->mConnectedApi == NATIVE_WINDOW_API_MEDIA) { ALOGD("About to force-disconnect API_MEDIA, mode=%d", mode); } api = mCore->mConnectedApi; // If we're asked to disconnect the currently connected api but // nobody is connected, it's not really an error. if (api == BufferQueueCore::NO_CONNECTED_API) { return NO_ERROR; } } switch (api) { case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_CPU: case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: if (mCore->mConnectedApi == api) { mCore->freeAllBuffersLocked(); #ifndef NO_BINDER // Remove our death notification callback if we have one if (mCore->mLinkedToDeath != nullptr) { sp token = IInterface::asBinder(mCore->mLinkedToDeath); // This can fail if we're here because of the death // notification, but we just ignore it token->unlinkToDeath( static_cast(this)); } #endif mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; mCore->mLinkedToDeath = nullptr; mCore->mConnectedProducerListener = nullptr; mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API; mCore->mConnectedPid = -1; mCore->mSidebandStream.clear(); mCore->mDequeueCondition.notify_all(); mCore->mAutoPrerotation = false; listener = mCore->mConsumerListener; } else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("disconnect: not connected (req=%d)", api); status = NO_INIT; } else { BQ_LOGE("disconnect: still connected to another API " "(cur=%d req=%d)", mCore->mConnectedApi, api); status = BAD_VALUE; } break; default: BQ_LOGE("disconnect: unknown API %d", api); status = BAD_VALUE; break; } } // Autolock scope // Call back without lock held if (listener != nullptr) { listener->onBuffersReleased(); listener->onDisconnect(); } return status; } status_t BufferQueueProducer::setSidebandStream(const sp& stream) { sp listener; { // Autolock scope std::lock_guard _l(mCore->mMutex); mCore->mSidebandStream = stream; listener = mCore->mConsumerListener; } // Autolock scope if (listener != nullptr) { listener->onSidebandStreamChanged(); } return NO_ERROR; } void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage) { ATRACE_CALL(); const bool useDefaultSize = !width && !height; while (true) { size_t newBufferCount = 0; uint32_t allocWidth = 0; uint32_t allocHeight = 0; PixelFormat allocFormat = PIXEL_FORMAT_UNKNOWN; uint64_t allocUsage = 0; std::string allocName; { // Autolock scope std::unique_lock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(lock); if (!mCore->mAllowAllocation) { BQ_LOGE("allocateBuffers: allocation is not allowed for this " "BufferQueue"); return; } // Only allocate one buffer at a time to reduce risks of overlapping an allocation from // both allocateBuffers and dequeueBuffer. newBufferCount = mCore->mFreeSlots.empty() ? 0 : 1; if (newBufferCount == 0) { return; } allocWidth = width > 0 ? width : mCore->mDefaultWidth; allocHeight = height > 0 ? height : mCore->mDefaultHeight; if (useDefaultSize && mCore->mAutoPrerotation && (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) { std::swap(allocWidth, allocHeight); } allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat; allocUsage = usage | mCore->mConsumerUsageBits; allocName.assign(mCore->mConsumerName.string(), mCore->mConsumerName.size()); mCore->mIsAllocating = true; } // Autolock scope Vector> buffers; for (size_t i = 0; i < newBufferCount; ++i) { sp graphicBuffer = new GraphicBuffer( allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT, allocUsage, allocName); status_t result = graphicBuffer->initCheck(); if (result != NO_ERROR) { BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format" " %u, usage %#" PRIx64 ")", width, height, format, usage); std::lock_guard lock(mCore->mMutex); mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.notify_all(); return; } buffers.push_back(graphicBuffer); } { // Autolock scope std::unique_lock lock(mCore->mMutex); uint32_t checkWidth = width > 0 ? width : mCore->mDefaultWidth; uint32_t checkHeight = height > 0 ? height : mCore->mDefaultHeight; if (useDefaultSize && mCore->mAutoPrerotation && (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) { std::swap(checkWidth, checkHeight); } PixelFormat checkFormat = format != 0 ? format : mCore->mDefaultBufferFormat; uint64_t checkUsage = usage | mCore->mConsumerUsageBits; if (checkWidth != allocWidth || checkHeight != allocHeight || checkFormat != allocFormat || checkUsage != allocUsage) { // Something changed while we released the lock. Retry. BQ_LOGV("allocateBuffers: size/format/usage changed while allocating. Retrying."); mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.notify_all(); continue; } for (size_t i = 0; i < newBufferCount; ++i) { if (mCore->mFreeSlots.empty()) { BQ_LOGV("allocateBuffers: a slot was occupied while " "allocating. Dropping allocated buffer."); continue; } auto slot = mCore->mFreeSlots.begin(); mCore->clearBufferSlotLocked(*slot); // Clean up the slot first mSlots[*slot].mGraphicBuffer = buffers[i]; mSlots[*slot].mFence = Fence::NO_FENCE; // freeBufferLocked puts this slot on the free slots list. Since // we then attached a buffer, move the slot to free buffer list. mCore->mFreeBuffers.push_front(*slot); BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", *slot); // Make sure the erase is done after all uses of the slot // iterator since it will be invalid after this point. mCore->mFreeSlots.erase(slot); } mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.notify_all(); VALIDATE_CONSISTENCY(); // If dequeue is waiting for to allocate a buffer, release the lock until it's not // waiting anymore so it can use the buffer we just allocated. while (mDequeueWaitingForAllocation) { mDequeueWaitingForAllocationCondition.wait(lock); } } // Autolock scope } } status_t BufferQueueProducer::allowAllocation(bool allow) { ATRACE_CALL(); BQ_LOGV("allowAllocation: %s", allow ? "true" : "false"); std::lock_guard lock(mCore->mMutex); mCore->mAllowAllocation = allow; return NO_ERROR; } status_t BufferQueueProducer::setGenerationNumber(uint32_t generationNumber) { ATRACE_CALL(); BQ_LOGV("setGenerationNumber: %u", generationNumber); std::lock_guard lock(mCore->mMutex); mCore->mGenerationNumber = generationNumber; return NO_ERROR; } String8 BufferQueueProducer::getConsumerName() const { ATRACE_CALL(); std::lock_guard lock(mCore->mMutex); BQ_LOGV("getConsumerName: %s", mConsumerName.string()); return mConsumerName; } status_t BufferQueueProducer::setSharedBufferMode(bool sharedBufferMode) { ATRACE_CALL(); BQ_LOGV("setSharedBufferMode: %d", sharedBufferMode); std::lock_guard lock(mCore->mMutex); if (!sharedBufferMode) { mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; } mCore->mSharedBufferMode = sharedBufferMode; return NO_ERROR; } status_t BufferQueueProducer::setAutoRefresh(bool autoRefresh) { ATRACE_CALL(); BQ_LOGV("setAutoRefresh: %d", autoRefresh); std::lock_guard lock(mCore->mMutex); mCore->mAutoRefresh = autoRefresh; return NO_ERROR; } status_t BufferQueueProducer::setDequeueTimeout(nsecs_t timeout) { ATRACE_CALL(); BQ_LOGV("setDequeueTimeout: %" PRId64, timeout); std::lock_guard lock(mCore->mMutex); bool dequeueBufferCannotBlock = timeout >= 0 ? false : mCore->mDequeueBufferCannotBlock; int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, dequeueBufferCannotBlock, mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked(); if (!mCore->adjustAvailableSlotsLocked(delta)) { BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number of " "available slots. Delta = %d", delta); return BAD_VALUE; } mDequeueTimeout = timeout; mCore->mDequeueBufferCannotBlock = dequeueBufferCannotBlock; if (timeout > 0) { mCore->mQueueBufferCanDrop = false; } VALIDATE_CONSISTENCY(); return NO_ERROR; } status_t BufferQueueProducer::setLegacyBufferDrop(bool drop) { ATRACE_CALL(); BQ_LOGV("setLegacyBufferDrop: drop = %d", drop); std::lock_guard lock(mCore->mMutex); mCore->mLegacyBufferDrop = drop; return NO_ERROR; } status_t BufferQueueProducer::getLastQueuedBuffer(sp* outBuffer, sp* outFence, float outTransformMatrix[16]) { ATRACE_CALL(); BQ_LOGV("getLastQueuedBuffer"); std::lock_guard lock(mCore->mMutex); if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; return NO_ERROR; } *outBuffer = mSlots[mCore->mLastQueuedSlot].mGraphicBuffer; *outFence = mLastQueueBufferFence; // Currently only SurfaceFlinger internally ever changes // GLConsumer's filtering mode, so we just use 'true' here as // this is slightly specialized for the current client of this API, // which does want filtering. GLConsumer::computeTransformMatrix(outTransformMatrix, mSlots[mCore->mLastQueuedSlot].mGraphicBuffer, mLastQueuedCrop, mLastQueuedTransform, true /* filter */); return NO_ERROR; } status_t BufferQueueProducer::getLastQueuedBuffer(sp* outBuffer, sp* outFence, Rect* outRect, uint32_t* outTransform) { ATRACE_CALL(); BQ_LOGV("getLastQueuedBuffer"); std::lock_guard lock(mCore->mMutex); if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; return NO_ERROR; } *outBuffer = mSlots[mCore->mLastQueuedSlot].mGraphicBuffer; *outFence = mLastQueueBufferFence; *outRect = mLastQueuedCrop; *outTransform = mLastQueuedTransform; return NO_ERROR; } void BufferQueueProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) { addAndGetFrameTimestamps(nullptr, outDelta); } void BufferQueueProducer::addAndGetFrameTimestamps( const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) { if (newTimestamps == nullptr && outDelta == nullptr) { return; } ATRACE_CALL(); BQ_LOGV("addAndGetFrameTimestamps"); sp listener; { std::lock_guard lock(mCore->mMutex); listener = mCore->mConsumerListener; } if (listener != nullptr) { listener->addAndGetFrameTimestamps(newTimestamps, outDelta); } } void BufferQueueProducer::binderDied(const wp& /* who */) { // If we're here, it means that a producer we were connected to died. // We're guaranteed that we are still connected to it because we remove // this callback upon disconnect. It's therefore safe to read mConnectedApi // without synchronization here. int api = mCore->mConnectedApi; disconnect(api); } status_t BufferQueueProducer::getUniqueId(uint64_t* outId) const { BQ_LOGV("getUniqueId"); *outId = mCore->mUniqueId; return NO_ERROR; } status_t BufferQueueProducer::getConsumerUsage(uint64_t* outUsage) const { BQ_LOGV("getConsumerUsage"); std::lock_guard lock(mCore->mMutex); *outUsage = mCore->mConsumerUsageBits; return NO_ERROR; } status_t BufferQueueProducer::setAutoPrerotation(bool autoPrerotation) { ATRACE_CALL(); BQ_LOGV("setAutoPrerotation: %d", autoPrerotation); std::lock_guard lock(mCore->mMutex); mCore->mAutoPrerotation = autoPrerotation; return NO_ERROR; } } // namespace android