/* * 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 "BufferQueueProducer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #define EGL_EGLEXT_PROTOTYPES #include #include #include #include #include #include #include namespace android { BufferQueueProducer::BufferQueueProducer(const sp& core) : mCore(core), mSlots(core->mSlots), mConsumerName() {} BufferQueueProducer::~BufferQueueProducer() {} status_t BufferQueueProducer::requestBuffer(int slot, sp* buf) { ATRACE_CALL(); BQ_LOGV("requestBuffer: slot %d", slot); Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("requestBuffer: BufferQueue has been abandoned"); 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 != BufferSlot::DEQUEUED) { BQ_LOGE("requestBuffer: slot %d is not owned by the producer " "(state = %d)", slot, mSlots[slot].mBufferState); return BAD_VALUE; } mSlots[slot].mRequestBufferCalled = true; *buf = mSlots[slot].mGraphicBuffer; return NO_ERROR; } status_t BufferQueueProducer::setBufferCount(int bufferCount) { ATRACE_CALL(); BQ_LOGV("setBufferCount: count = %d", bufferCount); sp listener; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("setBufferCount: BufferQueue has been abandoned"); return NO_INIT; } if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("setBufferCount: bufferCount %d too large (max %d)", bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } // There must be no dequeued buffers when changing the buffer count. for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) { BQ_LOGE("setBufferCount: buffer owned by producer"); return -EINVAL; } } if (bufferCount == 0) { mCore->mOverrideMaxBufferCount = 0; mCore->mDequeueCondition.broadcast(); return NO_ERROR; } const int minBufferSlots = mCore->getMinMaxBufferCountLocked(false); if (bufferCount < minBufferSlots) { BQ_LOGE("setBufferCount: requested buffer count %d is less than " "minimum %d", bufferCount, minBufferSlots); return BAD_VALUE; } // Here we are guaranteed that the producer doesn't have any dequeued // buffers and will release all of its buffer references. We don't // clear the queue, however, so that currently queued buffers still // get displayed. mCore->freeAllBuffersLocked(); mCore->mOverrideMaxBufferCount = bufferCount; mCore->mDequeueCondition.broadcast(); listener = mCore->mConsumerListener; } // Autolock scope // Call back without lock held if (listener != NULL) { listener->onBuffersReleased(); } return NO_ERROR; } status_t BufferQueueProducer::dequeueBuffer(int *outSlot, sp *outFence, bool async, uint32_t width, uint32_t height, uint32_t format, uint32_t usage) { ATRACE_CALL(); { // Autolock scope Mutex::Autolock lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; } // Autolock scope BQ_LOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x", async ? "true" : "false", 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; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); if (format == 0) { format = mCore->mDefaultBufferFormat; } // Enable the usage bits the consumer requested usage |= mCore->mConsumerUsageBits; int found = -1; bool tryAgain = true; while (tryAgain) { if (mCore->mIsAbandoned) { BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); return NO_INIT; } const int maxBufferCount = mCore->getMaxBufferCountLocked(async); if (async && mCore->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 (mCore->mOverrideMaxBufferCount < maxBufferCount) { BQ_LOGE("dequeueBuffer: async mode is invalid with " "buffer count override"); return BAD_VALUE; } } // Free up any buffers that are in slots beyond the max buffer count for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { assert(mSlots[s].mBufferState == BufferSlot::FREE); if (mSlots[s].mGraphicBuffer != NULL) { mCore->freeBufferLocked(s); returnFlags |= RELEASE_ALL_BUFFERS; } } // Look for a free buffer to give to the client found = BufferQueueCore::INVALID_BUFFER_SLOT; int dequeuedCount = 0; int acquiredCount = 0; for (int s = 0; s < maxBufferCount; ++s) { switch (mSlots[s].mBufferState) { 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, since the consumer // may still have pending reads of in-flight buffers if (found == BufferQueueCore::INVALID_BUFFER_SLOT || mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) { found = s; } break; default: break; } } // Producers are not allowed to dequeue more than one buffer if they // did not set a buffer count if (!mCore->mOverrideMaxBufferCount && dequeuedCount) { BQ_LOGE("dequeueBuffer: can't dequeue multiple buffers " "without setting the buffer count"); return -EINVAL; } // See whether a buffer has been queued since the last // setBufferCount so we know whether to perform the min undequeued // buffers check below if (mCore->mBufferHasBeenQueued) { // Make sure the producer is not trying to dequeue more buffers // than allowed const int newUndequeuedCount = maxBufferCount - (dequeuedCount + 1); const int minUndequeuedCount = mCore->getMinUndequeuedBufferCountLocked(async); if (newUndequeuedCount < minUndequeuedCount) { BQ_LOGE("dequeueBuffer: min undequeued buffer count (%d) " "exceeded (dequeued=%d undequeued=%d)", minUndequeuedCount, dequeuedCount, newUndequeuedCount); return -EBUSY; } } // If no buffer is found, wait for a buffer to be released or for // the max buffer count to change tryAgain = (found == BufferQueueCore::INVALID_BUFFER_SLOT); 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 && (acquiredCount <= mCore->mMaxAcquiredBufferCount)) { return WOULD_BLOCK; } mCore->mDequeueCondition.wait(mCore->mMutex); } } // while (tryAgain) // This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { BQ_LOGE("dequeueBuffer: no available buffer slots"); return -EBUSY; } *outSlot = found; ATRACE_BUFFER_INDEX(found); const bool useDefaultSize = !width && !height; if (useDefaultSize) { width = mCore->mDefaultWidth; height = mCore->mDefaultHeight; } mSlots[found].mBufferState = BufferSlot::DEQUEUED; const sp& buffer(mSlots[found].mGraphicBuffer); if ((buffer == NULL) || (static_cast(buffer->width) != width) || (static_cast(buffer->height) != height) || (static_cast(buffer->format) != format) || ((static_cast(buffer->usage) & usage) != usage)) { mSlots[found].mAcquireCalled = false; mSlots[found].mGraphicBuffer = NULL; mSlots[found].mRequestBufferCalled = false; mSlots[found].mEglDisplay = EGL_NO_DISPLAY; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; mSlots[found].mFence = Fence::NO_FENCE; returnFlags |= BUFFER_NEEDS_REALLOCATION; } if (CC_UNLIKELY(mSlots[found].mFence == NULL)) { 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; *outFence = mSlots[found].mFence; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; mSlots[found].mFence = Fence::NO_FENCE; } // Autolock scope if (returnFlags & BUFFER_NEEDS_REALLOCATION) { status_t error; sp graphicBuffer(mCore->mAllocator->createGraphicBuffer( width, height, format, usage, &error)); if (graphicBuffer == NULL) { BQ_LOGE("dequeueBuffer: createGraphicBuffer failed"); return error; } { // Autolock scope Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); return NO_INIT; } mSlots[*outSlot].mFrameNumber = ~0; mSlots[*outSlot].mGraphicBuffer = graphicBuffer; } // Autolock scope } 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/%llu buf=%p flags=%#x", *outSlot, mSlots[*outSlot].mFrameNumber, mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); return returnFlags; } status_t BufferQueueProducer::queueBuffer(int slot, const QueueBufferInput &input, QueueBufferOutput *output) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); int64_t timestamp; bool isAutoTimestamp; Rect crop; int scalingMode; uint32_t transform; bool async; sp fence; input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode, &transform, &async, &fence); if (fence == NULL) { BQ_LOGE("queueBuffer: fence is NULL"); return BAD_VALUE; } 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 -EINVAL; } sp listener; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("queueBuffer: BufferQueue has been abandoned"); return NO_INIT; } const int maxBufferCount = mCore->getMaxBufferCountLocked(async); if (async && mCore->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 (mCore->mOverrideMaxBufferCount < maxBufferCount) { BQ_LOGE("queueBuffer: async mode is invalid with " "buffer count override"); return BAD_VALUE; } } if (slot < 0 || slot >= maxBufferCount) { BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", slot, maxBufferCount); return -EINVAL; } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { BQ_LOGE("queueBuffer: slot %d is not owned by the producer " "(state = %d)", slot, mSlots[slot].mBufferState); return -EINVAL; } else if (!mSlots[slot].mRequestBufferCalled) { BQ_LOGE("queueBuffer: slot %d was queued without requesting " "a buffer", slot); return -EINVAL; } BQ_LOGV("queueBuffer: slot=%d/%llu time=%llu crop=[%d,%d,%d,%d] " "transform=%#x scale=%s", slot, mCore->mFrameCounter + 1, timestamp, crop.left, crop.top, crop.right, crop.bottom, transform, BufferItem::scalingModeName(scalingMode)); const sp& graphicBuffer(mSlots[slot].mGraphicBuffer); Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); Rect croppedRect; crop.intersect(bufferRect, &croppedRect); if (croppedRect != crop) { BQ_LOGE("queueBuffer: crop rect is not contained within the " "buffer in slot %d", slot); return -EINVAL; } mSlots[slot].mFence = fence; mSlots[slot].mBufferState = BufferSlot::QUEUED; ++mCore->mFrameCounter; mSlots[slot].mFrameNumber = mCore->mFrameCounter; BufferItem item; item.mAcquireCalled = mSlots[slot].mAcquireCalled; item.mGraphicBuffer = mSlots[slot].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 = mCore->mFrameCounter; item.mSlot = slot; item.mFence = fence; item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async; if (mCore->mQueue.empty()) { // When the queue is empty, we can ignore mDequeueBufferCannotBlock // and simply queue this buffer mCore->mQueue.push_back(item); listener = mCore->mConsumerListener; } else { // When the queue is not empty, we need to look at the front buffer // state to see if we need to replace it BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin()); if (front->mIsDroppable) { // If the front queued buffer is still being tracked, we first // mark it as freed if (mCore->stillTracking(front)) { mSlots[front->mSlot].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->mSlot].mFrameNumber = 0; } // Overwrite the droppable buffer with the incoming one *front = item; } else { mCore->mQueue.push_back(item); listener = mCore->mConsumerListener; } } mCore->mBufferHasBeenQueued = true; mCore->mDequeueCondition.broadcast(); output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, mCore->mTransformHint, mCore->mQueue.size()); ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size()); } // Autolock scope // Call back without lock held if (listener != NULL) { listener->onFrameAvailable(); } return NO_ERROR; } void BufferQueueProducer::cancelBuffer(int slot, const sp& fence) { ATRACE_CALL(); BQ_LOGV("cancelBuffer: slot %d", slot); Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("cancelBuffer: BufferQueue has been abandoned"); return; } 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; } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { BQ_LOGE("cancelBuffer: slot %d is not owned by the producer " "(state = %d)", slot, mSlots[slot].mBufferState); return; } else if (fence == NULL) { BQ_LOGE("cancelBuffer: fence is NULL"); return; } mSlots[slot].mBufferState = BufferSlot::FREE; mSlots[slot].mFrameNumber = 0; mSlots[slot].mFence = fence; mCore->mDequeueCondition.broadcast(); } int BufferQueueProducer::query(int what, int *outValue) { ATRACE_CALL(); Mutex::Autolock lock(mCore->mMutex); if (outValue == NULL) { 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 = mCore->mDefaultWidth; break; case NATIVE_WINDOW_HEIGHT: value = mCore->mDefaultHeight; break; case NATIVE_WINDOW_FORMAT: value = mCore->mDefaultBufferFormat; break; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: value = mCore->getMinUndequeuedBufferCountLocked(false); break; case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: value = (mCore->mQueue.size() > 1); break; case NATIVE_WINDOW_CONSUMER_USAGE_BITS: value = mCore->mConsumerUsageBits; break; default: return BAD_VALUE; } BQ_LOGV("query: %d? %d", what, value); *outValue = value; return NO_ERROR; } status_t BufferQueueProducer::connect(const sp &token, int api, bool producerControlledByApp, QueueBufferOutput *output) { ATRACE_CALL(); Mutex::Autolock lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; BQ_LOGV("connect(P): api=%d producerControlledByApp=%s", api, producerControlledByApp ? "true" : "false"); // 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. while (true) { if (mCore->mIsAbandoned) { BQ_LOGE("connect(P): BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConsumerListener == NULL) { BQ_LOGE("connect(P): BufferQueue has no consumer"); return NO_INIT; } if (output == NULL) { BQ_LOGE("connect(P): output was NULL"); return BAD_VALUE; } if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("connect(P): already connected (cur=%d req=%d)", mCore->mConnectedApi, api); return BAD_VALUE; } size_t maxBufferCount = mCore->getMaxBufferCountLocked(false); if (mCore->mQueue.size() <= maxBufferCount) { // The queue size seems small enough to proceed // TODO: Make this bound tighter? break; } BQ_LOGV("connect(P): queue size is %d, waiting", mCore->mQueue.size()); mCore->mDequeueCondition.wait(mCore->mMutex); } 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->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, mCore->mTransformHint, mCore->mQueue.size()); // Set up a death notification so that we can disconnect // automatically if the remote producer dies if (token != NULL && token->remoteBinder() != NULL) { status = token->linkToDeath( static_cast(this)); if (status == NO_ERROR) { mCore->mConnectedProducerToken = token; } else { BQ_LOGE("connect(P): linkToDeath failed: %s (%d)", strerror(-status), status); } } break; default: BQ_LOGE("connect(P): unknown API %d", api); status = BAD_VALUE; break; } mCore->mBufferHasBeenQueued = false; mCore->mDequeueBufferCannotBlock = mCore->mConsumerControlledByApp && producerControlledByApp; return status; } status_t BufferQueueProducer::disconnect(int api) { ATRACE_CALL(); BQ_LOGV("disconnect(P): api %d", api); int status = NO_ERROR; sp listener; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); 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; } 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(); // Remove our death notification callback if we have one sp token = mCore->mConnectedProducerToken; if (token != NULL) { // This can fail if we're here because of the death // notification, but we just ignore it token->unlinkToDeath( static_cast(this)); } mCore->mConnectedProducerToken = NULL; mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API; mCore->mDequeueCondition.broadcast(); listener = mCore->mConsumerListener; } else { BQ_LOGE("disconnect(P): connected to another API " "(cur=%d req=%d)", mCore->mConnectedApi, api); status = -EINVAL; } break; default: BQ_LOGE("disconnect(P): unknown API %d", api); status = -EINVAL; break; } } // Autolock scope // Call back without lock held if (listener != NULL) { listener->onBuffersReleased(); } return status; } 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); } } // namespace android