Split BufferQueue into core + producer + consumer

Change-Id: Idc39f1e511d68ce4f02202d35425a419bc0bcd92
This commit is contained in:
Dan Stoza 2014-02-28 11:17:17 -08:00
parent 646e2fbd91
commit 289ade165e
11 changed files with 2194 additions and 0 deletions

103
include/gui/BufferItem.h Normal file
View File

@ -0,0 +1,103 @@
/*
* 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.
*/
#ifndef ANDROID_GUI_BUFFERITEM_H
#define ANDROID_GUI_BUFFERITEM_H
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <gui/IGraphicBufferConsumer.h>
#include <ui/Rect.h>
#include <utils/Flattenable.h>
#include <utils/StrongPointer.h>
namespace android {
class Fence;
class GraphicBuffer;
class BufferItem : public Flattenable<BufferItem> {
friend class Flattenable<BufferItem>;
size_t getPodSize() const;
size_t getFlattenedSize() const;
size_t getFdCount() const;
status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
public:
// The default value of mBuf, used to indicate this doesn't correspond to a slot.
enum { INVALID_BUFFER_SLOT = -1 };
BufferItem();
operator IGraphicBufferConsumer::BufferItem() const;
static const char* scalingModeName(uint32_t scalingMode);
// mGraphicBuffer points to the buffer allocated for this slot, or is NULL
// if the buffer in this slot has been acquired in the past (see
// BufferSlot.mAcquireCalled).
sp<GraphicBuffer> mGraphicBuffer;
// mFence is a fence that will signal when the buffer is idle.
sp<Fence> mFence;
// mCrop is the current crop rectangle for this buffer slot.
Rect mCrop;
// mTransform is the current transform flags for this buffer slot.
// refer to NATIVE_WINDOW_TRANSFORM_* in <window.h>
uint32_t mTransform;
// mScalingMode is the current scaling mode for this buffer slot.
// refer to NATIVE_WINDOW_SCALING_* in <window.h>
uint32_t mScalingMode;
// mTimestamp is the current timestamp for this buffer slot. This gets
// to set by queueBuffer each time this slot is queued. This value
// is guaranteed to be monotonically increasing for each newly
// acquired buffer.
int64_t mTimestamp;
// mIsAutoTimestamp indicates whether mTimestamp was generated
// automatically when the buffer was queued.
bool mIsAutoTimestamp;
// mFrameNumber is the number of the queued frame for this slot.
uint64_t mFrameNumber;
// mSlot is the slot index of this buffer (default INVALID_BUFFER_SLOT).
int mSlot;
// mIsDroppable whether this buffer was queued with the
// property that it can be replaced by a new buffer for the purpose of
// making sure dequeueBuffer() won't block.
// i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer
// was queued.
bool mIsDroppable;
// Indicates whether this buffer has been seen by a consumer yet
bool mAcquireCalled;
// Indicates this buffer must be transformed by the inverse transform of the screen
// it is displayed onto. This is applied after mTransform.
bool mTransformToDisplayInverse;
};
} // namespace android
#endif

View File

@ -0,0 +1,148 @@
/*
* 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.
*/
#ifndef ANDROID_GUI_BUFFERQUEUECONSUMER_H
#define ANDROID_GUI_BUFFERQUEUECONSUMER_H
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <gui/BufferQueueCore.h>
#include <gui/IGraphicBufferConsumer.h>
namespace android {
class BufferQueueCore;
class BufferQueueConsumer : public BnGraphicBufferConsumer {
public:
BufferQueueConsumer(const sp<BufferQueueCore>& core);
virtual ~BufferQueueConsumer();
// acquireBuffer attempts to acquire ownership of the next pending buffer in
// the BufferQueue. If no buffer is pending then it returns
// NO_BUFFER_AVAILABLE. If a buffer is successfully acquired, the
// information about the buffer is returned in BufferItem. If the buffer
// returned had previously been acquired then the BufferItem::mGraphicBuffer
// field of buffer is set to NULL and it is assumed that the consumer still
// holds a reference to the buffer.
//
// If expectedPresent is nonzero, it indicates the time when the buffer
// will be displayed on screen. If the buffer's timestamp is farther in the
// future, the buffer won't be acquired, and PRESENT_LATER will be
// returned. The presentation time is in nanoseconds, and the time base
// is CLOCK_MONOTONIC.
virtual status_t acquireBuffer(BufferItem* outBuffer,
nsecs_t expectedPresent);
// releaseBuffer releases a buffer slot from the consumer back to the
// BufferQueue. This may be done while the buffer's contents are still
// being accessed. The fence will signal when the buffer is no longer
// in use. frameNumber is used to indentify the exact buffer returned.
//
// If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
// any references to the just-released buffer that it might have, as if it
// had received a onBuffersReleased() call with a mask set for the released
// buffer.
//
// Note that the dependencies on EGL will be removed once we switch to using
// the Android HW Sync HAL.
virtual status_t releaseBuffer(int slot, uint64_t frameNumber,
const sp<Fence>& releaseFence, EGLDisplay display,
EGLSyncKHR fence);
// connect connects a consumer to the BufferQueue. Only one
// consumer may be connected, and when that consumer disconnects the
// BufferQueue is placed into the "abandoned" state, causing most
// interactions with the BufferQueue by the producer to fail.
// controlledByApp indicates whether the consumer is controlled by
// the application.
//
// consumerListener may not be NULL.
virtual status_t connect(const sp<IConsumerListener>& consumerListener,
bool controlledByApp);
// disconnect disconnects a consumer from the BufferQueue. All
// buffers will be freed and the BufferQueue is placed in the "abandoned"
// state, causing most interactions with the BufferQueue by the producer to
// fail.
virtual status_t disconnect();
// getReleasedBuffers sets the value pointed to by outSlotMask to a bit mask
// indicating which buffer slots have been released by the BufferQueue
// but have not yet been released by the consumer.
//
// This should be called from the onBuffersReleased() callback.
virtual status_t getReleasedBuffers(uint32_t* outSlotMask);
// setDefaultBufferSize is used to set the size of buffers returned by
// dequeueBuffer when a width and height of zero is requested. Default
// is 1x1.
virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height);
// setDefaultMaxBufferCount sets the default value for the maximum buffer
// count (the initial default is 2). If the producer has requested a
// buffer count using setBufferCount, the default buffer count will only
// take effect if the producer sets the count back to zero.
//
// The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
virtual status_t setDefaultMaxBufferCount(int bufferCount);
// disableAsyncBuffer disables the extra buffer used in async mode
// (when both producer and consumer have set their "isControlledByApp"
// flag) and has dequeueBuffer() return WOULD_BLOCK instead.
//
// This can only be called before connect().
virtual status_t disableAsyncBuffer();
// setMaxAcquiredBufferCount sets the maximum number of buffers that can
// be acquired by the consumer at one time (default 1). This call will
// fail if a producer is connected to the BufferQueue.
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
// setConsumerName sets the name used in logging
virtual void setConsumerName(const String8& name);
// setDefaultBufferFormat allows the BufferQueue to create
// GraphicBuffers of a defaultFormat if no format is specified
// in dequeueBuffer. Formats are enumerated in graphics.h; the
// initial default is HAL_PIXEL_FORMAT_RGBA_8888.
virtual status_t setDefaultBufferFormat(uint32_t defaultFormat);
// setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
// These are merged with the bits passed to dequeueBuffer. The values are
// enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
virtual status_t setConsumerUsageBits(uint32_t usage);
// setTransformHint bakes in rotation to buffers so overlays can be used.
// The values are enumerated in window.h, e.g.
// NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
virtual status_t setTransformHint(uint32_t hint);
// dump our state in a String
virtual void dump(String8& result, const char* prefix) const;
private:
sp<BufferQueueCore> mCore;
BufferQueueCore::SlotsType& mSlots;
String8 mConsumerName; // Cached from mCore. Updated on setConsumerName.
}; // class BufferQueueConsumer
} // namespace android
#endif

View File

@ -0,0 +1,120 @@
/*
* 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.
*/
#ifndef ANDROID_GUI_BUFFERQUEUECORE_H
#define ANDROID_GUI_BUFFERQUEUECORE_H
#include <gui/BufferSlot.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/StrongPointer.h>
#include <utils/Trace.h>
#include <utils/Vector.h>
#define BQ_LOGV(x, ...) ALOGV("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define BQ_LOGD(x, ...) ALOGD("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define BQ_LOGI(x, ...) ALOGI("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define BQ_LOGW(x, ...) ALOGW("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define BQ_LOGE(x, ...) ALOGE("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define ATRACE_BUFFER_INDEX(index) \
if (ATRACE_ENABLED()) { \
char ___traceBuf[1024]; \
snprintf(___traceBuf, 1024, "%s: %d", \
mCore->mConsumerName.string(), (index)); \
android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \
}
namespace android {
class BufferItem;
class IBinder;
class IConsumerListener;
class IGraphicBufferAlloc;
class BufferQueueCore : public virtual RefBase {
friend class BufferQueueProducer;
friend class BufferQueueConsumer;
public:
// BufferQueue will keep track of at most this value of buffers. Attempts
// at runtime to increase the number of buffers past this will fail.
enum { NUM_BUFFER_SLOTS = 32 };
// Used as a placeholder slot number when the value isn't pointing to an
// existing buffer.
enum { INVALID_BUFFER_SLOT = -1 }; // TODO: Extract from IGBC::BufferItem
// We reserve two slots in order to guarantee that the producer and
// consumer can run asynchronously.
enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 };
// The default API number used to indicate that no producer is connected
enum { NO_CONNECTED_API = 0 };
typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
typedef Vector<BufferItem> Fifo;
// BufferQueueCore manages a pool of gralloc memory slots to be used by
// producers and consumers. allocator is used to allocate all the needed
// gralloc buffers.
BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator = NULL);
virtual ~BufferQueueCore();
private:
void dump(String8& result, const char* prefix) const;
int getMinUndequeuedBufferCountLocked(bool async) const;
int getMinMaxBufferCountLocked(bool async) const;
int getMaxBufferCountLocked(bool async) const;
status_t setDefaultMaxBufferCountLocked(int count);
void freeBufferLocked(int slot);
void freeAllBuffersLocked();
bool stillTracking(const BufferItem* item) const;
const sp<IGraphicBufferAlloc>& mAllocator;
mutable Mutex mMutex;
bool mIsAbandoned;
bool mConsumerControlledByApp;
String8 mConsumerName;
sp<IConsumerListener> mConsumerListener;
uint32_t mConsumerUsageBits;
int mConnectedApi;
sp<IBinder> mConnectedProducerToken;
BufferSlot mSlots[NUM_BUFFER_SLOTS];
Fifo mQueue;
int mOverrideMaxBufferCount;
mutable Condition mDequeueCondition;
bool mUseAsyncBuffer;
bool mDequeueBufferCannotBlock;
uint32_t mDefaultBufferFormat;
int mDefaultWidth;
int mDefaultHeight;
int mDefaultMaxBufferCount;
int mMaxAcquiredBufferCount;
bool mBufferHasBeenQueued;
uint64_t mFrameCounter;
uint32_t mTransformHint;
}; // class BufferQueueCore
} // namespace android
#endif

View File

@ -0,0 +1,165 @@
/*
* 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.
*/
#ifndef ANDROID_GUI_BUFFERQUEUEPRODUCER_H
#define ANDROID_GUI_BUFFERQUEUEPRODUCER_H
#include <gui/BufferQueueCore.h>
#include <gui/IGraphicBufferProducer.h>
namespace android {
class BufferSlot;
class BufferQueueProducer : public BnGraphicBufferProducer,
private IBinder::DeathRecipient {
public:
BufferQueueProducer(const sp<BufferQueueCore>& core);
virtual ~BufferQueueProducer();
// requestBuffer returns the GraphicBuffer for slot N.
//
// In normal operation, this is called the first time slot N is returned
// by dequeueBuffer. It must be called again if dequeueBuffer returns
// flags indicating that previously-returned buffers are no longer valid.
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
// setBufferCount updates the number of available buffer slots. If this
// method succeeds, buffer slots will be both unallocated and owned by
// the BufferQueue object (i.e. they are not owned by the producer or
// consumer).
//
// This will fail if the producer has dequeued any buffers, or if
// bufferCount is invalid. bufferCount must generally be a value
// between the minimum undequeued buffer count (exclusive) and NUM_BUFFER_SLOTS
// (inclusive). It may also be set to zero (the default) to indicate
// that the producer does not wish to set a value. The minimum value
// can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
// ...).
//
// This may only be called by the producer. The consumer will be told
// to discard buffers through the onBuffersReleased callback.
virtual status_t setBufferCount(int bufferCount);
// dequeueBuffer gets the next buffer slot index for the producer to use.
// If a buffer slot is available then that slot index is written to the
// location pointed to by the buf argument and a status of OK is returned.
// If no slot is available then a status of -EBUSY is returned and buf is
// unmodified.
//
// The outFence parameter will be updated to hold the fence associated with
// the buffer. The contents of the buffer must not be overwritten until the
// fence signals. If the fence is Fence::NO_FENCE, the buffer may be
// written immediately.
//
// The width and height parameters must be no greater than the minimum of
// GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
// An error due to invalid dimensions might not be reported until
// updateTexImage() is called. If width and height are both zero, the
// default values specified by setDefaultBufferSize() are used instead.
//
// The pixel formats are enumerated in graphics.h, e.g.
// HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format
// will be used.
//
// The usage argument specifies gralloc buffer usage flags. The values
// are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These
// will be merged with the usage flags specified by setConsumerUsageBits.
//
// The return value may be a negative error value or a non-negative
// collection of flags. If the flags are set, the return values are
// valid, but additional actions must be performed.
//
// If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the
// producer must discard cached GraphicBuffer references for the slot
// returned in buf.
// If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer
// must discard cached GraphicBuffer references for all slots.
//
// In both cases, the producer will need to call requestBuffer to get a
// GraphicBuffer handle for the returned slot.
virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence, bool async,
uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
// queueBuffer returns a filled buffer to the BufferQueue.
//
// Additional data is provided in the QueueBufferInput struct. Notably,
// a timestamp must be provided for the buffer. The timestamp is in
// nanoseconds, and must be monotonically increasing. Its other semantics
// (zero point, etc) are producer-specific and should be documented by the
// producer.
//
// The caller may provide a fence that signals when all rendering
// operations have completed. Alternatively, NO_FENCE may be used,
// indicating that the buffer is ready immediately.
//
// Some values are returned in the output struct: the current settings
// for default width and height, the current transform hint, and the
// number of queued buffers.
virtual status_t queueBuffer(int slot,
const QueueBufferInput& input, QueueBufferOutput* output);
// cancelBuffer returns a dequeued buffer to the BufferQueue, but doesn't
// queue it for use by the consumer.
//
// The buffer will not be overwritten until the fence signals. The fence
// will usually be the one obtained from dequeueBuffer.
virtual void cancelBuffer(int slot, const sp<Fence>& fence);
// Query native window attributes. The "what" values are enumerated in
// window.h (e.g. NATIVE_WINDOW_FORMAT).
virtual int query(int what, int* outValue);
// connect attempts to connect a producer API to the BufferQueue. This
// must be called before any other IGraphicBufferProducer methods are
// called except for getAllocator. A consumer must already be connected.
//
// This method will fail if connect was previously called on the
// BufferQueue and no corresponding disconnect call was made (i.e. if
// it's still connected to a producer).
//
// APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
virtual status_t connect(const sp<IBinder>& token,
int api, bool producerControlledByApp, QueueBufferOutput* output);
// disconnect attempts to disconnect a producer API from the BufferQueue.
// Calling this method will cause any subsequent calls to other
// IGraphicBufferProducer methods to fail except for getAllocator and connect.
// Successfully calling connect after this will allow the other methods to
// succeed again.
//
// This method will fail if the the BufferQueue is not currently
// connected to the specified producer API.
virtual status_t disconnect(int api);
private:
// This is required by the IBinder::DeathRecipient interface
virtual void binderDied(const wp<IBinder>& who);
sp<BufferQueueCore> mCore;
BufferQueueCore::SlotsType& mSlots;
// This is a cached copy of the name stored in the BufferQueueCore.
// It's updated during connect and dequeueBuffer (which should catch
// most updates).
String8 mConsumerName;
}; // class BufferQueueProducer
} // namespace android
#endif

136
include/gui/BufferSlot.h Normal file
View File

@ -0,0 +1,136 @@
/*
* 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.
*/
#ifndef ANDROID_GUI_BUFFERSLOT_H
#define ANDROID_GUI_BUFFERSLOT_H
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <utils/StrongPointer.h>
namespace android {
class Fence;
struct BufferSlot {
BufferSlot()
: mEglDisplay(EGL_NO_DISPLAY),
mBufferState(BufferSlot::FREE),
mRequestBufferCalled(false),
mFrameNumber(0),
mEglFence(EGL_NO_SYNC_KHR),
mAcquireCalled(false),
mNeedsCleanupOnRelease(false) {
}
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
// if no buffer has been allocated.
sp<GraphicBuffer> mGraphicBuffer;
// mEglDisplay is the EGLDisplay used to create EGLSyncKHR objects.
EGLDisplay mEglDisplay;
// BufferState represents the different states in which a buffer slot
// can be. All slots are initially FREE.
enum BufferState {
// FREE indicates that the buffer is available to be dequeued
// by the producer. The buffer may be in use by the consumer for
// a finite time, so the buffer must not be modified until the
// associated fence is signaled.
//
// The slot is "owned" by BufferQueue. It transitions to DEQUEUED
// when dequeueBuffer is called.
FREE = 0,
// DEQUEUED indicates that the buffer has been dequeued by the
// producer, but has not yet been queued or canceled. The
// producer may modify the buffer's contents as soon as the
// associated ready fence is signaled.
//
// The slot is "owned" by the producer. It can transition to
// QUEUED (via queueBuffer) or back to FREE (via cancelBuffer).
DEQUEUED = 1,
// QUEUED indicates that the buffer has been filled by the
// producer and queued for use by the consumer. The buffer
// contents may continue to be modified for a finite time, so
// the contents must not be accessed until the associated fence
// is signaled.
//
// The slot is "owned" by BufferQueue. It can transition to
// ACQUIRED (via acquireBuffer) or to FREE (if another buffer is
// queued in asynchronous mode).
QUEUED = 2,
// ACQUIRED indicates that the buffer has been acquired by the
// consumer. As with QUEUED, the contents must not be accessed
// by the consumer until the fence is signaled.
//
// The slot is "owned" by the consumer. It transitions to FREE
// when releaseBuffer is called.
ACQUIRED = 3
};
static const char* bufferStateName(BufferState state);
// mBufferState is the current state of this buffer slot.
BufferState mBufferState;
// mRequestBufferCalled is used for validating that the producer did
// call requestBuffer() when told to do so. Technically this is not
// needed but useful for debugging and catching producer bugs.
bool mRequestBufferCalled;
// mFrameNumber is the number of the queued frame for this slot. This
// is used to dequeue buffers in LRU order (useful because buffers
// may be released before their release fence is signaled).
uint64_t mFrameNumber;
// mEglFence is the EGL sync object that must signal before the buffer
// associated with this buffer slot may be dequeued. It is initialized
// to EGL_NO_SYNC_KHR when the buffer is created and may be set to a
// new sync object in releaseBuffer. (This is deprecated in favor of
// mFence, below.)
EGLSyncKHR mEglFence;
// mFence is a fence which will signal when work initiated by the
// previous owner of the buffer is finished. When the buffer is FREE,
// the fence indicates when the consumer has finished reading
// from the buffer, or when the producer has finished writing if it
// called cancelBuffer after queueing some writes. When the buffer is
// QUEUED, it indicates when the producer has finished filling the
// buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been
// passed to the consumer or producer along with ownership of the
// buffer, and mFence is set to NO_FENCE.
sp<Fence> mFence;
// Indicates whether this buffer has been seen by a consumer yet
bool mAcquireCalled;
// Indicates whether this buffer needs to be cleaned up by the
// consumer. This is set when a buffer in ACQUIRED state is freed.
// It causes releaseBuffer to return STALE_BUFFER_SLOT.
bool mNeedsCleanupOnRelease;
};
} // namespace android
#endif

View File

@ -5,8 +5,13 @@ LOCAL_SRC_FILES:= \
IGraphicBufferConsumer.cpp \
IConsumerListener.cpp \
BitTube.cpp \
BufferItem.cpp \
BufferItemConsumer.cpp \
BufferQueue.cpp \
BufferQueueConsumer.cpp \
BufferQueueCore.cpp \
BufferQueueProducer.cpp \
BufferSlot.cpp \
ConsumerBase.cpp \
CpuConsumer.cpp \
DisplayEventReceiver.cpp \

190
libs/gui/BufferItem.cpp Normal file
View File

@ -0,0 +1,190 @@
/*
* 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 <gui/BufferItem.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <system/window.h>
namespace android {
BufferItem::BufferItem() :
mTransform(0),
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mTimestamp(0),
mIsAutoTimestamp(false),
mFrameNumber(0),
mSlot(INVALID_BUFFER_SLOT),
mIsDroppable(false),
mAcquireCalled(false),
mTransformToDisplayInverse(false) {
mCrop.makeInvalid();
}
BufferItem::operator IGraphicBufferConsumer::BufferItem() const {
IGraphicBufferConsumer::BufferItem bufferItem;
bufferItem.mTransform = mTransform;
bufferItem.mScalingMode = mScalingMode;
bufferItem.mTimestamp = mTimestamp;
bufferItem.mIsAutoTimestamp = mIsAutoTimestamp;
bufferItem.mFrameNumber = mFrameNumber;
bufferItem.mBuf = mSlot;
bufferItem.mIsDroppable = mIsDroppable;
bufferItem.mAcquireCalled = mAcquireCalled;
bufferItem.mTransformToDisplayInverse = mTransformToDisplayInverse;
bufferItem.mCrop = mCrop;
return bufferItem;
}
size_t BufferItem::getPodSize() const {
size_t c = sizeof(mCrop) +
sizeof(mTransform) +
sizeof(mScalingMode) +
sizeof(mTimestamp) +
sizeof(mIsAutoTimestamp) +
sizeof(mFrameNumber) +
sizeof(mSlot) +
sizeof(mIsDroppable) +
sizeof(mAcquireCalled) +
sizeof(mTransformToDisplayInverse);
return c;
}
size_t 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 BufferItem::getFdCount() const {
size_t c = 0;
if (mGraphicBuffer != 0) {
c += mGraphicBuffer->getFdCount();
}
if (mFence != 0) {
c += mFence->getFdCount();
}
return c;
}
status_t 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, mSlot);
FlattenableUtils::write(buffer, size, mIsDroppable);
FlattenableUtils::write(buffer, size, mAcquireCalled);
FlattenableUtils::write(buffer, size, mTransformToDisplayInverse);
return NO_ERROR;
}
status_t 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, mSlot);
FlattenableUtils::read(buffer, size, mIsDroppable);
FlattenableUtils::read(buffer, size, mAcquireCalled);
FlattenableUtils::read(buffer, size, mTransformToDisplayInverse);
return NO_ERROR;
}
const char* BufferItem::scalingModeName(uint32_t scalingMode) {
switch (scalingMode) {
case NATIVE_WINDOW_SCALING_MODE_FREEZE: return "FREEZE";
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: return "SCALE_TO_WINDOW";
case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: return "SCALE_CROP";
default: return "Unknown";
}
}
} // namespace android

View File

@ -0,0 +1,398 @@
/*
* 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 <gui/BufferItem.h>
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
#include <gui/IConsumerListener.h>
namespace android {
BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
mCore(core),
mSlots(core->mSlots),
mConsumerName() {}
BufferQueueConsumer::~BufferQueueConsumer() {}
status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
nsecs_t expectedPresent) {
ATRACE_CALL();
Mutex::Autolock lock(mCore->mMutex);
// Check that the consumer doesn't currently have the maximum number of
// buffers acquired. We allow the max buffer count to be exceeded by one
// buffer so that the consumer can successfully set up the newly acquired
// buffer before releasing the old one.
int numAcquiredBuffers = 0;
for (int s = 0; s < BufferQueueCore::NUM_BUFFER_SLOTS; ++s) {
if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
++numAcquiredBuffers;
}
}
if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
BQ_LOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)",
numAcquiredBuffers, mCore->mMaxAcquiredBufferCount);
return INVALID_OPERATION;
}
// Check if the 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 (mCore->mQueue.empty()) {
return NO_BUFFER_AVAILABLE;
}
BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
// 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 will be displayed
// on time or possibly late if we show it as soon as possible -- 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, it probably doesn't want frames to
// be discarded based on them.
while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) {
// If entry[1] is timely, drop entry[0] (and repeat). We apply an
// additional criterion 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 criterion: don't drop the
// earlier buffer if entry[1]'s fence hasn't signaled yet.
const BufferItem& bufferItem(mCore->mQueue[1]);
nsecs_t desiredPresent = bufferItem.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 the screen sooner.
BQ_LOGV("acquireBuffer: nodrop desire=%lld expect=%lld "
"(%lld) now=%lld", desiredPresent, expectedPresent,
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC));
break;
}
BQ_LOGV("acquireBuffer: drop desire=%lld expect=%lld size=%d",
desiredPresent, expectedPresent, mCore->mQueue.size());
if (mCore->stillTracking(front)) {
// Front buffer is still in mSlots, so mark the slot as free
mSlots[front->mSlot].mBufferState = BufferSlot::FREE;
}
mCore->mQueue.erase(front);
front = mCore->mQueue.begin();
}
// See if the front buffer is due
nsecs_t desiredPresent = front->mTimestamp;
if (desiredPresent > expectedPresent &&
desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) {
BQ_LOGV("acquireBuffer: defer desire=%lld expect=%lld "
"(%lld) now=%lld", desiredPresent, expectedPresent,
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC));
return PRESENT_LATER;
}
BQ_LOGV("acquireBuffer: accept desire=%lld expect=%lld "
"(%lld) now=%lld", desiredPresent, expectedPresent,
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC));
}
int slot = front->mSlot;
*outBuffer = *front;
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("acquireBuffer: acquiring { slot=%d/%llu buffer=%p }",
slot, front->mFrameNumber, front->mGraphicBuffer->handle);
// If the front buffer is still being tracked, update its slot state
if (mCore->stillTracking(front)) {
mSlots[slot].mAcquireCalled = true;
mSlots[slot].mNeedsCleanupOnRelease = false;
mSlots[slot].mBufferState = BufferSlot::ACQUIRED;
mSlots[slot].mFence = Fence::NO_FENCE;
}
// 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 (outBuffer->mAcquireCalled) {
outBuffer->mGraphicBuffer = NULL;
}
mCore->mQueue.erase(front);
// TODO: Should this call be after we free a slot while dropping buffers?
// Simply acquiring the next buffer doesn't enable a producer to dequeue.
mCore->mDequeueCondition.broadcast();
ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
return NO_ERROR;
}
status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
const sp<Fence>& releaseFence, EGLDisplay eglDisplay,
EGLSyncKHR eglFence) {
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
if (slot == BufferQueueCore::INVALID_BUFFER_SLOT || releaseFence == NULL) {
return BAD_VALUE;
}
Mutex::Autolock lock(mCore->mMutex);
// If the frame number has changed because the buffer has been reallocated,
// we can ignore this releaseBuffer for the old buffer
if (frameNumber != mSlots[slot].mFrameNumber) {
return STALE_BUFFER_SLOT;
}
// Make sure this buffer hasn't been queued while acquired by the consumer
BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
while (current != mCore->mQueue.end()) {
if (current->mSlot == slot) {
BQ_LOGE("releaseBuffer: buffer slot %d pending release is "
"currently queued", slot);
return -EINVAL;
}
++current;
}
if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
mSlots[slot].mEglDisplay = eglDisplay;
mSlots[slot].mEglFence = eglFence;
mSlots[slot].mFence = releaseFence;
mSlots[slot].mBufferState = BufferSlot::FREE;
} else if (mSlots[slot].mNeedsCleanupOnRelease) {
BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d "
"(state = %d)", slot, mSlots[slot].mBufferState);
mSlots[slot].mNeedsCleanupOnRelease = false;
return STALE_BUFFER_SLOT;
} else {
BQ_LOGV("releaseBuffer: attempted to release buffer slot %d "
"but its state was %d", slot, mSlots[slot].mBufferState);
return -EINVAL;
}
mCore->mDequeueCondition.broadcast();
return NO_ERROR;
}
status_t BufferQueueConsumer::connect(
const sp<IConsumerListener>& consumerListener, bool controlledByApp) {
ATRACE_CALL();
if (consumerListener == NULL) {
BQ_LOGE("connect(C): consumerListener may not be NULL");
return BAD_VALUE;
}
BQ_LOGV("connect(C): controlledByApp=%s",
controlledByApp ? "true" : "false");
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("connect(C): BufferQueue has been abandoned");
return NO_INIT;
}
mCore->mConsumerListener = consumerListener;
mCore->mConsumerControlledByApp = controlledByApp;
return NO_ERROR;
}
status_t BufferQueueConsumer::disconnect() {
ATRACE_CALL();
BQ_LOGV("disconnect(C)");
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mConsumerListener == NULL) {
BQ_LOGE("disconnect(C): no consumer is connected");
return -EINVAL;
}
mCore->mIsAbandoned = true;
mCore->mConsumerListener = NULL;
mCore->mQueue.clear();
mCore->freeAllBuffersLocked();
mCore->mDequeueCondition.broadcast();
return NO_ERROR;
}
status_t BufferQueueConsumer::getReleasedBuffers(uint32_t *outSlotMask) {
ATRACE_CALL();
if (outSlotMask == NULL) {
BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL");
return BAD_VALUE;
}
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("getReleasedBuffers: BufferQueue has been abandoned");
return NO_INIT;
}
uint32_t mask = 0;
for (int s = 0; s < BufferQueueCore::NUM_BUFFER_SLOTS; ++s) {
if (!mSlots[s].mAcquireCalled) {
mask |= (1u << s);
}
}
// Remove from the mask queued buffers for which acquire has been called,
// since the consumer will not receive their buffer addresses and so must
// retain their cached information
BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
while (current != mCore->mQueue.end()) {
if (current->mAcquireCalled) {
mask &= ~(1u << current->mSlot);
}
++current;
}
BQ_LOGV("getReleasedBuffers: returning mask %#x", mask);
*outSlotMask = mask;
return NO_ERROR;
}
status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width,
uint32_t height) {
ATRACE_CALL();
if (width == 0 || height == 0) {
BQ_LOGV("setDefaultBufferSize: dimensions cannot be 0 (width=%u "
"height=%u)", width, height);
return BAD_VALUE;
}
BQ_LOGV("setDefaultBufferSize: width=%u height=%u", width, height);
Mutex::Autolock lock(mCore->mMutex);
mCore->mDefaultWidth = width;
mCore->mDefaultHeight = height;
return NO_ERROR;
}
status_t BufferQueueConsumer::setDefaultMaxBufferCount(int bufferCount) {
ATRACE_CALL();
Mutex::Autolock lock(mCore->mMutex);
return mCore->setDefaultMaxBufferCountLocked(bufferCount);
}
status_t BufferQueueConsumer::disableAsyncBuffer() {
ATRACE_CALL();
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mConsumerListener != NULL) {
BQ_LOGE("disableAsyncBuffer: consumer already connected");
return INVALID_OPERATION;
}
BQ_LOGV("disableAsyncBuffer");
mCore->mUseAsyncBuffer = false;
return NO_ERROR;
}
status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
int maxAcquiredBuffers) {
ATRACE_CALL();
if (maxAcquiredBuffers < 1 ||
maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d",
maxAcquiredBuffers);
return BAD_VALUE;
}
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("setMaxAcquiredBufferCount: producer is already connected");
return INVALID_OPERATION;
}
BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
return NO_ERROR;
}
void BufferQueueConsumer::setConsumerName(const String8& name) {
ATRACE_CALL();
BQ_LOGV("setConsumerName: '%s'", name.string());
Mutex::Autolock lock(mCore->mMutex);
mCore->mConsumerName = name;
mConsumerName = name;
}
status_t BufferQueueConsumer::setDefaultBufferFormat(uint32_t defaultFormat) {
ATRACE_CALL();
BQ_LOGV("setDefaultBufferFormat: %u", defaultFormat);
Mutex::Autolock lock(mCore->mMutex);
mCore->mDefaultBufferFormat = defaultFormat;
return NO_ERROR;
}
status_t BufferQueueConsumer::setConsumerUsageBits(uint32_t usage) {
ATRACE_CALL();
BQ_LOGV("setConsumerUsageBits: %#x", usage);
Mutex::Autolock lock(mCore->mMutex);
mCore->mConsumerUsageBits = usage;
return NO_ERROR;
}
status_t BufferQueueConsumer::setTransformHint(uint32_t hint) {
ATRACE_CALL();
BQ_LOGV("setTransformHint: %#x", hint);
Mutex::Autolock lock(mCore->mMutex);
mCore->mTransformHint = hint;
return NO_ERROR;
}
void BufferQueueConsumer::dump(String8& result, const char* prefix) const {
mCore->dump(result, prefix);
}
} // namespace android

View File

@ -0,0 +1,209 @@
/*
* 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 EGL_EGLEXT_PROTOTYPES
#include <gui/BufferItem.h>
#include <gui/BufferQueueCore.h>
#include <gui/IConsumerListener.h>
template <typename T>
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<IGraphicBufferAlloc>& allocator) :
mAllocator(allocator),
mMutex(),
mIsAbandoned(false),
mConsumerControlledByApp(false),
mConsumerName(getUniqueName()),
mConsumerListener(),
mConsumerUsageBits(0),
mConnectedApi(NO_CONNECTED_API),
mConnectedProducerToken(),
mSlots(),
mQueue(),
mOverrideMaxBufferCount(0),
mDequeueCondition(),
mUseAsyncBuffer(true),
mDequeueBufferCannotBlock(false),
mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
mDefaultWidth(1),
mDefaultHeight(1),
mDefaultMaxBufferCount(2),
mMaxAcquiredBufferCount(1),
mBufferHasBeenQueued(false),
mFrameCounter(0),
mTransformHint(0) {}
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=%#llx, 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(%d)={%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 = 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<GraphicBuffer>& 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 < 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 > NUM_BUFFER_SLOTS) {
BQ_LOGV("setDefaultMaxBufferCount: invalid count %d, should be in "
"[%d, %d]", minBufferCount, 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);
mSlots[slot].mGraphicBuffer.clear();
if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
mSlots[slot].mNeedsCleanupOnRelease = true;
}
mSlots[slot].mBufferState = BufferSlot::FREE;
mSlots[slot].mFrameNumber = 0;
mSlots[slot].mAcquireCalled = false;
// 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;
}
void BufferQueueCore::freeAllBuffersLocked() {
mBufferHasBeenQueued = false;
for (int s = 0; s < 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/%llu buffer=%p } "
"slot { slot=%d/%llu 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);
}
} // namespace android

View File

@ -0,0 +1,705 @@
/*
* 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 EGL_EGLEXT_PROTOTYPES
#include <gui/BufferItem.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferAlloc.h>
#include <utils/Log.h>
#include <utils/Trace.h>
namespace android {
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) :
mCore(core),
mSlots(core->mSlots),
mConsumerName() {}
BufferQueueProducer::~BufferQueueProducer() {}
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* 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 >= BufferQueueCore::NUM_BUFFER_SLOTS) {
BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
slot, BufferQueueCore::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<IConsumerListener> listener;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("setBufferCount: BufferQueue has been abandoned");
return NO_INIT;
}
if (bufferCount > BufferQueueCore::NUM_BUFFER_SLOTS) {
BQ_LOGE("setBufferCount: bufferCount %d too large (max %d)",
bufferCount, BufferQueueCore::NUM_BUFFER_SLOTS);
return BAD_VALUE;
}
// There must be no dequeued buffers when changing the buffer count.
for (int s = 0; s < BufferQueueCore::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<android::Fence> *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 < BufferQueueCore::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<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
if ((buffer == NULL) ||
(static_cast<uint32_t>(buffer->width) != width) ||
(static_cast<uint32_t>(buffer->height) != height) ||
(static_cast<uint32_t>(buffer->format) != format) ||
((static_cast<uint32_t>(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> 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> fence;
input.deflate(&timestamp, &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<IConsumerListener> 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>& 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>& 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 >= BufferQueueCore::NUM_BUFFER_SLOTS) {
BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)",
slot, BufferQueueCore::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<android::IBinder> &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<IBinder::DeathRecipient*>(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<IConsumerListener> 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<IBinder> 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<IBinder::DeathRecipient*>(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<android::IBinder>& /* 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

15
libs/gui/BufferSlot.cpp Normal file
View File

@ -0,0 +1,15 @@
#include <gui/BufferSlot.h>
namespace android {
const char* BufferSlot::bufferStateName(BufferState state) {
switch (state) {
case BufferSlot::DEQUEUED: return "DEQUEUED";
case BufferSlot::QUEUED: return "QUEUED";
case BufferSlot::FREE: return "FREE";
case BufferSlot::ACQUIRED: return "ACQUIRED";
default: return "Unknown";
}
}
} // namespace android