Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3325fe16 authored by Andy McFadden's avatar Andy McFadden Committed by Android (Google) Code Review
Browse files

Merge "Refactor SurfaceTexture a bit."

parents 3a6efcb8 bf974abe
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -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.
+38 −30
Original line number Diff line number Diff line
@@ -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,
+0 −1
Original line number Diff line number Diff line
@@ -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
+192 −138
Original line number Diff line number Diff line
@@ -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,21 +214,16 @@ 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);
            mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
        }
            if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) {
                ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d",
                      slot);
                // keep going
            }

    // 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 = EGL_NO_IMAGE_KHR;
        }
        mEglSlots[slot].mEglImage = image;
    }

    return NO_ERROR;
@@ -187,106 +231,71 @@ status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) {

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;
    }

    if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) ||
            ctx == EGL_NO_CONTEXT) {
        ST_LOGE("updateTexImage: invalid current EGLContext");
        return INVALID_OPERATION;
    // Confirm state.
    err = checkAndUpdateEglStateLocked();
    if (err != NO_ERROR) {
        return err;
    }

    mEglDisplay = dpy;
    mEglContext = ctx;

    BufferQueue::BufferItem item;

    // 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;
        }

        GLint error;
        while ((error = glGetError()) != GL_NO_ERROR) {
            ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
        }

        EGLImageKHR image = mEglSlots[buf].mEglImage;
        glBindTexture(mTexTarget, mTexName);
        glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);

        while ((error = glGetError()) != GL_NO_ERROR) {
            ST_LOGE("updateTexImage: error binding external texture image %p "
                    "(slot %d): %#04x", image, buf, error);
            err = UNKNOWN_ERROR;
    // If 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;
        }

        if (err == NO_ERROR) {
            err = syncForReleaseLocked(dpy);
        mEglSlots[buf].mEglImage = image;
    }

    // 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, dpy, EGL_NO_SYNC_KHR);
        releaseBufferLocked(buf, mEglDisplay, EGL_NO_SYNC_KHR);
        return err;
    }

        ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
    ST_LOGV("releaseAndUpdate: (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,
        status_t status = releaseBufferLocked(mCurrentTexture, mEglDisplay,
                mEglSlots[mCurrentTexture].mEglFence);
        if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
                ST_LOGE("updateTexImage: failed to release buffer: %s (%d)",
            ST_LOGE("releaseAndUpdate: failed to release buffer: %s (%d)",
                   strerror(-status), status);
            err = status;
            // keep going, with error raised [?]
        }
    }

@@ -298,28 +307,63 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync)
    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.

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);
        return OK;
    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;

    return err;
        glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);

        while ((error = glGetError()) != GL_NO_ERROR) {
            ST_LOGE("bindTextureImage: error binding external texture image %p"
                    ": %#04x", image, error);
            return UNKNOWN_ERROR;
        }
        return NO_ERROR;
    }
}

status_t SurfaceTexture::checkAndUpdateEglStateLocked() {
    EGLDisplay dpy = eglGetCurrentDisplay();
    EGLContext ctx = eglGetCurrentContext();

    if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) ||
            dpy == EGL_NO_DISPLAY) {
        ST_LOGE("checkAndUpdateEglState: invalid current EGLDisplay");
        return INVALID_OPERATION;
    }

    if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) ||
            ctx == EGL_NO_CONTEXT) {
        ST_LOGE("checkAndUpdateEglState: invalid current EGLContext");
        return INVALID_OPERATION;
    }

    mEglDisplay = dpy;
    mEglContext = ctx;
    return NO_ERROR;
}

void SurfaceTexture::setReleaseFence(int fenceFd) {
@@ -427,6 +471,25 @@ 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.
        status_t err = bindUnslottedBufferLocked(dpy);
        if (err != NO_ERROR) {
            return err;
        }
    }

    mEglDisplay = dpy;
    mEglContext = ctx;
    mTexName = tex;
    mAttached = true;

    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;
@@ -438,7 +501,7 @@ status_t SurfaceTexture::attachToContext(GLuint tex) {
    GLint error;
    status_t err = OK;
    while ((error = glGetError()) != GL_NO_ERROR) {
            ST_LOGE("attachToContext: error binding external texture image %p "
        ST_LOGE("bindUnslottedBuffer: error binding external texture image %p "
                "(slot %d): %#04x", image, mCurrentTexture, error);
        err = UNKNOWN_ERROR;
    }
@@ -450,18 +513,9 @@ status_t SurfaceTexture::attachToContext(GLuint tex) {
    // gets acquired in updateTexImage.
    eglDestroyImageKHR(dpy, image);

        if (err != OK) {
    return err;
}
    }

    mEglDisplay = dpy;
    mEglContext = ctx;
    mTexName = tex;
    mAttached = true;

    return OK;
}

status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
    ST_LOGV("syncForReleaseLocked");
+1 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \
    GLExtensions.cpp                        \
    MessageQueue.cpp                        \
    SurfaceFlinger.cpp                      \
    SurfaceFlingerConsumer.cpp              \
    SurfaceTextureLayer.cpp                 \
    Transform.cpp                           \
    
Loading