Removed dependecies between BufferQueue and SurfaceTexture

Refactored SurfaceTexture and BufferQueue such that share
no protected members.  Created an consumer facing interface
for BufferQueue in preparation of connecting SurfaceTexture
and BufferQueue through a binder.

Change-Id: I938e63e085128148c58d0e26c7213b30145c109f
This commit is contained in:
Daniel Lam 2012-01-22 15:26:27 -08:00
parent e9c382839f
commit eae59d2ea7
4 changed files with 444 additions and 168 deletions

View File

@ -40,6 +40,7 @@ public:
};
enum { NUM_BUFFER_SLOTS = 32 };
enum { NO_CONNECTED_API = 0 };
enum { INVALID_BUFFER_SLOT = -1 };
struct FrameAvailableListener : public virtual RefBase {
// onFrameAvailable() is called from queueBuffer() each time an
@ -119,8 +120,91 @@ public:
// connected to the specified client API.
virtual status_t disconnect(int api);
protected:
// dump our state in a String
virtual void dump(String8& result) const;
virtual void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
// public facing structure for BufferSlot
struct BufferItem {
BufferItem()
:
mTransform(0),
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mTimestamp(0),
mFrameNumber(0),
mBuf(INVALID_BUFFER_SLOT) {
mCrop.makeInvalid();
}
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
// if no buffer has been allocated.
sp<GraphicBuffer> mGraphicBuffer;
// mCrop is the current crop rectangle for this buffer slot. This gets
// set to mNextCrop each time queueBuffer gets called for this buffer.
Rect mCrop;
// mTransform is the current transform flags for this buffer slot. This
// gets set to mNextTransform each time queueBuffer gets called for this
// slot.
uint32_t mTransform;
// mScalingMode is the current scaling mode for this buffer slot. This
// gets set to mNextScalingMode each time queueBuffer gets called for
// this slot.
uint32_t mScalingMode;
// mTimestamp is the current timestamp for this buffer slot. This gets
// to set by queueBuffer each time this slot is queued.
int64_t mTimestamp;
// mFrameNumber is the number of the queued frame for this slot.
uint64_t mFrameNumber;
// buf is the slot index of this buffer
int mBuf;
};
// The following public functions is the consumer facing interface
// acquire consumes a buffer by transferring its ownership to a consumer.
// buffer contains the GraphicBuffer and its corresponding information.
// buffer.mGraphicsBuffer will be NULL when the buffer has been already
// acquired by the consumer.
status_t acquire(BufferItem *buffer);
// releaseBuffer releases a buffer slot from the consumer back to the
// BufferQueue pending a fence sync.
status_t releaseBuffer(int buf, EGLDisplay display, EGLSyncKHR fence);
// consumerDisconnect disconnects a consumer from the BufferQueue. All
// buffers will be freed.
status_t consumerDisconnect();
// setDefaultBufferSize is used to set the size of buffers returned by
// requestBuffers when a with and height of zero is requested.
status_t setDefaultBufferSize(uint32_t w, uint32_t h);
// setBufferCountServer set the buffer count. If the client has requested
// a buffer count using setBufferCount, the server-buffer count will
// take effect once the client sets the count back to zero.
status_t setBufferCountServer(int bufferCount);
// isSynchronousMode returns whether the SurfaceTexture is currently in
// synchronous mode.
bool isSynchronousMode() const;
// setConsumerName sets the name used in logging
void setConsumerName(const String8& name);
// setFrameAvailableListener sets the listener object that will be notified
// when a new frame becomes available.
void setFrameAvailableListener(const sp<FrameAvailableListener>& listener);
private:
// freeBufferLocked frees the resources (both GraphicBuffer and EGLImage)
// for the given slot.
void freeBufferLocked(int index);
@ -145,20 +229,18 @@ protected:
status_t setBufferCountServerLocked(int bufferCount);
enum { INVALID_BUFFER_SLOT = -1 };
struct BufferSlot {
BufferSlot()
: mEglImage(EGL_NO_IMAGE_KHR),
mEglDisplay(EGL_NO_DISPLAY),
: mEglDisplay(EGL_NO_DISPLAY),
mBufferState(BufferSlot::FREE),
mRequestBufferCalled(false),
mTransform(0),
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mTimestamp(0),
mFrameNumber(0),
mFence(EGL_NO_SYNC_KHR) {
mFence(EGL_NO_SYNC_KHR),
mAcquireCalled(false) {
mCrop.makeInvalid();
}
@ -166,9 +248,6 @@ protected:
// if no buffer has been allocated.
sp<GraphicBuffer> mGraphicBuffer;
// mEglImage is the EGLImage created from mGraphicBuffer.
EGLImageKHR mEglImage;
// mEglDisplay is the EGLDisplay used to create mEglImage.
EGLDisplay mEglDisplay;
@ -178,6 +257,7 @@ protected:
// FREE indicates that the buffer is not currently being used and
// will not be used in the future until it gets dequeued and
// subsequently queued by the client.
// aka "owned by BufferQueue, ready to be dequeued"
FREE = 0,
// DEQUEUED indicates that the buffer has been dequeued by the
@ -190,6 +270,7 @@ protected:
// dequeued by the client. That means that the current buffer can
// be in either the DEQUEUED or QUEUED state. In asynchronous mode,
// however, the current buffer is always in the QUEUED state.
// aka "owned by producer, ready to be queued"
DEQUEUED = 1,
// QUEUED indicates that the buffer has been queued by the client,
@ -199,7 +280,11 @@ protected:
// the current buffer may be dequeued by the client under some
// circumstances. See the note about the current buffer in the
// documentation for DEQUEUED.
// aka "owned by BufferQueue, ready to be acquired"
QUEUED = 2,
// aka "owned by consumer, ready to be released"
ACQUIRED = 3
};
// mBufferState is the current state of this buffer slot.
@ -236,6 +321,9 @@ protected:
// to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
// on a compile-time option) set to a new sync object in updateTexImage.
EGLSyncKHR mFence;
// Indicates whether this buffer has been seen by a consumer yet
bool mAcquireCalled;
};
// mSlots is the array of buffer slots that must be mirrored on the client
@ -245,7 +333,6 @@ protected:
// for a slot when requestBuffer is called with that slot's index.
BufferSlot mSlots[NUM_BUFFER_SLOTS];
// mDefaultWidth holds the default width of allocated buffers. It is used
// in requestBuffers() if a width and height of zero is specified.
uint32_t mDefaultWidth;
@ -271,14 +358,6 @@ protected:
// mServerBufferCount buffer count requested by the server-side
int mServerBufferCount;
// mCurrentTexture is the buffer slot index of the buffer that is currently
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
// indicating that no buffer slot is currently bound to the texture. Note,
// however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
// that no buffer is bound to the texture. A call to setBufferCount will
// reset mCurrentTexture to INVALID_BUFFER_SLOT.
int mCurrentTexture;
// mNextCrop is the crop rectangle that will be used for the next buffer
// that gets queued. It is set by calling setCrop.
Rect mNextCrop;
@ -327,7 +406,7 @@ protected:
// mName is a string used to identify the BufferQueue in log messages.
// It is set by the setName method.
String8 mName;
String8 mConsumerName;
// mMutex is the mutex used to prevent concurrent access to the member
// variables of BufferQueue objects. It must be locked whenever the
@ -337,6 +416,8 @@ protected:
// mFrameCounter is the free running counter, incremented for every buffer queued
// with the surface Texture.
uint64_t mFrameCounter;
bool mBufferHasBeenQueued;
};
// ----------------------------------------------------------------------------

View File

@ -153,8 +153,8 @@ public:
void setName(const String8& name);
// dump our state in a String
void dump(String8& result) const;
void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
virtual void dump(String8& result) const;
virtual void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
protected:
@ -217,6 +217,56 @@ private:
// browser's tile cache exceeds.
const GLenum mTexTarget;
// SurfaceTexture maintains EGL information about GraphicBuffers that corresponds
// directly with BufferQueue's buffers
struct EGLSlot {
EGLSlot()
: mEglImage(EGL_NO_IMAGE_KHR),
mEglDisplay(EGL_NO_DISPLAY),
mFence(EGL_NO_SYNC_KHR) {
}
sp<GraphicBuffer> mGraphicBuffer;
// mEglImage is the EGLImage created from mGraphicBuffer.
EGLImageKHR mEglImage;
// mEglDisplay is the EGLDisplay used to create mEglImage.
EGLDisplay mEglDisplay;
// mFence 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 (optionally, based
// on a compile-time option) set to a new sync object in updateTexImage.
EGLSyncKHR mFence;
};
EGLSlot mEGLSlots[NUM_BUFFER_SLOTS];
// mAbandoned indicates that the BufferQueue will no longer be used to
// consume images buffers pushed to it using the ISurfaceTexture interface.
// It is initialized to false, and set to true in the abandon method. A
// BufferQueue that has been abandoned will return the NO_INIT error from
// all ISurfaceTexture methods capable of returning an error.
bool mAbandoned;
// mName is a string used to identify the SurfaceTexture in log messages.
// It can be set by the setName method.
String8 mName;
// mMutex is the mutex used to prevent concurrent access to the member
// variables of SurfaceTexture objects. It must be locked whenever the
// member variables are accessed.
mutable Mutex mMutex;
// mCurrentTexture is the buffer slot index of the buffer that is currently
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
// indicating that no buffer slot is currently bound to the texture. Note,
// however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
// that no buffer is bound to the texture. A call to setBufferCount will
// reset mCurrentTexture to INVALID_BUFFER_SLOT.
int mCurrentTexture;
};
// ----------------------------------------------------------------------------

View File

@ -15,6 +15,7 @@
*/
#define LOG_TAG "BufferQueue"
//#define LOG_NDEBUG 0
#define GL_GLEXT_PROTOTYPES
#define EGL_EGLEXT_PROTOTYPES
@ -27,6 +28,7 @@
#include <private/gui/ComposerService.h>
#include <utils/Log.h>
#include <gui/SurfaceTexture.h>
// This compile option causes SurfaceTexture to return the buffer that is currently
// attached to the GL texture from dequeueBuffer when no other buffers are
@ -34,6 +36,10 @@
// implicit cross-process synchronization to prevent the buffer from being
// written to before the buffer has (a) been detached from the GL texture and
// (b) all GL reads from the buffer have completed.
// During refactoring, do not support dequeuing the current buffer
#undef ALLOW_DEQUEUE_CURRENT_BUFFER
#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true
#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled"
@ -42,11 +48,11 @@
#endif
// Macros for including the BufferQueue name in log messages
#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGV(x, ...) ALOGV("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define ST_LOGD(x, ...) ALOGD("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define ST_LOGI(x, ...) ALOGI("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define ST_LOGW(x, ...) ALOGW("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define ST_LOGE(x, ...) ALOGE("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
namespace android {
@ -63,17 +69,17 @@ BufferQueue::BufferQueue( bool allowSynchronousMode ) :
mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
mClientBufferCount(0),
mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
mCurrentTexture(INVALID_BUFFER_SLOT),
mNextTransform(0),
mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mSynchronousMode(false),
mAllowSynchronousMode(allowSynchronousMode),
mConnectedApi(NO_CONNECTED_API),
mAbandoned(false),
mFrameCounter(0)
mFrameCounter(0),
mBufferHasBeenQueued(false)
{
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
mConsumerName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
ST_LOGV("BufferQueue");
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
@ -119,6 +125,23 @@ status_t BufferQueue::setBufferCountServerLocked(int bufferCount) {
return OK;
}
bool BufferQueue::isSynchronousMode() const {
Mutex::Autolock lock(mMutex);
return mSynchronousMode;
}
void BufferQueue::setConsumerName(const String8& name) {
Mutex::Autolock lock(mMutex);
mConsumerName = name;
}
void BufferQueue::setFrameAvailableListener(
const sp<FrameAvailableListener>& listener) {
ST_LOGV("setFrameAvailableListener");
Mutex::Autolock lock(mMutex);
mFrameAvailableListener = listener;
}
status_t BufferQueue::setBufferCount(int bufferCount) {
ST_LOGV("setBufferCount: count=%d", bufferCount);
Mutex::Autolock lock(mMutex);
@ -160,7 +183,7 @@ status_t BufferQueue::setBufferCount(int bufferCount) {
freeAllBuffersLocked();
mBufferCount = bufferCount;
mClientBufferCount = bufferCount;
mCurrentTexture = INVALID_BUFFER_SLOT;
mBufferHasBeenQueued = false;
mQueue.clear();
mDequeueCondition.signal();
return OK;
@ -276,7 +299,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
mBufferCount = mServerBufferCount;
if (mBufferCount < minBufferCountNeeded)
mBufferCount = minBufferCountNeeded;
mCurrentTexture = INVALID_BUFFER_SLOT;
mBufferHasBeenQueued = false;
returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
}
@ -290,19 +313,12 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
dequeuedCount++;
}
// if buffer is FREE it CANNOT be current
ALOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
"dequeueBuffer: buffer %d is both FREE and current!",
i);
if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
if (state == BufferSlot::FREE || i == mCurrentTexture) {
foundSync = i;
if (i != mCurrentTexture) {
found = i;
break;
}
}
// this logic used to be if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER)
// but dequeuing the current buffer is disabled.
if (false) {
// This functionality has been temporarily removed so
// BufferQueue and SurfaceTexture can be refactored into
// separate objects
} else {
if (state == BufferSlot::FREE) {
/* We return the oldest of the free buffers to avoid
@ -331,8 +347,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
// See whether a buffer has been queued since the last
// setBufferCount so we know whether to perform the
// MIN_UNDEQUEUED_BUFFERS check below.
bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT;
if (bufferHasBeenQueued) {
if (mBufferHasBeenQueued) {
// make sure the client is not trying to dequeue more buffers
// than allowed.
const int avail = mBufferCount - (dequeuedCount+1);
@ -404,27 +419,23 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
if (updateFormat) {
mPixelFormat = format;
}
mSlots[buf].mAcquireCalled = false;
mSlots[buf].mGraphicBuffer = graphicBuffer;
mSlots[buf].mRequestBufferCalled = false;
mSlots[buf].mFence = EGL_NO_SYNC_KHR;
if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mSlots[buf].mEglDisplay,
mSlots[buf].mEglImage);
mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
}
if (mCurrentTexture == buf) {
// The current texture no longer references the buffer in this slot
// since we just allocated a new buffer.
mCurrentTexture = INVALID_BUFFER_SLOT;
}
mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
}
dpy = mSlots[buf].mEglDisplay;
fence = mSlots[buf].mFence;
mSlots[buf].mFence = EGL_NO_SYNC_KHR;
}
} // end lock scope
if (fence != EGL_NO_SYNC_KHR) {
EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
@ -437,6 +448,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
ALOGE("dequeueBuffer: timeout waiting for fence");
}
eglDestroySyncKHR(dpy, fence);
}
ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf,
@ -496,9 +508,6 @@ status_t BufferQueue::queueBuffer(int buf, int64_t timestamp,
ST_LOGE("queueBuffer: slot %d is not owned by the client "
"(state=%d)", buf, mSlots[buf].mBufferState);
return -EINVAL;
} else if (buf == mCurrentTexture) {
ST_LOGE("queueBuffer: slot %d is current!", buf);
return -EINVAL;
} else if (!mSlots[buf].mRequestBufferCalled) {
ST_LOGE("queueBuffer: slot %d was enqueued without requesting a "
"buffer", buf);
@ -538,6 +547,7 @@ status_t BufferQueue::queueBuffer(int buf, int64_t timestamp,
mFrameCounter++;
mSlots[buf].mFrameNumber = mFrameCounter;
mBufferHasBeenQueued = true;
mDequeueCondition.signal();
*outWidth = mDefaultWidth;
@ -647,6 +657,9 @@ status_t BufferQueue::connect(int api,
err = -EINVAL;
break;
}
mBufferHasBeenQueued = false;
return err;
}
@ -687,26 +700,185 @@ status_t BufferQueue::disconnect(int api) {
return err;
}
void BufferQueue::dump(String8& result) const
{
char buffer[1024];
BufferQueue::dump(result, "", buffer, 1024);
}
void BufferQueue::dump(String8& result, const char* prefix,
char* buffer, size_t SIZE) const
{
Mutex::Autolock _l(mMutex);
snprintf(buffer, SIZE,
"%snext : {crop=[%d,%d,%d,%d], transform=0x%02x}\n"
,prefix, mNextCrop.left, mNextCrop.top, mNextCrop.right,
mNextCrop.bottom, mNextTransform
);
result.append(buffer);
String8 fifo;
int fifoSize = 0;
Fifo::const_iterator i(mQueue.begin());
while (i != mQueue.end()) {
snprintf(buffer, SIZE, "%02d ", *i++);
fifoSize++;
fifo.append(buffer);
}
snprintf(buffer, SIZE,
"%s-BufferQueue mBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], "
"mPixelFormat=%d, FIFO(%d)={%s}\n",
prefix, mBufferCount, mSynchronousMode, mDefaultWidth,
mDefaultHeight, mPixelFormat, fifoSize, fifo.string());
result.append(buffer);
struct {
const char * operator()(int state) const {
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";
}
}
} stateName;
for (int i=0 ; i<mBufferCount ; i++) {
const BufferSlot& slot(mSlots[i]);
snprintf(buffer, SIZE,
"%s%s[%02d] "
"state=%-8s, crop=[%d,%d,%d,%d], "
"transform=0x%02x, timestamp=%lld",
prefix, (slot.mBufferState == BufferSlot::ACQUIRED)?">":" ", i,
stateName(slot.mBufferState),
slot.mCrop.left, slot.mCrop.top, slot.mCrop.right,
slot.mCrop.bottom, slot.mTransform, slot.mTimestamp
);
result.append(buffer);
const sp<GraphicBuffer>& buf(slot.mGraphicBuffer);
if (buf != NULL) {
snprintf(buffer, SIZE,
", %p [%4ux%4u:%4u,%3X]",
buf->handle, buf->width, buf->height, buf->stride,
buf->format);
result.append(buffer);
}
result.append("\n");
}
}
void BufferQueue::freeBufferLocked(int i) {
mSlots[i].mGraphicBuffer = 0;
mSlots[i].mBufferState = BufferSlot::FREE;
mSlots[i].mFrameNumber = 0;
if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
mSlots[i].mAcquireCalled = false;
// destroy fence as BufferQueue now takes ownership
if (mSlots[i].mFence != EGL_NO_SYNC_KHR) {
eglDestroySyncKHR(mSlots[i].mEglDisplay, mSlots[i].mFence);
mSlots[i].mFence = EGL_NO_SYNC_KHR;
}
}
void BufferQueue::freeAllBuffersLocked() {
ALOGW_IF(!mQueue.isEmpty(),
"freeAllBuffersLocked called but mQueue is not empty");
mCurrentTexture = INVALID_BUFFER_SLOT;
mQueue.clear();
mBufferHasBeenQueued = false;
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
freeBufferLocked(i);
}
}
status_t BufferQueue::acquire(BufferItem *buffer) {
Mutex::Autolock _l(mMutex);
// check if 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 (!mQueue.empty()) {
Fifo::iterator front(mQueue.begin());
int buf = *front;
if (mSlots[buf].mAcquireCalled) {
buffer->mGraphicBuffer = NULL;
}
else {
buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer;
}
buffer->mCrop = mSlots[buf].mCrop;
buffer->mTransform = mSlots[buf].mTransform;
buffer->mScalingMode = mSlots[buf].mScalingMode;
buffer->mFrameNumber = mSlots[buf].mFrameNumber;
buffer->mBuf = buf;
mSlots[buf].mAcquireCalled = true;
mSlots[buf].mBufferState = BufferSlot::ACQUIRED;
mQueue.erase(front);
}
else {
return -EINVAL; //should be a better return code
}
return OK;
}
status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display,
EGLSyncKHR fence) {
Mutex::Autolock _l(mMutex);
if (buf == INVALID_BUFFER_SLOT) {
return -EINVAL;
}
mSlots[buf].mEglDisplay = display;
mSlots[buf].mFence = fence;
// The current buffer becomes FREE if it was still in the queued
// state. If it has already been given to the client
// (synchronous mode), then it stays in DEQUEUED state.
if (mSlots[buf].mBufferState == BufferSlot::QUEUED
|| mSlots[buf].mBufferState == BufferSlot::ACQUIRED) {
mSlots[buf].mBufferState = BufferSlot::FREE;
}
mDequeueCondition.signal();
return OK;
}
status_t BufferQueue::consumerDisconnect() {
Mutex::Autolock lock(mMutex);
// Once the SurfaceTexture disconnects, the BufferQueue
// is considered abandoned
mAbandoned = true;
freeAllBuffersLocked();
mDequeueCondition.signal();
return OK;
}
status_t BufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h)
{
ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
if (!w || !h) {
ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)",
w, h);
return BAD_VALUE;
}
Mutex::Autolock lock(mMutex);
mDefaultWidth = w;
mDefaultHeight = h;
return OK;
}
status_t BufferQueue::setBufferCountServer(int bufferCount) {
Mutex::Autolock lock(mMutex);
return setBufferCountServerLocked(bufferCount);
}
void BufferQueue::freeAllBuffersExceptHeadLocked() {
ALOGW_IF(!mQueue.isEmpty(),
"freeAllBuffersExceptCurrentLocked called but mQueue is not empty");
@ -715,7 +887,7 @@ void BufferQueue::freeAllBuffersExceptHeadLocked() {
Fifo::iterator front(mQueue.begin());
head = *front;
}
mCurrentTexture = INVALID_BUFFER_SLOT;
mBufferHasBeenQueued = false;
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
if (i != head) {
freeBufferLocked(i);

View File

@ -96,6 +96,11 @@ static float mtxRot270[16] = {
static void mtxMul(float out[16], const float a[16], const float b[16]);
// Get an ID that's unique within this process.
static int32_t createProcessUniqueId() {
static volatile int32_t globalCounter = 0;
return android_atomic_inc(&globalCounter);
}
SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
GLenum texTarget, bool useFenceSync) :
@ -108,8 +113,13 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
#else
mUseFenceSync(false),
#endif
mTexTarget(texTarget)
mTexTarget(texTarget),
mAbandoned(false),
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT)
{
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
BufferQueue::setConsumerName(mName);
ST_LOGV("SurfaceTexture");
memcpy(mCurrentTransformMatrix, mtxIdentity,
@ -118,28 +128,18 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
SurfaceTexture::~SurfaceTexture() {
ST_LOGV("~SurfaceTexture");
freeAllBuffersLocked();
abandon();
}
status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
Mutex::Autolock lock(mMutex);
return setBufferCountServerLocked(bufferCount);
return BufferQueue::setBufferCountServer(bufferCount);
}
status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
{
ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
if (!w || !h) {
ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)",
w, h);
return BAD_VALUE;
}
Mutex::Autolock lock(mMutex);
mDefaultWidth = w;
mDefaultHeight = h;
return OK;
return BufferQueue::setDefaultBufferSize(w, h);
}
status_t SurfaceTexture::updateTexImage() {
@ -151,23 +151,35 @@ status_t SurfaceTexture::updateTexImage() {
return NO_INIT;
}
BufferItem item;
// In asynchronous mode the list is guaranteed to be one buffer
// deep, while in synchronous mode we use the oldest buffer.
if (!mQueue.empty()) {
Fifo::iterator front(mQueue.begin());
int buf = *front;
if (acquire(&item) == NO_ERROR) {
int buf = item.mBuf;
// This buffer was newly allocated, so we need to clean up on our side
if (item.mGraphicBuffer != NULL) {
mEGLSlots[buf].mGraphicBuffer = 0;
if (mEGLSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mEGLSlots[buf].mEglDisplay,
mEGLSlots[buf].mEglImage);
mEGLSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
mEGLSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
}
mEGLSlots[buf].mGraphicBuffer = item.mGraphicBuffer;
}
// Update the GL texture object.
EGLImageKHR image = mSlots[buf].mEglImage;
EGLImageKHR image = mEGLSlots[buf].mEglImage;
EGLDisplay dpy = eglGetCurrentDisplay();
if (image == EGL_NO_IMAGE_KHR) {
if (mSlots[buf].mGraphicBuffer == 0) {
if (item.mGraphicBuffer == 0) {
ST_LOGE("buffer at slot %d is null", buf);
return BAD_VALUE;
}
image = createImage(dpy, mSlots[buf].mGraphicBuffer);
mSlots[buf].mEglImage = image;
mSlots[buf].mEglDisplay = dpy;
image = createImage(dpy, item.mGraphicBuffer);
mEGLSlots[buf].mEglImage = image;
mEGLSlots[buf].mEglDisplay = dpy;
if (image == EGL_NO_IMAGE_KHR) {
// NOTE: if dpy was invalid, createImage() is guaranteed to
// fail. so we'd end up here.
@ -190,6 +202,8 @@ status_t SurfaceTexture::updateTexImage() {
failed = true;
}
if (failed) {
releaseBuffer(buf, mEGLSlots[buf].mEglDisplay,
mEGLSlots[buf].mFence);
return -EINVAL;
}
@ -200,40 +214,37 @@ status_t SurfaceTexture::updateTexImage() {
if (fence == EGL_NO_SYNC_KHR) {
ALOGE("updateTexImage: error creating fence: %#x",
eglGetError());
releaseBuffer(buf, mEGLSlots[buf].mEglDisplay,
mEGLSlots[buf].mFence);
return -EINVAL;
}
glFlush();
mSlots[mCurrentTexture].mFence = fence;
mEGLSlots[mCurrentTexture].mFence = fence;
}
}
ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
mCurrentTexture,
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
buf, mSlots[buf].mGraphicBuffer->handle);
buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0);
if (mCurrentTexture != INVALID_BUFFER_SLOT) {
// The current buffer becomes FREE if it was still in the queued
// state. If it has already been given to the client
// (synchronous mode), then it stays in DEQUEUED state.
if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) {
mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE;
}
}
// release old buffer
releaseBuffer(mCurrentTexture,
mEGLSlots[mCurrentTexture].mEglDisplay,
mEGLSlots[mCurrentTexture].mFence);
// Update the SurfaceTexture state.
mCurrentTexture = buf;
mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
mCurrentCrop = mSlots[buf].mCrop;
mCurrentTransform = mSlots[buf].mTransform;
mCurrentScalingMode = mSlots[buf].mScalingMode;
mCurrentTimestamp = mSlots[buf].mTimestamp;
mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer;
mCurrentCrop = item.mCrop;
mCurrentTransform = item.mTransform;
mCurrentScalingMode = item.mScalingMode;
mCurrentTimestamp = item.mTimestamp;
computeCurrentTransformMatrix();
// Now that we've passed the point at which failures can happen,
// it's safe to remove the buffer from the front of the queue.
mQueue.erase(front);
mDequeueCondition.signal();
} else {
// We always bind the texture even if we don't update its contents.
glBindTexture(mTexTarget, mTexName);
@ -299,7 +310,7 @@ void SurfaceTexture::computeCurrentTransformMatrix() {
}
}
sp<GraphicBuffer>& buf(mSlots[mCurrentTexture].mGraphicBuffer);
sp<GraphicBuffer>& buf(mCurrentTextureBuf);
float tx, ty, sx, sy;
if (!mCurrentCrop.isEmpty()) {
// In order to prevent bilinear sampling at the of the crop rectangle we
@ -371,7 +382,7 @@ void SurfaceTexture::setFrameAvailableListener(
const sp<FrameAvailableListener>& listener) {
ST_LOGV("setFrameAvailableListener");
Mutex::Autolock lock(mMutex);
mFrameAvailableListener = listener;
BufferQueue::setFrameAvailableListener(listener);
}
EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
@ -412,22 +423,33 @@ uint32_t SurfaceTexture::getCurrentScalingMode() const {
bool SurfaceTexture::isSynchronousMode() const {
Mutex::Autolock lock(mMutex);
return mSynchronousMode;
return BufferQueue::isSynchronousMode();
}
void SurfaceTexture::abandon() {
Mutex::Autolock lock(mMutex);
mQueue.clear();
mAbandoned = true;
mCurrentTextureBuf.clear();
freeAllBuffersLocked();
mDequeueCondition.signal();
// destroy all egl buffers
for (int i =0; i < NUM_BUFFER_SLOTS; i++) {
mEGLSlots[i].mGraphicBuffer = 0;
if (mEGLSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mEGLSlots[i].mEglDisplay,
mEGLSlots[i].mEglImage);
mEGLSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
mEGLSlots[i].mEglDisplay = EGL_NO_DISPLAY;
}
}
// disconnect from the BufferQueue
BufferQueue::consumerDisconnect();
}
void SurfaceTexture::setName(const String8& name) {
Mutex::Autolock _l(mMutex);
mName = name;
BufferQueue::setConsumerName(name);
}
void SurfaceTexture::dump(String8& result) const
@ -440,68 +462,19 @@ void SurfaceTexture::dump(String8& result, const char* prefix,
char* buffer, size_t SIZE) const
{
Mutex::Autolock _l(mMutex);
snprintf(buffer, SIZE,
"%smBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], "
"mPixelFormat=%d, mTexName=%d\n",
prefix, mBufferCount, mSynchronousMode, mDefaultWidth,
mDefaultHeight, mPixelFormat, mTexName);
snprintf(buffer, SIZE, "%smTexName=%d\n", prefix, mTexName);
result.append(buffer);
String8 fifo;
int fifoSize = 0;
Fifo::const_iterator i(mQueue.begin());
while (i != mQueue.end()) {
snprintf(buffer, SIZE, "%02d ", *i++);
fifoSize++;
fifo.append(buffer);
}
snprintf(buffer, SIZE,
"%scurrent: {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d}\n"
"%snext : {crop=[%d,%d,%d,%d], transform=0x%02x, FIFO(%d)={%s}}\n"
,
prefix, mCurrentCrop.left,
"%snext : {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d}\n"
,prefix, mCurrentCrop.left,
mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
mCurrentTransform, mCurrentTexture,
prefix, mNextCrop.left, mNextCrop.top, mNextCrop.right,
mNextCrop.bottom, mNextTransform, fifoSize, fifo.string()
mCurrentTransform, mCurrentTexture
);
result.append(buffer);
struct {
const char * operator()(int state) const {
switch (state) {
case BufferSlot::DEQUEUED: return "DEQUEUED";
case BufferSlot::QUEUED: return "QUEUED";
case BufferSlot::FREE: return "FREE";
default: return "Unknown";
}
}
} stateName;
for (int i=0 ; i<mBufferCount ; i++) {
const BufferSlot& slot(mSlots[i]);
snprintf(buffer, SIZE,
"%s%s[%02d] "
"state=%-8s, crop=[%d,%d,%d,%d], "
"transform=0x%02x, timestamp=%lld",
prefix, (i==mCurrentTexture)?">":" ", i,
stateName(slot.mBufferState),
slot.mCrop.left, slot.mCrop.top, slot.mCrop.right,
slot.mCrop.bottom, slot.mTransform, slot.mTimestamp
);
result.append(buffer);
const sp<GraphicBuffer>& buf(slot.mGraphicBuffer);
if (buf != NULL) {
snprintf(buffer, SIZE,
", %p [%4ux%4u:%4u,%3X]",
buf->handle, buf->width, buf->height, buf->stride,
buf->format);
result.append(buffer);
}
result.append("\n");
}
BufferQueue::dump(result, prefix, buffer, SIZE);
}
static void mtxMul(float out[16], const float a[16], const float b[16]) {