Loading include/gui/ConsumerBase.h +8 −0 Original line number Diff line number Diff line Loading @@ -189,6 +189,14 @@ 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. Loading include/gui/SurfaceTexture.h +85 −28 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ #include <gui/ISurfaceTexture.h> #include <gui/BufferQueue.h> #include <gui/ConsumerBase.h> #include <ui/GraphicBuffer.h> Loading @@ -40,9 +39,20 @@ namespace android { class String8; class SurfaceTexture : public ConsumerBase { class SurfaceTexture : public virtual RefBase, protected BufferQueue::ConsumerListener { 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 // name of the OpenGL ES texture to which images are to be streamed. Loading Loading @@ -72,6 +82,8 @@ 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. // Loading Loading @@ -120,6 +132,16 @@ 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 Loading Loading @@ -158,6 +180,17 @@ 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); Loading @@ -171,9 +204,7 @@ public: // getBufferQueue returns the BufferQueue object to which this // SurfaceTexture is connected. sp<BufferQueue> getBufferQueue() const { return mBufferQueue; } sp<BufferQueue> getBufferQueue() const; // detachFromContext detaches the SurfaceTexture from the calling thread's // current OpenGL ES context. This context must be the same as the context Loading Loading @@ -202,25 +233,17 @@ public: // current at the time of the last call to detachFromContext. status_t attachToContext(GLuint tex); protected: // 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; // 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; // acquireBufferLocked overrides the ConsumerBase method to update the // mEglSlots array in addition to the ConsumerBase behavior. virtual status_t acquireBufferLocked(BufferQueue::BufferItem *item); protected: // 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); // 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(); static bool isExternalFormat(uint32_t format); Loading Loading @@ -328,9 +351,11 @@ private: struct EGLSlot { EGLSlot() : 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. EGLImageKHR mEglImage; Loading @@ -338,7 +363,14 @@ 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 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 Loading @@ -360,7 +392,23 @@ 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]; 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 // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, Loading @@ -370,13 +418,22 @@ private: // reset mCurrentTexture to INVALID_BUFFER_SLOT. 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, // 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. // 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; }; // ---------------------------------------------------------------------------- Loading libs/gui/ConsumerBase.cpp +1 −2 Original line number Diff line number Diff line Loading @@ -53,7 +53,6 @@ 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()); Loading libs/gui/SurfaceTexture.cpp +193 −91 Original line number Diff line number Diff line Loading @@ -26,8 +26,6 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <hardware/hardware.h> #include <gui/IGraphicBufferAlloc.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> Loading Loading @@ -98,10 +96,14 @@ 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), Loading @@ -114,16 +116,48 @@ 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); Loading @@ -143,42 +177,6 @@ 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"); Loading Loading @@ -219,25 +217,53 @@ 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 = acquireBufferLocked(&item); err = mBufferQueue->acquireBuffer(&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(mSlots[buf].mGraphicBuffer, item)) { releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence); if (rejecter && rejecter->reject(mEGLSlots[buf].mGraphicBuffer, item)) { mBufferQueue->releaseBuffer(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); Loading @@ -250,34 +276,38 @@ 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. releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence); mBufferQueue->releaseBuffer(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, mSlots[buf].mGraphicBuffer->handle); buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0); // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { 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); 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"); err = status; } } // Update the SurfaceTexture state. mCurrentTexture = buf; mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; Loading @@ -300,20 +330,20 @@ void SurfaceTexture::setReleaseFence(int fenceFd) { sp<Fence> fence(new Fence(fenceFd)); if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) return; if (!mSlots[mCurrentTexture].mFence.get()) { mSlots[mCurrentTexture].mFence = fence; if (!mEGLSlots[mCurrentTexture].mReleaseFence.get()) { mEGLSlots[mCurrentTexture].mReleaseFence = fence; } else { sp<Fence> mergedFence = Fence::merge( String8("SurfaceTexture merged release"), mSlots[mCurrentTexture].mFence, fence); mEGLSlots[mCurrentTexture].mReleaseFence, 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 mSlots[mCurrentTexture].mFence = fence; mEGLSlots[mCurrentTexture].mReleaseFence = fence; return; } mSlots[mCurrentTexture].mFence = mergedFence; mEGLSlots[mCurrentTexture].mReleaseFence = mergedFence; } } Loading Loading @@ -360,10 +390,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; } } Loading Loading @@ -451,7 +481,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) { ST_LOGV("syncForReleaseLocked"); if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence; EGLSyncKHR fence = mEGLSlots[mCurrentTexture].mFence; 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 Loading Loading @@ -479,7 +509,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) { return UNKNOWN_ERROR; } glFlush(); mEglSlots[mCurrentTexture].mEglFence = fence; mEGLSlots[mCurrentTexture].mFence = fence; } return OK; Loading Loading @@ -577,12 +607,10 @@ 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; } } Loading Loading @@ -622,6 +650,13 @@ 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(); Loading Loading @@ -701,21 +736,35 @@ 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::abandonLocked() { ST_LOGV("abandonLocked"); void SurfaceTexture::abandon() { ST_LOGV("abandon"); Mutex::Autolock lock(mMutex); if (!mAbandoned) { mAbandoned = true; mCurrentTextureBuf.clear(); ConsumerBase::abandonLocked(); // 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) { Loading Loading @@ -747,18 +796,71 @@ status_t SurfaceTexture::setSynchronousMode(bool enabled) { return mBufferQueue->setSynchronousMode(enabled); } void SurfaceTexture::dumpLocked(String8& result, const char* prefix, char* buffer, size_t size) const // 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 { snprintf(buffer, size, "%smTexName=%d mCurrentTexture=%d\n" "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, 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, mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform); mCurrentTransform, mCurrentTexture ); result.append(buffer); ConsumerBase::dumpLocked(result, prefix, buffer, size); if (!mAbandoned) { mBufferQueue->dump(result, prefix, buffer, SIZE); } } static void mtxMul(float out[16], const float a[16], const float b[16]) { Loading Loading
include/gui/ConsumerBase.h +8 −0 Original line number Diff line number Diff line Loading @@ -189,6 +189,14 @@ 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. Loading
include/gui/SurfaceTexture.h +85 −28 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ #include <gui/ISurfaceTexture.h> #include <gui/BufferQueue.h> #include <gui/ConsumerBase.h> #include <ui/GraphicBuffer.h> Loading @@ -40,9 +39,20 @@ namespace android { class String8; class SurfaceTexture : public ConsumerBase { class SurfaceTexture : public virtual RefBase, protected BufferQueue::ConsumerListener { 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 // name of the OpenGL ES texture to which images are to be streamed. Loading Loading @@ -72,6 +82,8 @@ 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. // Loading Loading @@ -120,6 +132,16 @@ 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 Loading Loading @@ -158,6 +180,17 @@ 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); Loading @@ -171,9 +204,7 @@ public: // getBufferQueue returns the BufferQueue object to which this // SurfaceTexture is connected. sp<BufferQueue> getBufferQueue() const { return mBufferQueue; } sp<BufferQueue> getBufferQueue() const; // detachFromContext detaches the SurfaceTexture from the calling thread's // current OpenGL ES context. This context must be the same as the context Loading Loading @@ -202,25 +233,17 @@ public: // current at the time of the last call to detachFromContext. status_t attachToContext(GLuint tex); protected: // 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; // 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; // acquireBufferLocked overrides the ConsumerBase method to update the // mEglSlots array in addition to the ConsumerBase behavior. virtual status_t acquireBufferLocked(BufferQueue::BufferItem *item); protected: // 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); // 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(); static bool isExternalFormat(uint32_t format); Loading Loading @@ -328,9 +351,11 @@ private: struct EGLSlot { EGLSlot() : 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. EGLImageKHR mEglImage; Loading @@ -338,7 +363,14 @@ 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 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 Loading @@ -360,7 +392,23 @@ 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]; 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 // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, Loading @@ -370,13 +418,22 @@ private: // reset mCurrentTexture to INVALID_BUFFER_SLOT. 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, // 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. // 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; }; // ---------------------------------------------------------------------------- Loading
libs/gui/ConsumerBase.cpp +1 −2 Original line number Diff line number Diff line Loading @@ -53,7 +53,6 @@ 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()); Loading
libs/gui/SurfaceTexture.cpp +193 −91 Original line number Diff line number Diff line Loading @@ -26,8 +26,6 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <hardware/hardware.h> #include <gui/IGraphicBufferAlloc.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> Loading Loading @@ -98,10 +96,14 @@ 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), Loading @@ -114,16 +116,48 @@ 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); Loading @@ -143,42 +177,6 @@ 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"); Loading Loading @@ -219,25 +217,53 @@ 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 = acquireBufferLocked(&item); err = mBufferQueue->acquireBuffer(&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(mSlots[buf].mGraphicBuffer, item)) { releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence); if (rejecter && rejecter->reject(mEGLSlots[buf].mGraphicBuffer, item)) { mBufferQueue->releaseBuffer(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); Loading @@ -250,34 +276,38 @@ 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. releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence); mBufferQueue->releaseBuffer(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, mSlots[buf].mGraphicBuffer->handle); buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0); // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { 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); 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"); err = status; } } // Update the SurfaceTexture state. mCurrentTexture = buf; mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; Loading @@ -300,20 +330,20 @@ void SurfaceTexture::setReleaseFence(int fenceFd) { sp<Fence> fence(new Fence(fenceFd)); if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) return; if (!mSlots[mCurrentTexture].mFence.get()) { mSlots[mCurrentTexture].mFence = fence; if (!mEGLSlots[mCurrentTexture].mReleaseFence.get()) { mEGLSlots[mCurrentTexture].mReleaseFence = fence; } else { sp<Fence> mergedFence = Fence::merge( String8("SurfaceTexture merged release"), mSlots[mCurrentTexture].mFence, fence); mEGLSlots[mCurrentTexture].mReleaseFence, 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 mSlots[mCurrentTexture].mFence = fence; mEGLSlots[mCurrentTexture].mReleaseFence = fence; return; } mSlots[mCurrentTexture].mFence = mergedFence; mEGLSlots[mCurrentTexture].mReleaseFence = mergedFence; } } Loading Loading @@ -360,10 +390,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; } } Loading Loading @@ -451,7 +481,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) { ST_LOGV("syncForReleaseLocked"); if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence; EGLSyncKHR fence = mEGLSlots[mCurrentTexture].mFence; 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 Loading Loading @@ -479,7 +509,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) { return UNKNOWN_ERROR; } glFlush(); mEglSlots[mCurrentTexture].mEglFence = fence; mEGLSlots[mCurrentTexture].mFence = fence; } return OK; Loading Loading @@ -577,12 +607,10 @@ 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; } } Loading Loading @@ -622,6 +650,13 @@ 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(); Loading Loading @@ -701,21 +736,35 @@ 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::abandonLocked() { ST_LOGV("abandonLocked"); void SurfaceTexture::abandon() { ST_LOGV("abandon"); Mutex::Autolock lock(mMutex); if (!mAbandoned) { mAbandoned = true; mCurrentTextureBuf.clear(); ConsumerBase::abandonLocked(); // 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) { Loading Loading @@ -747,18 +796,71 @@ status_t SurfaceTexture::setSynchronousMode(bool enabled) { return mBufferQueue->setSynchronousMode(enabled); } void SurfaceTexture::dumpLocked(String8& result, const char* prefix, char* buffer, size_t size) const // 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 { snprintf(buffer, size, "%smTexName=%d mCurrentTexture=%d\n" "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, 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, mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform); mCurrentTransform, mCurrentTexture ); result.append(buffer); ConsumerBase::dumpLocked(result, prefix, buffer, size); if (!mAbandoned) { mBufferQueue->dump(result, prefix, buffer, SIZE); } } static void mtxMul(float out[16], const float a[16], const float b[16]) { Loading