Loading include/gui/SurfaceTexture.h +50 −21 Original line number Original line Diff line number Diff line Loading @@ -142,6 +142,13 @@ public: // getCurrentTransform returns the transform of the current buffer // getCurrentTransform returns the transform of the current buffer uint32_t getCurrentTransform() const; uint32_t getCurrentTransform() const; // setSynchronousMode set whether dequeueBuffer is synchronous or // asynchronous. In synchronous mode, dequeueBuffer blocks until // a buffer is available, the currently bound buffer can be dequeued and // queued buffers will be retired in order. // The default mode is asynchronous. status_t setSynchronousMode(bool enabled); protected: protected: // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for Loading @@ -159,6 +166,16 @@ private: enum { INVALID_BUFFER_SLOT = -1 }; enum { INVALID_BUFFER_SLOT = -1 }; struct BufferSlot { struct BufferSlot { BufferSlot() : mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY), mBufferState(BufferSlot::FREE), mRequestBufferCalled(false), mLastQueuedTransform(0), mLastQueuedTimestamp(0) { } // mGraphicBuffer points to the buffer allocated for this slot or is NULL // mGraphicBuffer points to the buffer allocated for this slot or is NULL // if no buffer has been allocated. // if no buffer has been allocated. sp<GraphicBuffer> mGraphicBuffer; sp<GraphicBuffer> mGraphicBuffer; Loading @@ -169,11 +186,32 @@ private: // mEglDisplay is the EGLDisplay used to create mEglImage. // mEglDisplay is the EGLDisplay used to create mEglImage. EGLDisplay mEglDisplay; EGLDisplay mEglDisplay; // mOwnedByClient indicates whether the slot is currently accessible to a // mBufferState indicates whether the slot is currently accessible to a // client and should not be used by the SurfaceTexture object. It gets // client and should not be used by the SurfaceTexture object. It gets // set to true when dequeueBuffer returns the slot and is reset to false // set to true when dequeueBuffer returns the slot and is reset to false // when the client calls either queueBuffer or cancelBuffer on the slot. // when the client calls either queueBuffer or cancelBuffer on the slot. bool mOwnedByClient; enum { DEQUEUED=-2, FREE=-1, QUEUED=0 }; int8_t mBufferState; // mRequestBufferCalled is used for validating that the client did // call requestBuffer() when told to do so. Technically this is not // needed but useful for debugging and catching client bugs. bool mRequestBufferCalled; // mLastQueuedCrop is the crop rectangle for the buffer that was most // recently queued. This gets set to mNextCrop each time queueBuffer gets // called. Rect mLastQueuedCrop; // mLastQueuedTransform is the transform identifier for the buffer that was // most recently queued. This gets set to mNextTransform each time // queueBuffer gets called. uint32_t mLastQueuedTransform; // mLastQueuedTimestamp is the timestamp for the buffer that was most // recently queued. This gets set by queueBuffer. int64_t mLastQueuedTimestamp; }; }; // mSlots is the array of buffer slots that must be mirrored on the client // mSlots is the array of buffer slots that must be mirrored on the client Loading Loading @@ -230,25 +268,6 @@ private: // gets set to mLastQueuedTimestamp each time updateTexImage is called. // gets set to mLastQueuedTimestamp each time updateTexImage is called. int64_t mCurrentTimestamp; int64_t mCurrentTimestamp; // mLastQueued is the buffer slot index of the most recently enqueued buffer. // At construction time it is initialized to INVALID_BUFFER_SLOT, and is // updated each time queueBuffer is called. int mLastQueued; // mLastQueuedCrop is the crop rectangle for the buffer that was most // recently queued. This gets set to mNextCrop each time queueBuffer gets // called. Rect mLastQueuedCrop; // mLastQueuedTransform is the transform identifier for the buffer that was // most recently queued. This gets set to mNextTransform each time // queueBuffer gets called. uint32_t mLastQueuedTransform; // mLastQueuedTimestamp is the timestamp for the buffer that was most // recently queued. This gets set by queueBuffer. int64_t mLastQueuedTimestamp; // mNextCrop is the crop rectangle that will be used for the next buffer // mNextCrop is the crop rectangle that will be used for the next buffer // that gets queued. It is set by calling setCrop. // that gets queued. It is set by calling setCrop. Rect mNextCrop; Rect mNextCrop; Loading @@ -271,6 +290,16 @@ private: // queueBuffer. // queueBuffer. sp<FrameAvailableListener> mFrameAvailableListener; sp<FrameAvailableListener> mFrameAvailableListener; // mSynchronousMode whether we're in synchronous mode or not bool mSynchronousMode; // mDequeueCondition condition used for dequeueBuffer in synchronous mode mutable Condition mDequeueCondition; // mQueue is a FIFO of queued buffers used in synchronous mode typedef Vector<int> Fifo; Fifo mQueue; // mMutex is the mutex used to prevent concurrent access to the member // mMutex is the mutex used to prevent concurrent access to the member // variables of SurfaceTexture objects. It must be locked whenever the // variables of SurfaceTexture objects. It must be locked whenever the // member variables are accessed. // member variables are accessed. Loading libs/gui/SurfaceTexture.cpp +134 −49 Original line number Original line Diff line number Diff line Loading @@ -86,17 +86,10 @@ SurfaceTexture::SurfaceTexture(GLuint tex) : mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES), mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES), mCurrentTransform(0), mCurrentTransform(0), mCurrentTimestamp(0), mCurrentTimestamp(0), mLastQueued(INVALID_BUFFER_SLOT), mLastQueuedTransform(0), mLastQueuedTimestamp(0), mNextTransform(0), mNextTransform(0), mTexName(tex) { mTexName(tex), mSynchronousMode(false) { LOGV("SurfaceTexture::SurfaceTexture"); LOGV("SurfaceTexture::SurfaceTexture"); for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; mSlots[i].mEglDisplay = EGL_NO_DISPLAY; mSlots[i].mOwnedByClient = false; } sp<ISurfaceComposer> composer(ComposerService::getComposerService()); sp<ISurfaceComposer> composer(ComposerService::getComposerService()); mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); mNextCrop.makeInvalid(); mNextCrop.makeInvalid(); Loading @@ -109,16 +102,21 @@ SurfaceTexture::~SurfaceTexture() { status_t SurfaceTexture::setBufferCount(int bufferCount) { status_t SurfaceTexture::setBufferCount(int bufferCount) { LOGV("SurfaceTexture::setBufferCount"); LOGV("SurfaceTexture::setBufferCount"); Mutex::Autolock lock(mMutex); const int minBufferSlots = mSynchronousMode ? MIN_BUFFER_SLOTS-1 : MIN_BUFFER_SLOTS; if (bufferCount < MIN_BUFFER_SLOTS) { if (bufferCount < minBufferSlots) { return BAD_VALUE; return BAD_VALUE; } } Mutex::Autolock lock(mMutex); freeAllBuffers(); freeAllBuffers(); mBufferCount = bufferCount; mBufferCount = bufferCount; mCurrentTexture = INVALID_BUFFER_SLOT; mCurrentTexture = INVALID_BUFFER_SLOT; mLastQueued = INVALID_BUFFER_SLOT; mQueue.clear(); mQueue.reserve(mSynchronousMode ? mBufferCount : 1); mDequeueCondition.signal(); return OK; return OK; } } Loading @@ -140,6 +138,7 @@ sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf) { mBufferCount, buf); mBufferCount, buf); return 0; return 0; } } mSlots[buf].mRequestBufferCalled = true; return mSlots[buf].mGraphicBuffer; return mSlots[buf].mGraphicBuffer; } } Loading @@ -153,14 +152,44 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, } } Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex); int found = INVALID_BUFFER_SLOT; int found, foundSync; int dequeuedCount = 0; bool tryAgain = true; while (tryAgain) { found = INVALID_BUFFER_SLOT; foundSync = INVALID_BUFFER_SLOT; dequeuedCount = 0; for (int i = 0; i < mBufferCount; i++) { for (int i = 0; i < mBufferCount; i++) { if (!mSlots[i].mOwnedByClient && i != mCurrentTexture && i != mLastQueued) { const int state = mSlots[i].mBufferState; mSlots[i].mOwnedByClient = true; if (state == BufferSlot::DEQUEUED) { dequeuedCount++; } if (state == BufferSlot::FREE || i == mCurrentTexture) { foundSync = i; if (i != mCurrentTexture) { found = i; found = i; break; break; } } } } } // we're in synchronous mode and didn't find a buffer, we need to wait tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); if (tryAgain) { mDequeueCondition.wait(mMutex); } } if (mSynchronousMode) { // we're dequeuing more buffers than allowed in synchronous mode if ((mBufferCount - (dequeuedCount+1)) < MIN_UNDEQUEUED_BUFFERS-1) return -EBUSY; if (found == INVALID_BUFFER_SLOT) { // foundSync guaranteed to be != INVALID_BUFFER_SLOT found = foundSync; } } if (found == INVALID_BUFFER_SLOT) { if (found == INVALID_BUFFER_SLOT) { return -EBUSY; return -EBUSY; } } Loading @@ -181,7 +210,11 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, format = mPixelFormat; format = mPixelFormat; } } const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); // buffer is now in DEQUEUED (but can also be current at the same time, // if we're in synchronous mode) mSlots[buf].mBufferState = BufferSlot::DEQUEUED; const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); if ((buffer == NULL) || if ((buffer == NULL) || (uint32_t(buffer->width) != w) || (uint32_t(buffer->width) != w) || (uint32_t(buffer->height) != h) || (uint32_t(buffer->height) != h) || Loading @@ -199,6 +232,7 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, mPixelFormat = format; mPixelFormat = format; } } mSlots[buf].mGraphicBuffer = graphicBuffer; mSlots[buf].mGraphicBuffer = graphicBuffer; mSlots[buf].mRequestBufferCalled = false; if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage); eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage); mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; Loading @@ -209,44 +243,78 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, return OK; return OK; } } status_t SurfaceTexture::setSynchronousMode(bool enabled) { Mutex::Autolock lock(mMutex); if (mSynchronousMode != enabled) { mSynchronousMode = enabled; freeAllBuffers(); mCurrentTexture = INVALID_BUFFER_SLOT; mQueue.clear(); mQueue.reserve(mSynchronousMode ? mBufferCount : 1); mDequeueCondition.signal(); } return NO_ERROR; } status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) { status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) { LOGV("SurfaceTexture::queueBuffer"); LOGV("SurfaceTexture::queueBuffer"); Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex); if (buf < 0 || mBufferCount <= buf) { if (buf < 0 || buf >= mBufferCount) { LOGE("queueBuffer: slot index out of range [0, %d]: %d", LOGE("queueBuffer: slot index out of range [0, %d]: %d", mBufferCount, buf); mBufferCount, buf); return -EINVAL; return -EINVAL; } else if (!mSlots[buf].mOwnedByClient) { } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { LOGE("queueBuffer: slot %d is not owned by the client", buf); LOGE("queueBuffer: slot %d is not owned by the client (state=%d)", buf, mSlots[buf].mBufferState); return -EINVAL; return -EINVAL; } else if (mSlots[buf].mGraphicBuffer == 0) { } else if (!mSlots[buf].mRequestBufferCalled) { LOGE("queueBuffer: slot %d was enqueued without requesting a buffer", LOGE("queueBuffer: slot %d was enqueued without requesting a buffer", buf); buf); return -EINVAL; return -EINVAL; } } mSlots[buf].mOwnedByClient = false; mLastQueued = buf; if (mSynchronousMode) { mLastQueuedCrop = mNextCrop; // in synchronous mode we queue all buffers in a FIFO mLastQueuedTransform = mNextTransform; mQueue.push_back(buf); mLastQueuedTimestamp = timestamp; } else { // in asynchronous mode we only keep the most recent buffer if (mQueue.empty()) { mQueue.push_back(buf); } else { Fifo::iterator front(mQueue.begin()); // buffer currently queued is freed mSlots[*front].mBufferState = BufferSlot::FREE; // and we record the new buffer index in the queued list *front = buf; } } mSlots[buf].mBufferState = BufferSlot::QUEUED; mSlots[buf].mLastQueuedCrop = mNextCrop; mSlots[buf].mLastQueuedTransform = mNextTransform; mSlots[buf].mLastQueuedTimestamp = timestamp; if (mFrameAvailableListener != 0) { if (mFrameAvailableListener != 0) { mFrameAvailableListener->onFrameAvailable(); mFrameAvailableListener->onFrameAvailable(); } } mDequeueCondition.signal(); return OK; return OK; } } void SurfaceTexture::cancelBuffer(int buf) { void SurfaceTexture::cancelBuffer(int buf) { LOGV("SurfaceTexture::cancelBuffer"); LOGV("SurfaceTexture::cancelBuffer"); Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex); if (buf < 0 || mBufferCount <= buf) { if (buf < 0 || buf >= mBufferCount) { LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount, LOGE("cancelBuffer: slot index out of range [0, %d]: %d", buf); mBufferCount, buf); return; return; } else if (!mSlots[buf].mOwnedByClient) { } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { LOGE("cancelBuffer: slot %d is not owned by the client", buf); LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", buf, mSlots[buf].mBufferState); return; return; } } mSlots[buf].mOwnedByClient = false; mSlots[buf].mBufferState = BufferSlot::FREE; mDequeueCondition.signal(); } } status_t SurfaceTexture::setCrop(const Rect& crop) { status_t SurfaceTexture::setCrop(const Rect& crop) { Loading @@ -267,16 +335,25 @@ status_t SurfaceTexture::updateTexImage() { LOGV("SurfaceTexture::updateTexImage"); LOGV("SurfaceTexture::updateTexImage"); Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex); // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT, int buf = mCurrentTexture; if (!mQueue.empty()) { // in asynchronous mode the list is guaranteed to be one buffer deep, // while in synchronous mode we use the oldest buffer Fifo::iterator front(mQueue.begin()); buf = *front; mQueue.erase(front); } // Initially both mCurrentTexture and buf are INVALID_BUFFER_SLOT, // so this check will fail until a buffer gets queued. // so this check will fail until a buffer gets queued. if (mCurrentTexture != mLastQueued) { if (mCurrentTexture != buf) { // Update the GL texture object. // Update the GL texture object. EGLImageKHR image = mSlots[mLastQueued].mEglImage; EGLImageKHR image = mSlots[buf].mEglImage; if (image == EGL_NO_IMAGE_KHR) { if (image == EGL_NO_IMAGE_KHR) { EGLDisplay dpy = eglGetCurrentDisplay(); EGLDisplay dpy = eglGetCurrentDisplay(); image = createImage(dpy, mSlots[mLastQueued].mGraphicBuffer); image = createImage(dpy, mSlots[buf].mGraphicBuffer); mSlots[mLastQueued].mEglImage = image; mSlots[buf].mEglImage = image; mSlots[mLastQueued].mEglDisplay = dpy; mSlots[buf].mEglDisplay = dpy; if (image == EGL_NO_IMAGE_KHR) { if (image == EGL_NO_IMAGE_KHR) { // NOTE: if dpy was invalid, createImage() is guaranteed to // NOTE: if dpy was invalid, createImage() is guaranteed to // fail. so we'd end up here. // fail. so we'd end up here. Loading @@ -289,8 +366,7 @@ status_t SurfaceTexture::updateTexImage() { LOGE("GL error cleared before updating SurfaceTexture: %#04x", error); LOGE("GL error cleared before updating SurfaceTexture: %#04x", error); } } GLenum target = getTextureTarget( GLenum target = getTextureTarget(mSlots[buf].mGraphicBuffer->format); mSlots[mLastQueued].mGraphicBuffer->format); if (target != mCurrentTextureTarget) { if (target != mCurrentTextureTarget) { glDeleteTextures(1, &mTexName); glDeleteTextures(1, &mTexName); } } Loading @@ -300,20 +376,29 @@ status_t SurfaceTexture::updateTexImage() { bool failed = false; bool failed = false; while ((error = glGetError()) != GL_NO_ERROR) { while ((error = glGetError()) != GL_NO_ERROR) { LOGE("error binding external texture image %p (slot %d): %#04x", LOGE("error binding external texture image %p (slot %d): %#04x", image, mLastQueued, error); image, buf, error); failed = true; failed = true; } } if (failed) { if (failed) { return -EINVAL; return -EINVAL; } } if (mCurrentTexture != INVALID_BUFFER_SLOT) { // the current buffer becomes FREE if it was still in the queued // state. If it has already been given to the client // (synchronous mode), then it stays in DEQUEUED state. if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE; } // Update the SurfaceTexture state. // Update the SurfaceTexture state. mCurrentTexture = mLastQueued; mCurrentTexture = buf; mCurrentTextureTarget = target; mCurrentTextureTarget = target; mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer; mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; mCurrentCrop = mLastQueuedCrop; mCurrentCrop = mSlots[buf].mLastQueuedCrop; mCurrentTransform = mLastQueuedTransform; mCurrentTransform = mSlots[buf].mLastQueuedTransform; mCurrentTimestamp = mLastQueuedTimestamp; mCurrentTimestamp = mSlots[buf].mLastQueuedTimestamp; mDequeueCondition.signal(); } else { } else { // We always bind the texture even if we don't update its contents. // We always bind the texture even if we don't update its contents. glBindTexture(mCurrentTextureTarget, mTexName); glBindTexture(mCurrentTextureTarget, mTexName); Loading Loading @@ -469,7 +554,7 @@ sp<IBinder> SurfaceTexture::getAllocator() { void SurfaceTexture::freeAllBuffers() { void SurfaceTexture::freeAllBuffers() { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { mSlots[i].mGraphicBuffer = 0; mSlots[i].mGraphicBuffer = 0; mSlots[i].mOwnedByClient = false; mSlots[i].mBufferState = BufferSlot::FREE; if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; Loading Loading
include/gui/SurfaceTexture.h +50 −21 Original line number Original line Diff line number Diff line Loading @@ -142,6 +142,13 @@ public: // getCurrentTransform returns the transform of the current buffer // getCurrentTransform returns the transform of the current buffer uint32_t getCurrentTransform() const; uint32_t getCurrentTransform() const; // setSynchronousMode set whether dequeueBuffer is synchronous or // asynchronous. In synchronous mode, dequeueBuffer blocks until // a buffer is available, the currently bound buffer can be dequeued and // queued buffers will be retired in order. // The default mode is asynchronous. status_t setSynchronousMode(bool enabled); protected: protected: // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for Loading @@ -159,6 +166,16 @@ private: enum { INVALID_BUFFER_SLOT = -1 }; enum { INVALID_BUFFER_SLOT = -1 }; struct BufferSlot { struct BufferSlot { BufferSlot() : mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY), mBufferState(BufferSlot::FREE), mRequestBufferCalled(false), mLastQueuedTransform(0), mLastQueuedTimestamp(0) { } // mGraphicBuffer points to the buffer allocated for this slot or is NULL // mGraphicBuffer points to the buffer allocated for this slot or is NULL // if no buffer has been allocated. // if no buffer has been allocated. sp<GraphicBuffer> mGraphicBuffer; sp<GraphicBuffer> mGraphicBuffer; Loading @@ -169,11 +186,32 @@ private: // mEglDisplay is the EGLDisplay used to create mEglImage. // mEglDisplay is the EGLDisplay used to create mEglImage. EGLDisplay mEglDisplay; EGLDisplay mEglDisplay; // mOwnedByClient indicates whether the slot is currently accessible to a // mBufferState indicates whether the slot is currently accessible to a // client and should not be used by the SurfaceTexture object. It gets // client and should not be used by the SurfaceTexture object. It gets // set to true when dequeueBuffer returns the slot and is reset to false // set to true when dequeueBuffer returns the slot and is reset to false // when the client calls either queueBuffer or cancelBuffer on the slot. // when the client calls either queueBuffer or cancelBuffer on the slot. bool mOwnedByClient; enum { DEQUEUED=-2, FREE=-1, QUEUED=0 }; int8_t mBufferState; // mRequestBufferCalled is used for validating that the client did // call requestBuffer() when told to do so. Technically this is not // needed but useful for debugging and catching client bugs. bool mRequestBufferCalled; // mLastQueuedCrop is the crop rectangle for the buffer that was most // recently queued. This gets set to mNextCrop each time queueBuffer gets // called. Rect mLastQueuedCrop; // mLastQueuedTransform is the transform identifier for the buffer that was // most recently queued. This gets set to mNextTransform each time // queueBuffer gets called. uint32_t mLastQueuedTransform; // mLastQueuedTimestamp is the timestamp for the buffer that was most // recently queued. This gets set by queueBuffer. int64_t mLastQueuedTimestamp; }; }; // mSlots is the array of buffer slots that must be mirrored on the client // mSlots is the array of buffer slots that must be mirrored on the client Loading Loading @@ -230,25 +268,6 @@ private: // gets set to mLastQueuedTimestamp each time updateTexImage is called. // gets set to mLastQueuedTimestamp each time updateTexImage is called. int64_t mCurrentTimestamp; int64_t mCurrentTimestamp; // mLastQueued is the buffer slot index of the most recently enqueued buffer. // At construction time it is initialized to INVALID_BUFFER_SLOT, and is // updated each time queueBuffer is called. int mLastQueued; // mLastQueuedCrop is the crop rectangle for the buffer that was most // recently queued. This gets set to mNextCrop each time queueBuffer gets // called. Rect mLastQueuedCrop; // mLastQueuedTransform is the transform identifier for the buffer that was // most recently queued. This gets set to mNextTransform each time // queueBuffer gets called. uint32_t mLastQueuedTransform; // mLastQueuedTimestamp is the timestamp for the buffer that was most // recently queued. This gets set by queueBuffer. int64_t mLastQueuedTimestamp; // mNextCrop is the crop rectangle that will be used for the next buffer // mNextCrop is the crop rectangle that will be used for the next buffer // that gets queued. It is set by calling setCrop. // that gets queued. It is set by calling setCrop. Rect mNextCrop; Rect mNextCrop; Loading @@ -271,6 +290,16 @@ private: // queueBuffer. // queueBuffer. sp<FrameAvailableListener> mFrameAvailableListener; sp<FrameAvailableListener> mFrameAvailableListener; // mSynchronousMode whether we're in synchronous mode or not bool mSynchronousMode; // mDequeueCondition condition used for dequeueBuffer in synchronous mode mutable Condition mDequeueCondition; // mQueue is a FIFO of queued buffers used in synchronous mode typedef Vector<int> Fifo; Fifo mQueue; // mMutex is the mutex used to prevent concurrent access to the member // mMutex is the mutex used to prevent concurrent access to the member // variables of SurfaceTexture objects. It must be locked whenever the // variables of SurfaceTexture objects. It must be locked whenever the // member variables are accessed. // member variables are accessed. Loading
libs/gui/SurfaceTexture.cpp +134 −49 Original line number Original line Diff line number Diff line Loading @@ -86,17 +86,10 @@ SurfaceTexture::SurfaceTexture(GLuint tex) : mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES), mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES), mCurrentTransform(0), mCurrentTransform(0), mCurrentTimestamp(0), mCurrentTimestamp(0), mLastQueued(INVALID_BUFFER_SLOT), mLastQueuedTransform(0), mLastQueuedTimestamp(0), mNextTransform(0), mNextTransform(0), mTexName(tex) { mTexName(tex), mSynchronousMode(false) { LOGV("SurfaceTexture::SurfaceTexture"); LOGV("SurfaceTexture::SurfaceTexture"); for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; mSlots[i].mEglDisplay = EGL_NO_DISPLAY; mSlots[i].mOwnedByClient = false; } sp<ISurfaceComposer> composer(ComposerService::getComposerService()); sp<ISurfaceComposer> composer(ComposerService::getComposerService()); mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); mNextCrop.makeInvalid(); mNextCrop.makeInvalid(); Loading @@ -109,16 +102,21 @@ SurfaceTexture::~SurfaceTexture() { status_t SurfaceTexture::setBufferCount(int bufferCount) { status_t SurfaceTexture::setBufferCount(int bufferCount) { LOGV("SurfaceTexture::setBufferCount"); LOGV("SurfaceTexture::setBufferCount"); Mutex::Autolock lock(mMutex); const int minBufferSlots = mSynchronousMode ? MIN_BUFFER_SLOTS-1 : MIN_BUFFER_SLOTS; if (bufferCount < MIN_BUFFER_SLOTS) { if (bufferCount < minBufferSlots) { return BAD_VALUE; return BAD_VALUE; } } Mutex::Autolock lock(mMutex); freeAllBuffers(); freeAllBuffers(); mBufferCount = bufferCount; mBufferCount = bufferCount; mCurrentTexture = INVALID_BUFFER_SLOT; mCurrentTexture = INVALID_BUFFER_SLOT; mLastQueued = INVALID_BUFFER_SLOT; mQueue.clear(); mQueue.reserve(mSynchronousMode ? mBufferCount : 1); mDequeueCondition.signal(); return OK; return OK; } } Loading @@ -140,6 +138,7 @@ sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf) { mBufferCount, buf); mBufferCount, buf); return 0; return 0; } } mSlots[buf].mRequestBufferCalled = true; return mSlots[buf].mGraphicBuffer; return mSlots[buf].mGraphicBuffer; } } Loading @@ -153,14 +152,44 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, } } Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex); int found = INVALID_BUFFER_SLOT; int found, foundSync; int dequeuedCount = 0; bool tryAgain = true; while (tryAgain) { found = INVALID_BUFFER_SLOT; foundSync = INVALID_BUFFER_SLOT; dequeuedCount = 0; for (int i = 0; i < mBufferCount; i++) { for (int i = 0; i < mBufferCount; i++) { if (!mSlots[i].mOwnedByClient && i != mCurrentTexture && i != mLastQueued) { const int state = mSlots[i].mBufferState; mSlots[i].mOwnedByClient = true; if (state == BufferSlot::DEQUEUED) { dequeuedCount++; } if (state == BufferSlot::FREE || i == mCurrentTexture) { foundSync = i; if (i != mCurrentTexture) { found = i; found = i; break; break; } } } } } // we're in synchronous mode and didn't find a buffer, we need to wait tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); if (tryAgain) { mDequeueCondition.wait(mMutex); } } if (mSynchronousMode) { // we're dequeuing more buffers than allowed in synchronous mode if ((mBufferCount - (dequeuedCount+1)) < MIN_UNDEQUEUED_BUFFERS-1) return -EBUSY; if (found == INVALID_BUFFER_SLOT) { // foundSync guaranteed to be != INVALID_BUFFER_SLOT found = foundSync; } } if (found == INVALID_BUFFER_SLOT) { if (found == INVALID_BUFFER_SLOT) { return -EBUSY; return -EBUSY; } } Loading @@ -181,7 +210,11 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, format = mPixelFormat; format = mPixelFormat; } } const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); // buffer is now in DEQUEUED (but can also be current at the same time, // if we're in synchronous mode) mSlots[buf].mBufferState = BufferSlot::DEQUEUED; const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); if ((buffer == NULL) || if ((buffer == NULL) || (uint32_t(buffer->width) != w) || (uint32_t(buffer->width) != w) || (uint32_t(buffer->height) != h) || (uint32_t(buffer->height) != h) || Loading @@ -199,6 +232,7 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, mPixelFormat = format; mPixelFormat = format; } } mSlots[buf].mGraphicBuffer = graphicBuffer; mSlots[buf].mGraphicBuffer = graphicBuffer; mSlots[buf].mRequestBufferCalled = false; if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage); eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage); mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; Loading @@ -209,44 +243,78 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, return OK; return OK; } } status_t SurfaceTexture::setSynchronousMode(bool enabled) { Mutex::Autolock lock(mMutex); if (mSynchronousMode != enabled) { mSynchronousMode = enabled; freeAllBuffers(); mCurrentTexture = INVALID_BUFFER_SLOT; mQueue.clear(); mQueue.reserve(mSynchronousMode ? mBufferCount : 1); mDequeueCondition.signal(); } return NO_ERROR; } status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) { status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) { LOGV("SurfaceTexture::queueBuffer"); LOGV("SurfaceTexture::queueBuffer"); Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex); if (buf < 0 || mBufferCount <= buf) { if (buf < 0 || buf >= mBufferCount) { LOGE("queueBuffer: slot index out of range [0, %d]: %d", LOGE("queueBuffer: slot index out of range [0, %d]: %d", mBufferCount, buf); mBufferCount, buf); return -EINVAL; return -EINVAL; } else if (!mSlots[buf].mOwnedByClient) { } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { LOGE("queueBuffer: slot %d is not owned by the client", buf); LOGE("queueBuffer: slot %d is not owned by the client (state=%d)", buf, mSlots[buf].mBufferState); return -EINVAL; return -EINVAL; } else if (mSlots[buf].mGraphicBuffer == 0) { } else if (!mSlots[buf].mRequestBufferCalled) { LOGE("queueBuffer: slot %d was enqueued without requesting a buffer", LOGE("queueBuffer: slot %d was enqueued without requesting a buffer", buf); buf); return -EINVAL; return -EINVAL; } } mSlots[buf].mOwnedByClient = false; mLastQueued = buf; if (mSynchronousMode) { mLastQueuedCrop = mNextCrop; // in synchronous mode we queue all buffers in a FIFO mLastQueuedTransform = mNextTransform; mQueue.push_back(buf); mLastQueuedTimestamp = timestamp; } else { // in asynchronous mode we only keep the most recent buffer if (mQueue.empty()) { mQueue.push_back(buf); } else { Fifo::iterator front(mQueue.begin()); // buffer currently queued is freed mSlots[*front].mBufferState = BufferSlot::FREE; // and we record the new buffer index in the queued list *front = buf; } } mSlots[buf].mBufferState = BufferSlot::QUEUED; mSlots[buf].mLastQueuedCrop = mNextCrop; mSlots[buf].mLastQueuedTransform = mNextTransform; mSlots[buf].mLastQueuedTimestamp = timestamp; if (mFrameAvailableListener != 0) { if (mFrameAvailableListener != 0) { mFrameAvailableListener->onFrameAvailable(); mFrameAvailableListener->onFrameAvailable(); } } mDequeueCondition.signal(); return OK; return OK; } } void SurfaceTexture::cancelBuffer(int buf) { void SurfaceTexture::cancelBuffer(int buf) { LOGV("SurfaceTexture::cancelBuffer"); LOGV("SurfaceTexture::cancelBuffer"); Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex); if (buf < 0 || mBufferCount <= buf) { if (buf < 0 || buf >= mBufferCount) { LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount, LOGE("cancelBuffer: slot index out of range [0, %d]: %d", buf); mBufferCount, buf); return; return; } else if (!mSlots[buf].mOwnedByClient) { } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { LOGE("cancelBuffer: slot %d is not owned by the client", buf); LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", buf, mSlots[buf].mBufferState); return; return; } } mSlots[buf].mOwnedByClient = false; mSlots[buf].mBufferState = BufferSlot::FREE; mDequeueCondition.signal(); } } status_t SurfaceTexture::setCrop(const Rect& crop) { status_t SurfaceTexture::setCrop(const Rect& crop) { Loading @@ -267,16 +335,25 @@ status_t SurfaceTexture::updateTexImage() { LOGV("SurfaceTexture::updateTexImage"); LOGV("SurfaceTexture::updateTexImage"); Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex); // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT, int buf = mCurrentTexture; if (!mQueue.empty()) { // in asynchronous mode the list is guaranteed to be one buffer deep, // while in synchronous mode we use the oldest buffer Fifo::iterator front(mQueue.begin()); buf = *front; mQueue.erase(front); } // Initially both mCurrentTexture and buf are INVALID_BUFFER_SLOT, // so this check will fail until a buffer gets queued. // so this check will fail until a buffer gets queued. if (mCurrentTexture != mLastQueued) { if (mCurrentTexture != buf) { // Update the GL texture object. // Update the GL texture object. EGLImageKHR image = mSlots[mLastQueued].mEglImage; EGLImageKHR image = mSlots[buf].mEglImage; if (image == EGL_NO_IMAGE_KHR) { if (image == EGL_NO_IMAGE_KHR) { EGLDisplay dpy = eglGetCurrentDisplay(); EGLDisplay dpy = eglGetCurrentDisplay(); image = createImage(dpy, mSlots[mLastQueued].mGraphicBuffer); image = createImage(dpy, mSlots[buf].mGraphicBuffer); mSlots[mLastQueued].mEglImage = image; mSlots[buf].mEglImage = image; mSlots[mLastQueued].mEglDisplay = dpy; mSlots[buf].mEglDisplay = dpy; if (image == EGL_NO_IMAGE_KHR) { if (image == EGL_NO_IMAGE_KHR) { // NOTE: if dpy was invalid, createImage() is guaranteed to // NOTE: if dpy was invalid, createImage() is guaranteed to // fail. so we'd end up here. // fail. so we'd end up here. Loading @@ -289,8 +366,7 @@ status_t SurfaceTexture::updateTexImage() { LOGE("GL error cleared before updating SurfaceTexture: %#04x", error); LOGE("GL error cleared before updating SurfaceTexture: %#04x", error); } } GLenum target = getTextureTarget( GLenum target = getTextureTarget(mSlots[buf].mGraphicBuffer->format); mSlots[mLastQueued].mGraphicBuffer->format); if (target != mCurrentTextureTarget) { if (target != mCurrentTextureTarget) { glDeleteTextures(1, &mTexName); glDeleteTextures(1, &mTexName); } } Loading @@ -300,20 +376,29 @@ status_t SurfaceTexture::updateTexImage() { bool failed = false; bool failed = false; while ((error = glGetError()) != GL_NO_ERROR) { while ((error = glGetError()) != GL_NO_ERROR) { LOGE("error binding external texture image %p (slot %d): %#04x", LOGE("error binding external texture image %p (slot %d): %#04x", image, mLastQueued, error); image, buf, error); failed = true; failed = true; } } if (failed) { if (failed) { return -EINVAL; return -EINVAL; } } if (mCurrentTexture != INVALID_BUFFER_SLOT) { // the current buffer becomes FREE if it was still in the queued // state. If it has already been given to the client // (synchronous mode), then it stays in DEQUEUED state. if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE; } // Update the SurfaceTexture state. // Update the SurfaceTexture state. mCurrentTexture = mLastQueued; mCurrentTexture = buf; mCurrentTextureTarget = target; mCurrentTextureTarget = target; mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer; mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; mCurrentCrop = mLastQueuedCrop; mCurrentCrop = mSlots[buf].mLastQueuedCrop; mCurrentTransform = mLastQueuedTransform; mCurrentTransform = mSlots[buf].mLastQueuedTransform; mCurrentTimestamp = mLastQueuedTimestamp; mCurrentTimestamp = mSlots[buf].mLastQueuedTimestamp; mDequeueCondition.signal(); } else { } else { // We always bind the texture even if we don't update its contents. // We always bind the texture even if we don't update its contents. glBindTexture(mCurrentTextureTarget, mTexName); glBindTexture(mCurrentTextureTarget, mTexName); Loading Loading @@ -469,7 +554,7 @@ sp<IBinder> SurfaceTexture::getAllocator() { void SurfaceTexture::freeAllBuffers() { void SurfaceTexture::freeAllBuffers() { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { mSlots[i].mGraphicBuffer = 0; mSlots[i].mGraphicBuffer = 0; mSlots[i].mOwnedByClient = false; mSlots[i].mBufferState = BufferSlot::FREE; if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; Loading