Merge "add OES postfix without truncating function"
diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h
index d2d3bb8..e705c6f 100644
--- a/include/gui/ISurfaceTexture.h
+++ b/include/gui/ISurfaceTexture.h
@@ -31,20 +31,27 @@
namespace android {
// ----------------------------------------------------------------------------
+class SurfaceTextureClient;
+
class ISurfaceTexture : public IInterface
{
public:
DECLARE_META_INTERFACE(SurfaceTexture);
- enum { BUFFER_NEEDS_REALLOCATION = 1 };
+protected:
+ friend class SurfaceTextureClient;
+
+ enum {
+ BUFFER_NEEDS_REALLOCATION = 0x1,
+ RELEASE_ALL_BUFFERS = 0x2,
+ };
// requestBuffer requests a new buffer for the given index. The server (i.e.
// the ISurfaceTexture implementation) assigns the newly created buffer to
// the given slot index, and the client is expected to mirror the
// slot->buffer mapping so that it's not necessary to transfer a
// GraphicBuffer for every dequeue operation.
- virtual sp<GraphicBuffer> requestBuffer(int slot, uint32_t w, uint32_t h,
- uint32_t format, uint32_t usage) = 0;
+ virtual sp<GraphicBuffer> requestBuffer(int slot) = 0;
// setBufferCount sets the number of buffer slots available. Calling this
// will also cause all buffer slots to be emptied. The caller should empty
@@ -60,7 +67,8 @@
// in the contents of its associated buffer contents and call queueBuffer.
// If dequeueBuffer return BUFFER_NEEDS_REALLOCATION, the client is
// expected to call requestBuffer immediately.
- virtual status_t dequeueBuffer(int *slot) = 0;
+ virtual status_t dequeueBuffer(int *slot, uint32_t w, uint32_t h,
+ uint32_t format, uint32_t usage) = 0;
// queueBuffer indicates that the client has finished filling in the
// contents of the buffer associated with slot and transfers ownership of
@@ -85,6 +93,17 @@
// Holding this binder reference prevents SurfaceFlinger from freeing the
// buffers before the client is done with them.
virtual sp<IBinder> getAllocator() = 0;
+
+ // query retrieves some information for this surface
+ // 'what' tokens allowed are that of android_natives.h
+ virtual int query(int what, int* value) = 0;
+
+ // setSynchronousMode set whether dequeueBuffer is synchronous or
+ // asynchronous. In synchronous mode, dequeueBuffer blocks until
+ // a buffer is available, the currently bound buffer can be dequeued and
+ // queued buffers will be retired in order.
+ // The default mode is asynchronous.
+ virtual status_t setSynchronousMode(bool enabled) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 96828c6..43b2fa9 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -34,14 +34,23 @@
// ----------------------------------------------------------------------------
class IGraphicBufferAlloc;
+class String8;
class SurfaceTexture : public BnSurfaceTexture {
public:
enum { MIN_UNDEQUEUED_BUFFERS = 2 };
- enum { MIN_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1 };
+ enum {
+ MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1,
+ MIN_SYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS
+ };
enum { NUM_BUFFER_SLOTS = 32 };
struct FrameAvailableListener : public virtual RefBase {
+ // onFrameAvailable() is called from queueBuffer() is the FIFO is
+ // empty. You can use SurfaceTexture::getQueuedCount() to
+ // figure out if there are more frames waiting.
+ // This is called without any lock held can be called concurrently by
+ // multiple threads.
virtual void onFrameAvailable() = 0;
};
@@ -56,15 +65,15 @@
// SurfaceTexture object (i.e. they are not owned by the client).
virtual status_t setBufferCount(int bufferCount);
- virtual sp<GraphicBuffer> requestBuffer(int buf, uint32_t w, uint32_t h,
- uint32_t format, uint32_t usage);
+ virtual sp<GraphicBuffer> requestBuffer(int buf);
// dequeueBuffer gets the next buffer slot index for the client 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.
- virtual status_t dequeueBuffer(int *buf);
+ virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h,
+ uint32_t format, uint32_t usage);
// queueBuffer returns a filled buffer to the SurfaceTexture. In addition, a
// timestamp must be provided for the buffer. The timestamp is in
@@ -76,6 +85,15 @@
virtual status_t setCrop(const Rect& reg);
virtual status_t setTransform(uint32_t transform);
+ virtual int query(int what, int* value);
+
+ // setSynchronousMode set whether dequeueBuffer is synchronous or
+ // asynchronous. In synchronous mode, dequeueBuffer blocks until
+ // a buffer is available, the currently bound buffer can be dequeued and
+ // queued buffers will be retired in order.
+ // The default mode is asynchronous.
+ virtual status_t setSynchronousMode(bool enabled);
+
// updateTexImage sets the image contents of the target texture to that of
// the most recently queued buffer.
//
@@ -83,6 +101,16 @@
// target texture belongs is bound to the calling thread.
status_t updateTexImage();
+ // getqueuedCount returns the number of queued frames waiting in the
+ // FIFO. In asynchronous mode, this always returns 0 or 1 since
+ // frames are not accumulating in the FIFO.
+ size_t getQueuedCount() const;
+
+ // 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);
+
// getTransformMatrix retrieves the 4x4 texture coordinate transform matrix
// associated with the texture image set by the most recent call to
// updateTexImage.
@@ -140,6 +168,10 @@
// getCurrentTransform returns the transform of the current buffer
uint32_t getCurrentTransform() const;
+ // dump our state in a String
+ void dump(String8& result) const;
+ void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
+
protected:
// freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
@@ -154,9 +186,21 @@
EGLImageKHR createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer);
+ status_t setBufferCountServerLocked(int bufferCount);
+
enum { INVALID_BUFFER_SLOT = -1 };
struct BufferSlot {
+
+ BufferSlot()
+ : mEglImage(EGL_NO_IMAGE_KHR),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mBufferState(BufferSlot::FREE),
+ mRequestBufferCalled(false),
+ mLastQueuedTransform(0),
+ mLastQueuedTimestamp(0) {
+ }
+
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
// if no buffer has been allocated.
sp<GraphicBuffer> mGraphicBuffer;
@@ -167,11 +211,32 @@
// mEglDisplay is the EGLDisplay used to create mEglImage.
EGLDisplay mEglDisplay;
- // mOwnedByClient indicates whether the slot is currently accessible to a
+ // mBufferState indicates whether the slot is currently accessible to a
// client and should not be used by the SurfaceTexture object. It gets
// set to true when dequeueBuffer returns the slot and is reset to false
// when the client calls either queueBuffer or cancelBuffer on the slot.
- bool mOwnedByClient;
+ enum { DEQUEUED=-2, FREE=-1, QUEUED=0 };
+ int8_t mBufferState;
+
+
+ // mRequestBufferCalled is used for validating that the client did
+ // call requestBuffer() when told to do so. Technically this is not
+ // needed but useful for debugging and catching client bugs.
+ bool mRequestBufferCalled;
+
+ // mLastQueuedCrop is the crop rectangle for the buffer that was most
+ // recently queued. This gets set to mNextCrop each time queueBuffer gets
+ // called.
+ Rect mLastQueuedCrop;
+
+ // mLastQueuedTransform is the transform identifier for the buffer that was
+ // most recently queued. This gets set to mNextTransform each time
+ // queueBuffer gets called.
+ uint32_t mLastQueuedTransform;
+
+ // mLastQueuedTimestamp is the timestamp for the buffer that was most
+ // recently queued. This gets set by queueBuffer.
+ int64_t mLastQueuedTimestamp;
};
// mSlots is the array of buffer slots that must be mirrored on the client
@@ -193,16 +258,19 @@
// in requestBuffers() if a format of zero is specified.
uint32_t mPixelFormat;
- // mUseDefaultSize indicates whether or not the default size should be used
- // that is, if the last requestBuffer has been called with both width
- // and height null.
- bool mUseDefaultSize;
-
// mBufferCount is the number of buffer slots that the client and server
- // must maintain. It defaults to MIN_BUFFER_SLOTS and can be changed by
- // calling setBufferCount.
+ // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed
+ // by calling setBufferCount or setBufferCountServer
int mBufferCount;
+ // mRequestedBufferCount is the number of buffer slots requested by the
+ // client. The default is zero, which means the client doesn't care how
+ // many buffers there is.
+ int mClientBufferCount;
+
+ // 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,
@@ -233,25 +301,6 @@
// gets set to mLastQueuedTimestamp each time updateTexImage is called.
int64_t mCurrentTimestamp;
- // mLastQueued is the buffer slot index of the most recently enqueued buffer.
- // At construction time it is initialized to INVALID_BUFFER_SLOT, and is
- // updated each time queueBuffer is called.
- int mLastQueued;
-
- // mLastQueuedCrop is the crop rectangle for the buffer that was most
- // recently queued. This gets set to mNextCrop each time queueBuffer gets
- // called.
- Rect mLastQueuedCrop;
-
- // mLastQueuedTransform is the transform identifier for the buffer that was
- // most recently queued. This gets set to mNextTransform each time
- // queueBuffer gets called.
- uint32_t mLastQueuedTransform;
-
- // mLastQueuedTimestamp is the timestamp for the buffer that was most
- // recently queued. This gets set by queueBuffer.
- int64_t mLastQueuedTimestamp;
-
// mNextCrop is the crop rectangle that will be used for the next buffer
// that gets queued. It is set by calling setCrop.
Rect mNextCrop;
@@ -274,6 +323,16 @@
// queueBuffer.
sp<FrameAvailableListener> mFrameAvailableListener;
+ // mSynchronousMode whether we're in synchronous mode or not
+ bool mSynchronousMode;
+
+ // mDequeueCondition condition used for dequeueBuffer in synchronous mode
+ mutable Condition mDequeueCondition;
+
+ // mQueue is a FIFO of queued buffers used in synchronous mode
+ typedef Vector<int> Fifo;
+ Fifo mQueue;
+
// 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.
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index c77bc4c..e7c6e24 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -84,7 +84,6 @@
int getConnectedApi() const;
enum { MIN_UNDEQUEUED_BUFFERS = SurfaceTexture::MIN_UNDEQUEUED_BUFFERS };
- enum { MIN_BUFFER_SLOTS = SurfaceTexture::MIN_BUFFER_SLOTS };
enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp
index bc14ad5..16e3780 100644
--- a/libs/gui/ISurfaceTexture.cpp
+++ b/libs/gui/ISurfaceTexture.cpp
@@ -39,6 +39,8 @@
SET_CROP,
SET_TRANSFORM,
GET_ALLOCATOR,
+ QUERY,
+ SET_SYNCHRONOUS_MODE,
};
@@ -50,15 +52,10 @@
{
}
- virtual sp<GraphicBuffer> requestBuffer(int bufferIdx,
- uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
+ virtual sp<GraphicBuffer> requestBuffer(int bufferIdx) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
data.writeInt32(bufferIdx);
- data.writeInt32(w);
- data.writeInt32(h);
- data.writeInt32(format);
- data.writeInt32(usage);
remote()->transact(REQUEST_BUFFER, data, &reply);
sp<GraphicBuffer> buffer;
bool nonNull = reply.readInt32();
@@ -79,9 +76,14 @@
return err;
}
- virtual status_t dequeueBuffer(int *buf) {
+ virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h,
+ uint32_t format, uint32_t usage) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInt32(w);
+ data.writeInt32(h);
+ data.writeInt32(format);
+ data.writeInt32(usage);
remote()->transact(DEQUEUE_BUFFER, data, &reply);
*buf = reply.readInt32();
int result = reply.readInt32();
@@ -132,6 +134,27 @@
remote()->transact(GET_ALLOCATOR, data, &reply);
return reply.readStrongBinder();
}
+
+ virtual int query(int what, int* value) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInt32(what);
+ remote()->transact(QUERY, data, &reply);
+ value[0] = reply.readInt32();
+ status_t result = reply.readInt32();
+ return result;
+ }
+
+ virtual status_t setSynchronousMode(bool enabled) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInt32(enabled);
+ remote()->transact(SET_SYNCHRONOUS_MODE, data, &reply);
+ status_t result = reply.readInt32();
+ return result;
+ }
+
+
};
IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture");
@@ -145,12 +168,7 @@
case REQUEST_BUFFER: {
CHECK_INTERFACE(ISurfaceTexture, data, reply);
int bufferIdx = data.readInt32();
- uint32_t w = data.readInt32();
- uint32_t h = data.readInt32();
- uint32_t format = data.readInt32();
- uint32_t usage = data.readInt32();
- sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format,
- usage));
+ sp<GraphicBuffer> buffer(requestBuffer(bufferIdx));
reply->writeInt32(buffer != 0);
if (buffer != 0) {
reply->write(*buffer);
@@ -166,8 +184,12 @@
} break;
case DEQUEUE_BUFFER: {
CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ uint32_t w = data.readInt32();
+ uint32_t h = data.readInt32();
+ uint32_t format = data.readInt32();
+ uint32_t usage = data.readInt32();
int buf;
- int result = dequeueBuffer(&buf);
+ int result = dequeueBuffer(&buf, w, h, format, usage);
reply->writeInt32(buf);
reply->writeInt32(result);
return NO_ERROR;
@@ -210,6 +232,22 @@
reply->writeStrongBinder(result);
return NO_ERROR;
} break;
+ case QUERY: {
+ CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ int value;
+ int what = data.readInt32();
+ int res = query(what, &value);
+ reply->writeInt32(value);
+ reply->writeInt32(res);
+ return NO_ERROR;
+ } break;
+ case SET_SYNCHRONOUS_MODE: {
+ CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ bool enabled = data.readInt32();
+ status_t res = setSynchronousMode(enabled);
+ reply->writeInt32(res);
+ return NO_ERROR;
+ } break;
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 2619629..d7c449c 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -34,6 +34,7 @@
#include <surfaceflinger/IGraphicBufferAlloc.h>
#include <utils/Log.h>
+#include <utils/String8.h>
namespace android {
@@ -81,23 +82,17 @@
mDefaultWidth(1),
mDefaultHeight(1),
mPixelFormat(PIXEL_FORMAT_RGBA_8888),
- mUseDefaultSize(true),
- mBufferCount(MIN_BUFFER_SLOTS),
+ mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
+ mClientBufferCount(0),
+ mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
mCurrentTexture(INVALID_BUFFER_SLOT),
mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES),
mCurrentTransform(0),
mCurrentTimestamp(0),
- mLastQueued(INVALID_BUFFER_SLOT),
- mLastQueuedTransform(0),
- mLastQueuedTimestamp(0),
mNextTransform(0),
- mTexName(tex) {
+ mTexName(tex),
+ mSynchronousMode(false) {
LOGV("SurfaceTexture::SurfaceTexture");
- for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
- mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
- mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
- mSlots[i].mOwnedByClient = false;
- }
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
mNextCrop.makeInvalid();
@@ -108,18 +103,80 @@
freeAllBuffers();
}
+status_t SurfaceTexture::setBufferCountServerLocked(int bufferCount) {
+ if (bufferCount > NUM_BUFFER_SLOTS)
+ return BAD_VALUE;
+
+ // special-case, nothing to do
+ if (bufferCount == mBufferCount)
+ return OK;
+
+ if (!mClientBufferCount &&
+ bufferCount >= mBufferCount) {
+ // easy, we just have more buffers
+ mBufferCount = bufferCount;
+ mServerBufferCount = bufferCount;
+ mDequeueCondition.signal();
+ } else {
+ // we're here because we're either
+ // - reducing the number of available buffers
+ // - or there is a client-buffer-count in effect
+
+ // less than 2 buffers is never allowed
+ if (bufferCount < 2)
+ return BAD_VALUE;
+
+ // when there is non client-buffer-count in effect, the client is not
+ // allowed to dequeue more than one buffer at a time,
+ // so the next time they dequeue a buffer, we know that they don't
+ // own one. the actual resizing will happen during the next
+ // dequeueBuffer.
+
+ mServerBufferCount = bufferCount;
+ }
+ return OK;
+}
+
+status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
+ Mutex::Autolock lock(mMutex);
+ return setBufferCountServerLocked(bufferCount);
+}
+
status_t SurfaceTexture::setBufferCount(int bufferCount) {
LOGV("SurfaceTexture::setBufferCount");
+ Mutex::Autolock lock(mMutex);
- if (bufferCount < MIN_BUFFER_SLOTS) {
+ // Error out if the user has dequeued buffers
+ for (int i=0 ; i<mBufferCount ; i++) {
+ if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
+ LOGE("setBufferCount: client owns some buffers");
+ return -EINVAL;
+ }
+ }
+
+ if (bufferCount == 0) {
+ const int minBufferSlots = mSynchronousMode ?
+ MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+ mClientBufferCount = 0;
+ bufferCount = (mServerBufferCount >= minBufferSlots) ?
+ mServerBufferCount : minBufferSlots;
+ return setBufferCountServerLocked(bufferCount);
+ }
+
+ // We don't allow the client to set a buffer-count less than
+ // MIN_ASYNC_BUFFER_SLOTS (3), there is no reason for it.
+ if (bufferCount < MIN_ASYNC_BUFFER_SLOTS) {
return BAD_VALUE;
}
- Mutex::Autolock lock(mMutex);
+ // here we're guaranteed that the client doesn't have dequeued buffers
+ // and will release all of its buffer references.
freeAllBuffers();
mBufferCount = bufferCount;
+ mClientBufferCount = bufferCount;
mCurrentTexture = INVALID_BUFFER_SLOT;
- mLastQueued = INVALID_BUFFER_SLOT;
+ mQueue.clear();
+ mDequeueCondition.signal();
return OK;
}
@@ -133,8 +190,7 @@
return OK;
}
-sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf,
- uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
+sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf) {
LOGV("SurfaceTexture::requestBuffer");
Mutex::Autolock lock(mMutex);
if (buf < 0 || mBufferCount <= buf) {
@@ -142,11 +198,123 @@
mBufferCount, buf);
return 0;
}
+ mSlots[buf].mRequestBufferCalled = true;
+ return mSlots[buf].mGraphicBuffer;
+}
+
+status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
+ uint32_t format, uint32_t usage) {
+ LOGV("SurfaceTexture::dequeueBuffer");
+
if ((w && !h) || (!w & h)) {
- LOGE("requestBuffer: invalid size: w=%u, h=%u: %d", w, h, buf);
- return 0;
+ LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
+ return BAD_VALUE;
}
+ Mutex::Autolock lock(mMutex);
+
+ status_t returnFlags(OK);
+
+ int found, foundSync;
+ int dequeuedCount = 0;
+ bool tryAgain = true;
+ while (tryAgain) {
+ // We need to wait for the FIFO to drain if the number of buffer
+ // needs to change.
+ //
+ // The condition "number of buffer needs to change" is true if
+ // - the client doesn't care about how many buffers there are
+ // - AND the actual number of buffer is different from what was
+ // set in the last setBufferCountServer()
+ // - OR -
+ // setBufferCountServer() was set to a value incompatible with
+ // the synchronization mode (for instance because the sync mode
+ // changed since)
+ //
+ // As long as this condition is true AND the FIFO is not empty, we
+ // wait on mDequeueCondition.
+
+ int minBufferCountNeeded = mSynchronousMode ?
+ MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+
+ if (!mClientBufferCount &&
+ ((mServerBufferCount != mBufferCount) ||
+ (mServerBufferCount < minBufferCountNeeded))) {
+ // wait for the FIFO to drain
+ while (!mQueue.isEmpty()) {
+ mDequeueCondition.wait(mMutex);
+ }
+ minBufferCountNeeded = mSynchronousMode ?
+ MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+ }
+
+
+ if (!mClientBufferCount &&
+ ((mServerBufferCount != mBufferCount) ||
+ (mServerBufferCount < minBufferCountNeeded))) {
+ // here we're guaranteed that mQueue is empty
+ freeAllBuffers();
+ mBufferCount = mServerBufferCount;
+ if (mBufferCount < minBufferCountNeeded)
+ mBufferCount = minBufferCountNeeded;
+ mCurrentTexture = INVALID_BUFFER_SLOT;
+ returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
+ }
+
+ // look for a free buffer to give to the client
+ found = INVALID_BUFFER_SLOT;
+ foundSync = INVALID_BUFFER_SLOT;
+ dequeuedCount = 0;
+ for (int i = 0; i < mBufferCount; i++) {
+ const int state = mSlots[i].mBufferState;
+ if (state == BufferSlot::DEQUEUED) {
+ dequeuedCount++;
+ }
+ if (state == BufferSlot::FREE || i == mCurrentTexture) {
+ foundSync = i;
+ if (i != mCurrentTexture) {
+ found = i;
+ break;
+ }
+ }
+ }
+
+ // clients are not allowed to dequeue more than one buffer
+ // if they didn't set a buffer count.
+ if (!mClientBufferCount && dequeuedCount) {
+ return -EINVAL;
+ }
+
+ // make sure the client is not trying to dequeue more buffers
+ // than allowed.
+ const int avail = mBufferCount - (dequeuedCount+1);
+ if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
+ LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded (dequeued=%d)",
+ MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
+ dequeuedCount);
+ return -EBUSY;
+ }
+
+ // we're in synchronous mode and didn't find a buffer, we need to wait
+ // for for some buffers to be consumed
+ tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
+ if (tryAgain) {
+ mDequeueCondition.wait(mMutex);
+ }
+ }
+
+ if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
+ // foundSync guaranteed to be != INVALID_BUFFER_SLOT
+ found = foundSync;
+ }
+
+ if (found == INVALID_BUFFER_SLOT) {
+ return -EBUSY;
+ }
+
+ const int buf = found;
+ *outBuf = found;
+
const bool useDefaultSize = !w && !h;
if (useDefaultSize) {
// use the default size
@@ -160,78 +328,115 @@
format = mPixelFormat;
}
- usage |= GraphicBuffer::USAGE_HW_TEXTURE;
- sp<GraphicBuffer> graphicBuffer(
- mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
- if (graphicBuffer == 0) {
- LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed");
- } else {
- mUseDefaultSize = useDefaultSize;
+ // buffer is now in DEQUEUED (but can also be current at the same time,
+ // if we're in synchronous mode)
+ mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
+
+ const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
+ if ((buffer == NULL) ||
+ (uint32_t(buffer->width) != w) ||
+ (uint32_t(buffer->height) != h) ||
+ (uint32_t(buffer->format) != format) ||
+ ((uint32_t(buffer->usage) & usage) != usage))
+ {
+ usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+ sp<GraphicBuffer> graphicBuffer(
+ mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
+ if (graphicBuffer == 0) {
+ LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer failed");
+ return NO_MEMORY;
+ }
if (updateFormat) {
mPixelFormat = format;
}
mSlots[buf].mGraphicBuffer = graphicBuffer;
+ mSlots[buf].mRequestBufferCalled = false;
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;
}
+ returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
}
- return graphicBuffer;
+ return returnFlags;
}
-status_t SurfaceTexture::dequeueBuffer(int *buf) {
- LOGV("SurfaceTexture::dequeueBuffer");
+status_t SurfaceTexture::setSynchronousMode(bool enabled) {
Mutex::Autolock lock(mMutex);
- int found = INVALID_BUFFER_SLOT;
- for (int i = 0; i < mBufferCount; i++) {
- if (!mSlots[i].mOwnedByClient && i != mCurrentTexture && i != mLastQueued) {
- mSlots[i].mOwnedByClient = true;
- found = i;
- break;
+
+ status_t err = OK;
+ if (!enabled) {
+ // going to asynchronous mode, drain the queue
+ while (mSynchronousMode != enabled && !mQueue.isEmpty()) {
+ mDequeueCondition.wait(mMutex);
}
}
- if (found == INVALID_BUFFER_SLOT) {
- return -EBUSY;
- }
- *buf = found;
-
- const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
- if (buffer == NULL) {
- return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+ if (mSynchronousMode != enabled) {
+ // - if we're going to asynchronous mode, the queue is guaranteed to be
+ // empty here
+ // - if the client set the number of buffers, we're guaranteed that
+ // we have at least 3 (because we don't allow less)
+ mSynchronousMode = enabled;
+ mDequeueCondition.signal();
}
-
- if ((mUseDefaultSize) &&
- ((uint32_t(buffer->width) != mDefaultWidth) ||
- (uint32_t(buffer->height) != mDefaultHeight))) {
- return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
- }
- return OK;
+ return err;
}
status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) {
LOGV("SurfaceTexture::queueBuffer");
+
+ sp<FrameAvailableListener> listener;
+
+ { // scope for the lock
Mutex::Autolock lock(mMutex);
- if (buf < 0 || mBufferCount <= buf) {
+ if (buf < 0 || buf >= mBufferCount) {
LOGE("queueBuffer: slot index out of range [0, %d]: %d",
mBufferCount, buf);
return -EINVAL;
- } else if (!mSlots[buf].mOwnedByClient) {
- LOGE("queueBuffer: slot %d is not owned by the client", buf);
+ } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+ LOGE("queueBuffer: slot %d is not owned by the client (state=%d)",
+ buf, mSlots[buf].mBufferState);
return -EINVAL;
- } else if (mSlots[buf].mGraphicBuffer == 0) {
+ } else if (buf == mCurrentTexture) {
+ LOGE("queueBuffer: slot %d is current!", buf);
+ return -EINVAL;
+ } else if (!mSlots[buf].mRequestBufferCalled) {
LOGE("queueBuffer: slot %d was enqueued without requesting a buffer",
buf);
return -EINVAL;
}
- mSlots[buf].mOwnedByClient = false;
- mLastQueued = buf;
- mLastQueuedCrop = mNextCrop;
- mLastQueuedTransform = mNextTransform;
- mLastQueuedTimestamp = timestamp;
- if (mFrameAvailableListener != 0) {
- mFrameAvailableListener->onFrameAvailable();
+
+ if (mQueue.empty()) {
+ listener = mFrameAvailableListener;
+ }
+
+ if (mSynchronousMode) {
+ // in synchronous mode we queue all buffers in a FIFO
+ mQueue.push_back(buf);
+ } else {
+ // in asynchronous mode we only keep the most recent buffer
+ if (mQueue.empty()) {
+ mQueue.push_back(buf);
+ } else {
+ Fifo::iterator front(mQueue.begin());
+ // buffer currently queued is freed
+ mSlots[*front].mBufferState = BufferSlot::FREE;
+ // and we record the new buffer index in the queued list
+ *front = buf;
+ }
+ }
+
+ mSlots[buf].mBufferState = BufferSlot::QUEUED;
+ mSlots[buf].mLastQueuedCrop = mNextCrop;
+ mSlots[buf].mLastQueuedTransform = mNextTransform;
+ mSlots[buf].mLastQueuedTimestamp = timestamp;
+ mDequeueCondition.signal();
+ } // scope for the lock
+
+ // call back without lock held
+ if (listener != 0) {
+ listener->onFrameAvailable();
}
return OK;
}
@@ -239,15 +444,17 @@
void SurfaceTexture::cancelBuffer(int buf) {
LOGV("SurfaceTexture::cancelBuffer");
Mutex::Autolock lock(mMutex);
- if (buf < 0 || mBufferCount <= buf) {
- LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount,
- buf);
+ if (buf < 0 || buf >= mBufferCount) {
+ LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
+ mBufferCount, buf);
return;
- } else if (!mSlots[buf].mOwnedByClient) {
- LOGE("cancelBuffer: slot %d is not owned by the client", buf);
+ } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+ LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
+ buf, mSlots[buf].mBufferState);
return;
}
- mSlots[buf].mOwnedByClient = false;
+ mSlots[buf].mBufferState = BufferSlot::FREE;
+ mDequeueCondition.signal();
}
status_t SurfaceTexture::setCrop(const Rect& crop) {
@@ -266,18 +473,31 @@
status_t SurfaceTexture::updateTexImage() {
LOGV("SurfaceTexture::updateTexImage");
+
Mutex::Autolock lock(mMutex);
- // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT,
+ int buf = mCurrentTexture;
+ if (!mQueue.empty()) {
+ // in asynchronous mode the list is guaranteed to be one buffer deep,
+ // while in synchronous mode we use the oldest buffer
+ Fifo::iterator front(mQueue.begin());
+ buf = *front;
+ mQueue.erase(front);
+ if (mQueue.isEmpty()) {
+ mDequeueCondition.signal();
+ }
+ }
+
+ // Initially both mCurrentTexture and buf are INVALID_BUFFER_SLOT,
// so this check will fail until a buffer gets queued.
- if (mCurrentTexture != mLastQueued) {
+ if (mCurrentTexture != buf) {
// Update the GL texture object.
- EGLImageKHR image = mSlots[mLastQueued].mEglImage;
+ EGLImageKHR image = mSlots[buf].mEglImage;
if (image == EGL_NO_IMAGE_KHR) {
EGLDisplay dpy = eglGetCurrentDisplay();
- image = createImage(dpy, mSlots[mLastQueued].mGraphicBuffer);
- mSlots[mLastQueued].mEglImage = image;
- mSlots[mLastQueued].mEglDisplay = dpy;
+ image = createImage(dpy, mSlots[buf].mGraphicBuffer);
+ mSlots[buf].mEglImage = image;
+ mSlots[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.
@@ -287,11 +507,10 @@
GLint error;
while ((error = glGetError()) != GL_NO_ERROR) {
- LOGE("GL error cleared before updating SurfaceTexture: %#04x", error);
+ LOGW("updateTexImage: clearing GL error: %#04x", error);
}
- GLenum target = getTextureTarget(
- mSlots[mLastQueued].mGraphicBuffer->format);
+ GLenum target = getTextureTarget(mSlots[buf].mGraphicBuffer->format);
if (target != mCurrentTextureTarget) {
glDeleteTextures(1, &mTexName);
}
@@ -301,20 +520,29 @@
bool failed = false;
while ((error = glGetError()) != GL_NO_ERROR) {
LOGE("error binding external texture image %p (slot %d): %#04x",
- image, mLastQueued, error);
+ image, buf, error);
failed = true;
}
if (failed) {
return -EINVAL;
}
+ 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;
+ }
+
// Update the SurfaceTexture state.
- mCurrentTexture = mLastQueued;
+ mCurrentTexture = buf;
mCurrentTextureTarget = target;
- mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer;
- mCurrentCrop = mLastQueuedCrop;
- mCurrentTransform = mLastQueuedTransform;
- mCurrentTimestamp = mLastQueuedTimestamp;
+ mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
+ mCurrentCrop = mSlots[buf].mLastQueuedCrop;
+ mCurrentTransform = mSlots[buf].mLastQueuedTransform;
+ mCurrentTimestamp = mSlots[buf].mLastQueuedTimestamp;
+ mDequeueCondition.signal();
} else {
// We always bind the texture even if we don't update its contents.
glBindTexture(mCurrentTextureTarget, mTexName);
@@ -322,6 +550,11 @@
return OK;
}
+size_t SurfaceTexture::getQueuedCount() const {
+ Mutex::Autolock lock(mMutex);
+ return mQueue.size();
+}
+
bool SurfaceTexture::isExternalFormat(uint32_t format)
{
switch (format) {
@@ -470,7 +703,7 @@
void SurfaceTexture::freeAllBuffers() {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i].mGraphicBuffer = 0;
- mSlots[i].mOwnedByClient = false;
+ mSlots[i].mBufferState = BufferSlot::FREE;
if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
@@ -510,6 +743,98 @@
return mCurrentTransform;
}
+int SurfaceTexture::query(int what, int* outValue)
+{
+ Mutex::Autolock lock(mMutex);
+ int value;
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ value = mDefaultWidth;
+ if (!mDefaultWidth && !mDefaultHeight && mCurrentTextureBuf!=0)
+ value = mCurrentTextureBuf->width;
+ break;
+ case NATIVE_WINDOW_HEIGHT:
+ value = mDefaultHeight;
+ if (!mDefaultWidth && !mDefaultHeight && mCurrentTextureBuf!=0)
+ value = mCurrentTextureBuf->height;
+ break;
+ case NATIVE_WINDOW_FORMAT:
+ value = mPixelFormat;
+ break;
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ value = mSynchronousMode ?
+ (MIN_UNDEQUEUED_BUFFERS-1) : MIN_UNDEQUEUED_BUFFERS;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+ outValue[0] = value;
+ return NO_ERROR;
+}
+
+void SurfaceTexture::dump(String8& result) const
+{
+ char buffer[1024];
+ dump(result, "", buffer, 1024);
+}
+
+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);
+ 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, target=0x%04x}\n"
+ "%snext : {crop=[%d,%d,%d,%d], transform=0x%02x, FIFO(%d)={%s}}\n"
+ ,
+ prefix, mCurrentCrop.left,
+ mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
+ mCurrentTransform, mCurrentTexture, mCurrentTextureTarget,
+ prefix, mNextCrop.left, mNextCrop.top, mNextCrop.right, mNextCrop.bottom,
+ mCurrentTransform, 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";
+ 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\n"
+ ,
+ prefix, (i==mCurrentTexture)?">":" ", i, stateName(slot.mBufferState),
+ slot.mLastQueuedCrop.left, slot.mLastQueuedCrop.top,
+ slot.mLastQueuedCrop.right, slot.mLastQueuedCrop.bottom,
+ slot.mLastQueuedTransform, slot.mLastQueuedTimestamp
+ );
+ result.append(buffer);
+ }
+}
static void mtxMul(float out[16], const float a[16], const float b[16]) {
out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3];
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index ec6da43..6f10320 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -39,6 +39,9 @@
ANativeWindow::query = query;
ANativeWindow::perform = perform;
+ const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
+ const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
+
// Get a reference to the allocator.
mAllocator = mSurfaceTexture->getAllocator();
}
@@ -90,27 +93,40 @@
}
int SurfaceTextureClient::setSwapInterval(int interval) {
- return INVALID_OPERATION;
+ // EGL specification states:
+ // interval is silently clamped to minimum and maximum implementation
+ // dependent values before being stored.
+ // Although we don't have to, we apply the same logic here.
+
+ if (interval < minSwapInterval)
+ interval = minSwapInterval;
+
+ if (interval > maxSwapInterval)
+ interval = maxSwapInterval;
+
+ status_t res = mSurfaceTexture->setSynchronousMode(interval ? true : false);
+
+ return res;
}
int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) {
LOGV("SurfaceTextureClient::dequeueBuffer");
Mutex::Autolock lock(mMutex);
int buf = -1;
- status_t err = mSurfaceTexture->dequeueBuffer(&buf);
- if (err < 0) {
- LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer failed: %d", err);
- return err;
+ status_t result = mSurfaceTexture->dequeueBuffer(&buf, mReqWidth, mReqHeight,
+ mReqFormat, mReqUsage);
+ if (result < 0) {
+ LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)"
+ "failed: %d", result, mReqWidth, mReqHeight, mReqFormat, mReqUsage);
+ return result;
}
sp<GraphicBuffer>& gbuf(mSlots[buf]);
- if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION ||
- gbuf == 0 ||
- (mReqWidth && gbuf->getWidth() != mReqWidth) ||
- (mReqHeight && gbuf->getHeight() != mReqHeight) ||
- (mReqFormat && uint32_t(gbuf->getPixelFormat()) != mReqFormat) ||
- (gbuf->getUsage() & mReqUsage) != mReqUsage) {
- gbuf = mSurfaceTexture->requestBuffer(buf, mReqWidth, mReqHeight,
- mReqFormat, mReqUsage);
+ if (result & ISurfaceTexture::RELEASE_ALL_BUFFERS) {
+ freeAllBuffers();
+ }
+
+ if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
+ gbuf = mSurfaceTexture->requestBuffer(buf);
if (gbuf == 0) {
LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed");
return NO_MEMORY;
@@ -163,29 +179,17 @@
int SurfaceTextureClient::query(int what, int* value) const {
LOGV("SurfaceTextureClient::query");
- Mutex::Autolock lock(mMutex);
switch (what) {
- case NATIVE_WINDOW_WIDTH:
- *value = mQueryWidth ? mQueryWidth : mReqWidth;
- return NO_ERROR;
- case NATIVE_WINDOW_HEIGHT:
- *value = mQueryHeight ? mQueryHeight : mReqHeight;
- return NO_ERROR;
- case NATIVE_WINDOW_FORMAT:
- *value = mQueryFormat ? mQueryFormat : mReqFormat;
- return NO_ERROR;
- case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
- *value = MIN_UNDEQUEUED_BUFFERS;
- return NO_ERROR;
case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
- // SurfaceTextureClient currently never queues frames to SurfaceFlinger.
+ // TODO: this is not needed anymore
*value = 0;
return NO_ERROR;
case NATIVE_WINDOW_CONCRETE_TYPE:
+ // TODO: this is not needed anymore
*value = NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT;
return NO_ERROR;
}
- return BAD_VALUE;
+ return mSurfaceTexture->query(what, value);
}
int SurfaceTextureClient::perform(int operation, va_list args)
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 753e933..59a4cc5 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -17,23 +17,78 @@
#include <EGL/egl.h>
#include <gtest/gtest.h>
#include <gui/SurfaceTextureClient.h>
+#include <utils/threads.h>
namespace android {
class SurfaceTextureClientTest : public ::testing::Test {
protected:
+ SurfaceTextureClientTest():
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglSurface(EGL_NO_SURFACE),
+ mEglContext(EGL_NO_CONTEXT) {
+ }
+
virtual void SetUp() {
mST = new SurfaceTexture(123);
mSTC = new SurfaceTextureClient(mST);
+
+ // We need a valid GL context so we can test updateTexImage()
+ // This initializes EGL and create a dummy GL context with a
+ // pbuffer render target.
+ mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
+
+ EGLint majorVersion, minorVersion;
+ EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLConfig myConfig;
+ EGLint numConfigs = 0;
+ EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(),
+ &myConfig, 1, &numConfigs));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLint pbufferAttribs[] = {
+ EGL_WIDTH, 16,
+ EGL_HEIGHT, 16,
+ EGL_NONE };
+ mEglSurface = eglCreatePbufferSurface(mEglDisplay, myConfig, pbufferAttribs);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
+
+ mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, 0);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
+
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
}
virtual void TearDown() {
mST.clear();
mSTC.clear();
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(mEglDisplay, mEglContext);
+ eglDestroySurface(mEglDisplay, mEglSurface);
+ eglTerminate(mEglDisplay);
+ }
+
+ virtual EGLint const* getConfigAttribs() {
+ static EGLint sDefaultConfigAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_NONE
+ };
+
+ return sDefaultConfigAttribs;
}
sp<SurfaceTexture> mST;
sp<SurfaceTextureClient> mSTC;
+ EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
+ EGLContext mEglContext;
};
TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) {
@@ -94,8 +149,8 @@
EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, anw.get(),
NULL);
- ASSERT_NE(EGL_NO_SURFACE, eglSurface);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ EXPECT_NE(EGL_NO_SURFACE, eglSurface);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
eglTerminate(dpy);
}
@@ -204,6 +259,7 @@
sp<ANativeWindow> anw(mSTC);
sp<SurfaceTexture> st(mST);
ANativeWindowBuffer* buf[2];
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
EXPECT_NE(buf[0], buf[1]);
@@ -225,6 +281,7 @@
sp<ANativeWindow> anw(mSTC);
sp<SurfaceTexture> st(mST);
ANativeWindowBuffer* buf[2];
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
@@ -247,4 +304,203 @@
ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
}
+TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ ASSERT_EQ(OK, st->setSynchronousMode(false));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(OK, st->updateTexImage());
+
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(OK, st->updateTexImage());
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeSlowRetire) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+ EXPECT_NE(buf[0], buf[1]);
+ EXPECT_NE(buf[1], buf[2]);
+ EXPECT_NE(buf[2], buf[0]);
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]);
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeFastRetire) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+ EXPECT_NE(buf[0], buf[1]);
+ EXPECT_NE(buf[1], buf[2]);
+ EXPECT_NE(buf[2], buf[0]);
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]);
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]);
+
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ EXPECT_NE(buf[0], buf[1]);
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+ EXPECT_NE(buf[1], buf[2]);
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDequeueCurrent) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ android_native_buffer_t* firstBuf;
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &firstBuf));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), firstBuf));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), firstBuf);
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+ EXPECT_NE(buf[0], buf[1]);
+ EXPECT_NE(buf[1], buf[2]);
+ EXPECT_NE(buf[2], buf[0]);
+ EXPECT_EQ(firstBuf, buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+ EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ EXPECT_EQ(-EBUSY, anw->dequeueBuffer(anw.get(), &buf[2]));
+
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+
+ EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[2]));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeWaitRetire) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+
+ class MyThread : public Thread {
+ sp<SurfaceTexture> st;
+ EGLContext ctx;
+ EGLSurface sur;
+ EGLDisplay dpy;
+ bool mBufferRetired;
+ Mutex mLock;
+ virtual bool threadLoop() {
+ eglMakeCurrent(dpy, sur, sur, ctx);
+ usleep(20000);
+ Mutex::Autolock _l(mLock);
+ st->updateTexImage();
+ mBufferRetired = true;
+ eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ return false;
+ }
+ public:
+ MyThread(const sp<SurfaceTexture>& st)
+ : st(st), mBufferRetired(false) {
+ ctx = eglGetCurrentContext();
+ sur = eglGetCurrentSurface(EGL_DRAW);
+ dpy = eglGetCurrentDisplay();
+ eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ }
+ ~MyThread() {
+ eglMakeCurrent(dpy, sur, sur, ctx);
+ }
+ void bufferDequeued() {
+ Mutex::Autolock _l(mLock);
+ EXPECT_EQ(true, mBufferRetired);
+ }
+ };
+
+ android_native_buffer_t* buf[3];
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+ // dequeue/queue/update so we have a current buffer
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ st->updateTexImage();
+
+ MyThread* thread = new MyThread(st);
+ sp<Thread> threadBase(thread);
+
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+ thread->run();
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+ thread->bufferDequeued();
+ thread->requestExitAndWait();
+}
+
}