SurfaceTexture: inherit from ConsumerBase (try 2)

This change makes SurfaceTexture inherit from ConsumerBase.  It removes all of
the functionality from SurfaceTexture that is now provided by the base class.
This includes fixes for two bugs that were found after checking this change in
the first time and then reverting it.

Change-Id: Ie2d9f4f27cfef26fdac341de3152e842b01a58d2
This commit is contained in:
Jamie Gennis 2012-08-07 18:03:04 -07:00
parent 317357fdf7
commit 9fea3421ff
4 changed files with 166 additions and 318 deletions

View File

@ -86,42 +86,25 @@ private:
protected:
// TODO: Fix this comment
// ConsumerBase constructs a new ConsumerBase object. tex indicates the
// name of the OpenGL ES texture to which images are to be streamed.
// allowSynchronousMode specifies whether or not synchronous mode can be
// enabled. texTarget specifies the OpenGL ES texture target to which the
// texture will be bound in updateTexImage. useFenceSync specifies whether
// fences should be used to synchronize access to buffers if that behavior
// is enabled at compile-time. A custom bufferQueue can be specified
// if behavior for queue/dequeue/connect etc needs to be customized.
// Otherwise a default BufferQueue will be created and used.
//
// For legacy reasons, the ConsumerBase is created in a state where it is
// considered attached to an OpenGL ES context for the purposes of the
// attachToContext and detachFromContext methods. However, despite being
// considered "attached" to a context, the specific OpenGL ES context
// doesn't get latched until the first call to updateTexImage. After that
// point, all calls to updateTexImage must be made with the same OpenGL ES
// context current.
//
// A ConsumerBase may be detached from one OpenGL ES context and then
// attached to a different context using the detachFromContext and
// attachToContext methods, respectively. The intention of these methods is
// purely to allow a ConsumerBase to be transferred from one consumer
// context to another. If such a transfer is not needed there is no
// requirement that either of these methods be called.
// ConsumerBase constructs a new ConsumerBase object to consume image
// buffers from the given BufferQueue.
ConsumerBase(const sp<BufferQueue> &bufferQueue);
// Implementation of the BufferQueue::ConsumerListener interface. These
// calls are used to notify the ConsumerBase of asynchronous events in the
// BufferQueue.
// BufferQueue. These methods should not need to be overridden by derived
// classes, but if they are overridden the ConsumerBase implementation
// must be called from the derived class.
virtual void onFrameAvailable();
virtual void onBuffersReleased();
// freeBufferLocked frees up the given buffer slot. If the slot has been
// initialized this will release the reference to the GraphicBuffer in that
// slot and destroy the EGLImage in that slot. Otherwise it has no effect.
// slot. Otherwise it has no effect.
//
// Derived classes should override this method to clean up any state they
// keep per slot. If it is overridden, the derived class's implementation
// must call ConsumerBase::freeBufferLocked.
//
// This method must be called with mMutex locked.
virtual void freeBufferLocked(int slotIndex);
@ -131,18 +114,43 @@ protected:
// abandon method should be overridden by child classes to add abandon-
// time behavior.
//
// Derived classes should override this method to clean up any object
// state they keep (as opposed to per-slot state). If it is overridden,
// the derived class's implementation must call ConsumerBase::abandonLocked.
//
// This method must be called with mMutex locked.
virtual void abandonLocked();
// dumpLocked dumps the current state of the ConsumerBase object to the
// result string. Each line is prefixed with the string pointed to by the
// prefix argument. The buffer argument points to a buffer that may be
// used for intermediate formatting data, and the size of that buffer is
// indicated by the size argument.
//
// Derived classes should override this method to dump their internal
// state. If this method is overridden the derived class's implementation
// should call ConsumerBase::dumpLocked.
//
// This method must be called with mMutex locked.
virtual void dumpLocked(String8& result, const char* prefix, char* buffer,
size_t SIZE) const;
size_t size) const;
// acquireBufferLocked fetches the next buffer from the BufferQueue and
// updates the buffer slot for the buffer returned.
//
// Derived classes should override this method to perform any
// initialization that must take place the first time a buffer is assigned
// to a slot. If it is overridden the derived class's implementation must
// call ConsumerBase::acquireBufferLocked.
virtual status_t acquireBufferLocked(BufferQueue::BufferItem *item);
// releaseBufferLocked relinquishes control over a buffer, returning that
// control to the BufferQueue.
//
// Derived classes should override this method to perform any cleanup that
// must take place when a buffer is released back to the BufferQueue. If
// it is overridden the derived class's implementation must call
// ConsumerBase::acquireBufferLocked.
virtual status_t releaseBufferLocked(int buf, EGLDisplay display,
EGLSyncKHR eglFence, const sp<Fence>& fence);
@ -189,17 +197,12 @@ protected:
// if none is supplied
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
// variables of ConsumerBase objects. It must be locked whenever the
// member variables are accessed.
// member variables are accessed or when any of the *Locked methods are
// called.
//
// This mutex is intended to be locked by derived classes.
mutable Mutex mMutex;
};

View File

@ -24,6 +24,7 @@
#include <gui/ISurfaceTexture.h>
#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <ui/GraphicBuffer.h>
@ -39,20 +40,9 @@ namespace android {
class String8;
class SurfaceTexture : public virtual RefBase,
protected BufferQueue::ConsumerListener {
class SurfaceTexture : public ConsumerBase {
public:
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;
};
typedef ConsumerBase::FrameAvailableListener FrameAvailableListener;
// SurfaceTexture constructs a new SurfaceTexture object. tex indicates the
// name of the OpenGL ES texture to which images are to be streamed.
@ -82,8 +72,6 @@ public:
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true,
const sp<BufferQueue> &bufferQueue = 0);
virtual ~SurfaceTexture();
// updateTexImage sets the image contents of the target texture to that of
// the most recently queued buffer.
//
@ -132,16 +120,6 @@ public:
// documented by the source.
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
// requestBuffers when a with and height of zero is requested.
// A call to setDefaultBufferSize() may trigger requestBuffers() to
@ -180,17 +158,6 @@ public:
// synchronous mode.
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
// log messages.
void setName(const String8& name);
@ -204,7 +171,9 @@ public:
// getBufferQueue returns the BufferQueue object to which this
// SurfaceTexture is connected.
sp<BufferQueue> getBufferQueue() const;
sp<BufferQueue> getBufferQueue() const {
return mBufferQueue;
}
// detachFromContext detaches the SurfaceTexture from the calling thread's
// current OpenGL ES context. This context must be the same as the context
@ -233,17 +202,25 @@ public:
// current at the time of the last call to detachFromContext.
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:
// Implementation of the BufferQueue::ConsumerListener interface. These
// calls are used to notify the SurfaceTexture of asynchronous events in the
// BufferQueue.
virtual void onFrameAvailable();
virtual void onBuffersReleased();
// abandonLocked overrides the ConsumerBase method to clear
// mCurrentTextureBuf in addition to the ConsumerBase behavior.
virtual void abandonLocked();
// dumpLocked overrides the ConsumerBase method to dump SurfaceTexture-
// 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);
@ -271,7 +248,7 @@ private:
// slot and destroy the EGLImage in that slot. Otherwise it has no effect.
//
// This method must be called with mMutex locked.
void freeBufferLocked(int slotIndex);
virtual void freeBufferLocked(int slotIndex);
// computeCurrentTransformMatrix computes the transform matrix for the
// current texture. It uses mCurrentTransform and the current GraphicBuffer
@ -351,11 +328,9 @@ private:
struct EGLSlot {
EGLSlot()
: mEglImage(EGL_NO_IMAGE_KHR),
mFence(EGL_NO_SYNC_KHR) {
mEglFence(EGL_NO_SYNC_KHR) {
}
sp<GraphicBuffer> mGraphicBuffer;
// mEglImage is the EGLImage created from mGraphicBuffer.
EGLImageKHR mEglImage;
@ -363,14 +338,7 @@ private:
// 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;
// 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;
EGLSyncKHR mEglFence;
};
// mEglDisplay is the EGLDisplay with which this SurfaceTexture is currently
@ -392,23 +360,7 @@ private:
// 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
// of the buffer allocated to a slot.
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;
EGLSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS];
// mCurrentTexture is the buffer slot index of the buffer that is currently
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
@ -418,22 +370,13 @@ private:
// reset mCurrentTexture to INVALID_BUFFER_SLOT.
int mCurrentTexture;
// 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
// mAttached indicates whether the ConsumerBase is currently attached to
// an OpenGL ES context. For legacy reasons, this is initialized to true,
// indicating that the SurfaceTexture is considered to be attached to
// 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
// variables of SurfaceTexture objects. It must be locked whenever the
// member variables are accessed.
mutable Mutex mMutex;
};
// ----------------------------------------------------------------------------

View File

@ -53,7 +53,8 @@ static int32_t createProcessUniqueId() {
}
ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) :
mBufferQueue(bufferQueue) {
mAbandoned(false),
mBufferQueue(bufferQueue) {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());

View File

@ -26,6 +26,8 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <hardware/hardware.h>
#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
@ -96,14 +98,10 @@ static float mtxRot270[16] = {
static void mtxMul(float out[16], const float a[16], const float b[16]);
// Get an ID that's unique within this process.
static int32_t createProcessUniqueId() {
static volatile int32_t globalCounter = 0;
return android_atomic_inc(&globalCounter);
}
SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) :
ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue),
mCurrentTransform(0),
mCurrentTimestamp(0),
mFilteringEnabled(true),
@ -116,47 +114,15 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
mTexTarget(texTarget),
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
mAbandoned(false),
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mAttached(true)
{
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
ST_LOGV("SurfaceTexture");
if (bufferQueue == 0) {
ST_LOGV("Creating a new BufferQueue");
mBufferQueue = new BufferQueue(allowSynchronousMode);
}
else {
mBufferQueue = bufferQueue;
}
memcpy(mCurrentTransformMatrix, mtxIdentity,
sizeof(mCurrentTransformMatrix));
// 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();
mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
}
status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
@ -177,6 +143,44 @@ status_t SurfaceTexture::updateTexImage() {
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.
if (mEglSlots[slot].mEglImage == EGL_NO_IMAGE_KHR) {
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) {
ATRACE_CALL();
ST_LOGV("updateTexImage");
@ -217,97 +221,65 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
// In asynchronous mode the list is guaranteed to be one buffer
// deep, while in synchronous mode we use the oldest buffer.
err = mBufferQueue->acquireBuffer(&item);
err = acquireBufferLocked(&item);
if (err == NO_ERROR) {
int buf = item.mBuf;
// This buffer was newly allocated, so we need to clean up on our side
if (item.mGraphicBuffer != NULL) {
mEGLSlots[buf].mGraphicBuffer = 0;
if (mEGLSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(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
// not accept this buffer. this is used by SurfaceFlinger to
// reject buffers which have the wrong size
if (rejecter && rejecter->reject(mEGLSlots[buf].mGraphicBuffer, item)) {
mBufferQueue->releaseBuffer(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) {
releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
glBindTexture(mTexTarget, mTexName);
return NO_ERROR;
}
// 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 = mEGLSlots[buf].mEglImage;
if (image == EGL_NO_IMAGE_KHR) {
if (mEGLSlots[buf].mGraphicBuffer == NULL) {
ST_LOGE("updateTexImage: buffer at slot %d is null", buf);
err = BAD_VALUE;
} else {
image = createImage(dpy, mEGLSlots[buf].mGraphicBuffer);
mEGLSlots[buf].mEglImage = image;
if (image == EGL_NO_IMAGE_KHR) {
// NOTE: if dpy was invalid, createImage() is guaranteed to
// fail. so we'd end up here.
err = UNKNOWN_ERROR;
}
}
GLint error;
while ((error = glGetError()) != GL_NO_ERROR) {
ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
}
EGLImageKHR image = mEglSlots[buf].mEglImage;
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) {
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);
}
err = syncForReleaseLocked(dpy);
}
if (err != NO_ERROR) {
// Release the buffer we just acquired. It's not safe to
// release the old buffer, so instead we just drop the new frame.
mBufferQueue->releaseBuffer(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
return err;
}
ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
mCurrentTexture,
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0);
buf, mSlots[buf].mGraphicBuffer->handle);
// release old buffer
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
status_t status = mBufferQueue->releaseBuffer(mCurrentTexture, dpy,
mEGLSlots[mCurrentTexture].mFence,
mEGLSlots[mCurrentTexture].mReleaseFence);
mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR;
mEGLSlots[mCurrentTexture].mReleaseFence.clear();
if (status == BufferQueue::STALE_BUFFER_SLOT) {
freeBufferLocked(mCurrentTexture);
} else if (status != NO_ERROR) {
ST_LOGE("updateTexImage: released invalid buffer");
status_t status = releaseBufferLocked(mCurrentTexture, dpy,
mEglSlots[mCurrentTexture].mEglFence,
mSlots[mCurrentTexture].mFence);
if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
ST_LOGE("updateTexImage: failed to release buffer: %s (%d)",
strerror(-status), status);
err = status;
}
}
// Update the SurfaceTexture state.
mCurrentTexture = buf;
mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer;
mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
mCurrentCrop = item.mCrop;
mCurrentTransform = item.mTransform;
mCurrentScalingMode = item.mScalingMode;
@ -330,20 +302,20 @@ void SurfaceTexture::setReleaseFence(int fenceFd) {
sp<Fence> fence(new Fence(fenceFd));
if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
return;
if (!mEGLSlots[mCurrentTexture].mReleaseFence.get()) {
mEGLSlots[mCurrentTexture].mReleaseFence = fence;
if (!mSlots[mCurrentTexture].mFence.get()) {
mSlots[mCurrentTexture].mFence = fence;
} else {
sp<Fence> mergedFence = Fence::merge(
String8("SurfaceTexture merged release"),
mEGLSlots[mCurrentTexture].mReleaseFence, fence);
mSlots[mCurrentTexture].mFence, fence);
if (!mergedFence.get()) {
ST_LOGE("failed to merge release fences");
// synchronization is broken, the best we can do is hope fences
// signal in order so the new fence will act like a union
mEGLSlots[mCurrentTexture].mReleaseFence = fence;
mSlots[mCurrentTexture].mFence = fence;
return;
}
mEGLSlots[mCurrentTexture].mReleaseFence = mergedFence;
mSlots[mCurrentTexture].mFence = mergedFence;
}
}
@ -390,10 +362,10 @@ status_t SurfaceTexture::detachFromContext() {
// SurfaceTexture gets attached to a new OpenGL ES context (and thus gets a
// new EGLDisplay).
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) {
eglDestroyImageKHR(mEglDisplay, img);
mEGLSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
}
}
@ -481,7 +453,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
ST_LOGV("syncForReleaseLocked");
if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
EGLSyncKHR fence = mEGLSlots[mCurrentTexture].mFence;
EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence;
if (fence != EGL_NO_SYNC_KHR) {
// 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
@ -509,7 +481,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
return UNKNOWN_ERROR;
}
glFlush();
mEGLSlots[mCurrentTexture].mFence = fence;
mEglSlots[mCurrentTexture].mEglFence = fence;
}
return OK;
@ -607,10 +579,12 @@ void SurfaceTexture::computeCurrentTransformMatrix() {
// only need to shrink by a half a pixel.
shrinkAmount = 0.5;
break;
default:
// If we don't recognize the format, we must assume the
// worst case (that we care about), which is YUV420.
shrinkAmount = 1.0;
break;
}
}
@ -650,13 +624,6 @@ nsecs_t SurfaceTexture::getTimestamp() {
return mCurrentTimestamp;
}
void SurfaceTexture::setFrameAvailableListener(
const sp<FrameAvailableListener>& listener) {
ST_LOGV("setFrameAvailableListener");
Mutex::Autolock lock(mMutex);
mFrameAvailableListener = listener;
}
EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer) {
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
@ -736,35 +703,22 @@ bool SurfaceTexture::isSynchronousMode() const {
void SurfaceTexture::freeBufferLocked(int slotIndex) {
ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
mEGLSlots[slotIndex].mGraphicBuffer = 0;
if (slotIndex == mCurrentTexture) {
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
}
EGLImageKHR img = mEGLSlots[slotIndex].mEglImage;
EGLImageKHR img = mEglSlots[slotIndex].mEglImage;
if (img != EGL_NO_IMAGE_KHR) {
ST_LOGV("destroying EGLImage dpy=%p img=%p", mEglDisplay, img);
eglDestroyImageKHR(mEglDisplay, img);
}
mEGLSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
mEglSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
ConsumerBase::freeBufferLocked(slotIndex);
}
void SurfaceTexture::abandon() {
ST_LOGV("abandon");
Mutex::Autolock lock(mMutex);
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::abandonLocked() {
ST_LOGV("abandonLocked");
mCurrentTextureBuf.clear();
ConsumerBase::abandonLocked();
}
void SurfaceTexture::setName(const String8& name) {
@ -796,71 +750,18 @@ status_t SurfaceTexture::setSynchronousMode(bool enabled) {
return mBufferQueue->setSynchronousMode(enabled);
}
// Used for refactoring, should not be in final interface
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
void SurfaceTexture::dumpLocked(String8& result, const char* prefix,
char* buffer, size_t size) 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, "%smTexName=%d, mAbandoned=%d\n", prefix, mTexName,
int(mAbandoned));
snprintf(buffer, size,
"%smTexName=%d mCurrentTexture=%d\n"
"%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
mCurrentTransform);
result.append(buffer);
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);
}
ConsumerBase::dumpLocked(result, prefix, buffer, size);
}
static void mtxMul(float out[16], const float a[16], const float b[16]) {