diff options
author | 2012-12-04 16:51:15 -0800 | |
---|---|---|
committer | 2012-12-11 09:42:51 -0800 | |
commit | bf974abe92f7495529916fe0f483f3b56e7c30e3 (patch) | |
tree | 147906c9c1e1f7f67dba5c8965c9c5d507a93b91 | |
parent | 0e1e53e376ccb3c173ff8e5efeab39b2c5dfd6bd (diff) |
Refactor SurfaceTexture a bit.
Rearranges updateTexImage() so that the SurfaceFlinger-specific
behavior is in a new SurfaceFlingerConsumer subclass.
SurfaceTexture behavior should not be altered. Instead of
acquire-bind-release we now do acquire-release-bind, but since
it's all done with the lock held there shouldn't be any
externally-visible change.
Change-Id: Ia566e4727945e2cfb9359fc6d2a8f8af64d7b7b7
-rw-r--r-- | include/gui/BufferQueue.h | 5 | ||||
-rw-r--r-- | include/gui/SurfaceTexture.h | 68 | ||||
-rw-r--r-- | libs/gui/BufferQueue.cpp | 1 | ||||
-rw-r--r-- | libs/gui/SurfaceTexture.cpp | 330 | ||||
-rw-r--r-- | services/surfaceflinger/Android.mk | 1 | ||||
-rw-r--r-- | services/surfaceflinger/Layer.cpp | 77 | ||||
-rw-r--r-- | services/surfaceflinger/Layer.h | 11 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlingerConsumer.cpp | 90 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlingerConsumer.h | 59 |
9 files changed, 429 insertions, 213 deletions
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h index 9e265bab72..88bcd8f4ab 100644 --- a/include/gui/BufferQueue.h +++ b/include/gui/BufferQueue.h @@ -182,8 +182,9 @@ public: mBuf(INVALID_BUFFER_SLOT) { mCrop.makeInvalid(); } - // mGraphicBuffer points to the buffer allocated for this slot or is NULL - // if no buffer has been allocated. + // mGraphicBuffer points to the buffer allocated for this slot, or is NULL + // if the buffer in this slot has been acquired in the past (see + // BufferSlot.mAcquireCalled). sp<GraphicBuffer> mGraphicBuffer; // mCrop is the current crop rectangle for this buffer slot. diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index b498a5c523..a8b7d74368 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -74,14 +74,13 @@ public: GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true, const sp<BufferQueue> &bufferQueue = 0); - // updateTexImage sets the image contents of the target texture to that of - // the most recently queued buffer. + // updateTexImage acquires the most recently queued buffer, and sets the + // image contents of the target texture to it. // // This call may only be made while the OpenGL ES context to which the // target texture belongs is bound to the calling thread. // - // After calling this method the doGLFenceWait method must be called - // before issuing OpenGL ES commands that access the texture contents. + // This calls doGLFenceWait to ensure proper synchronization. status_t updateTexImage(); // setReleaseFence stores a fence file descriptor that will signal when the @@ -161,8 +160,7 @@ public: // doGLFenceWait inserts a wait command into the OpenGL ES command stream // to ensure that it is safe for future OpenGL ES commands to access the - // current texture buffer. This must be called each time updateTexImage - // is called before issuing OpenGL ES commands that access the texture. + // current texture buffer. status_t doGLFenceWait() const; // isSynchronousMode returns whether the SurfaceTexture is currently in @@ -233,23 +231,33 @@ protected: virtual status_t releaseBufferLocked(int buf, EGLDisplay display, EGLSyncKHR eglFence); + status_t releaseBufferLocked(int buf, EGLSyncKHR eglFence) { + return releaseBufferLocked(buf, mEglDisplay, eglFence); + } + static bool isExternalFormat(uint32_t format); -private: - // this version of updateTexImage() takes a functor used to reject or not - // the newly acquired buffer. - // this API is TEMPORARY and intended to be used by SurfaceFlinger only, - // which is why class Layer is made a friend of SurfaceTexture below. - class BufferRejecter { - friend class SurfaceTexture; - virtual bool reject(const sp<GraphicBuffer>& buf, - const BufferQueue::BufferItem& item) = 0; - protected: - virtual ~BufferRejecter() { } - }; - friend class Layer; - status_t updateTexImage(BufferRejecter* rejecter, bool skipSync); + // This releases the buffer in the slot referenced by mCurrentTexture, + // then updates state to refer to the BufferItem, which must be a + // newly-acquired buffer. + status_t releaseAndUpdateLocked(const BufferQueue::BufferItem& item); + + // Binds mTexName and the current buffer to mTexTarget. Uses + // mCurrentTexture if it's set, mCurrentTextureBuf if not. + status_t bindTextureImage(); + + // doGLFenceWaitLocked inserts a wait command into the OpenGL ES command + // stream to ensure that it is safe for future OpenGL ES commands to + // access the current texture buffer. + status_t doGLFenceWaitLocked() const; + + // Gets the current EGLDisplay and EGLContext values, and compares them + // to mEglDisplay and mEglContext. If the fields have been previously + // set, the values must match; if not, the fields are set to the current + // values. + status_t checkAndUpdateEglStateLocked(); +private: // createImage creates a new EGLImage from a GraphicBuffer. EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer); @@ -267,19 +275,19 @@ private: // mCurrentTextureBuf must not be NULL. void computeCurrentTransformMatrixLocked(); - // doGLFenceWaitLocked inserts a wait command into the OpenGL ES command - // stream to ensure that it is safe for future OpenGL ES commands to - // access the current texture buffer. This must be called each time - // updateTexImage is called before issuing OpenGL ES commands that access - // the texture. - status_t doGLFenceWaitLocked() const; - // syncForReleaseLocked performs the synchronization needed to release the // current slot from an OpenGL ES context. If needed it will set the // current slot's fence to guard against a producer accessing the buffer // before the outstanding accesses have completed. status_t syncForReleaseLocked(EGLDisplay dpy); + // Normally, when we bind a buffer to a texture target, we bind a buffer + // that is referenced by an entry in mEglSlots. In some situations we + // have a buffer in mCurrentTextureBuf, but no corresponding entry for + // it in our slot array. bindUnslottedBuffer handles that situation by + // binding the buffer without touching the EglSlots. + status_t bindUnslottedBufferLocked(EGLDisplay dpy); + // The default consumer usage flags that SurfaceTexture always sets on its // BufferQueue instance; these will be OR:d with any additional flags passed // from the SurfaceTexture user. In particular, SurfaceTexture will always @@ -344,8 +352,8 @@ private: // EGLSlot contains the information and object references that // SurfaceTexture maintains about a BufferQueue buffer slot. - struct EGLSlot { - EGLSlot() + struct EglSlot { + EglSlot() : mEglImage(EGL_NO_IMAGE_KHR), mEglFence(EGL_NO_SYNC_KHR) { } @@ -379,7 +387,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]; + 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, diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index 086e298196..609e7d2c9e 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -29,7 +29,6 @@ #include <private/gui/ComposerService.h> #include <utils/Log.h> -#include <gui/SurfaceTexture.h> #include <utils/Trace.h> // Macros for including the BufferQueue name in log messages diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index b4dfb5e68d..037f5fb6a6 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -154,7 +154,56 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) } status_t SurfaceTexture::updateTexImage() { - return SurfaceTexture::updateTexImage(NULL, false); + ATRACE_CALL(); + ST_LOGV("updateTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("updateTexImage: SurfaceTexture is abandoned!"); + return NO_INIT; + } + + // Make sure the EGL state is the same as in previous calls. + status_t err = checkAndUpdateEglStateLocked(); + if (err != NO_ERROR) { + return err; + } + + BufferQueue::BufferItem item; + + // Acquire the next buffer. + // In asynchronous mode the list is guaranteed to be one buffer + // deep, while in synchronous mode we use the oldest buffer. + err = acquireBufferLocked(&item); + if (err != NO_ERROR) { + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + // We always bind the texture even if we don't update its contents. + ST_LOGV("updateTexImage: no buffers were available"); + glBindTexture(mTexTarget, mTexName); + err = NO_ERROR; + } else { + ST_LOGE("updateTexImage: acquire failed: %s (%d)", + strerror(-err), err); + } + return err; + } + + // Release the previous buffer. + err = releaseAndUpdateLocked(item); + if (err != NO_ERROR) { + // We always bind the texture. + glBindTexture(mTexTarget, mTexName); + return err; + } + + // Bind the new buffer to the GL texture. + err = bindTextureImage(); + if (err != NO_ERROR) { + return err; + } + + // Wait for the new buffer to be ready. + return doGLFenceWaitLocked(); } status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) { @@ -165,161 +214,156 @@ status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) { int slot = item->mBuf; if (item->mGraphicBuffer != NULL) { + // This buffer has not been acquired before, so we must assume + // that any EGLImage in mEglSlots is stale. if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage); + if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) { + ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", + slot); + // keep going + } 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) { - status_t err = ConsumerBase::releaseBufferLocked(buf, mEglDisplay, - eglFence); + status_t err = ConsumerBase::releaseBufferLocked(buf, display, eglFence); mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; return err; } -status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) { - ATRACE_CALL(); - ST_LOGV("updateTexImage"); - Mutex::Autolock lock(mMutex); - +status_t SurfaceTexture::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) +{ status_t err = NO_ERROR; - if (mAbandoned) { - ST_LOGE("updateTexImage: SurfaceTexture is abandoned!"); - return NO_INIT; - } - if (!mAttached) { - ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL " + ST_LOGE("releaseAndUpdate: SurfaceTexture is not attached to an OpenGL " "ES context"); return INVALID_OPERATION; } - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || - dpy == EGL_NO_DISPLAY) { - ST_LOGE("updateTexImage: invalid current EGLDisplay"); - return INVALID_OPERATION; + // Confirm state. + err = checkAndUpdateEglStateLocked(); + if (err != NO_ERROR) { + return err; } - if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || - ctx == EGL_NO_CONTEXT) { - ST_LOGE("updateTexImage: invalid current EGLContext"); - return INVALID_OPERATION; + int buf = item.mBuf; + + // If the mEglSlot entry is empty, create an EGLImage for the gralloc + // buffer currently in the slot in ConsumerBase. + // + // We may have to do this even when item.mGraphicBuffer == NULL (which + // means the buffer was previously acquired), if we destroyed the + // EGLImage when detaching from a context but the buffer has not been + // re-allocated. + if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) { + EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer); + if (image == EGL_NO_IMAGE_KHR) { + ST_LOGW("releaseAndUpdate: unable to createImage on display=%p slot=%d", + mEglDisplay, buf); + return UNKNOWN_ERROR; + } + mEglSlots[buf].mEglImage = image; } - mEglDisplay = dpy; - mEglContext = ctx; + // Do whatever sync ops we need to do before releasing the old slot. + err = syncForReleaseLocked(mEglDisplay); + 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, mEglDisplay, EGL_NO_SYNC_KHR); + return err; + } - BufferQueue::BufferItem item; + ST_LOGV("releaseAndUpdate: (slot=%d buf=%p) -> (slot=%d buf=%p)", + mCurrentTexture, + mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, + buf, mSlots[buf].mGraphicBuffer->handle); - // In asynchronous mode the list is guaranteed to be one buffer - // deep, while in synchronous mode we use the oldest buffer. - err = acquireBufferLocked(&item); - if (err == NO_ERROR) { - int buf = item.mBuf; - - // 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); - glBindTexture(mTexTarget, mTexName); - return NO_ERROR; + // release old buffer + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + status_t status = releaseBufferLocked(mCurrentTexture, mEglDisplay, + mEglSlots[mCurrentTexture].mEglFence); + if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) { + ST_LOGE("releaseAndUpdate: failed to release buffer: %s (%d)", + strerror(-status), status); + err = status; + // keep going, with error raised [?] } + } - GLint error; - while ((error = glGetError()) != GL_NO_ERROR) { - ST_LOGW("updateTexImage: clearing GL error: %#04x", error); + // Update the SurfaceTexture state. + mCurrentTexture = buf; + mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; + mCurrentCrop = item.mCrop; + mCurrentTransform = item.mTransform; + mCurrentScalingMode = item.mScalingMode; + mCurrentTimestamp = item.mTimestamp; + mCurrentFence = item.mFence; + + computeCurrentTransformMatrixLocked(); + + return err; +} + +status_t SurfaceTexture::bindTextureImage() { + if (mEglDisplay == EGL_NO_DISPLAY) { + ALOGE("bindTextureImage: invalid display"); + return INVALID_OPERATION; + } + + GLint error; + while ((error = glGetError()) != GL_NO_ERROR) { + ST_LOGW("bindTextureImage: clearing GL error: %#04x", error); + } + + glBindTexture(mTexTarget, mTexName); + if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { + if (mCurrentTextureBuf == NULL) { + ST_LOGE("bindTextureImage: no currently-bound texture"); + return NO_INIT; } + return bindUnslottedBufferLocked(mEglDisplay); + } else { + EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage; - 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) { - err = syncForReleaseLocked(dpy); + ST_LOGE("bindTextureImage: error binding external texture image %p" + ": %#04x", image, error); + return UNKNOWN_ERROR; } + return NO_ERROR; + } +} - 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); - return err; - } +status_t SurfaceTexture::checkAndUpdateEglStateLocked() { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); - ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", - mCurrentTexture, - mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, - buf, mSlots[buf].mGraphicBuffer->handle); - - // release old buffer - if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - status_t status = releaseBufferLocked(mCurrentTexture, dpy, - mEglSlots[mCurrentTexture].mEglFence); - if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) { - ST_LOGE("updateTexImage: failed to release buffer: %s (%d)", - strerror(-status), status); - err = status; - } - } + if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || + dpy == EGL_NO_DISPLAY) { + ST_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); + return INVALID_OPERATION; + } - // Update the SurfaceTexture state. - mCurrentTexture = buf; - mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; - mCurrentCrop = item.mCrop; - mCurrentTransform = item.mTransform; - mCurrentScalingMode = item.mScalingMode; - mCurrentTimestamp = item.mTimestamp; - mCurrentFence = item.mFence; - if (!skipSync) { - // SurfaceFlinger needs to lazily perform GLES synchronization - // only when it's actually going to use GLES for compositing. - // Eventually SurfaceFlinger should have its own consumer class, - // but for now we'll just hack it in to SurfaceTexture. - // SurfaceFlinger is responsible for calling doGLFenceWait before - // texturing from this SurfaceTexture. - doGLFenceWaitLocked(); - } - computeCurrentTransformMatrixLocked(); - } else { - if (err < 0) { - ST_LOGE("updateTexImage: acquire failed: %s (%d)", - strerror(-err), err); - return err; - } - // We always bind the texture even if we don't update its contents. - glBindTexture(mTexTarget, mTexName); - return OK; + if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || + ctx == EGL_NO_CONTEXT) { + ST_LOGE("checkAndUpdateEglState: invalid current EGLContext"); + return INVALID_OPERATION; } - return err; + mEglDisplay = dpy; + mEglContext = ctx; + return NO_ERROR; } void SurfaceTexture::setReleaseFence(int fenceFd) { @@ -427,30 +471,8 @@ status_t SurfaceTexture::attachToContext(GLuint tex) { // The EGLImageKHR that was associated with the slot was destroyed when // the SurfaceTexture was detached from the old context, so we need to // recreate it here. - EGLImageKHR image = createImage(dpy, mCurrentTextureBuf); - if (image == EGL_NO_IMAGE_KHR) { - return UNKNOWN_ERROR; - } - - // Attach the current buffer to the GL texture. - glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); - - GLint error; - status_t err = OK; - while ((error = glGetError()) != GL_NO_ERROR) { - ST_LOGE("attachToContext: error binding external texture image %p " - "(slot %d): %#04x", image, mCurrentTexture, error); - err = UNKNOWN_ERROR; - } - - // We destroy the EGLImageKHR here because the current buffer may no - // longer be associated with one of the buffer slots, so we have - // nowhere to to store it. If the buffer is still associated with a - // slot then another EGLImageKHR will be created next time that buffer - // gets acquired in updateTexImage. - eglDestroyImageKHR(dpy, image); - - if (err != OK) { + status_t err = bindUnslottedBufferLocked(dpy); + if (err != NO_ERROR) { return err; } } @@ -463,6 +485,38 @@ status_t SurfaceTexture::attachToContext(GLuint tex) { return OK; } +status_t SurfaceTexture::bindUnslottedBufferLocked(EGLDisplay dpy) { + ST_LOGV("bindUnslottedBuffer ct=%d ctb=%p", + mCurrentTexture, mCurrentTextureBuf.get()); + + // Create a temporary EGLImageKHR. + EGLImageKHR image = createImage(dpy, mCurrentTextureBuf); + if (image == EGL_NO_IMAGE_KHR) { + return UNKNOWN_ERROR; + } + + // Attach the current buffer to the GL texture. + glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); + + GLint error; + status_t err = OK; + while ((error = glGetError()) != GL_NO_ERROR) { + ST_LOGE("bindUnslottedBuffer: error binding external texture image %p " + "(slot %d): %#04x", image, mCurrentTexture, error); + err = UNKNOWN_ERROR; + } + + // We destroy the EGLImageKHR here because the current buffer may no + // longer be associated with one of the buffer slots, so we have + // nowhere to to store it. If the buffer is still associated with a + // slot then another EGLImageKHR will be created next time that buffer + // gets acquired in updateTexImage. + eglDestroyImageKHR(dpy, image); + + return err; +} + + status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) { ST_LOGV("syncForReleaseLocked"); diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index 329bbd5ff1..f7870f49f8 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \ GLExtensions.cpp \ MessageQueue.cpp \ SurfaceFlinger.cpp \ + SurfaceFlingerConsumer.cpp \ SurfaceTextureLayer.cpp \ Transform.cpp \ diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index b6aa0058db..1c5403ff16 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -73,7 +73,7 @@ void Layer::onLayerDisplayed(const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface* layer) { LayerBaseClient::onLayerDisplayed(hw, layer); if (layer) { - mSurfaceTexture->setReleaseFence(layer->getAndResetReleaseFenceFd()); + mSurfaceFlingerConsumer->setReleaseFence(layer->getAndResetReleaseFenceFd()); } } @@ -81,20 +81,20 @@ void Layer::onFirstRef() { LayerBaseClient::onFirstRef(); - // Creates a custom BufferQueue for SurfaceTexture to use + // Creates a custom BufferQueue for SurfaceFlingerConsumer to use sp<BufferQueue> bq = new SurfaceTextureLayer(); - mSurfaceTexture = new SurfaceTexture(mTextureName, true, + mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(mTextureName, true, GL_TEXTURE_EXTERNAL_OES, false, bq); - mSurfaceTexture->setConsumerUsageBits(getEffectiveUsage(0)); - mSurfaceTexture->setFrameAvailableListener(this); - mSurfaceTexture->setSynchronousMode(true); + mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0)); + mSurfaceFlingerConsumer->setFrameAvailableListener(this); + mSurfaceFlingerConsumer->setSynchronousMode(true); #ifdef TARGET_DISABLE_TRIPLE_BUFFERING #warning "disabling triple buffering" - mSurfaceTexture->setDefaultMaxBufferCount(2); + mSurfaceFlingerConsumer->setDefaultMaxBufferCount(2); #else - mSurfaceTexture->setDefaultMaxBufferCount(3); + mSurfaceFlingerConsumer->setDefaultMaxBufferCount(3); #endif const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice()); @@ -115,12 +115,12 @@ void Layer::onFrameAvailable() { // in the purgatory list void Layer::onRemoved() { - mSurfaceTexture->abandon(); + mSurfaceFlingerConsumer->abandon(); } void Layer::setName(const String8& name) { LayerBase::setName(name); - mSurfaceTexture->setName(name); + mSurfaceFlingerConsumer->setName(name); } sp<ISurface> Layer::createSurface() @@ -131,7 +131,7 @@ sp<ISurface> Layer::createSurface() sp<ISurfaceTexture> res; sp<const Layer> that( mOwner.promote() ); if (that != NULL) { - res = that->mSurfaceTexture->getBufferQueue(); + res = that->mSurfaceFlingerConsumer->getBufferQueue(); } return res; } @@ -146,7 +146,7 @@ sp<ISurface> Layer::createSurface() wp<IBinder> Layer::getSurfaceTextureBinder() const { - return mSurfaceTexture->getBufferQueue()->asBinder(); + return mSurfaceFlingerConsumer->getBufferQueue()->asBinder(); } status_t Layer::setBuffers( uint32_t w, uint32_t h, @@ -177,15 +177,15 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, mOpaqueLayer = (flags & ISurfaceComposerClient::eOpaque); mCurrentOpacity = getOpacityForFormat(format); - mSurfaceTexture->setDefaultBufferSize(w, h); - mSurfaceTexture->setDefaultBufferFormat(format); - mSurfaceTexture->setConsumerUsageBits(getEffectiveUsage(0)); + mSurfaceFlingerConsumer->setDefaultBufferSize(w, h); + mSurfaceFlingerConsumer->setDefaultBufferFormat(format); + mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0)); return NO_ERROR; } Rect Layer::computeBufferCrop() const { - // Start with the SurfaceTexture's buffer crop... + // Start with the SurfaceFlingerConsumer's buffer crop... Rect crop; if (!mCurrentCrop.isEmpty()) { crop = mCurrentCrop; @@ -202,7 +202,7 @@ Rect Layer::computeBufferCrop() const { if (!s.active.crop.isEmpty()) { // Transform the window crop to match the buffer coordinate system, // which means using the inverse of the current transform set on the - // SurfaceTexture. + // SurfaceFlingerConsumer. uint32_t invTransform = mCurrentTransform; int winWidth = s.active.w; int winHeight = s.active.h; @@ -284,7 +284,7 @@ void Layer::setAcquireFence(const sp<const DisplayDevice>& hw, // acquire fence the first time a new buffer is acquired on EACH display. if (layer.getCompositionType() == HWC_OVERLAY) { - sp<Fence> fence = mSurfaceTexture->getCurrentFence(); + sp<Fence> fence = mSurfaceFlingerConsumer->getCurrentFence(); if (fence.get()) { fenceFd = fence->dup(); if (fenceFd == -1) { @@ -327,7 +327,15 @@ void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const return; } - status_t err = mSurfaceTexture->doGLFenceWait(); + // Bind the current buffer to the GL texture. + status_t err = mSurfaceFlingerConsumer->bindTextureImage(); + if (err != NO_ERROR) { + ALOGW("Layer::onDraw: bindTextureImage failed"); + // keep going + } + + // Wait for the buffer to be ready for us to draw into. + err = mSurfaceFlingerConsumer->doGLFenceWait(); if (err != OK) { ALOGE("onDraw: failed waiting for fence: %d", err); // Go ahead and draw the buffer anyway; no matter what we do the screen @@ -342,8 +350,8 @@ void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const // Query the texture matrix given our current filtering mode. float textureMatrix[16]; - mSurfaceTexture->setFilteringEnabled(useFiltering); - mSurfaceTexture->getTransformMatrix(textureMatrix); + mSurfaceFlingerConsumer->setFilteringEnabled(useFiltering); + mSurfaceFlingerConsumer->getTransformMatrix(textureMatrix); // Set things up for texturing. glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureName); @@ -462,7 +470,7 @@ uint32_t Layer::doTransaction(uint32_t flags) // record the new size, form this point on, when the client request // a buffer, it'll get the new size. - mSurfaceTexture->setDefaultBufferSize( + mSurfaceFlingerConsumer->setDefaultBufferSize( temp.requested.w, temp.requested.h); } @@ -507,13 +515,10 @@ bool Layer::onPreComposition() { void Layer::onPostComposition() { if (mFrameLatencyNeeded) { - nsecs_t desiredPresentTime = mSurfaceTexture->getTimestamp(); + nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp(); mFrameTracker.setDesiredPresentTime(desiredPresentTime); - sp<Fence> frameReadyFence = mSurfaceTexture->getCurrentFence(); - // XXX: Temporarily don't use the fence from the SurfaceTexture to - // work around a driver bug. - frameReadyFence.clear(); + sp<Fence> frameReadyFence = mSurfaceFlingerConsumer->getCurrentFence(); if (frameReadyFence != NULL) { mFrameTracker.setFrameReadyFence(frameReadyFence); } else { @@ -570,7 +575,7 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) mFlinger->signalLayerUpdate(); } - struct Reject : public SurfaceTexture::BufferRejecter { + struct Reject : public SurfaceFlingerConsumer::BufferRejecter { Layer::State& front; Layer::State& current; bool& recomputeVisibleRegions; @@ -655,14 +660,14 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) Reject r(mDrawingState, currentState(), recomputeVisibleRegions); - if (mSurfaceTexture->updateTexImage(&r, true) < NO_ERROR) { + if (mSurfaceFlingerConsumer->updateTexImage(&r) != NO_ERROR) { // something happened! recomputeVisibleRegions = true; return outDirtyRegion; } // update the active buffer - mActiveBuffer = mSurfaceTexture->getCurrentBuffer(); + mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer(); if (mActiveBuffer == NULL) { // this can only happen if the very first buffer was rejected. return outDirtyRegion; @@ -676,9 +681,9 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) recomputeVisibleRegions = true; } - Rect crop(mSurfaceTexture->getCurrentCrop()); - const uint32_t transform(mSurfaceTexture->getCurrentTransform()); - const uint32_t scalingMode(mSurfaceTexture->getCurrentScalingMode()); + Rect crop(mSurfaceFlingerConsumer->getCurrentCrop()); + const uint32_t transform(mSurfaceFlingerConsumer->getCurrentTransform()); + const uint32_t scalingMode(mSurfaceFlingerConsumer->getCurrentScalingMode()); if ((crop != mCurrentCrop) || (transform != mCurrentTransform) || (scalingMode != mCurrentScalingMode)) @@ -737,8 +742,8 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const result.append(buffer); - if (mSurfaceTexture != 0) { - mSurfaceTexture->dump(result, " ", buffer, SIZE); + if (mSurfaceFlingerConsumer != 0) { + mSurfaceFlingerConsumer->dump(result, " ", buffer, SIZE); } } @@ -780,7 +785,7 @@ void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const { orientation = 0; } } - mSurfaceTexture->setTransformHint(orientation); + mSurfaceFlingerConsumer->setTransformHint(orientation); } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 5ea3eb426c..2d4afc483e 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -20,8 +20,6 @@ #include <stdint.h> #include <sys/types.h> -#include <gui/SurfaceTexture.h> - #include <utils/Timers.h> #include <ui/GraphicBuffer.h> @@ -34,6 +32,7 @@ #include <GLES/gl.h> #include <GLES/glext.h> +#include "SurfaceFlingerConsumer.h" #include "FrameTracker.h" #include "LayerBase.h" #include "SurfaceTextureLayer.h" @@ -49,7 +48,7 @@ class GLExtensions; // --------------------------------------------------------------------------- class Layer : public LayerBaseClient, - public SurfaceTexture::FrameAvailableListener + public SurfaceFlingerConsumer::FrameAvailableListener { public: Layer(SurfaceFlinger* flinger, const sp<Client>& client); @@ -92,7 +91,7 @@ public: // only for debugging inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; } - // Updates the transform hint in our SurfaceTexture to match + // Updates the transform hint in our SurfaceFlingerConsumer to match // the current orientation of the display device. virtual void updateTransformHint(const sp<const DisplayDevice>& hw) const; @@ -110,13 +109,13 @@ private: Rect computeBufferCrop() const; static bool getOpacityForFormat(uint32_t format); - // Interface implementation for SurfaceTexture::FrameAvailableListener + // Interface implementation for SurfaceFlingerConsumer::FrameAvailableListener virtual void onFrameAvailable(); // ----------------------------------------------------------------------- // constants - sp<SurfaceTexture> mSurfaceTexture; + sp<SurfaceFlingerConsumer> mSurfaceFlingerConsumer; GLuint mTextureName; // thread-safe diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp new file mode 100644 index 0000000000..dbe187b6ae --- /dev/null +++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "SurfaceFlingerConsumer.h" + +#include <utils/Trace.h> +#include <utils/Errors.h> + + +namespace android { + +// --------------------------------------------------------------------------- + +status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter) +{ + ATRACE_CALL(); + ALOGV("updateTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ALOGE("updateTexImage: SurfaceTexture is abandoned!"); + return NO_INIT; + } + + // Make sure the EGL state is the same as in previous calls. + status_t err = checkAndUpdateEglStateLocked(); + if (err != NO_ERROR) { + return err; + } + + BufferQueue::BufferItem item; + + // Acquire the next buffer. + // In asynchronous mode the list is guaranteed to be one buffer + // deep, while in synchronous mode we use the oldest buffer. + err = acquireBufferLocked(&item); + if (err != NO_ERROR) { + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + // This variant of updateTexImage does not guarantee that the + // texture is bound, so no need to call glBindTexture. + err = NO_ERROR; + } else { + ALOGE("updateTexImage: acquire failed: %s (%d)", + strerror(-err), err); + } + return err; + } + + + // 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 + int buf = item.mBuf; + if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) { + releaseBufferLocked(buf, EGL_NO_SYNC_KHR); + return NO_ERROR; + } + + // Release the previous buffer. + err = releaseAndUpdateLocked(item); + if (err != NO_ERROR) { + return err; + } + + // Bind the new buffer to the GL texture. + // TODO: skip this on devices that support explicit sync + // (glEGLImageTargetTexture2DOES provides required implicit sync; + // without this we get wedged on older devices, but newer devices + // don't need it.) + return bindTextureImage(); +} + +// --------------------------------------------------------------------------- +}; // namespace android + diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h new file mode 100644 index 0000000000..d91f27e593 --- /dev/null +++ b/services/surfaceflinger/SurfaceFlingerConsumer.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SURFACEFLINGERCONSUMER_H +#define ANDROID_SURFACEFLINGERCONSUMER_H + +#include <gui/SurfaceTexture.h> + +namespace android { +// ---------------------------------------------------------------------------- + +/* + * This is a thin wrapper around SurfaceTexture. + */ +class SurfaceFlingerConsumer : public SurfaceTexture { +public: + SurfaceFlingerConsumer(GLuint tex, bool allowSynchronousMode = true, + GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true, + const sp<BufferQueue> &bufferQueue = 0) + : SurfaceTexture(tex, allowSynchronousMode, texTarget, useFenceSync, + bufferQueue) + {} + + class BufferRejecter { + friend class SurfaceFlingerConsumer; + virtual bool reject(const sp<GraphicBuffer>& buf, + const BufferQueue::BufferItem& item) = 0; + + protected: + virtual ~BufferRejecter() { } + }; + + // This version of updateTexImage() takes a functor that may be used to + // reject the newly acquired buffer. Unlike the SurfaceTexture version, + // this does not guarantee that the buffer has been bound to the GL + // texture. + status_t updateTexImage(BufferRejecter* rejecter); + + // Pass-through to SurfaceTexture implementation. + status_t bindTextureImage() { return SurfaceTexture::bindTextureImage(); } +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_SURFACEFLINGERCONSUMER_H |