Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed059a8d authored by Jamie Gennis's avatar Jamie Gennis
Browse files

SurfaceTexture: inherit from ConsumerBase

This change makes SurfaceTexture inherit from ConsumerBase.  It removes all of
the functionality from SurfaceTexture that is now provided by the base class.

Change-Id: I4a881df42810a14ee32d4ef7c8772a8f2510f4c7
parent 3559b07a
Loading
Loading
Loading
Loading
+0 −8
Original line number Diff line number Diff line
@@ -189,14 +189,6 @@ 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.
+28 −85
Original line number Diff line number Diff line
@@ -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);

@@ -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;
};

// ----------------------------------------------------------------------------
+2 −1
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ static int32_t createProcessUniqueId() {
}

ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) :
        mAbandoned(false),
        mBufferQueue(bufferQueue) {
    // Choose a name using the PID and a process-unique ID.
    mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
+91 −193
Original line number Diff line number Diff line
@@ -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,48 +114,16 @@ 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();
}

status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
    Mutex::Autolock lock(mMutex);
@@ -177,6 +143,42 @@ 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.
    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,53 +219,25 @@ 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;
                }
            }
        }

        if (err == NO_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);

@@ -276,38 +250,34 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
        if (err == NO_ERROR) {
            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 +300,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 +360,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 +451,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 +479,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
            return UNKNOWN_ERROR;
        }
        glFlush();
        mEGLSlots[mCurrentTexture].mFence = fence;
        mEglSlots[mCurrentTexture].mEglFence = fence;
    }

    return OK;
@@ -607,10 +577,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 +622,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 +701,21 @@ 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;
}

void SurfaceTexture::abandon() {
    ST_LOGV("abandon");
    Mutex::Autolock lock(mMutex);

    if (!mAbandoned) {
        mAbandoned = true;
void SurfaceTexture::abandonLocked() {
    ST_LOGV("abandonLocked");
    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();
    }
    ConsumerBase::abandonLocked();
}

void SurfaceTexture::setName(const String8& name) {
@@ -796,71 +747,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));
    result.append(buffer);

    snprintf(buffer, SIZE,
            "%snext   : {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d}\n",
            prefix, mCurrentCrop.left,
    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, mCurrentTexture
    );
       mCurrentTransform);
    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]) {