diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h index d2bf0f649..a0ddca872 100644 --- a/include/gui/ConsumerBase.h +++ b/include/gui/ConsumerBase.h @@ -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); // 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); @@ -189,17 +197,12 @@ protected: // if none is supplied sp 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; }; diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 66c390a84..98741c550 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -24,6 +24,7 @@ #include #include +#include #include @@ -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 = 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& 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 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 getBufferQueue() const; + sp 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); 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 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 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 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 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; }; // ---------------------------------------------------------------------------- diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index af19ac081..17bbfd11e 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -53,7 +53,8 @@ static int32_t createProcessUniqueId() { } ConsumerBase::ConsumerBase(const sp& 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()); diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 66660810f..c0b20dfda 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include #include #include @@ -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) : + 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 listener; - sp proxy; - listener = static_cast(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) { + 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(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 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& listener) { - ST_LOGV("setFrameAvailableListener"); - Mutex::Autolock lock(mMutex); - mFrameAvailableListener = listener; -} - EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, const sp& 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 SurfaceTexture::getBufferQueue() const { - Mutex::Autolock lock(mMutex); - return mBufferQueue; -} - -void SurfaceTexture::onFrameAvailable() { - ST_LOGV("onFrameAvailable"); - - sp 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]) {