Merge "Revert "SurfaceTexture: inherit from ConsumerBase"" into jb-mr1-dev

This commit is contained in:
Jamie Gennis 2012-08-20 14:33:23 -07:00 committed by Android (Google) Code Review
commit 729f48082e
4 changed files with 288 additions and 122 deletions

View File

@ -189,6 +189,14 @@ protected:
// if none is supplied // if none is supplied
sp<BufferQueue> mBufferQueue; sp<BufferQueue> mBufferQueue;
// mAttached indicates whether the ConsumerBase is currently attached to
// an OpenGL ES context. For legacy reasons, this is initialized to true,
// indicating that the ConsumerBase is considered to be attached to
// whatever context is current at the time of the first updateTexImage call.
// It is set to false by detachFromContext, and then set to true again by
// attachToContext.
bool mAttached;
// 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 ConsumerBase objects. It must be locked whenever the // variables of ConsumerBase objects. It must be locked whenever the
// member variables are accessed. // member variables are accessed.

View File

@ -24,7 +24,6 @@
#include <gui/ISurfaceTexture.h> #include <gui/ISurfaceTexture.h>
#include <gui/BufferQueue.h> #include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <ui/GraphicBuffer.h> #include <ui/GraphicBuffer.h>
@ -40,9 +39,20 @@ namespace android {
class String8; class String8;
class SurfaceTexture : public ConsumerBase { class SurfaceTexture : public virtual RefBase,
protected BufferQueue::ConsumerListener {
public: public:
typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; struct FrameAvailableListener : public virtual RefBase {
// onFrameAvailable() is called each time an additional frame becomes
// available for consumption. This means that frames that are queued
// while in asynchronous mode only trigger the callback if no previous
// frames are pending. Frames queued while in synchronous mode always
// trigger the callback.
//
// This is called without any lock held and can be called concurrently
// by multiple threads.
virtual void onFrameAvailable() = 0;
};
// SurfaceTexture constructs a new SurfaceTexture object. tex indicates the // SurfaceTexture constructs a new SurfaceTexture object. tex indicates the
// name of the OpenGL ES texture to which images are to be streamed. // name of the OpenGL ES texture to which images are to be streamed.
@ -72,6 +82,8 @@ public:
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true, GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true,
const sp<BufferQueue> &bufferQueue = 0); const sp<BufferQueue> &bufferQueue = 0);
virtual ~SurfaceTexture();
// updateTexImage sets the image contents of the target texture to that of // updateTexImage sets the image contents of the target texture to that of
// the most recently queued buffer. // the most recently queued buffer.
// //
@ -120,6 +132,16 @@ public:
// documented by the source. // documented by the source.
int64_t getTimestamp(); int64_t getTimestamp();
// setFrameAvailableListener sets the listener object that will be notified
// when a new frame becomes available.
void setFrameAvailableListener(const sp<FrameAvailableListener>& listener);
// getAllocator retrieves the binder object that must be referenced as long
// as the GraphicBuffers dequeued from this SurfaceTexture are referenced.
// Holding this binder reference prevents SurfaceFlinger from freeing the
// buffers before the client is done with them.
sp<IBinder> getAllocator();
// setDefaultBufferSize is used to set the size of buffers returned by // setDefaultBufferSize is used to set the size of buffers returned by
// requestBuffers when a with and height of zero is requested. // requestBuffers when a with and height of zero is requested.
// A call to setDefaultBufferSize() may trigger requestBuffers() to // A call to setDefaultBufferSize() may trigger requestBuffers() to
@ -158,6 +180,17 @@ public:
// synchronous mode. // synchronous mode.
bool isSynchronousMode() const; bool isSynchronousMode() const;
// abandon frees all the buffers and puts the SurfaceTexture into the
// 'abandoned' state. Once put in this state the SurfaceTexture can never
// leave it. When in the 'abandoned' state, all methods of the
// ISurfaceTexture interface will fail with the NO_INIT error.
//
// Note that while calling this method causes all the buffers to be freed
// from the perspective of the the SurfaceTexture, if there are additional
// references on the buffers (e.g. if a buffer is referenced by a client or
// by OpenGL ES as a texture) then those buffer will remain allocated.
void abandon();
// set the name of the SurfaceTexture that will be used to identify it in // set the name of the SurfaceTexture that will be used to identify it in
// log messages. // log messages.
void setName(const String8& name); void setName(const String8& name);
@ -171,9 +204,7 @@ public:
// getBufferQueue returns the BufferQueue object to which this // getBufferQueue returns the BufferQueue object to which this
// SurfaceTexture is connected. // SurfaceTexture is connected.
sp<BufferQueue> getBufferQueue() const { sp<BufferQueue> getBufferQueue() const;
return mBufferQueue;
}
// detachFromContext detaches the SurfaceTexture from the calling thread's // detachFromContext detaches the SurfaceTexture from the calling thread's
// current OpenGL ES context. This context must be the same as the context // current OpenGL ES context. This context must be the same as the context
@ -202,25 +233,17 @@ public:
// current at the time of the last call to detachFromContext. // current at the time of the last call to detachFromContext.
status_t attachToContext(GLuint tex); status_t attachToContext(GLuint tex);
// 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;
protected: protected:
// abandonLocked overrides the ConsumerBase method to clear // Implementation of the BufferQueue::ConsumerListener interface. These
// mCurrentTextureBuf in addition to the ConsumerBase behavior. // calls are used to notify the SurfaceTexture of asynchronous events in the
virtual void abandonLocked(); // BufferQueue.
virtual void onFrameAvailable();
// dumpLocked overrides the ConsumerBase method to dump SurfaceTexture- virtual void onBuffersReleased();
// specific info in addition to the ConsumerBase behavior.
virtual void dumpLocked(String8& result, const char* prefix, char* buffer,
size_t size) const;
// acquireBufferLocked overrides the ConsumerBase method to update the
// mEglSlots array in addition to the ConsumerBase behavior.
virtual status_t acquireBufferLocked(BufferQueue::BufferItem *item);
// releaseBufferLocked overrides the ConsumerBase method to update the
// mEglSlots array in addition to the ConsumerBase.
virtual status_t releaseBufferLocked(int buf, EGLDisplay display,
EGLSyncKHR eglFence, const sp<Fence>& fence);
static bool isExternalFormat(uint32_t format); static bool isExternalFormat(uint32_t format);
@ -328,9 +351,11 @@ private:
struct EGLSlot { struct EGLSlot {
EGLSlot() EGLSlot()
: mEglImage(EGL_NO_IMAGE_KHR), : mEglImage(EGL_NO_IMAGE_KHR),
mEglFence(EGL_NO_SYNC_KHR) { mFence(EGL_NO_SYNC_KHR) {
} }
sp<GraphicBuffer> mGraphicBuffer;
// mEglImage is the EGLImage created from mGraphicBuffer. // mEglImage is the EGLImage created from mGraphicBuffer.
EGLImageKHR mEglImage; EGLImageKHR mEglImage;
@ -338,7 +363,14 @@ private:
// associated with this buffer slot may be dequeued. It is initialized // associated with this buffer slot may be dequeued. It is initialized
// 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 mEglFence; EGLSyncKHR mFence;
// mReleaseFence is a fence which will signal when the buffer
// associated with this buffer slot is no longer being used by the
// consumer and can be overwritten. The buffer can be dequeued before
// the fence signals; the producer is responsible for delaying writes
// until it signals.
sp<Fence> mReleaseFence;
}; };
// mEglDisplay is the EGLDisplay with which this SurfaceTexture is currently // mEglDisplay is the EGLDisplay with which this SurfaceTexture is currently
@ -360,7 +392,23 @@ private:
// slot that has not yet been used. The buffer allocated to a slot will also // slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that // be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot. // of the buffer allocated to a slot.
EGLSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS]; EGLSlot mEGLSlots[BufferQueue::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;
// mFrameAvailableListener is the listener object that will be called when a
// new frame becomes available. If it is not NULL it will be called from
// queueBuffer.
sp<FrameAvailableListener> mFrameAvailableListener;
// mCurrentTexture is the buffer slot index of the buffer that is currently // mCurrentTexture is the buffer slot index of the buffer that is currently
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
@ -370,13 +418,22 @@ private:
// reset mCurrentTexture to INVALID_BUFFER_SLOT. // reset mCurrentTexture to INVALID_BUFFER_SLOT.
int mCurrentTexture; int mCurrentTexture;
// mAttached indicates whether the ConsumerBase is currently attached to // The SurfaceTexture has-a BufferQueue and is responsible for creating this object
// if none is supplied
sp<BufferQueue> mBufferQueue;
// mAttached indicates whether the SurfaceTexture is currently attached to
// an OpenGL ES context. For legacy reasons, this is initialized to true, // an OpenGL ES context. For legacy reasons, this is initialized to true,
// indicating that the ConsumerBase is considered to be attached to // indicating that the SurfaceTexture is considered to be attached to
// whatever context is current at the time of the first updateTexImage call. // whatever context is current at the time of the first updateTexImage call.
// It is set to false by detachFromContext, and then set to true again by // It is set to false by detachFromContext, and then set to true again by
// attachToContext. // attachToContext.
bool mAttached; bool mAttached;
// 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;
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -53,8 +53,7 @@ static int32_t createProcessUniqueId() {
} }
ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) : ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) :
mAbandoned(false), mBufferQueue(bufferQueue) {
mBufferQueue(bufferQueue) {
// 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()); mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());

View File

@ -26,8 +26,6 @@
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
#include <GLES2/gl2ext.h> #include <GLES2/gl2ext.h>
#include <hardware/hardware.h>
#include <gui/IGraphicBufferAlloc.h> #include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h> #include <gui/SurfaceComposerClient.h>
@ -98,10 +96,14 @@ 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, const sp<BufferQueue> &bufferQueue) : GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) :
ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue),
mCurrentTransform(0), mCurrentTransform(0),
mCurrentTimestamp(0), mCurrentTimestamp(0),
mFilteringEnabled(true), mFilteringEnabled(true),
@ -114,15 +116,47 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
mTexTarget(texTarget), mTexTarget(texTarget),
mEglDisplay(EGL_NO_DISPLAY), mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT), mEglContext(EGL_NO_CONTEXT),
mAbandoned(false),
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mAttached(true) mAttached(true)
{ {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
ST_LOGV("SurfaceTexture"); ST_LOGV("SurfaceTexture");
if (bufferQueue == 0) {
ST_LOGV("Creating a new BufferQueue");
mBufferQueue = new BufferQueue(allowSynchronousMode);
}
else {
mBufferQueue = bufferQueue;
}
memcpy(mCurrentTransformMatrix, mtxIdentity, memcpy(mCurrentTransformMatrix, mtxIdentity,
sizeof(mCurrentTransformMatrix)); sizeof(mCurrentTransformMatrix));
mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); // Note that we can't create an sp<...>(this) in a ctor that will not keep a
// reference once the ctor ends, as that would cause the refcount of 'this'
// dropping to 0 at the end of the ctor. Since all we need is a wp<...>
// that's what we create.
wp<BufferQueue::ConsumerListener> listener;
sp<BufferQueue::ConsumerListener> proxy;
listener = static_cast<BufferQueue::ConsumerListener*>(this);
proxy = new BufferQueue::ProxyConsumerListener(listener);
status_t err = mBufferQueue->consumerConnect(proxy);
if (err != NO_ERROR) {
ST_LOGE("SurfaceTexture: error connecting to BufferQueue: %s (%d)",
strerror(-err), err);
} else {
mBufferQueue->setConsumerName(mName);
mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
}
}
SurfaceTexture::~SurfaceTexture() {
ST_LOGV("~SurfaceTexture");
abandon();
} }
status_t SurfaceTexture::setBufferCountServer(int bufferCount) { status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
@ -143,42 +177,6 @@ status_t SurfaceTexture::updateTexImage() {
return SurfaceTexture::updateTexImage(NULL); return SurfaceTexture::updateTexImage(NULL);
} }
status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) {
status_t err = ConsumerBase::acquireBufferLocked(item);
if (err != NO_ERROR) {
return err;
}
int slot = item->mBuf;
if (item->mGraphicBuffer != NULL) {
if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage);
mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
}
}
// Update the GL texture object. We may have to do this even when
// item.mGraphicBuffer == NULL, if we destroyed the EGLImage when
// detaching from a context but the buffer has not been re-allocated.
EGLImageKHR image = createImage(mEglDisplay, mSlots[slot].mGraphicBuffer);
if (image == EGL_NO_IMAGE_KHR) {
return UNKNOWN_ERROR;
}
mEglSlots[slot].mEglImage = image;
return NO_ERROR;
}
status_t SurfaceTexture::releaseBufferLocked(int buf, EGLDisplay display,
EGLSyncKHR eglFence, const sp<Fence>& fence) {
status_t err = ConsumerBase::releaseBufferLocked(buf, mEglDisplay,
eglFence, fence);
mEglSlots[mCurrentTexture].mEglFence = EGL_NO_SYNC_KHR;
return err;
}
status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) { status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
ATRACE_CALL(); ATRACE_CALL();
ST_LOGV("updateTexImage"); ST_LOGV("updateTexImage");
@ -219,65 +217,97 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
// 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.
err = acquireBufferLocked(&item); err = mBufferQueue->acquireBuffer(&item);
if (err == NO_ERROR) { if (err == NO_ERROR) {
int buf = item.mBuf; 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(dpy, mEGLSlots[buf].mEglImage);
mEGLSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
}
mEGLSlots[buf].mGraphicBuffer = item.mGraphicBuffer;
}
// we call the rejecter here, in case the caller has a reason to // we call the rejecter here, in case the caller has a reason to
// not accept this buffer. this is used by SurfaceFlinger to // not accept this buffer. this is used by SurfaceFlinger to
// reject buffers which have the wrong size // reject buffers which have the wrong size
if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) { if (rejecter && rejecter->reject(mEGLSlots[buf].mGraphicBuffer, item)) {
releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence); mBufferQueue->releaseBuffer(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
glBindTexture(mTexTarget, mTexName); glBindTexture(mTexTarget, mTexName);
return NO_ERROR; return NO_ERROR;
} }
GLint error; // Update the GL texture object. We may have to do this even when
while ((error = glGetError()) != GL_NO_ERROR) { // item.mGraphicBuffer == NULL, if we destroyed the EGLImage when
ST_LOGW("updateTexImage: clearing GL error: %#04x", error); // detaching from a context but the buffer has not been re-allocated.
} EGLImageKHR image = mEGLSlots[buf].mEglImage;
if (image == EGL_NO_IMAGE_KHR) {
EGLImageKHR image = mEglSlots[buf].mEglImage; if (mEGLSlots[buf].mGraphicBuffer == NULL) {
glBindTexture(mTexTarget, mTexName); ST_LOGE("updateTexImage: buffer at slot %d is null", buf);
glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); err = BAD_VALUE;
} else {
while ((error = glGetError()) != GL_NO_ERROR) { image = createImage(dpy, mEGLSlots[buf].mGraphicBuffer);
ST_LOGE("updateTexImage: error binding external texture image %p " mEGLSlots[buf].mEglImage = image;
"(slot %d): %#04x", image, buf, error); if (image == EGL_NO_IMAGE_KHR) {
err = UNKNOWN_ERROR; // NOTE: if dpy was invalid, createImage() is guaranteed to
// fail. so we'd end up here.
err = UNKNOWN_ERROR;
}
}
} }
if (err == NO_ERROR) { if (err == NO_ERROR) {
err = syncForReleaseLocked(dpy); GLint error;
while ((error = glGetError()) != GL_NO_ERROR) {
ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
}
glBindTexture(mTexTarget, mTexName);
glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
while ((error = glGetError()) != GL_NO_ERROR) {
ST_LOGE("updateTexImage: error binding external texture image %p "
"(slot %d): %#04x", image, buf, error);
err = UNKNOWN_ERROR;
}
if (err == NO_ERROR) {
err = syncForReleaseLocked(dpy);
}
} }
if (err != NO_ERROR) { if (err != NO_ERROR) {
// Release the buffer we just acquired. It's not safe to // Release the buffer we just acquired. It's not safe to
// release the old buffer, so instead we just drop the new frame. // release the old buffer, so instead we just drop the new frame.
releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence); mBufferQueue->releaseBuffer(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
return err; return err;
} }
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 != NULL ? item.mGraphicBuffer->handle : 0);
// release old buffer // release old buffer
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
status_t status = releaseBufferLocked(mCurrentTexture, dpy, status_t status = mBufferQueue->releaseBuffer(mCurrentTexture, dpy,
mEglSlots[mCurrentTexture].mEglFence, mEGLSlots[mCurrentTexture].mFence,
mSlots[mCurrentTexture].mFence); mEGLSlots[mCurrentTexture].mReleaseFence);
if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) { mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR;
ST_LOGE("updateTexImage: failed to release buffer: %s (%d)", mEGLSlots[mCurrentTexture].mReleaseFence.clear();
strerror(-status), status); if (status == BufferQueue::STALE_BUFFER_SLOT) {
freeBufferLocked(mCurrentTexture);
} else if (status != NO_ERROR) {
ST_LOGE("updateTexImage: released invalid buffer");
err = status; err = status;
} }
} }
// Update the SurfaceTexture state. // Update the SurfaceTexture state.
mCurrentTexture = buf; mCurrentTexture = buf;
mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer;
mCurrentCrop = item.mCrop; mCurrentCrop = item.mCrop;
mCurrentTransform = item.mTransform; mCurrentTransform = item.mTransform;
mCurrentScalingMode = item.mScalingMode; mCurrentScalingMode = item.mScalingMode;
@ -300,20 +330,20 @@ void SurfaceTexture::setReleaseFence(int fenceFd) {
sp<Fence> fence(new Fence(fenceFd)); sp<Fence> fence(new Fence(fenceFd));
if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
return; return;
if (!mSlots[mCurrentTexture].mFence.get()) { if (!mEGLSlots[mCurrentTexture].mReleaseFence.get()) {
mSlots[mCurrentTexture].mFence = fence; mEGLSlots[mCurrentTexture].mReleaseFence = fence;
} else { } else {
sp<Fence> mergedFence = Fence::merge( sp<Fence> mergedFence = Fence::merge(
String8("SurfaceTexture merged release"), String8("SurfaceTexture merged release"),
mSlots[mCurrentTexture].mFence, fence); mEGLSlots[mCurrentTexture].mReleaseFence, fence);
if (!mergedFence.get()) { if (!mergedFence.get()) {
ST_LOGE("failed to merge release fences"); ST_LOGE("failed to merge release fences");
// synchronization is broken, the best we can do is hope fences // synchronization is broken, the best we can do is hope fences
// signal in order so the new fence will act like a union // signal in order so the new fence will act like a union
mSlots[mCurrentTexture].mFence = fence; mEGLSlots[mCurrentTexture].mReleaseFence = fence;
return; return;
} }
mSlots[mCurrentTexture].mFence = mergedFence; mEGLSlots[mCurrentTexture].mReleaseFence = mergedFence;
} }
} }
@ -360,10 +390,10 @@ status_t SurfaceTexture::detachFromContext() {
// SurfaceTexture gets attached to a new OpenGL ES context (and thus gets a // SurfaceTexture gets attached to a new OpenGL ES context (and thus gets a
// new EGLDisplay). // new EGLDisplay).
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
EGLImageKHR img = mEglSlots[i].mEglImage; EGLImageKHR img = mEGLSlots[i].mEglImage;
if (img != EGL_NO_IMAGE_KHR) { if (img != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mEglDisplay, img); eglDestroyImageKHR(mEglDisplay, img);
mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR; mEGLSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
} }
} }
@ -451,7 +481,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
ST_LOGV("syncForReleaseLocked"); ST_LOGV("syncForReleaseLocked");
if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence; EGLSyncKHR fence = mEGLSlots[mCurrentTexture].mFence;
if (fence != EGL_NO_SYNC_KHR) { if (fence != EGL_NO_SYNC_KHR) {
// There is already a fence for the current slot. We need to wait // There is already a fence for the current slot. We need to wait
// on that before replacing it with another fence to ensure that all // on that before replacing it with another fence to ensure that all
@ -479,7 +509,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
glFlush(); glFlush();
mEglSlots[mCurrentTexture].mEglFence = fence; mEGLSlots[mCurrentTexture].mFence = fence;
} }
return OK; return OK;
@ -577,12 +607,10 @@ void SurfaceTexture::computeCurrentTransformMatrix() {
// only need to shrink by a half a pixel. // only need to shrink by a half a pixel.
shrinkAmount = 0.5; shrinkAmount = 0.5;
break; break;
default: default:
// If we don't recognize the format, we must assume the // If we don't recognize the format, we must assume the
// worst case (that we care about), which is YUV420. // worst case (that we care about), which is YUV420.
shrinkAmount = 1.0; shrinkAmount = 1.0;
break;
} }
} }
@ -622,6 +650,13 @@ nsecs_t SurfaceTexture::getTimestamp() {
return mCurrentTimestamp; return mCurrentTimestamp;
} }
void SurfaceTexture::setFrameAvailableListener(
const sp<FrameAvailableListener>& listener) {
ST_LOGV("setFrameAvailableListener");
Mutex::Autolock lock(mMutex);
mFrameAvailableListener = listener;
}
EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer) { const sp<GraphicBuffer>& graphicBuffer) {
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
@ -701,21 +736,35 @@ bool SurfaceTexture::isSynchronousMode() const {
void SurfaceTexture::freeBufferLocked(int slotIndex) { void SurfaceTexture::freeBufferLocked(int slotIndex) {
ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
mEGLSlots[slotIndex].mGraphicBuffer = 0;
if (slotIndex == mCurrentTexture) { if (slotIndex == mCurrentTexture) {
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
} }
EGLImageKHR img = mEglSlots[slotIndex].mEglImage; EGLImageKHR img = mEGLSlots[slotIndex].mEglImage;
if (img != EGL_NO_IMAGE_KHR) { if (img != EGL_NO_IMAGE_KHR) {
ST_LOGV("destroying EGLImage dpy=%p img=%p", mEglDisplay, img); ST_LOGV("destroying EGLImage dpy=%p img=%p", mEglDisplay, img);
eglDestroyImageKHR(mEglDisplay, img); eglDestroyImageKHR(mEglDisplay, img);
} }
mEglSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR; mEGLSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
} }
void SurfaceTexture::abandonLocked() { void SurfaceTexture::abandon() {
ST_LOGV("abandonLocked"); ST_LOGV("abandon");
mCurrentTextureBuf.clear(); Mutex::Autolock lock(mMutex);
ConsumerBase::abandonLocked();
if (!mAbandoned) {
mAbandoned = true;
mCurrentTextureBuf.clear();
// destroy all egl buffers
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
freeBufferLocked(i);
}
// disconnect from the BufferQueue
mBufferQueue->consumerDisconnect();
mBufferQueue.clear();
}
} }
void SurfaceTexture::setName(const String8& name) { void SurfaceTexture::setName(const String8& name) {
@ -747,18 +796,71 @@ status_t SurfaceTexture::setSynchronousMode(bool enabled) {
return mBufferQueue->setSynchronousMode(enabled); return mBufferQueue->setSynchronousMode(enabled);
} }
void SurfaceTexture::dumpLocked(String8& result, const char* prefix, // Used for refactoring, should not be in final interface
char* buffer, size_t size) const sp<BufferQueue> SurfaceTexture::getBufferQueue() const {
Mutex::Autolock lock(mMutex);
return mBufferQueue;
}
void SurfaceTexture::onFrameAvailable() {
ST_LOGV("onFrameAvailable");
sp<FrameAvailableListener> listener;
{ // scope for the lock
Mutex::Autolock lock(mMutex);
listener = mFrameAvailableListener;
}
if (listener != NULL) {
ST_LOGV("actually calling onFrameAvailable");
listener->onFrameAvailable();
}
}
void SurfaceTexture::onBuffersReleased() {
ST_LOGV("onBuffersReleased");
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
// Nothing to do if we're already abandoned.
return;
}
uint32_t mask = 0;
mBufferQueue->getReleasedBuffers(&mask);
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
if (mask & (1 << i)) {
freeBufferLocked(i);
}
}
}
void SurfaceTexture::dump(String8& result) const
{ {
snprintf(buffer, size, char buffer[1024];
"%smTexName=%d mCurrentTexture=%d\n" dump(result, "", buffer, 1024);
"%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", }
prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom, void SurfaceTexture::dump(String8& result, const char* prefix,
mCurrentTransform); char* buffer, size_t SIZE) const
{
Mutex::Autolock _l(mMutex);
snprintf(buffer, SIZE, "%smTexName=%d, mAbandoned=%d\n", prefix, mTexName,
int(mAbandoned));
result.append(buffer); result.append(buffer);
ConsumerBase::dumpLocked(result, prefix, buffer, size); snprintf(buffer, SIZE,
"%snext : {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d}\n",
prefix, mCurrentCrop.left,
mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
mCurrentTransform, mCurrentTexture
);
result.append(buffer);
if (!mAbandoned) {
mBufferQueue->dump(result, prefix, buffer, SIZE);
}
} }
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]) {