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

Commit f440f670 authored by Mathias Agopian's avatar Mathias Agopian Committed by Android (Google) Code Review
Browse files

Merge changes I5174a6ec,I1f8538b6,Idb71fdcf

* changes:
  fix a crasher in SurfaceTexture::updateTexImage()
  rework dequeueBuffer()'s main loop.
  error out when SurfaceTexture APIs are called while not connected
parents b2928c90 8e19c2e9
Loading
Loading
Loading
Loading
+22 −3
Original line number Diff line number Diff line
@@ -208,9 +208,28 @@ public:

protected:

    // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
    // all slots.
    void freeAllBuffers();
    // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage)
    // for the given slot.
    void freeBufferLocked(int index);

    // freeAllBuffersLocked frees the resources (both GraphicBuffer and
    // EGLImage) for all slots.
    void freeAllBuffersLocked();

    // freeAllBuffersExceptHeadLocked frees the resources (both GraphicBuffer
    // and EGLImage) for all slots except the head of mQueue
    void freeAllBuffersExceptHeadLocked();

    // drainQueueLocked drains the buffer queue if we're in synchronous mode
    // returns immediately otherwise. return NO_INIT if SurfaceTexture
    // became abandoned or disconnected during this call.
    status_t drainQueueLocked();

    // drainQueueAndFreeBuffersLocked drains the buffer queue if we're in
    // synchronous mode and free all buffers. In asynchronous mode, all buffers
    // are freed except the current buffer.
    status_t drainQueueAndFreeBuffersLocked();

    static bool isExternalFormat(uint32_t format);

private:
+111 −48
Original line number Diff line number Diff line
@@ -104,7 +104,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode) :

SurfaceTexture::~SurfaceTexture() {
    LOGV("SurfaceTexture::~SurfaceTexture");
    freeAllBuffers();
    freeAllBuffersLocked();
}

status_t SurfaceTexture::setBufferCountServerLocked(int bufferCount) {
@@ -154,7 +154,10 @@ status_t SurfaceTexture::setBufferCount(int bufferCount) {
        LOGE("setBufferCount: SurfaceTexture has been abandoned!");
        return NO_INIT;
    }

    if (mConnectedApi == NO_CONNECTED_API) {
        LOGE("setBufferCount: SurfaceTexture is not connected!");
        return NO_INIT;
    }
    if (bufferCount > NUM_BUFFER_SLOTS) {
        LOGE("setBufferCount: bufferCount larger than slots available");
        return BAD_VALUE;
@@ -185,7 +188,7 @@ status_t SurfaceTexture::setBufferCount(int bufferCount) {

    // here we're guaranteed that the client doesn't have dequeued buffers
    // and will release all of its buffer references.
    freeAllBuffers();
    freeAllBuffersLocked();
    mBufferCount = bufferCount;
    mClientBufferCount = bufferCount;
    mCurrentTexture = INVALID_BUFFER_SLOT;
@@ -214,6 +217,10 @@ status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
        LOGE("requestBuffer: SurfaceTexture has been abandoned!");
        return NO_INIT;
    }
    if (mConnectedApi == NO_CONNECTED_API) {
        LOGE("requestBuffer: SurfaceTexture is not connected!");
        return NO_INIT;
    }
    if (slot < 0 || mBufferCount <= slot) {
        LOGE("requestBuffer: slot index out of range [0, %d]: %d",
                mBufferCount, slot);
@@ -228,11 +235,6 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
        uint32_t format, uint32_t usage) {
    LOGV("SurfaceTexture::dequeueBuffer");

    if (mAbandoned) {
        LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
        return NO_INIT;
    }

    if ((w && !h) || (!w && h)) {
        LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
        return BAD_VALUE;
@@ -246,10 +248,19 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
    int dequeuedCount = 0;
    bool tryAgain = true;
    while (tryAgain) {
        if (mAbandoned) {
            LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
            return NO_INIT;
        }
        if (mConnectedApi == NO_CONNECTED_API) {
            LOGE("dequeueBuffer: SurfaceTexture is not connected!");
            return NO_INIT;
        }

        // We need to wait for the FIFO to drain if the number of buffer
        // needs to change.
        //
        // The condition "number of buffer needs to change" is true if
        // The condition "number of buffers needs to change" is true if
        // - the client doesn't care about how many buffers there are
        // - AND the actual number of buffer is different from what was
        //   set in the last setBufferCountServer()
@@ -261,31 +272,24 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
        // As long as this condition is true AND the FIFO is not empty, we
        // wait on mDequeueCondition.

        int minBufferCountNeeded = mSynchronousMode ?
        const int minBufferCountNeeded = mSynchronousMode ?
                MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;

        if (!mClientBufferCount &&
        const bool numberOfBuffersNeedsToChange = !mClientBufferCount &&
                ((mServerBufferCount != mBufferCount) ||
                        (mServerBufferCount < minBufferCountNeeded))) {
                        (mServerBufferCount < minBufferCountNeeded));

        if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) {
            // wait for the FIFO to drain
            while (!mQueue.isEmpty()) {
            mDequeueCondition.wait(mMutex);
                if (mAbandoned) {
                    LOGE("dequeueBuffer: SurfaceTexture was abandoned while "
                            "blocked!");
                    return NO_INIT;
                }
            }
            minBufferCountNeeded = mSynchronousMode ?
                    MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
            // NOTE: we continue here because we need to reevaluate our
            // whole state (eg: we could be abandoned or disconnected)
            continue;
        }


        if (!mClientBufferCount &&
                ((mServerBufferCount != mBufferCount) ||
                        (mServerBufferCount < minBufferCountNeeded))) {
        if (numberOfBuffersNeedsToChange) {
            // here we're guaranteed that mQueue is empty
            freeAllBuffers();
            freeAllBuffersLocked();
            mBufferCount = mServerBufferCount;
            if (mBufferCount < minBufferCountNeeded)
                mBufferCount = minBufferCountNeeded;
@@ -414,9 +418,9 @@ status_t SurfaceTexture::setSynchronousMode(bool enabled) {

    if (!enabled) {
        // going to asynchronous mode, drain the queue
        while (mSynchronousMode != enabled && !mQueue.isEmpty()) {
            mDequeueCondition.wait(mMutex);
        }
        err = drainQueueLocked();
        if (err != NO_ERROR)
            return err;
    }

    if (mSynchronousMode != enabled) {
@@ -442,6 +446,10 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
            LOGE("queueBuffer: SurfaceTexture has been abandoned!");
            return NO_INIT;
        }
        if (mConnectedApi == NO_CONNECTED_API) {
            LOGE("queueBuffer: SurfaceTexture is not connected!");
            return NO_INIT;
        }
        if (buf < 0 || buf >= mBufferCount) {
            LOGE("queueBuffer: slot index out of range [0, %d]: %d",
                    mBufferCount, buf);
@@ -512,6 +520,10 @@ void SurfaceTexture::cancelBuffer(int buf) {
        LOGW("cancelBuffer: SurfaceTexture has been abandoned!");
        return;
    }
    if (mConnectedApi == NO_CONNECTED_API) {
        LOGE("cancelBuffer: SurfaceTexture is not connected!");
        return;
    }

    if (buf < 0 || buf >= mBufferCount) {
        LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
@@ -533,6 +545,10 @@ status_t SurfaceTexture::setCrop(const Rect& crop) {
        LOGE("setCrop: SurfaceTexture has been abandoned!");
        return NO_INIT;
    }
    if (mConnectedApi == NO_CONNECTED_API) {
        LOGE("setCrop: SurfaceTexture is not connected!");
        return NO_INIT;
    }
    mNextCrop = crop;
    return OK;
}
@@ -544,6 +560,10 @@ status_t SurfaceTexture::setTransform(uint32_t transform) {
        LOGE("setTransform: SurfaceTexture has been abandoned!");
        return NO_INIT;
    }
    if (mConnectedApi == NO_CONNECTED_API) {
        LOGE("setTransform: SurfaceTexture is not connected!");
        return NO_INIT;
    }
    mNextTransform = transform;
    return OK;
}
@@ -598,8 +618,9 @@ status_t SurfaceTexture::disconnect(int api) {
        case NATIVE_WINDOW_API_MEDIA:
        case NATIVE_WINDOW_API_CAMERA:
            if (mConnectedApi == api) {
                drainQueueAndFreeBuffersLocked();
                mConnectedApi = NO_CONNECTED_API;
                freeAllBuffers();
                mDequeueCondition.signal();
            } else {
                LOGE("disconnect: connected to another api (cur=%d, req=%d)",
                        mConnectedApi, api);
@@ -635,7 +656,7 @@ status_t SurfaceTexture::updateTexImage() {

    if (mAbandoned) {
        LOGE("calling updateTexImage() on an abandoned SurfaceTexture");
        //return NO_INIT;
        return NO_INIT;
    }

    // In asynchronous mode the list is guaranteed to be one buffer
@@ -644,21 +665,14 @@ status_t SurfaceTexture::updateTexImage() {
        Fifo::iterator front(mQueue.begin());
        int buf = *front;

        if (uint32_t(buf) >= NUM_BUFFER_SLOTS) {
            LOGE("buffer index out of range (index=%d)", buf);
            //return BAD_VALUE;
        }

        // Update the GL texture object.
        EGLImageKHR image = mSlots[buf].mEglImage;
        if (image == EGL_NO_IMAGE_KHR) {
            EGLDisplay dpy = eglGetCurrentDisplay();

            if (mSlots[buf].mGraphicBuffer == 0) {
                LOGE("buffer at slot %d is null", buf);
                //return BAD_VALUE;
                return BAD_VALUE;
            }

            image = createImage(dpy, mSlots[buf].mGraphicBuffer);
            mSlots[buf].mEglImage = image;
            mSlots[buf].mEglDisplay = dpy;
@@ -848,8 +862,7 @@ void SurfaceTexture::setFrameAvailableListener(
    mFrameAvailableListener = listener;
}

void SurfaceTexture::freeAllBuffers() {
    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
void SurfaceTexture::freeBufferLocked(int i) {
    mSlots[i].mGraphicBuffer = 0;
    mSlots[i].mBufferState = BufferSlot::FREE;
    if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
@@ -858,6 +871,55 @@ void SurfaceTexture::freeAllBuffers() {
        mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
    }
}

void SurfaceTexture::freeAllBuffersLocked() {
    LOGW_IF(!mQueue.isEmpty(),
            "freeAllBuffersLocked called but mQueue is not empty");
    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
        freeBufferLocked(i);
    }
}

void SurfaceTexture::freeAllBuffersExceptHeadLocked() {
    LOGW_IF(!mQueue.isEmpty(),
            "freeAllBuffersExceptCurrentLocked called but mQueue is not empty");
    int head = -1;
    if (!mQueue.empty()) {
        Fifo::iterator front(mQueue.begin());
        head = *front;
    }
    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
        if (i != head) {
            freeBufferLocked(i);
        }
    }
}

status_t SurfaceTexture::drainQueueLocked() {
    while (mSynchronousMode && !mQueue.isEmpty()) {
        mDequeueCondition.wait(mMutex);
        if (mAbandoned) {
            LOGE("drainQueueLocked: SurfaceTexture has been abandoned!");
            return NO_INIT;
        }
        if (mConnectedApi == NO_CONNECTED_API) {
            LOGE("drainQueueLocked: SurfaceTexture is not connected!");
            return NO_INIT;
        }
    }
    return NO_ERROR;
}

status_t SurfaceTexture::drainQueueAndFreeBuffersLocked() {
    status_t err = drainQueueLocked();
    if (err == NO_ERROR) {
        if (mSynchronousMode) {
            freeAllBuffersLocked();
        } else {
            freeAllBuffersExceptHeadLocked();
        }
    }
    return err;
}

EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
@@ -929,9 +991,10 @@ int SurfaceTexture::query(int what, int* outValue)

void SurfaceTexture::abandon() {
    Mutex::Autolock lock(mMutex);
    freeAllBuffers();
    mQueue.clear();
    mAbandoned = true;
    mCurrentTextureBuf.clear();
    freeAllBuffersLocked();
    mDequeueCondition.signal();
}