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: Iff55e740e36a7f70c9f7a17ee7a5af38e3d21f0f
This commit is contained in:
Daniel Lam 2012-01-22 15:26:27 -08:00
parent 3ef8d80ee4
commit 3330238841
4 changed files with 437 additions and 167 deletions

View File

@ -40,6 +40,7 @@ public:
}; };
enum { NUM_BUFFER_SLOTS = 32 }; enum { NUM_BUFFER_SLOTS = 32 };
enum { NO_CONNECTED_API = 0 }; enum { NO_CONNECTED_API = 0 };
enum { INVALID_BUFFER_SLOT = -1 };
struct FrameAvailableListener : public virtual RefBase { struct FrameAvailableListener : public virtual RefBase {
// onFrameAvailable() is called from queueBuffer() each time an // onFrameAvailable() is called from queueBuffer() each time an
@ -119,8 +120,91 @@ public:
// connected to the specified client API. // connected to the specified client API.
virtual status_t disconnect(int 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) // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage)
// for the given slot. // for the given slot.
void freeBufferLocked(int index); void freeBufferLocked(int index);
@ -145,20 +229,18 @@ protected:
status_t setBufferCountServerLocked(int bufferCount); status_t setBufferCountServerLocked(int bufferCount);
enum { INVALID_BUFFER_SLOT = -1 };
struct BufferSlot { struct BufferSlot {
BufferSlot() BufferSlot()
: mEglImage(EGL_NO_IMAGE_KHR), : mEglDisplay(EGL_NO_DISPLAY),
mEglDisplay(EGL_NO_DISPLAY),
mBufferState(BufferSlot::FREE), mBufferState(BufferSlot::FREE),
mRequestBufferCalled(false), mRequestBufferCalled(false),
mTransform(0), mTransform(0),
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mTimestamp(0), mTimestamp(0),
mFrameNumber(0), mFrameNumber(0),
mFence(EGL_NO_SYNC_KHR) { mFence(EGL_NO_SYNC_KHR),
mAcquireCalled(false) {
mCrop.makeInvalid(); mCrop.makeInvalid();
} }
@ -166,9 +248,6 @@ protected:
// if no buffer has been allocated. // if no buffer has been allocated.
sp<GraphicBuffer> mGraphicBuffer; sp<GraphicBuffer> mGraphicBuffer;
// mEglImage is the EGLImage created from mGraphicBuffer.
EGLImageKHR mEglImage;
// mEglDisplay is the EGLDisplay used to create mEglImage. // mEglDisplay is the EGLDisplay used to create mEglImage.
EGLDisplay mEglDisplay; EGLDisplay mEglDisplay;
@ -178,6 +257,7 @@ protected:
// FREE indicates that the buffer is not currently being used and // FREE indicates that the buffer is not currently being used and
// will not be used in the future until it gets dequeued and // will not be used in the future until it gets dequeued and
// subsequently queued by the client. // subsequently queued by the client.
// aka "owned by BufferQueue, ready to be dequeued"
FREE = 0, FREE = 0,
// DEQUEUED indicates that the buffer has been dequeued by the // 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 // dequeued by the client. That means that the current buffer can
// be in either the DEQUEUED or QUEUED state. In asynchronous mode, // be in either the DEQUEUED or QUEUED state. In asynchronous mode,
// however, the current buffer is always in the QUEUED state. // however, the current buffer is always in the QUEUED state.
// aka "owned by producer, ready to be queued"
DEQUEUED = 1, DEQUEUED = 1,
// QUEUED indicates that the buffer has been queued by the client, // 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 // the current buffer may be dequeued by the client under some
// circumstances. See the note about the current buffer in the // circumstances. See the note about the current buffer in the
// documentation for DEQUEUED. // documentation for DEQUEUED.
// aka "owned by BufferQueue, ready to be acquired"
QUEUED = 2, QUEUED = 2,
// aka "owned by consumer, ready to be released"
ACQUIRED = 3
}; };
// mBufferState is the current state of this buffer slot. // 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 // 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. // on a compile-time option) set to a new sync object in updateTexImage.
EGLSyncKHR mFence; 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 // 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. // for a slot when requestBuffer is called with that slot's index.
BufferSlot mSlots[NUM_BUFFER_SLOTS]; BufferSlot mSlots[NUM_BUFFER_SLOTS];
// mDefaultWidth holds the default width of allocated buffers. It is used // mDefaultWidth holds the default width of allocated buffers. It is used
// in requestBuffers() if a width and height of zero is specified. // in requestBuffers() if a width and height of zero is specified.
uint32_t mDefaultWidth; uint32_t mDefaultWidth;
@ -271,14 +358,6 @@ protected:
// mServerBufferCount buffer count requested by the server-side // mServerBufferCount buffer count requested by the server-side
int mServerBufferCount; 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 // mNextCrop is the crop rectangle that will be used for the next buffer
// that gets queued. It is set by calling setCrop. // that gets queued. It is set by calling setCrop.
Rect mNextCrop; Rect mNextCrop;
@ -327,7 +406,7 @@ protected:
// mName is a string used to identify the BufferQueue in log messages. // mName is a string used to identify the BufferQueue in log messages.
// It is set by the setName method. // It is set by the setName method.
String8 mName; String8 mConsumerName;
// mMutex is the mutex used to prevent concurrent access to the member // mMutex is the mutex used to prevent concurrent access to the member
// variables of BufferQueue objects. It must be locked whenever the // 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 // mFrameCounter is the free running counter, incremented for every buffer queued
// with the surface Texture. // with the surface Texture.
uint64_t mFrameCounter; uint64_t mFrameCounter;
bool mBufferHasBeenQueued;
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -153,8 +153,8 @@ public:
void setName(const String8& name); void setName(const String8& name);
// dump our state in a String // dump our state in a String
void dump(String8& result) const; virtual void dump(String8& result) const;
void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const; virtual void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
protected: protected:
@ -217,6 +217,56 @@ private:
// browser's tile cache exceeds. // browser's tile cache exceeds.
const GLenum mTexTarget; 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_TAG "BufferQueue"
//#define LOG_NDEBUG 0
#define GL_GLEXT_PROTOTYPES #define GL_GLEXT_PROTOTYPES
#define EGL_EGLEXT_PROTOTYPES #define EGL_EGLEXT_PROTOTYPES
@ -27,6 +28,7 @@
#include <surfaceflinger/ISurfaceComposer.h> #include <surfaceflinger/ISurfaceComposer.h>
#include <utils/Log.h> #include <utils/Log.h>
#include <gui/SurfaceTexture.h>
// This compile option causes SurfaceTexture to return the buffer that is currently // This compile option causes SurfaceTexture to return the buffer that is currently
// attached to the GL texture from dequeueBuffer when no other buffers are // attached to the GL texture from dequeueBuffer when no other buffers are
@ -42,11 +44,11 @@
#endif #endif
// Macros for including the BufferQueue name in log messages // Macros for including the BufferQueue name in log messages
#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) #define ST_LOGV(x, ...) ALOGV("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) #define ST_LOGD(x, ...) ALOGD("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) #define ST_LOGI(x, ...) ALOGI("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) #define ST_LOGW(x, ...) ALOGW("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) #define ST_LOGE(x, ...) ALOGE("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
namespace android { namespace android {
@ -63,17 +65,17 @@ BufferQueue::BufferQueue( bool allowSynchronousMode ) :
mBufferCount(MIN_ASYNC_BUFFER_SLOTS), mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
mClientBufferCount(0), mClientBufferCount(0),
mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS), mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
mCurrentTexture(INVALID_BUFFER_SLOT),
mNextTransform(0), mNextTransform(0),
mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mSynchronousMode(false), mSynchronousMode(false),
mAllowSynchronousMode(allowSynchronousMode), mAllowSynchronousMode(allowSynchronousMode),
mConnectedApi(NO_CONNECTED_API), mConnectedApi(NO_CONNECTED_API),
mAbandoned(false), mAbandoned(false),
mFrameCounter(0) mFrameCounter(0),
mBufferHasBeenQueued(false)
{ {
// Choose a name using the PID and a process-unique ID. // 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"); ST_LOGV("BufferQueue");
sp<ISurfaceComposer> composer(ComposerService::getComposerService()); sp<ISurfaceComposer> composer(ComposerService::getComposerService());
@ -119,6 +121,23 @@ status_t BufferQueue::setBufferCountServerLocked(int bufferCount) {
return OK; 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) { status_t BufferQueue::setBufferCount(int bufferCount) {
ST_LOGV("setBufferCount: count=%d", bufferCount); ST_LOGV("setBufferCount: count=%d", bufferCount);
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
@ -160,7 +179,7 @@ status_t BufferQueue::setBufferCount(int bufferCount) {
freeAllBuffersLocked(); freeAllBuffersLocked();
mBufferCount = bufferCount; mBufferCount = bufferCount;
mClientBufferCount = bufferCount; mClientBufferCount = bufferCount;
mCurrentTexture = INVALID_BUFFER_SLOT; mBufferHasBeenQueued = false;
mQueue.clear(); mQueue.clear();
mDequeueCondition.signal(); mDequeueCondition.signal();
return OK; return OK;
@ -276,7 +295,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
mBufferCount = mServerBufferCount; mBufferCount = mServerBufferCount;
if (mBufferCount < minBufferCountNeeded) if (mBufferCount < minBufferCountNeeded)
mBufferCount = minBufferCountNeeded; mBufferCount = minBufferCountNeeded;
mCurrentTexture = INVALID_BUFFER_SLOT; mBufferHasBeenQueued = false;
returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
} }
@ -290,19 +309,10 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
dequeuedCount++; 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 (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
if (state == BufferSlot::FREE || i == mCurrentTexture) { // This functionality has been temporarily removed so
foundSync = i; // BufferQueue and SurfaceTexture can be refactored into
if (i != mCurrentTexture) { // separate objects
found = i;
break;
}
}
} else { } else {
if (state == BufferSlot::FREE) { if (state == BufferSlot::FREE) {
/* We return the oldest of the free buffers to avoid /* We return the oldest of the free buffers to avoid
@ -331,8 +341,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
// See whether a buffer has been queued since the last // See whether a buffer has been queued since the last
// setBufferCount so we know whether to perform the // setBufferCount so we know whether to perform the
// MIN_UNDEQUEUED_BUFFERS check below. // MIN_UNDEQUEUED_BUFFERS check below.
bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; if (mBufferHasBeenQueued) {
if (bufferHasBeenQueued) {
// make sure the client is not trying to dequeue more buffers // make sure the client is not trying to dequeue more buffers
// than allowed. // than allowed.
const int avail = mBufferCount - (dequeuedCount+1); const int avail = mBufferCount - (dequeuedCount+1);
@ -404,27 +413,23 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
if (updateFormat) { if (updateFormat) {
mPixelFormat = format; mPixelFormat = format;
} }
mSlots[buf].mAcquireCalled = false;
mSlots[buf].mGraphicBuffer = graphicBuffer; mSlots[buf].mGraphicBuffer = graphicBuffer;
mSlots[buf].mRequestBufferCalled = false; mSlots[buf].mRequestBufferCalled = false;
mSlots[buf].mFence = EGL_NO_SYNC_KHR; mSlots[buf].mFence = EGL_NO_SYNC_KHR;
if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
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;
}
returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
} }
dpy = mSlots[buf].mEglDisplay; dpy = mSlots[buf].mEglDisplay;
fence = mSlots[buf].mFence; fence = mSlots[buf].mFence;
mSlots[buf].mFence = EGL_NO_SYNC_KHR; mSlots[buf].mFence = EGL_NO_SYNC_KHR;
} } // end lock scope
if (fence != EGL_NO_SYNC_KHR) { if (fence != EGL_NO_SYNC_KHR) {
EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
@ -437,6 +442,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
ALOGE("dequeueBuffer: timeout waiting for fence"); ALOGE("dequeueBuffer: timeout waiting for fence");
} }
eglDestroySyncKHR(dpy, fence); eglDestroySyncKHR(dpy, fence);
} }
ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf,
@ -496,9 +502,6 @@ status_t BufferQueue::queueBuffer(int buf, int64_t timestamp,
ST_LOGE("queueBuffer: slot %d is not owned by the client " ST_LOGE("queueBuffer: slot %d is not owned by the client "
"(state=%d)", buf, mSlots[buf].mBufferState); "(state=%d)", buf, mSlots[buf].mBufferState);
return -EINVAL; return -EINVAL;
} else if (buf == mCurrentTexture) {
ST_LOGE("queueBuffer: slot %d is current!", buf);
return -EINVAL;
} else if (!mSlots[buf].mRequestBufferCalled) { } else if (!mSlots[buf].mRequestBufferCalled) {
ST_LOGE("queueBuffer: slot %d was enqueued without requesting a " ST_LOGE("queueBuffer: slot %d was enqueued without requesting a "
"buffer", buf); "buffer", buf);
@ -538,6 +541,7 @@ status_t BufferQueue::queueBuffer(int buf, int64_t timestamp,
mFrameCounter++; mFrameCounter++;
mSlots[buf].mFrameNumber = mFrameCounter; mSlots[buf].mFrameNumber = mFrameCounter;
mBufferHasBeenQueued = true;
mDequeueCondition.signal(); mDequeueCondition.signal();
*outWidth = mDefaultWidth; *outWidth = mDefaultWidth;
@ -647,6 +651,9 @@ status_t BufferQueue::connect(int api,
err = -EINVAL; err = -EINVAL;
break; break;
} }
mBufferHasBeenQueued = false;
return err; return err;
} }
@ -687,26 +694,185 @@ status_t BufferQueue::disconnect(int api) {
return err; 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) { void BufferQueue::freeBufferLocked(int i) {
mSlots[i].mGraphicBuffer = 0; mSlots[i].mGraphicBuffer = 0;
mSlots[i].mBufferState = BufferSlot::FREE; mSlots[i].mBufferState = BufferSlot::FREE;
mSlots[i].mFrameNumber = 0; mSlots[i].mFrameNumber = 0;
if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { mSlots[i].mAcquireCalled = false;
eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; // destroy fence as BufferQueue now takes ownership
mSlots[i].mEglDisplay = EGL_NO_DISPLAY; 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() { void BufferQueue::freeAllBuffersLocked() {
ALOGW_IF(!mQueue.isEmpty(), ALOGW_IF(!mQueue.isEmpty(),
"freeAllBuffersLocked called but mQueue is not empty"); "freeAllBuffersLocked called but mQueue is not empty");
mCurrentTexture = INVALID_BUFFER_SLOT; mQueue.clear();
mBufferHasBeenQueued = false;
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
freeBufferLocked(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() { void BufferQueue::freeAllBuffersExceptHeadLocked() {
ALOGW_IF(!mQueue.isEmpty(), ALOGW_IF(!mQueue.isEmpty(),
"freeAllBuffersExceptCurrentLocked called but mQueue is not empty"); "freeAllBuffersExceptCurrentLocked called but mQueue is not empty");
@ -715,7 +881,7 @@ void BufferQueue::freeAllBuffersExceptHeadLocked() {
Fifo::iterator front(mQueue.begin()); Fifo::iterator front(mQueue.begin());
head = *front; head = *front;
} }
mCurrentTexture = INVALID_BUFFER_SLOT; mBufferHasBeenQueued = false;
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
if (i != head) { if (i != head) {
freeBufferLocked(i); freeBufferLocked(i);

View File

@ -97,6 +97,11 @@ static float mtxRot270[16] = {
static void mtxMul(float out[16], const float a[16], const float b[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, SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
GLenum texTarget, bool useFenceSync) : GLenum texTarget, bool useFenceSync) :
@ -109,8 +114,13 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
#else #else
mUseFenceSync(false), mUseFenceSync(false),
#endif #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"); ST_LOGV("SurfaceTexture");
memcpy(mCurrentTransformMatrix, mtxIdentity, memcpy(mCurrentTransformMatrix, mtxIdentity,
@ -119,28 +129,18 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
SurfaceTexture::~SurfaceTexture() { SurfaceTexture::~SurfaceTexture() {
ST_LOGV("~SurfaceTexture"); ST_LOGV("~SurfaceTexture");
freeAllBuffersLocked(); abandon();
} }
status_t SurfaceTexture::setBufferCountServer(int bufferCount) { status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
return setBufferCountServerLocked(bufferCount); return BufferQueue::setBufferCountServer(bufferCount);
} }
status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
{ {
ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h); return BufferQueue::setDefaultBufferSize(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 SurfaceTexture::updateTexImage() { status_t SurfaceTexture::updateTexImage() {
@ -152,23 +152,35 @@ status_t SurfaceTexture::updateTexImage() {
return NO_INIT; return NO_INIT;
} }
BufferItem item;
// In asynchronous mode the list is guaranteed to be one buffer // In asynchronous mode the list is guaranteed to be one buffer
// deep, while in synchronous mode we use the oldest buffer. // deep, while in synchronous mode we use the oldest buffer.
if (!mQueue.empty()) { if (acquire(&item) == NO_ERROR) {
Fifo::iterator front(mQueue.begin()); int buf = item.mBuf;
int buf = *front; // 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. // Update the GL texture object.
EGLImageKHR image = mSlots[buf].mEglImage; EGLImageKHR image = mEGLSlots[buf].mEglImage;
EGLDisplay dpy = eglGetCurrentDisplay(); EGLDisplay dpy = eglGetCurrentDisplay();
if (image == EGL_NO_IMAGE_KHR) { if (image == EGL_NO_IMAGE_KHR) {
if (mSlots[buf].mGraphicBuffer == 0) { if (item.mGraphicBuffer == 0) {
ST_LOGE("buffer at slot %d is null", buf); ST_LOGE("buffer at slot %d is null", buf);
return BAD_VALUE; return BAD_VALUE;
} }
image = createImage(dpy, mSlots[buf].mGraphicBuffer); image = createImage(dpy, item.mGraphicBuffer);
mSlots[buf].mEglImage = image; mEGLSlots[buf].mEglImage = image;
mSlots[buf].mEglDisplay = dpy; mEGLSlots[buf].mEglDisplay = dpy;
if (image == EGL_NO_IMAGE_KHR) { if (image == EGL_NO_IMAGE_KHR) {
// NOTE: if dpy was invalid, createImage() is guaranteed to // NOTE: if dpy was invalid, createImage() is guaranteed to
// fail. so we'd end up here. // fail. so we'd end up here.
@ -191,6 +203,8 @@ status_t SurfaceTexture::updateTexImage() {
failed = true; failed = true;
} }
if (failed) { if (failed) {
releaseBuffer(buf, mEGLSlots[buf].mEglDisplay,
mEGLSlots[buf].mFence);
return -EINVAL; return -EINVAL;
} }
@ -201,40 +215,37 @@ status_t SurfaceTexture::updateTexImage() {
if (fence == EGL_NO_SYNC_KHR) { if (fence == EGL_NO_SYNC_KHR) {
ALOGE("updateTexImage: error creating fence: %#x", ALOGE("updateTexImage: error creating fence: %#x",
eglGetError()); eglGetError());
releaseBuffer(buf, mEGLSlots[buf].mEglDisplay,
mEGLSlots[buf].mFence);
return -EINVAL; return -EINVAL;
} }
glFlush(); glFlush();
mSlots[mCurrentTexture].mFence = fence; mEGLSlots[mCurrentTexture].mFence = fence;
} }
} }
ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
mCurrentTexture, mCurrentTexture,
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
buf, mSlots[buf].mGraphicBuffer->handle); buf, item.mGraphicBuffer->handle);
if (mCurrentTexture != INVALID_BUFFER_SLOT) { // release old buffer
// The current buffer becomes FREE if it was still in the queued releaseBuffer(mCurrentTexture,
// state. If it has already been given to the client mEGLSlots[mCurrentTexture].mEglDisplay,
// (synchronous mode), then it stays in DEQUEUED state. mEGLSlots[mCurrentTexture].mFence);
if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) {
mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE;
}
}
// Update the SurfaceTexture state. // Update the SurfaceTexture state.
mCurrentTexture = buf; mCurrentTexture = buf;
mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer;
mCurrentCrop = mSlots[buf].mCrop; mCurrentCrop = item.mCrop;
mCurrentTransform = mSlots[buf].mTransform; mCurrentTransform = item.mTransform;
mCurrentScalingMode = mSlots[buf].mScalingMode; mCurrentScalingMode = item.mScalingMode;
mCurrentTimestamp = mSlots[buf].mTimestamp; mCurrentTimestamp = item.mTimestamp;
computeCurrentTransformMatrix(); computeCurrentTransformMatrix();
// Now that we've passed the point at which failures can happen, // 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. // it's safe to remove the buffer from the front of the queue.
mQueue.erase(front);
mDequeueCondition.signal();
} else { } else {
// We always bind the texture even if we don't update its contents. // We always bind the texture even if we don't update its contents.
glBindTexture(mTexTarget, mTexName); glBindTexture(mTexTarget, mTexName);
@ -300,7 +311,7 @@ void SurfaceTexture::computeCurrentTransformMatrix() {
} }
} }
sp<GraphicBuffer>& buf(mSlots[mCurrentTexture].mGraphicBuffer); sp<GraphicBuffer>& buf(mCurrentTextureBuf);
float tx, ty, sx, sy; float tx, ty, sx, sy;
if (!mCurrentCrop.isEmpty()) { if (!mCurrentCrop.isEmpty()) {
// In order to prevent bilinear sampling at the of the crop rectangle we // In order to prevent bilinear sampling at the of the crop rectangle we
@ -372,7 +383,7 @@ void SurfaceTexture::setFrameAvailableListener(
const sp<FrameAvailableListener>& listener) { const sp<FrameAvailableListener>& listener) {
ST_LOGV("setFrameAvailableListener"); ST_LOGV("setFrameAvailableListener");
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
mFrameAvailableListener = listener; BufferQueue::setFrameAvailableListener(listener);
} }
EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
@ -413,22 +424,33 @@ uint32_t SurfaceTexture::getCurrentScalingMode() const {
bool SurfaceTexture::isSynchronousMode() const { bool SurfaceTexture::isSynchronousMode() const {
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
return mSynchronousMode; return BufferQueue::isSynchronousMode();
} }
void SurfaceTexture::abandon() { void SurfaceTexture::abandon() {
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
mQueue.clear();
mAbandoned = true; mAbandoned = true;
mCurrentTextureBuf.clear(); 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) { void SurfaceTexture::setName(const String8& name) {
Mutex::Autolock _l(mMutex);
mName = name; mName = name;
BufferQueue::setConsumerName(name);
} }
void SurfaceTexture::dump(String8& result) const void SurfaceTexture::dump(String8& result) const
@ -441,68 +463,19 @@ void SurfaceTexture::dump(String8& result, const char* prefix,
char* buffer, size_t SIZE) const char* buffer, size_t SIZE) const
{ {
Mutex::Autolock _l(mMutex); Mutex::Autolock _l(mMutex);
snprintf(buffer, SIZE, snprintf(buffer, SIZE, "%smTexName=%d\n", prefix, mTexName);
"%smBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], "
"mPixelFormat=%d, mTexName=%d\n",
prefix, mBufferCount, mSynchronousMode, mDefaultWidth,
mDefaultHeight, mPixelFormat, mTexName);
result.append(buffer); 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, snprintf(buffer, SIZE,
"%scurrent: {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d}\n" "%snext : {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,
,
prefix, mCurrentCrop.left,
mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom, mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
mCurrentTransform, mCurrentTexture, mCurrentTransform, mCurrentTexture
prefix, mNextCrop.left, mNextCrop.top, mNextCrop.right,
mNextCrop.bottom, mNextTransform, fifoSize, fifo.string()
); );
result.append(buffer); 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++) { BufferQueue::dump(result, prefix, buffer, SIZE);
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");
}
} }
static void mtxMul(float out[16], const float a[16], const float b[16]) { static void mtxMul(float out[16], const float a[16], const float b[16]) {