status_t VirtualDisplaySurface::queueBuffer(int pslot, const QueueBufferInput& input, QueueBufferOutput* output) { if (mDisplayId < 0) return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output); VDS_LOGW_IF(mDbgState != DBG_STATE_GLES, "Unexpected queueBuffer(pslot=%d) in %s state", pslot, dbgStateStr()); mDbgState = DBG_STATE_GLES_DONE; VDS_LOGV("queueBuffer pslot=%d", pslot); status_t result; if (mCompositionType == COMPOSITION_MIXED) { // Queue the buffer back into the scratch pool QueueBufferOutput scratchQBO; int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot); result = mSource[SOURCE_SCRATCH]->queueBuffer(sslot, input, &scratchQBO); if (result != NO_ERROR) return result; // Now acquire the buffer from the scratch pool -- should be the same // slot and fence as we just queued. Mutex::Autolock lock(mMutex); BufferItem item; result = acquireBufferLocked(&item, 0); if (result != NO_ERROR) return result; VDS_LOGW_IF(item.mBuf != sslot, "queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d", item.mBuf, sslot); mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mBuf); mFbFence = mSlots[item.mBuf].mFence; } else { LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES, "Unexpected queueBuffer in state %s for compositionType %s", dbgStateStr(), dbgCompositionTypeStr(mCompositionType)); // Extract the GLES release fence for HWC to acquire int64_t timestamp; bool isAutoTimestamp; android_dataspace dataSpace; Rect crop; int scalingMode; uint32_t transform; bool async; input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode, &transform, &async, &mFbFence); mFbProducerSlot = pslot; mOutputFence = mFbFence; } *output = mQueueBufferOutput; return NO_ERROR; }
status_t GonkBufferQueueProducer::queueBuffer(int slot, const QueueBufferInput &input, QueueBufferOutput *output) { ATRACE_CALL(); int64_t timestamp; bool isAutoTimestamp; Rect crop; int scalingMode; uint32_t transform; uint32_t stickyTransform; bool async; sp<Fence> fence; input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode, &transform, &async, &fence, &stickyTransform); if (fence == NULL) { ALOGE("queueBuffer: fence is NULL"); // Temporary workaround for b/17946343: soldier-on instead of returning an error. This // prevents the client from dying, at the risk of visible corruption due to hwcomposer // reading the buffer before the producer is done rendering it. Unless the buffer is the // last frame of an animation, the corruption will be transient. fence = Fence::NO_FENCE; // 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: ALOGE("queueBuffer: unknown scaling mode %d", scalingMode); return BAD_VALUE; } sp<IConsumerListener> listener; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { ALOGE("queueBuffer: GonkBufferQueue 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) { ALOGE("queueBuffer: async mode is invalid with " "buffer count override"); return BAD_VALUE; } } if (slot < 0 || slot >= maxBufferCount) { ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot, maxBufferCount); return BAD_VALUE; } else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) { ALOGE("queueBuffer: slot %d is not owned by the producer " "(state = %d)", slot, mSlots[slot].mBufferState); return BAD_VALUE; } else if (!mSlots[slot].mRequestBufferCalled) { ALOGE("queueBuffer: slot %d was queued without requesting " "a buffer", slot); return BAD_VALUE; } ALOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " crop=[%d,%d,%d,%d] transform=%#x scale=%s", slot, mCore->mFrameCounter + 1, timestamp, crop.left, crop.top, crop.right, crop.bottom, transform, GonkBufferItem::scalingModeName(scalingMode)); const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer); Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); Rect croppedRect; crop.intersect(bufferRect, &croppedRect); if (croppedRect != crop) { ALOGE("queueBuffer: crop rect is not contained within the " "buffer in slot %d", slot); return BAD_VALUE; } mSlots[slot].mFence = fence; mSlots[slot].mBufferState = GonkBufferSlot::QUEUED; ++mCore->mFrameCounter; mSlots[slot].mFrameNumber = mCore->mFrameCounter; GonkBufferItem 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; mStickyTransform = stickyTransform; 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 GonkBufferQueueCore::Fifo::iterator front(mCore->mQueue.begin()); if (front->mIsDroppable || !mSynchronousMode) { // If the front queued buffer is still being tracked, we first // mark it as freed if (mCore->stillTracking(front)) { mSlots[front->mSlot].mBufferState = GonkBufferSlot::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; listener = mCore->mConsumerListener; } 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()); } // Autolock scope // Call back without lock held if (listener != NULL) { listener->onFrameAvailable(); } return NO_ERROR; }
status_t BufferQueue::queueBuffer(int buf, const QueueBufferInput& input, QueueBufferOutput* output) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(buf); Rect crop; uint32_t transform; int scalingMode; int64_t timestamp; input.deflate(×tamp, &crop, &scalingMode, &transform); 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)); sp<ConsumerListener> listener; { // scope for the lock Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!"); return NO_INIT; } if (buf < 0 || buf >= mBufferCount) { ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d", mBufferCount, buf); return -EINVAL; } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { ST_LOGE("queueBuffer: slot %d is not owned by the client " "(state=%d)", buf, mSlots[buf].mBufferState); return -EINVAL; } else if (!mSlots[buf].mRequestBufferCalled) { ST_LOGE("queueBuffer: slot %d was enqueued without requesting a " "buffer", buf); return -EINVAL; } const sp<GraphicBuffer>& graphicBuffer(mSlots[buf].mGraphicBuffer); Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); Rect croppedCrop; crop.intersect(bufferRect, &croppedCrop); if (croppedCrop != crop) { ST_LOGE("queueBuffer: crop rect is not contained within the " "buffer in slot %d", buf); return -EINVAL; } if (mSynchronousMode) { // In synchronous mode we queue all buffers in a FIFO. mQueue.push_back(buf); // Synchronous mode always signals that an additional frame should // be consumed. 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; } 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; } } mSlots[buf].mTimestamp = timestamp; mSlots[buf].mCrop = crop; mSlots[buf].mTransform = transform; 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(); output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, mQueue.size()); ATRACE_INT(mConsumerName.string(), mQueue.size()); } // scope for the lock // call back without lock held if (listener != 0) { listener->onFrameAvailable(); } return OK; }
status_t GonkBufferQueue::queueBuffer(int buf, const QueueBufferInput& input, QueueBufferOutput* output) { Rect crop; uint32_t transform; int scalingMode; int64_t timestamp; sp<Fence> fence; input.deflate(×tamp, &crop, &scalingMode, &transform, &fence); #if ANDROID_VERSION >= 18 if (fence == NULL) { ST_LOGE("queueBuffer: fence is NULL"); return BAD_VALUE; } #endif 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)); sp<ConsumerListener> listener; { // scope for the lock Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("queueBuffer: GonkBufferQueue has been abandoned!"); return NO_INIT; } int maxBufferCount = getMaxBufferCountLocked(); if (buf < 0 || buf >= maxBufferCount) { ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d", maxBufferCount, buf); return -EINVAL; } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { ST_LOGE("queueBuffer: slot %d is not owned by the client " "(state=%d)", buf, mSlots[buf].mBufferState); return -EINVAL; } else if (!mSlots[buf].mRequestBufferCalled) { ST_LOGE("queueBuffer: slot %d was enqueued without requesting a " "buffer", buf); return -EINVAL; } const sp<GraphicBuffer>& graphicBuffer(mSlots[buf].mGraphicBuffer); Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); Rect croppedCrop; crop.intersect(bufferRect, &croppedCrop); if (croppedCrop != crop) { ST_LOGE("queueBuffer: crop rect is not contained within the " "buffer in slot %d", buf); return -EINVAL; } if (mSynchronousMode) { // In synchronous mode we queue all buffers in a FIFO. mQueue.push_back(buf); } else { // In asynchronous mode we only keep the most recent buffer. if (mQueue.empty()) { mQueue.push_back(buf); } 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; } } // always signals that an additional frame should be consumed // to handle max acquired buffer count reached case. 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(); output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, mQueue.size()); } // scope for the lock // call back without lock held if (listener != 0) { listener->onFrameAvailable(); } return NO_ERROR; }