status_t CpuConsumer::releaseAcquiredBufferLocked(int lockedIdx) {
    status_t err;

    err = mAcquiredBuffers[lockedIdx].mGraphicBuffer->unlock();
    if (err != OK) {
        CC_LOGE("%s: Unable to unlock graphic buffer %d", __FUNCTION__,
                lockedIdx);
        return err;
    }
    int buf = mAcquiredBuffers[lockedIdx].mSlot;

    // release the buffer if it hasn't already been freed by the BufferQueue.
    // This can happen, for example, when the producer of this buffer
    // disconnected after this buffer was acquired.
    if (CC_LIKELY(mAcquiredBuffers[lockedIdx].mGraphicBuffer ==
            mSlots[buf].mGraphicBuffer)) {
        releaseBufferLocked(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
    }

    AcquiredBuffer &ab = mAcquiredBuffers.editItemAt(lockedIdx);
    ab.mSlot = BufferQueue::INVALID_BUFFER_SLOT;
    ab.mBufferPointer = NULL;
    ab.mGraphicBuffer.clear();

    mCurrentLockedBuffers--;
    return OK;
}
status_t CpuConsumer::unlockBuffer(const LockedBuffer &nativeBuffer) {
    Mutex::Autolock _l(mMutex);
    int slotIndex = 0;
    status_t err;

    void *bufPtr = reinterpret_cast<void *>(nativeBuffer.data);
    for (; slotIndex < BufferQueue::NUM_BUFFER_SLOTS; slotIndex++) {
        if (bufPtr == mBufferPointers[slotIndex]) break;
    }
    if (slotIndex == BufferQueue::NUM_BUFFER_SLOTS) {
        CC_LOGE("%s: Can't find buffer to free", __FUNCTION__);
        return BAD_VALUE;
    }

    mBufferPointers[slotIndex] = NULL;
    err = mSlots[slotIndex].mGraphicBuffer->unlock();
    if (err != OK) {
        CC_LOGE("%s: Unable to unlock graphic buffer %d", __FUNCTION__, slotIndex);
        return err;
    }
    releaseBufferLocked(slotIndex, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);

    mCurrentLockedBuffers--;

    return OK;
}
Exemplo n.º 3
0
status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence) {
    Mutex::Autolock lock(mMutex);

    BufferQueue::BufferItem item;
#if ANDROID_VERSION >= 19
    status_t err = acquireBufferLocked(&item, 0);
#else
    status_t err = acquireBufferLocked(&item);
#endif
    if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
        outBuffer = mCurrentBuffer;
        return NO_ERROR;
    } else if (err != NO_ERROR) {
        ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
        return err;
    }

    // If the BufferQueue has freed and reallocated a buffer in mCurrentSlot
    // then we may have acquired the slot we already own.  If we had released
    // our current buffer before we call acquireBuffer then that release call
    // would have returned STALE_BUFFER_SLOT, and we would have called
    // freeBufferLocked on that slot.  Because the buffer slot has already
    // been overwritten with the new buffer all we have to do is skip the
    // releaseBuffer call and we should be in the same state we'd be in if we
    // had released the old buffer first.
    if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT &&
        item.mBuf != mCurrentBufferSlot) {
        // Release the previous buffer.
#if ANDROID_VERSION >= 19
        err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer,
                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
#else
        err = releaseBufferLocked(mCurrentBufferSlot, EGL_NO_DISPLAY,
                EGL_NO_SYNC_KHR);
#endif
        if (err != NO_ERROR && err != BufferQueue::STALE_BUFFER_SLOT) {
            ALOGE("error releasing buffer: %s (%d)", strerror(-err), err);
            return err;
        }
    }
    mCurrentBufferSlot = item.mBuf;
    mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;
    outFence = item.mFence;
    outBuffer = mCurrentBuffer;
    return NO_ERROR;
}
void VirtualDisplaySurface::onFrameCommitted() {
    if (mDisplayId < 0)
        return;

    VDS_LOGW_IF(mDbgState != DBG_STATE_HWC,
            "Unexpected onFrameCommitted() in %s state", dbgStateStr());
    mDbgState = DBG_STATE_IDLE;

    sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId);
    if (mFbProducerSlot >= 0) {
        // release the scratch buffer back to the pool
        Mutex::Autolock lock(mMutex);
        int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
        VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot);
        addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], fbFence);
        releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot],
                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
    }

    if (mOutputProducerSlot >= 0) {
        int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot);
        QueueBufferOutput qbo;
        sp<Fence> outFence = mHwc.getLastRetireFence(mDisplayId);
        VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot);
        // Allow queuing to sink buffer if mMustRecompose is true or
        // mForceHwcCopy is true. This is required to support Miracast WFD Sink
        // Initiatied Pause/Resume feature support
        if (mForceHwcCopy || mMustRecompose) {
            status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot,
                    QueueBufferInput(
                        systemTime(), false /* isAutoTimestamp */,
                        Rect(mSinkBufferWidth, mSinkBufferHeight),
                        NATIVE_WINDOW_SCALING_MODE_FREEZE, 0 /* transform */,
                        true /* async*/,
                        outFence),
                    &qbo);
            if (result == NO_ERROR) {
                updateQueueBufferOutput(qbo);
            }
        } else {
            // If the surface hadn't actually been updated, then we only went
            // through the motions of updating the display to keep our state
            // machine happy. We cancel the buffer to avoid triggering another
            // re-composition and causing an infinite loop.
            mSource[SOURCE_SINK]->cancelBuffer(sslot, outFence);
        }
    }

    resetPerFrameState();
}
status_t GonkNativeWindow::releaseBuffer(const BufferItem &item,
        const sp<Fence>& releaseFence) {
    status_t err;

    Mutex::Autolock _l(mMutex);

    err = addReleaseFenceLocked(item.mBuf, releaseFence);

    err = releaseBufferLocked(item.mBuf);
    if (err != OK) {
        BI_LOGE("Failed to release buffer: %s (%d)",
                strerror(-err), err);
    }
    return err;
}
status_t BufferItemConsumer::releaseBuffer(const BufferItem &item,
        const sp<Fence>& releaseFence) {
    status_t err;

    Mutex::Autolock _l(mMutex);

    err = addReleaseFenceLocked(item.mBuf, item.mGraphicBuffer, releaseFence);

    err = releaseBufferLocked(item.mBuf, item.mGraphicBuffer, EGL_NO_DISPLAY,
            EGL_NO_SYNC_KHR);
    if (err != OK) {
        BI_LOGE("Failed to release buffer: %s (%d)",
                strerror(-err), err);
    }
    return err;
}
bool
GonkNativeWindow::returnBuffer(uint32_t aIndex, uint32_t aGeneration) {
    BI_LOGD("GonkNativeWindow::returnBuffer: slot=%d (generation=%d)", aIndex, aGeneration);
    Mutex::Autolock lock(mMutex);

    if (aGeneration != mBufferQueue->getGeneration()) {
        BI_LOGD("returnBuffer: buffer is from generation %d (current is %d)",
          aGeneration, mBufferQueue->getGeneration());
        return false;
    }

    status_t err = releaseBufferLocked(aIndex);
    if (err != NO_ERROR) {
        return false;
    }
  return true;
}
Exemplo n.º 8
0
void GonkNativeWindow::returnBuffer(TextureClient* client) {
    BI_LOGD("GonkNativeWindow::returnBuffer");
    Mutex::Autolock lock(mMutex);

    int index =  mBufferQueue->getSlotFromTextureClientLocked(client);
    if (index < 0) {
    }

    sp<Fence> fence = client->GetReleaseFenceHandle().mFence;
    if (!fence.get()) {
      fence = Fence::NO_FENCE;
    }

    status_t err;
    err = addReleaseFenceLocked(index, fence);

    err = releaseBufferLocked(index);
}
Exemplo n.º 9
0
bool
GonkNativeWindow::returnBuffer(uint32_t index, uint32_t generation, const sp<Fence>& fence) {
    BI_LOGD("GonkNativeWindow::returnBuffer: slot=%d (generation=%d)", index, generation);
    Mutex::Autolock lock(mMutex);

    if (generation != mBufferQueue->getGeneration()) {
        BI_LOGD("returnBuffer: buffer is from generation %d (current is %d)",
          generation, mBufferQueue->getGeneration());
        return false;
    }

    status_t err;
    err = addReleaseFenceLocked(index, fence);

    err = releaseBufferLocked(index);
    if (err != NO_ERROR) {
        return false;
    }
  return true;
}
Exemplo n.º 10
0
void VirtualDisplaySurface::onFrameCommitted() {
    if (mDisplayId < 0)
        return;

    VDS_LOGW_IF(mDbgState != DBG_STATE_HWC,
            "Unexpected onFrameCommitted() in %s state", dbgStateStr());
    mDbgState = DBG_STATE_IDLE;

    sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId);
    if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
        // release the scratch buffer back to the pool
        Mutex::Autolock lock(mMutex);
        int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
        VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot);
        addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], fbFence);
        releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot],
                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
    }

    if (mOutputProducerSlot >= 0) {
        int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot);
        QueueBufferOutput qbo;
        sp<Fence> outFence = mHwc.getLastRetireFence(mDisplayId);
        VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot);
        status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot,
                QueueBufferInput(
                    systemTime(), false /* isAutoTimestamp */,
                    Rect(mSinkBufferWidth, mSinkBufferHeight),
                    NATIVE_WINDOW_SCALING_MODE_FREEZE, 0 /* transform */,
                    true /* async*/,
                    outFence),
                &qbo);
        if (result == NO_ERROR) {
            updateQueueBufferOutput(qbo);
        }
    }

    resetPerFrameState();
}
Exemplo n.º 11
0
status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item)
{
    status_t err = NO_ERROR;

    if (!mAttached) {
        ST_LOGE("updateAndRelease: GLConsumer is not attached to an OpenGL "
                "ES context");
        return INVALID_OPERATION;
    }

    // Confirm state.
    err = checkAndUpdateEglStateLocked();
    if (err != NO_ERROR) {
        return err;
    }

    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, item.mCrop);
        if (image == EGL_NO_IMAGE_KHR) {
            ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
                  mEglDisplay, buf);
            return UNKNOWN_ERROR;
        }
        mEglSlots[buf].mEglImage = image;
        mEglSlots[buf].mCropRect = item.mCrop;
    }

    // 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.
        // As we are still under lock since acquireBuffer, it is safe to
        // release by slot.
        releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
                mEglDisplay, EGL_NO_SYNC_KHR);
        return err;
    }

    ST_LOGV("updateAndRelease: (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, mCurrentTextureBuf, mEglDisplay,
                mEglSlots[mCurrentTexture].mEglFence);
        if (status < NO_ERROR) {
            ST_LOGE("updateAndRelease: failed to release buffer: %s (%d)",
                   strerror(-status), status);
            err = status;
            // keep going, with error raised [?]
        }
    }

    // Update the GLConsumer state.
    mCurrentTexture = buf;
    mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
    mCurrentCrop = item.mCrop;
    mCurrentTransform = item.mTransform;
    mCurrentScalingMode = item.mScalingMode;
    mCurrentTimestamp = item.mTimestamp;
    mCurrentFence = item.mFence;
    mCurrentFrameNumber = item.mFrameNumber;

    computeCurrentTransformMatrixLocked();

    return err;
}
status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync, bool isComposition) {
#else
status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) {
#endif
    ATRACE_CALL();
    ST_LOGV("updateTexImage");
    Mutex::Autolock lock(mMutex);

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

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

#ifdef DECIDE_TEXTURE_TARGET
        // GPU is not efficient in handling GL_TEXTURE_EXTERNAL_OES
        // texture target. Depending on the image format, decide,
        // the texture target to be used
        if(isComposition){
            switch (mSlots[buf].mGraphicBuffer->format) {
                case HAL_PIXEL_FORMAT_RGBA_8888:
                case HAL_PIXEL_FORMAT_RGBX_8888:
                case HAL_PIXEL_FORMAT_RGB_888:
                case HAL_PIXEL_FORMAT_RGB_565:
                case HAL_PIXEL_FORMAT_BGRA_8888:
                case HAL_PIXEL_FORMAT_RGBA_5551:
                case HAL_PIXEL_FORMAT_RGBA_4444:
                    mTexTarget = GL_TEXTURE_2D;
                    break;
                default:
                    mTexTarget = GL_TEXTURE_EXTERNAL_OES;
                    break;
            }
        }
#endif

        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 (err == NO_ERROR) {
            err = syncForReleaseLocked(dpy);
        }

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

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

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

    return err;
}
status_t RingBufferConsumer::releaseOldestBufferLocked(size_t* pinnedFrames) {
    status_t err = OK;

    List<RingBufferItem>::iterator it, end, accIt;

    it = mBufferItemList.begin();
    end = mBufferItemList.end();
    accIt = end;

    if (it == end) {
        /**
         * This is fine. We really care about being able to acquire a buffer
         * successfully after this function completes, not about it releasing
         * some buffer.
         */
        BI_LOGV("%s: No buffers yet acquired, can't release anything",
              __FUNCTION__);
        return NOT_ENOUGH_DATA;
    }

    for (; it != end; ++it) {
        RingBufferItem& find = *it;

        if (find.mPinCount > 0) {
            if (pinnedFrames != NULL) {
                ++(*pinnedFrames);
            }
            // Filter out pinned frame when searching for buffer to release
            continue;
        }

        if (find.mTimestamp < accIt->mTimestamp || accIt == end) {
            accIt = it;
        }
    }

    if (accIt != end) {
        RingBufferItem& item = *accIt;

        // In case the object was never pinned, pass the acquire fence
        // back to the release fence. If the fence was already waited on,
        // it'll just be a no-op to wait on it again.

        // item.mGraphicBuffer was populated with the proper graphic-buffer
        // at acquire even if it was previously acquired
        err = addReleaseFenceLocked(item.mBuf,
                item.mGraphicBuffer, item.mFence);

        if (err != OK) {
            BI_LOGE("Failed to add release fence to buffer "
                    "(timestamp %lld, framenumber %lld",
                    item.mTimestamp, item.mFrameNumber);
            return err;
        }

        BI_LOGV("Attempting to release buffer timestamp %lld, frame %lld",
                item.mTimestamp, item.mFrameNumber);

        // item.mGraphicBuffer was populated with the proper graphic-buffer
        // at acquire even if it was previously acquired
        err = releaseBufferLocked(item.mBuf, item.mGraphicBuffer,
                                  EGL_NO_DISPLAY,
                                  EGL_NO_SYNC_KHR);
        if (err != OK) {
            BI_LOGE("Failed to release buffer: %s (%d)",
                    strerror(-err), err);
            return err;
        }

        BI_LOGV("Buffer timestamp %lld, frame %lld evicted",
                item.mTimestamp, item.mFrameNumber);

        size_t currentSize = mBufferItemList.size();
        mBufferItemList.erase(accIt);
        assert(mBufferItemList.size() == currentSize - 1);
    } else {
        BI_LOGW("All buffers pinned, could not find any to release");
        return NO_BUFFER_AVAILABLE;

    }

    return OK;
}
status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item)
{
    status_t err = NO_ERROR;

    int buf = item.mBuf;

    if (!mAttached) {
        GLC_LOGE("updateAndRelease: GLConsumer is not attached to an OpenGL "
                "ES context");
        releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
                mEglDisplay, EGL_NO_SYNC_KHR);
        return INVALID_OPERATION;
    }

    // Confirm state.
    err = checkAndUpdateEglStateLocked();
    if (err != NO_ERROR) {
        releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
                mEglDisplay, EGL_NO_SYNC_KHR);
        return err;
    }

    // Ensure we have a valid EglImageKHR for the slot, creating an EglImage
    // if nessessary, 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).
    err = mEglSlots[buf].mEglImage->createIfNeeded(mEglDisplay, item.mCrop);
    if (err != NO_ERROR) {
        GLC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
                mEglDisplay, buf);
        releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
                mEglDisplay, EGL_NO_SYNC_KHR);
        return UNKNOWN_ERROR;
    }

    // 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.
        // As we are still under lock since acquireBuffer, it is safe to
        // release by slot.
        releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
                mEglDisplay, EGL_NO_SYNC_KHR);
        return err;
    }

    GLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)",
            mCurrentTexture, mCurrentTextureImage != NULL ?
                    mCurrentTextureImage->graphicBufferHandle() : 0,
            buf, mSlots[buf].mGraphicBuffer->handle);

    // release old buffer
    if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
        status_t status = releaseBufferLocked(
                mCurrentTexture, mCurrentTextureImage->graphicBuffer(),
                mEglDisplay, mEglSlots[mCurrentTexture].mEglFence);
        if (status < NO_ERROR) {
            GLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)",
                   strerror(-status), status);
            err = status;
            // keep going, with error raised [?]
        }
    }

    // Update the GLConsumer state.
    mCurrentTexture = buf;
    mCurrentTextureImage = mEglSlots[buf].mEglImage;
    mCurrentCrop = item.mCrop;
    mCurrentTransform = item.mTransform;
    mCurrentScalingMode = item.mScalingMode;
    mCurrentTimestamp = item.mTimestamp;
    mCurrentFence = item.mFence;
    mCurrentFrameNumber = item.mFrameNumber;

    computeCurrentTransformMatrixLocked();

    return err;
}
status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) {
#else
status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync, bool deferConversion) {
#endif
    ATRACE_CALL();
    ST_LOGV("updateTexImage");
    Mutex::Autolock lock(mMutex);

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

    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;

#ifdef STE_HARDWARE
        EGLImageKHR image;
        if (conversionIsNeeded(mSlots[buf].mGraphicBuffer)) {
            mNeedsConversion = deferConversion;
            // If color conversion is needed we can't use the graphic buffers
            // located in mSlots for the textures (wrong color format). Instead
            // color convert it into a buffer in mBlitSlots and use that instead.
            image = mBlitSlots[mNextBlitSlot].mEglImage;

            // If there exists an image already, make sure that
            // the dimensions match the current source buffer.
            // Otherwise, destroy the buffer and let a new one be allocated.
            if (image != EGL_NO_IMAGE_KHR &&
                    mSlots[buf].mGraphicBuffer != NULL &&
                    mBlitSlots[mNextBlitSlot].mGraphicBuffer != NULL) {
                sp<GraphicBuffer> &srcBuf = mSlots[buf].mGraphicBuffer;
                sp<GraphicBuffer> &bltBuf =
                    mBlitSlots[mNextBlitSlot].mGraphicBuffer;
                if (srcBuf->getWidth() != bltBuf->getWidth() ||
                        srcBuf->getHeight() != bltBuf->getHeight()) {
                    eglDestroyImageKHR(mBlitSlots[mNextBlitSlot].mEglDisplay,
                        image);
                    mBlitSlots[mNextBlitSlot].mEglImage = EGL_NO_IMAGE_KHR;
                    mBlitSlots[mNextBlitSlot].mGraphicBuffer = NULL;
                    image = EGL_NO_IMAGE_KHR;
                }
            }
            if (image == EGL_NO_IMAGE_KHR) {
                sp<GraphicBuffer> &srcBuf = mSlots[buf].mGraphicBuffer;
                status_t res = 0;

                sp<GraphicBuffer> blitBuffer(
                        mGraphicBufferAlloc->createGraphicBuffer(
                                srcBuf->getWidth(), srcBuf->getHeight(),
                                PIXEL_FORMAT_RGBA_8888, srcBuf->getUsage(),
                                &res));
                if (blitBuffer == 0) {
                    ST_LOGE("updateTexImage: SurfaceComposer::createGraphicBuffer failed");
                    return NO_MEMORY;
                }
                if (res != NO_ERROR) {
                    ST_LOGW("updateTexImage: SurfaceComposer::createGraphicBuffer error=%#04x", res);
                }
                mBlitSlots[mNextBlitSlot].mGraphicBuffer = blitBuffer;

                EGLDisplay dpy = eglGetCurrentDisplay();
                image = createImage(dpy, blitBuffer);
                mBlitSlots[mNextBlitSlot].mEglImage = image;
                mBlitSlots[mNextBlitSlot].mEglDisplay = dpy;
            }

            if (deferConversion) {
                item.mGraphicBuffer = mSlots[buf].mGraphicBuffer;
                mConversionSrcSlot = buf;
                mConversionBltSlot = mNextBlitSlot;
                // At this point item.mGraphicBuffer and image do not point
                // at matching buffers. This is intentional as this
                // surface might end up being taken care of by HWComposer,
                // which needs access to the original buffer.
                // GL however, is fed an EGLImage that is created from
                // a conversion buffer. It will have its
                // content updated once the surface is actually drawn
                // in Layer::onDraw()
            } else {
                if (convert(mSlots[buf].mGraphicBuffer,
                        mBlitSlots[mNextBlitSlot].mGraphicBuffer) != OK) {
                    ALOGE("updateTexImage: convert failed");
                    return UNKNOWN_ERROR;
                }
                item.mGraphicBuffer = mBlitSlots[mNextBlitSlot].mGraphicBuffer;
            }
            // mBlitSlots contains several buffers (NUM_BLIT_BUFFER_SLOTS),
            // advance (potentially wrap) the index
            mNextBlitSlot = (mNextBlitSlot + 1) % BufferQueue::NUM_BLIT_BUFFER_SLOTS;
        } else {
            mNeedsConversion = false;
            image = mEglSlots[buf].mEglImage;
            item.mGraphicBuffer = mSlots[buf].mGraphicBuffer;
            if (image == EGL_NO_IMAGE_KHR) {
                EGLDisplay dpy = eglGetCurrentDisplay();
                if (item.mGraphicBuffer == 0) {
                    ST_LOGE("buffer at slot %d is null", buf);
                    return BAD_VALUE;
                }
                image = createImage(dpy, item.mGraphicBuffer);
                mEglSlots[buf].mEglImage = image;
                mEglDisplay = dpy;
                if (image == EGL_NO_IMAGE_KHR) {
                    // NOTE: if dpy was invalid, createImage() is guaranteed to
                    // fail. so we'd end up here.
                    return -EINVAL;
                }
            }
        }
#endif

        // 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
#ifdef STE_HARDWARE
        if (rejecter && rejecter->reject(item.mGraphicBuffer, item)) {
#else
        if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) {
#endif
            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);
        }

#ifndef STE_HARDWARE
        EGLImageKHR image = mEglSlots[buf].mEglImage;
#endif
        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);
        }

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

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

        // Update the SurfaceTexture state.
        mCurrentTexture = buf;
#ifndef STE_HARDWARE
        mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
#else
        mCurrentTextureBuf = item.mGraphicBuffer;
#endif
        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;
    }

    return err;
}

void SurfaceTexture::setReleaseFence(int fenceFd) {
    sp<Fence> fence(new Fence(fenceFd));
    if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
        return;
    status_t err = addReleaseFence(mCurrentTexture, fence);
    if (err != OK) {
        ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)",
                strerror(-err), err);
    }
}
status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter)
{
    ATRACE_CALL();
    ALOGV("updateTexImage");
    Mutex::Autolock lock(mMutex);

    if (mAbandoned) {
        ALOGE("updateTexImage: GLConsumer 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;
    }

    if (!SyncFeatures::getInstance().useNativeFenceSync()) {
        // Bind the new buffer to the GL texture.
        //
        // Older devices require the "implicit" synchronization provided
        // by glEGLImageTargetTexture2DOES, which this method calls.  Newer
        // devices will either call this in Layer::onDraw, or (if it's not
        // a GL-composited layer) not at all.
        err = bindTextureImageLocked();
    }

    return err;
}
Exemplo n.º 17
0
status_t GLConsumer::releaseTexImage() {
    ATRACE_CALL();
    ST_LOGV("releaseTexImage");
    Mutex::Autolock lock(mMutex);

    if (mAbandoned) {
        ST_LOGE("releaseTexImage: GLConsumer is abandoned!");
        return NO_INIT;
    }

    // Make sure the EGL state is the same as in previous calls.
    status_t err = NO_ERROR;

    if (mAttached) {
        err = checkAndUpdateEglStateLocked(true);
        if (err != NO_ERROR) {
            return err;
        }
    } else {
        // if we're detached, no need to validate EGL's state -- we won't use it.
    }

    // Update the GLConsumer state.
    int buf = mCurrentTexture;
    if (buf != BufferQueue::INVALID_BUFFER_SLOT) {

        ST_LOGV("releaseTexImage: (slot=%d, mAttached=%d)", buf, mAttached);

        if (mAttached) {
            // Do whatever sync ops we need to do before releasing the slot.
            err = syncForReleaseLocked(mEglDisplay);
            if (err != NO_ERROR) {
                ST_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err);
                return err;
            }
        } else {
            // if we're detached, we just use the fence that was created in detachFromContext()
            // so... basically, nothing more to do here.
        }

        err = releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
        if (err < NO_ERROR) {
            ST_LOGE("releaseTexImage: failed to release buffer: %s (%d)",
                    strerror(-err), err);
            return err;
        }

        mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
        mCurrentTextureBuf = getDebugTexImageBuffer();
        mCurrentCrop.makeInvalid();
        mCurrentTransform = 0;
        mCurrentScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
        mCurrentTimestamp = 0;
        mCurrentFence = Fence::NO_FENCE;

        if (mAttached) {
            // bind a dummy texture
            glBindTexture(mTexTarget, mTexName);
            bindUnslottedBufferLocked(mEglDisplay);
        } else {
            // detached, don't touch the texture (and we may not even have an
            // EGLDisplay here.
        }
    }

    return NO_ERROR;
}