OSDN Git Service

Refactor SurfaceTexture a bit.
authorAndy McFadden <fadden@android.com>
Wed, 5 Dec 2012 00:51:15 +0000 (16:51 -0800)
committerAndy McFadden <fadden@android.com>
Tue, 11 Dec 2012 17:42:51 +0000 (09:42 -0800)
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

include/gui/BufferQueue.h
include/gui/SurfaceTexture.h
libs/gui/BufferQueue.cpp
libs/gui/SurfaceTexture.cpp
services/surfaceflinger/Android.mk
services/surfaceflinger/Layer.cpp
services/surfaceflinger/Layer.h
services/surfaceflinger/SurfaceFlingerConsumer.cpp [new file with mode: 0644]
services/surfaceflinger/SurfaceFlingerConsumer.h [new file with mode: 0644]

index 9e265ba..88bcd8f 100644 (file)
@@ -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.
index b498a5c..a8b7d74 100644 (file)
@@ -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,
index 086e298..609e7d2 100644 (file)
@@ -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
index b4dfb5e..037f5fb 100644 (file)
@@ -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");
 
index 329bbd5..f7870f4 100644 (file)
@@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \
     GLExtensions.cpp                        \
     MessageQueue.cpp                        \
     SurfaceFlinger.cpp                      \
+    SurfaceFlingerConsumer.cpp              \
     SurfaceTextureLayer.cpp                 \
     Transform.cpp                           \
     
index b6aa005..1c5403f 100644 (file)
@@ -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);
 }
 
 // ---------------------------------------------------------------------------
index 5ea3eb4..2d4afc4 100644 (file)
@@ -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 (file)
index 0000000..dbe187b
--- /dev/null
@@ -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 (file)
index 0000000..d91f27e
--- /dev/null
@@ -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