Android 12(S) 圖形顯示系統 – Surface 一點補充知識(十二)


必讀:

Android 12(S) 圖形顯示系統 – 開篇


 

一、前言


因為個人工作主要是Android多媒體播放的內容,在工作中查看源碼或設計程式經常會遇到調用API:

static inline int native_window_api_connect(struct ANativeWindow* window, int api)
static inline int native_window_api_disconnect(struct ANativeWindow* window, int api)

所以也一直好奇這兩個方法都做了什麼事情?這篇文章就來一探究竟。

 

二、native_window_api_connect 解析


Android系統中,開始播放影片並設置Surface後,都會做一次 connectToSurface 的操作,比如MediaCodec中,在初始化階段setSurface後就會調用方法:

status_t MediaCodec::connectToSurface(const sp<Surface> &surface) {
    ...
    err = nativeWindowConnect(surface.get(), "connectToSurface");
    ...
}

這裡就是去調用了 /frameworks/av/media/libstagefright/SurfaceUtils.cpp  中的方法:

status_t nativeWindowConnect(ANativeWindow *surface, const char *reason) {
    ALOGD("connecting to surface %p, reason %s", surface, reason);

    status_t err = native_window_api_connect(surface, NATIVE_WINDOW_API_MEDIA);
    ALOGE_IF(err != OK, "Failed to connect to surface %p, err %d", surface, err);

    return err;
}

是不是看到了 native_window_api_connect ,其中參數 NATIVE_WINDOW_API_MEDIA 表明 video decoder會作為生產者來生成buffer數據。

再接著往下走,看看到底做了什麼?

大體的調用流程如下:

 /frameworks/native/libs/gui/Surface.cpp

 /frameworks/native/libs/gui/BufferQueueProducer.cpp


>>> static inline int native_window_api_connect(struct ANativeWindow* window, int api)

>>> int Surface::hook_perform(ANativeWindow* window, int operation, …)

>>> int Surface::perform(int operation, va_list args)

>>> int Surface::dispatchConnect(va_list args)

>>> int Surface::connect(int api)

>>> int Surface::connect(int api, const sp<IProducerListener>& listener)

>>> int Surface::connect(int api, const sp<IProducerListener>& listener, bool reportBufferRemoval)

>>> status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
                int api, bool producerControlledByApp, QueueBufferOutput *output) 


最終進入到了BufferQueueProducer::connect函數中,看起來這裡應該就是做具體事情的地方了

老規矩,看源碼:

status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
        int api, bool producerControlledByApp, QueueBufferOutput *output) {
    ATRACE_CALL();
    std::lock_guard<std::mutex> lock(mCore->mMutex);
    mConsumerName = mCore->mConsumerName; // 消費者名字
    BQ_LOGV("connect: api=%d producerControlledByApp=%s", api,
            producerControlledByApp ? "true" : "false");
    // 對一些條件進行判斷,必要時直接返回return
    if (mCore->mIsAbandoned) {
        BQ_LOGE("connect: BufferQueue has been abandoned");
        return NO_INIT;
    }

    if (mCore->mConsumerListener == nullptr) {
        BQ_LOGE("connect: BufferQueue has no consumer");
        return NO_INIT;
    }

    if (output == nullptr) {
        BQ_LOGE("connect: output was NULL");
        return BAD_VALUE;
    }

    if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
        BQ_LOGE("connect: already connected (cur=%d req=%d)",
                mCore->mConnectedApi, api);
        return BAD_VALUE;
    }

    int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
            mDequeueTimeout < 0 ?
            mCore->mConsumerControlledByApp && producerControlledByApp : false,
            mCore->mMaxBufferCount) -
            mCore->getMaxBufferCountLocked();
    if (!mCore->adjustAvailableSlotsLocked(delta)) {
        BQ_LOGE("connect: BufferQueue failed to adjust the number of available "
                "slots. Delta = %d", delta);
        return BAD_VALUE;
    }

    int status = NO_ERROR;
    switch (api) {
        case NATIVE_WINDOW_API_EGL:
        case NATIVE_WINDOW_API_CPU:
        case NATIVE_WINDOW_API_MEDIA:
        case NATIVE_WINDOW_API_CAMERA:
            mCore->mConnectedApi = api;

            output->width = mCore->mDefaultWidth;
            output->height = mCore->mDefaultHeight;
            output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint;
            output->numPendingBuffers =
                    static_cast<uint32_t>(mCore->mQueue.size());
            output->nextFrameNumber = mCore->mFrameCounter + 1;
            output->bufferReplaced = false;
            output->maxBufferCount = mCore->mMaxBufferCount;

            if (listener != nullptr) {
                // Set up a death notification so that we can disconnect
                // automatically if the remote producer dies
#ifndef NO_BINDER
                if (IInterface::asBinder(listener)->remoteBinder() != nullptr) {
                    status = IInterface::asBinder(listener)->linkToDeath(
                            static_cast<IBinder::DeathRecipient*>(this));
                    if (status != NO_ERROR) {
                        BQ_LOGE("connect: linkToDeath failed: %s (%d)",
                                strerror(-status), status);
                    }
                    mCore->mLinkedToDeath = listener;
                }
#endif
                mCore->mConnectedProducerListener = listener; // 設置producer listener
                mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify();
            }
            break;
        default:
            BQ_LOGE("connect: unknown API %d", api);
            status = BAD_VALUE;
            break;
    }
    mCore->mConnectedPid = BufferQueueThreadState::getCallingPid();
    mCore->mBufferHasBeenQueued = false;
    mCore->mDequeueBufferCannotBlock = false;
    mCore->mQueueBufferCanDrop = false;
    mCore->mLegacyBufferDrop = true;
    if (mCore->mConsumerControlledByApp && producerControlledByApp) {
        mCore->mDequeueBufferCannotBlock = mDequeueTimeout < 0;
        mCore->mQueueBufferCanDrop = mDequeueTimeout <= 0;
    }

    mCore->mAllowAllocation = true; // 允許分配 graphic buffer
    VALIDATE_CONSISTENCY();
    return status;
}

我才疏學淺,按我理解,就是完成了一些初始化的操作,貌似也沒啥了,這之後應該produder就可以 dequeue buffer 了

另外一點,這裡有設置producer listener,之前文章中也講過,貌似也沒啥作用(也許我錯了)

    mCore->mConnectedProducerListener = listener;
    mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify();

三、native_window_api_disconnect 解析


disconnect的調用流程和connect的流程類似。

先看 Surface::disconnect 中做了啥

int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) {
    ATRACE_CALL();
    ALOGV("Surface::disconnect");
    Mutex::Autolock lock(mMutex);
    mRemovedBuffers.clear();
    mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
    mSharedBufferHasBeenQueued = false;
    freeAllBuffers();
    int err = mGraphicBufferProducer->disconnect(api, mode);
    if (!err) {
        mReqFormat = 0;
        mReqWidth = 0;
        mReqHeight = 0;
        mReqUsage = 0;
        mCrop.clear();
        mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
        mTransform = 0;
        mStickyTransform = 0;
        mAutoPrerotation = false;
        mEnableFrameTimestamps = false;
        mMaxBufferCount = NUM_BUFFER_SLOTS;

        if (api == NATIVE_WINDOW_API_CPU) {
            mConnectedToCpu = false;
        }
    }
    return err;
}

看起來主要是把reset一些變數和release一些資源,看到調用

    freeAllBuffers();
    int err = mGraphicBufferProducer->disconnect(api, mode)

freeAllBuffers是把mSlots裡面的元素都置為 nullptr

void Surface::freeAllBuffers() {
    if (!mDequeuedSlots.empty()) {
        ALOGE("%s: %zu buffers were freed while being dequeued!",
                __FUNCTION__, mDequeuedSlots.size());
    }
    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
        mSlots[i].buffer = nullptr;
    }
}

本文作者@二的次方  2022-03-24 發佈於部落格園


BufferQueueProducer::disconnect源碼如下


status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) {
    ATRACE_CALL();
    BQ_LOGV("disconnect: api %d", api);

    int status = NO_ERROR;
    sp<IConsumerListener> listener;
    { // Autolock scope
        std::unique_lock<std::mutex> lock(mCore->mMutex);

        if (mode == DisconnectMode::AllLocal) {
            if (BufferQueueThreadState::getCallingPid() != mCore->mConnectedPid) {
                return NO_ERROR;
            }
            api = BufferQueueCore::CURRENTLY_CONNECTED_API;
        }

        mCore->waitWhileAllocatingLocked(lock);

        if (mCore->mIsAbandoned) {
            // It's not really an error to disconnect after the surface has
            // been abandoned; it should just be a no-op.
            return NO_ERROR;
        }

        if (api == BufferQueueCore::CURRENTLY_CONNECTED_API) {
            if (mCore->mConnectedApi == NATIVE_WINDOW_API_MEDIA) {
                ALOGD("About to force-disconnect API_MEDIA, mode=%d", mode);
            }
            api = mCore->mConnectedApi;
            // If we're asked to disconnect the currently connected api but
            // nobody is connected, it's not really an error.
            if (api == BufferQueueCore::NO_CONNECTED_API) {
                return NO_ERROR;
            }
        }

        switch (api) {
            case NATIVE_WINDOW_API_EGL:
            case NATIVE_WINDOW_API_CPU:
            case NATIVE_WINDOW_API_MEDIA:
            case NATIVE_WINDOW_API_CAMERA:
                if (mCore->mConnectedApi == api) {
                    mCore->freeAllBuffersLocked();

#ifndef NO_BINDER
                    // Remove our death notification callback if we have one
                    if (mCore->mLinkedToDeath != nullptr) {
                        sp<IBinder> token =
                                IInterface::asBinder(mCore->mLinkedToDeath);
                        // This can fail if we're here because of the death
                        // notification, but we just ignore it
                        token->unlinkToDeath(
                                static_cast<IBinder::DeathRecipient*>(this));
                    }
#endif
                    mCore->mSharedBufferSlot =
                            BufferQueueCore::INVALID_BUFFER_SLOT;
                    mCore->mLinkedToDeath = nullptr;
                    mCore->mConnectedProducerListener = nullptr;
                    mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
                    mCore->mConnectedPid = -1;
                    mCore->mSidebandStream.clear();
                    mCore->mDequeueCondition.notify_all();
                    mCore->mAutoPrerotation = false;
                    listener = mCore->mConsumerListener;
                } else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
                    BQ_LOGE("disconnect: not connected (req=%d)", api);
                    status = NO_INIT;
                } else {
                    BQ_LOGE("disconnect: still connected to another API "
                            "(cur=%d req=%d)", mCore->mConnectedApi, api);
                    status = BAD_VALUE;
                }
                break;
            default:
                BQ_LOGE("disconnect: unknown API %d", api);
                status = BAD_VALUE;
                break;
        }
    } // Autolock scope

    // Call back without lock held
    if (listener != nullptr) {
        listener->onBuffersReleased();
        listener->onDisconnect();
    }

    return status;
}

看樣子也是reset一些變數和release資源。

 

調用了mCore->freeAllBuffersLocked()

void BufferQueueCore::freeAllBuffersLocked() {
    for (int s : mFreeSlots) {
        clearBufferSlotLocked(s);
    }

    for (int s : mFreeBuffers) {
        mFreeSlots.insert(s);
        clearBufferSlotLocked(s);
    }
    mFreeBuffers.clear();

    for (int s : mActiveBuffers) {
        mFreeSlots.insert(s);
        clearBufferSlotLocked(s);
    }
    mActiveBuffers.clear();

    for (auto& b : mQueue) {
        b.mIsStale = true;
        b.mAcquireCalled = false;
    }

    VALIDATE_CONSISTENCY();
}

 

通知了消費者 listener->onBuffersReleased()  and listener->onDisconnect()

消費者響應 onBuffersReleased 也是去free buffer

void ConsumerBase::onBuffersReleased() {
    Mutex::Autolock lock(mMutex);

    CB_LOGV("onBuffersReleased");

    if (mAbandoned) {
        // Nothing to do if we're already abandoned.
        return;
    }

    uint64_t mask = 0;
    mConsumer->getReleasedBuffers(&mask);
    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
        if (mask & (1ULL << i)) {
            freeBufferLocked(i);
        }
    }
}

消費者響應 onDisconnect

void BLASTBufferItemConsumer::onDisconnect() {
    Mutex::Autolock lock(mMutex);
    mPreviouslyConnected = mCurrentlyConnected;
    mCurrentlyConnected = false;
    if (mPreviouslyConnected) {
        mDisconnectEvents.push(mCurrentFrameNumber);
    }
    mFrameEventHistory.onDisconnect();
}

到此,就講完了,感覺好乏味…,沒有什麼實質的東西

 

♦ Android 12 Google將buffer queue組件從SurfaceFlinger端移動到了客戶端(舊版本是在BufferQueueLayer中去createBufferQueue的)。
buffer queue組件的創建和初始化也放在BLASTBufferQueue中。通過類名可以看出BLASTBufferQueue更像是buffer queue組件的一層封裝或裝飾。
♦ 通過前面系列文章的分析,可以看到,整個生產消費模型都在客戶端,圖形緩衝區的出隊、入隊、獲取等操作都在客戶端完成,預示著producer — buffer queue — consumer 間的通訊都變成了本地通訊。
♦ BLASTBufferQueue需要通過事務Transaction來向SurfaceFlinger端提交Buffer與圖層的屬性。
 
♦ 如本文講的 disconnect event 在12平台就無法傳遞到SurfaceFlinger了。如要傳遞資訊,就要使用 Transaction 傳遞過去。

 


不過我還是有個疑問:disconnect過程中,可以看到producer/consumer/surface都有去free buffer,此時,為GraphicBuffer分配的記憶體真的就釋放了嗎?

(mSlots[slot].mGraphicBuffer.clear() or  mSlots[slotIndex].mGraphicBuffer = nullptr)

 

四、小結


感覺沒啥說的了,就此結束吧