2018年1月11日 星期四

從 BootAnimation 探索 SurfaceFlinger (四) 等這場戰鬥結束以後,我就要把Buffer畫出來了

在開始描述 BufferQueue 的行為之前, 有必要先了解幾個元素:

1.) BufferQueueDefs
namespace android {
    class BufferQueueCore;
    
    namespace BufferQueueDefs {
        // BufferQueue will keep track of at most this value of buffers.
        // Attempts at runtime to increase the number of buffers past this
        // will fail.
        enum { NUM_BUFFER_SLOTS = 64 };
    
        typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
    } // namespace BufferQueueDefs
} // namespace android

BufferQueueDefs 是一個 namespace, 用來保護 gui 資料夾下的 BufferSlot 定義
在 Surface.h 之中, 也有一個名為 BufferSlot 的結構, 但它只是單純的指向一塊 GraphicBuffer

BufferQueueDefs::BufferSlot 則稍微複雜一些, 後面我們會看到完整的結構
這裡將 BufferSlot 重新命名為 SlotsType
使用 SlotsType 宣告時表示這是一個有 64 個 BufferSlot 的 array
使用場景如下:
// BufferQueueCore.h
BufferQueueDefs::SlotsType mSlots; // 等同於 BufferSlot[64]

2.) BufferState
BufferState 是在 BufferSlot 裡面標記狀態的 enum
我們看一下它的描述:

enum BufferState {
    FREE = 0,
    /*
        FREE 表示這塊 buffer 可以被 producer dequeue,
        這塊 buffer 可能會被 consumer 在一段時間內繼續使用,
        因此這塊 buffer 一定不能被修改, 直到對應的 fence 被觸發.
        
        這個 slot 被 BufferQueue 所持有, 當呼叫 dequeueBuffer 後會轉換為 DEQUEUED    
    */

    DEQUEUED = 1,
    /*
        DEQUEUED 表示這塊 buffer 已經被 producer dequeued 了,
        而它還沒有被重新 queue 或者 cancel.
        Producer 在觸發 fence 之後就可以開始修改 buffer 內容.
        
        這個 slot 被 producer 所持有, 當呼叫 queueBuffer 後會轉為 QUEUED
        當呼叫 cancelBuffer 後會回到 FREE
    */

    QUEUED = 2,
    /*
        QUEUED 表示這塊 buffer 已經被 producer 所填充,
        並且 queue 進去通知 consumer 可以消費了.
        這塊 buffer 的內容可能會在一段時間內被修改,
        因此需要等到對應的 fence 觸發後才能開始存取.
        
        這個 slot 被 BufferQueue 所持有,
        當呼叫 acuquireBuffer 後會轉為 ACQUIRED,
        或者在另一塊 buffer 以非同步方式 queue 進後轉回 FREE 
    */

    ACQUIRED = 3
    /*
        ACQUIRED 表示這塊 buffer 被 consumer 獲取了.
        與其他人相同, 這塊 buffer 必須等到 fence 被觸發後才能存取
        
        這個 slot 是被 consumer 所持有,
        等到呼叫 releaseBuffer 後會轉回 FREE 狀態
    */
};

* 注意: 上述的 BufferState 標記的是 Slot 的狀態

我們可以看到敘述中出現了 buffer 跟 slot 這兩個對象.
而這裡的 buffer 指的是 GraphicBuffer.
slot 就好比是文件夾一樣, 而 buffer 就是放置在文件夾內的文件.

你可以想成是櫃子裏面有許多的文件夾,
這些文件夾可能有人使用了, 可能沒有人使用.
沒有人使用的文件夾, 就是 FREE.
有人拿走了文件夾想要寫入新的文件, 就是 DEQUEUED
他寫完文件後放回櫃子, 就是 QUEUED
有人想要讀取文件把它取走, 就是 ACQUIRED
slot 內一開始是沒有 buffer 的.
當 Producer 呼叫 dequeueBuffer 去獲取 Buffer 時,
BufferQueue 會尋找一塊 FREE 的 slot,
如果 slot 內部沒有 buffer 的話, 那麼 allocator 就會獲取一塊 GraphicBuffer 放進去.
一開始文件夾是空的,
所以你跟總務要了一疊紙
接著就可以開始寫了
使用 buffer 時必須跟一個叫做 fence 的傢伙進行連動.
當我們要執行繪圖的動作時, 會從 CPU 端下指令給 GPU
由於 Command 操作是 asynchronous 的形式, 並不能保證 GPU 已經完成了工作.
有一些阻塞的 API 可以呼叫, 例如 glFinish() 會等到 GPU 做完再返回
但這樣形同浪費  CPU 的 resource.
這個 fence 就是一個鎖的概念, GPU 使用完成之後, 會釋放 fence 使得上層可以存取, 反之亦然.
這使得 CPU 可以繼續執行, 等到真的要使用到 GraphicBuffer 時才去等待 GPU.

流程如下:
第一次呼叫 dequeueBuffer 時 GraphicBuffer 初次生成, 此時是沒有 fence 的.
填入 buffer 並 queueBuffer 時, 會生成一個 acquireFence, 因為 GPU 可能還沒有完成動作.
CPU queue 進以後, consumer 會被通知到有新的資料,
它藉由 acquireBuffer 來拿到這個 slot, 此時就要等待 acquireFence 觸發 (真的寫完了) 才能真的取出 buffer 來用.
反之, 當 releaseBuffer 時, 會生成一個 releaseFence.
下一次 producer 呼叫 dequeueBuffer 時, 就要等待這個 releaseFence 觸發 (真的讀完了) 後才能存取 buffer.
順帶一提, 這裡還有兩個操作跟整個 dequeue - queue 的行為比較脫鉤,
他們是 detachBuffer 跟 attachBuffer
detachBuffer 會將 GraphicBuffer 從 slot 之中取出
attachBuffer 則是把 GraphicBuffer 放進一個 slot 裡面去
就像是你可以從文件夾裡面把文件整個拿走
再放到另外一個文件夾之中
3.) BufferSlot
現在可以完整的看一下 BufferSlot 裡面究竟定義了哪些東西:

struct BufferSlot {
    // 指向這個 slot 所關聯到的 buffer, 初始值為 NULL
    sp<GraphicBuffer> mGraphicBuffer;

    // EGLDisplay 會使用它來創造 EGLSyncKHR 的物件, 進一步的產生 fence
    EGLDisplay mEglDisplay;

    // 取得這個 slot 的當前狀態名稱
    static const char* bufferStateName(BufferState state);

    // Slot 的當前狀態
    BufferState mBufferState;

    // 標註 producer 有沒有呼叫 requestBuffer, 主要用於 debug 
    bool mRequestBufferCalled;

    // 標註這個 slot 裡面有多少已經 queued 的 frame 數.
    // 這個變數通常用以讓 buffer 可以依照 LRU (Least Recently Used) 次序來 dequeue 
    uint64_t mFrameNumber;

    // 這是一個 EGL sync object,
    // 它必須在這個 buffer slot 關聯到的 buffer 被 dequeue 之前觸發
    // 初始值為 EGL_NO_SYNC_KHR, 並且當 releaseBuffer 時會生成一個新的 sync 物件
    // 有了 mFence 支援後捨棄不用 (This is deprecated in favor of mFence, below.)
    EGLSyncKHR mEglFence;
    
    // mFence 用來通知 "前一個使用 buffer 的人" 已經結束工作了
    // 1.) 當 buffer 現在在 FREE 狀態, 則會在前一個 consumer 完成讀取時觸發 fence,
    //     或者是在前一個 producer 已經寫了一些東西但是呼叫了 cancelBuffer 後觸發
    // 2.) 當 buffer 現在在 QUEUED 狀態, 則會在 producer 完成寫入後觸發 fence
    // 3.) 當 buffer 現在在 DEQUEUED 或者 ACQUIRED 狀態,
    //     此時會將 fence 轉交給 consumer 或者是 producer, 此時 mFence 設置為 NO_FENCE
    sp<Fence> mFence;

    // 標註這塊 buffer 已經被給 consumer 看見了沒
    // (在 BufferQueueConsumer::acquireBuffer 會修改到)
    bool mAcquireCalled;    

    // 標註在 Consumer 使用完畢後是否需要被清除 (cleaned up)
    // 如果呼叫 freeBuferLocked 時這塊 buffer 處於 ACQUIRED 的狀態, 就會舉起這個 flag
    // 使得 releaseBuffer 時回傳 STALE_BUFFER_SLOT (表示 consumer 剛剛清掉這塊 buffer)
    bool mNeedsCleanupOnRelease;
    
    // 標註這塊 buffer 是否已經 attach 給 consumer 了
    // 如果是的話, 則 dequeue 時會設置 BUFFER_NEEDS_REALLOCATION 這個 flag,
    // 以防止 producer 使用了前一塊 stale 的 cached buffer 
    bool mAttachedByConsumer;
}

大致上的變數都在這裡提到了, 接下來我們回到 BootAnimation 來看看實際上的操作.

在上一篇文章中, 我們生成了一個 EGLSurface 對象, 帶入了我們的 Surface 結構,
並且在 eglMakeCurrent 的時候呼叫 dequeueBuffer 將 GraphicBuffer 指給了了 EGLSurface 的 buffer 參數.
經過了一連串的 EGL 操作後, 最後會呼叫到 eglSwapBuffers
在 eglSwapBuffers 中, 將會執行到 nativeWindow->queueBuffer(nativeWindow, buffer, -1);
以及 nativeWindow->dequeueBuffer(nativeWindow, &buffer, &fenceFd)
就是把前一張畫好的 buffer 給 queue 進去, 並且再 dequeue 一張新的 buffer 出來使用.

調用流程如下:

eglSwapBuffers (EGL API)
  -> eglSwapBuffersWithDamageKHR
    -> eglSwapBuffers (OEM EGL library)
      -> nativeWindow->queueBuffer (Surface::hook_queueBuffer)
        -> queueBuffer (Surface)
          -> queueBuffer (MonitoredProducer)
            -> queueBuffer (BufferQueueProducer)
              -> frameAvailableListener->onFrameAvailable (Layer)
                -> mFlinger->signalLayerUpdate() (SurfaceFlinger)

休息一下, 下一篇要再度進入到 SurfaceFlinger 的世界了

2018年1月10日 星期三

從 BootAnimation 探索 SurfaceFlinger (三) 野生的四天王.現身

前情提要

上一篇文章我們分析了 Layer class 裡面的 BufferQueue 相關構造,
但是還沒有看到 Layer 究竟會做哪些工作.

Layer 生成了 mProducer 跟 mSurfaceFlingerConsumer,
因為 Layer 本身繼承了 SurfaceFlingerConsumer::ContentsChangedListener,
它會通知 SurfaceFlinger 說已經有新的資料來到了.
另一方面, 這個 mProducer 應該要有人負責去輸入資料,
我們接著看看是誰來接走了 mProducer 的功能.

回到 createNormalLayer:
status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client,
        const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
        sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
{
    // initialize the surfaces
    switch (format) {
    case PIXEL_FORMAT_TRANSPARENT:
    case PIXEL_FORMAT_TRANSLUCENT:
        format = PIXEL_FORMAT_RGBA_8888;
        break;
    case PIXEL_FORMAT_OPAQUE:
        format = PIXEL_FORMAT_RGBX_8888;
        break;
    }

    *outLayer = new Layer(this, client, name, w, h, flags);
    status_t err = (*outLayer)->setBuffers(w, h, format, flags);
    if (err == NO_ERROR) {
        *handle = (*outLayer)->getHandle();
        *gbp = (*outLayer)->getProducer();
    }

    ALOGE_IF(err, "createNormalLayer() failed (%s)", strerror(-err));
    return err;
}

透過 setBuffer 設置 mSurfaceFlingerConsumer 的屬性,
然後把 handle 跟 gbp 設置好, 準備返回給 SurfaceComposerClient
(忘記的人可以點開第一篇來回顧一下)
sp<IBinder> Layer::getHandle() {
    Mutex::Autolock _l(mLock);

    LOG_ALWAYS_FATAL_IF(mHasSurface,
            "Layer::getHandle() has already been called");

    mHasSurface = true;

    /*
     * The layer handle is just a BBinder object passed to the client
     * (remote process) -- we don't keep any reference on our side such that
     * the dtor is called when the remote side let go of its reference.
     *
     * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
     * this layer when the handle is destroyed.
     */

    class Handle : public BBinder, public LayerCleaner {
        wp<const Layer> mOwner;
    public:
        Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
            : LayerCleaner(flinger, layer), mOwner(layer) {
        }
    };

    return new Handle(mFlinger, this);
}

sp<IGraphicBufferProducer> Layer::getProducer() const {
    return mProducer;
}

Handle 的功能是將這個 new 出來的 Layer 轉交給 SurfaceComposerClient,
而不是將 Layer 的指針保存在 SurfaceFlinger 裡面
而 mProducer 就是 MonitoredProducer, 可以透過它存取 BufferQueueProducer

再度回到 SurfaceComposerClient (中間還有一層 Binder 的 IPC, 這裡就不特別論述)
sp<SurfaceControl> SurfaceComposerClient::createSurface(
        const String8& name,
        uint32_t w,
        uint32_t h,
        PixelFormat format,
        uint32_t flags)
{
    sp<SurfaceControl> sur;
    if (mStatus == NO_ERROR) {
        sp<IBinder> handle;
        sp<IGraphicBufferProducer> gbp;
        status_t err = mClient->createSurface(name, w, h, format, flags,
                &handle, &gbp);
        ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
        if (err == NO_ERROR) {
            sur = new SurfaceControl(this, handle, gbp);
        }
    }
    return sur;
}

handle 跟 gbp 又被轉交給了 SurfaceControl 對象
SurfaceControl::SurfaceControl(
        const sp<SurfaceComposerClient>& client,
        const sp<IBinder>& handle,
        const sp<IGraphicBufferProducer>& gbp)
    : mClient(client), mHandle(handle), mGraphicBufferProducer(gbp)
{
}

好的, 終於要回到 BootAnimation 了
可怕的是...我們還在 readyToRun() 的第五行
跟 Inception 一樣每下一層的時間都是上一層的 20 倍
status_t BootAnimation::readyToRun() {
    mAssets.addDefaultAssets();

    sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
            ISurfaceComposer::eDisplayIdMain));
    DisplayInfo dinfo;
    status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
    if (status)
        return -1;

    // create the native surface
    sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
            dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);

    SurfaceComposerClient::openGlobalTransaction();
    control->setLayer(0x40000000);
    SurfaceComposerClient::closeGlobalTransaction();

    sp<Surface> s = control->getSurface();

這裡的 openGlobalTransaction() ~ closeGlobalTransaction() 會在多執行序時讓希望通知 SurfaceFlinger的人可以進行一次通知
它會使用變量 mTransactionNestCount 來判斷是否還有其他人也想同時操作
假設有多個用戶在呼叫 (此例是 SurfaceControl 呼叫 SurfaceComposerClient::setLayer())
每個人在 openGlobalTransaction() 時會增加 mTransactionNestCount
並在 closeGlobalTransaction() 時去減掉 mTransactionNestCount
只有當 mTransactionNestCount 為 0 的時候才會去呼叫 SurfaceFlinger, 如下:
void Composer::openGlobalTransactionImpl() {
    { // scope for the lock
        Mutex::Autolock _l(mLock);
        mTransactionNestCount += 1;
    }
}

void Composer::closeGlobalTransactionImpl(bool synchronous) {
    sp<ISurfaceComposer> sm(ComposerService::getComposerService());

    Vector<ComposerState> transaction;
    Vector<DisplayState> displayTransaction;
    uint32_t flags = 0;

    { // scope for the lock
        Mutex::Autolock _l(mLock);
        mForceSynchronous |= synchronous;
        if (!mTransactionNestCount) {
            ALOGW("At least one call to closeGlobalTransaction() was not matched by a prior "
                    "call to openGlobalTransaction().");
        } else if (--mTransactionNestCount) {
            return;
        }

        transaction = mComposerStates;
        mComposerStates.clear();

        displayTransaction = mDisplayStates;
        mDisplayStates.clear();

        if (mForceSynchronous) {
            flags |= ISurfaceComposer::eSynchronous;
        }
        if (mAnimation) {
            flags |= ISurfaceComposer::eAnimation;
        }

        mForceSynchronous = false;
        mAnimation = false;
    }

   sm->setTransactionState(transaction, displayTransaction, flags);
}

這裡呼叫的設定是 SurfaceControl->setLayer(0x40000000)
我們過一下這整個 code flow:
// SurfaceControl.cpp
status_t SurfaceControl::setLayer(uint32_t layer) {
    status_t err = validate();
    if (err < 0) return err;
    return mClient->setLayer(mHandle, layer);
}   

// SurfaceComposerClient.cpp
status_t SurfaceComposerClient::setLayer(const sp<IBinder>& id, uint32_t z) {
    return getComposer().setLayer(this, id, z);
}

status_t Composer::setLayer(const sp<SurfaceComposerClient>& client,
        const sp<IBinder>& id, uint32_t z) {
    Mutex::Autolock _l(mLock);
    layer_state_t* s = getLayerStateLocked(client, id);
    if (!s)
        return BAD_INDEX;
    s->what |= layer_state_t::eLayerChanged;
    s->z = z;
    return NO_ERROR;
}

layer_state_t* Composer::getLayerStateLocked(
        const sp<SurfaceComposerClient>& client, const sp<IBinder>& id) {
    
    ComposerState s; 
    s.client = client->mClient;
    s.state.surface = id;
    
    ssize_t index = mComposerStates.indexOf(s);
    if (index < 0) {
        // we don't have it, add an initialized layer_state to our list
        index = mComposerStates.add(s);
    }

    ComposerState* const out = mComposerStates.editArray();
    return &(out[index].state);
}

// LayerState.h
struct ComposerState {
    sp<ISurfaceComposerClient> client;
    layer_state_t state;
    status_t    write(Parcel& output) const;
    status_t    read(const Parcel& input);
};

// LayerState.cpp
struct layer_state_t {
    layer_state_t()
        :   what(0),
            x(0), y(0), z(0), w(0), h(0), layerStack(0),
            alpha(0), flags(0), mask(0),
            reserved(0)
    {
        matrix.dsdx = matrix.dtdy = 1.0f;
        matrix.dsdy = matrix.dtdx = 0.0f;
        crop.makeInvalid();
    }

    status_t    write(Parcel& output) const;
    status_t    read(const Parcel& input);

    struct matrix22_t {
        float   dsdx;
        float   dtdx;
        float   dsdy;
        float   dtdy;
    };
    sp<IBinder>     surface;
    uint32_t        what;
    float           x;
    float           y;
    uint32_t        z;
    uint32_t        w;
    uint32_t        h;
    uint32_t        layerStack;
    float           alpha;
    uint8_t         flags;
    uint8_t         mask;
    uint8_t         reserved;
    matrix22_t      matrix;
    Rect            crop;
    // non POD must be last. see write/read
    Region          transparentRegion;
};

第一次呼叫時, 應該會把一個新的 ComposerState 加入 mComposerStates 這個 SortedVector 裡面
接著對 ComposerState 裡面的 layer_state_t 設定其 z 值
這個 z 值其實是拿來計算不同 layer 的上下關係, 如果被覆蓋住的區塊就不會特別去繪製了
後面在繪圖的部分會再看到這部分的計算.

接下來再回到 BootAnimation, 接下來要呼叫的是
sp<Surface> s = control->getSurface();
sp<Surface> SurfaceControl::getSurface() const
{
    Mutex::Autolock _l(mLock);
    if (mSurfaceData == 0) {
        // This surface is always consumed by SurfaceFlinger, so the
        // producerControlledByApp value doesn't matter; using false.
        mSurfaceData = new Surface(mGraphicBufferProducer, false);
    }
    return mSurfaceData;
}

這個 Surface 對象會跟 openGL 的 API 接軌
我們看一下他的建構子:
Surface::Surface(
        const sp<IGraphicBufferProducer>& bufferProducer,
        bool controlledByApp)
    : mGraphicBufferProducer(bufferProducer),
      mGenerationNumber(0)
{
    // Initialize the ANativeWindow function pointers.
    ANativeWindow::setSwapInterval  = hook_setSwapInterval;
    ANativeWindow::dequeueBuffer    = hook_dequeueBuffer;
    ANativeWindow::cancelBuffer     = hook_cancelBuffer;
    ANativeWindow::queueBuffer      = hook_queueBuffer;
    ANativeWindow::query            = hook_query;
    ANativeWindow::perform          = hook_perform;

    ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
    ANativeWindow::cancelBuffer_DEPRECATED  = hook_cancelBuffer_DEPRECATED;
    ANativeWindow::lockBuffer_DEPRECATED    = hook_lockBuffer_DEPRECATED;
    ANativeWindow::queueBuffer_DEPRECATED   = hook_queueBuffer_DEPRECATED;

    const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
    const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;

    mReqWidth = 0;
    mReqHeight = 0;
    mReqFormat = 0;
    mReqUsage = 0;
    mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
    mDataSpace = HAL_DATASPACE_UNKNOWN;
    mCrop.clear();
    mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
    mTransform = 0;
    mStickyTransform = 0;
    mDefaultWidth = 0;
    mDefaultHeight = 0;
    mUserWidth = 0;
    mUserHeight = 0;
    mTransformHint = 0;
    mConsumerRunningBehind = false;
    mConnectedToCpu = false;
    mProducerControlledByApp = controlledByApp;
    mSwapIntervalZero = false;
}

可以看到一開始生成的 mGraphicBufferProducer 被帶進來了
之後它會在呼叫 Surface::dequeueBuffer() 時使用到, 我們晚點再一起談論.

接下來是一連串 openGL / EGL 的操作

關於 EGL 跟 OpenGL 的關係, 可以參考這篇文章
簡單摘錄一下重點:
OpenGL 會負責操作 GPU 進行圖形的渲染等工作
但因為 OpenGL 需要跟平台的視窗系統進行溝通, EGL 因此被設計出來擔任這兩者的橋樑
EGL 提供的 API 主要負責下列幾點功能:

  • 幫 OpenGL 創建 Context (OpenGL 的狀態會儲存在 EGL 管理的 Context 之中)
  • 繪製目標 Surface
  • 配置 FrameBuffer 屬性
  • Swap 提交繪製結果
順帶一提, 雖然我們可以不用去管 OpenGL 實際上做了甚麼.
不過 Android 有為了 Emulator 製作了一份 libagl, 用來 cover 沒有 GPU 的狀況.
可以查看 EGL 的 Loader (frameworks/native/opengl/libs/EGL/Loader.cpp)
我會在後面的文章補充 EGL initial 的流程

這邊先列出整個 EGL 操作的流程:

1.) 獲取 Display: eglGetDisplay
EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id)
獲取顯示器 (FrameBuffer), 參數一般使EGL_DEFAULT_DISPLAY 即可
eglGetDisplay 的 return 是一個 EGLDisplay
 2.) 初始化: eglInitialize
EGLBoolean eglInitialize (EGLDisplay dpy, EGLint *major, EGLint *minor);
使用 EGLDisplay 之前, 必須經過初始化.
呼叫 eglInitialize 同時會回傳版本號, 這邊可以帶入 NULL 表示你不在意
 3.) 取得平台設置: EGLConfig
EGLBoolean eglGetConfigs (EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);
描述 EGL Surface 所用, 跟平台會有強相關性. (例如只支援 16-bit...etc)
 4.) 取得 Render type: eglGetConfigAttrib
EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value) 
 5.) 建立 Context: eglCreateContext
EGLContext APIENTRY eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_list, const EGLint *attrib_list)
這裡的 attrib_list 表示版本號
 6.) 建立 Surface: eglCreateWindowSurface
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, NativeWindow window, const EGLint *attrib_list)
這個 window 是本地的 window, 會回傳一個 EGLSurface
 7.) 設置 Display 的當前 Surface: eglMakeCurrent
EGLBoolean eglMakeCurrent(
 EGLDisplay dpy,   //dpy: display.
 EGLSurface draw,  //正在繪製的surface, 由eglCreateWindowSurface生成
 EGLSurface read,
 EGLContext ctx )
 8.) 交換當前 Surface: eglSwapBuffers
EGLBoolean eglSwapBuffers(EGLDisplay display, EGLSurface surface);
Process 使用 OpenGL API 繪製完成後, 藉由 eglSwapBuffers 將繪製好的圖形顯示出來
大多時候你的 EGLSurface 會是雙緩衝的,
back-surface 用於儲存 rendor 後的結果,
front-surface 則連結到 NativeWindow, NativeWindow 負責顯示到設備上.
有了這些基本知識, 我們回頭看一下 BootAnimation 做的事情:
status_t BootAnimation::readyToRun() {
    ...
    // initialize opengl and egl
    const EGLint attribs[] = {
            EGL_RED_SIZE,   8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE,  8,
            EGL_DEPTH_SIZE, 0,
            EGL_NONE
    };
    EGLint w, h;
    EGLint numConfigs;
    EGLConfig config;
    EGLSurface surface;
    EGLContext context;
        
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

    eglInitialize(display, 0, 0);
    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
    surface = eglCreateWindowSurface(display, config, s.get(), NULL);
    context = eglCreateContext(display, config, NULL, NULL);
    eglQuerySurface(display, surface, EGL_WIDTH, &w);
    eglQuerySurface(display, surface, EGL_HEIGHT, &h);

    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
        return NO_INIT;
    ...
}

其實就是把 (1) ~ (7) 的事情都做過一次
注意在 eglCreateWindowSurface 裡面我們帶入了 s.get(),
s 是藉由 SurfaceControl 建立的 Surface 對象,
還記得上面它提供了很多 ANativeWindow 的 function pointer 嗎?
就是要提供給 EGL 呼叫所使用的
關於 ANativeWindow 的結構, 可以查看 /system/core/include/system/window.h
struct ANativeWindow
{
    struct android_native_base_t common;
    const uint32_t flags;
    const int   minSwapInterval;
    const float xdpi, ydpi;
    
    int (*queueBuffer)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer, int fenceFd);
    int (*dequeueBuffer)(struct ANativeWindow* window, struct ANativeWindowBuffer** buffer, int* fenceFd);
    int (*perform)(struct ANativeWindow* window, int operation, ... );
}

由於每一種系統的視窗系統都不同,
EGL 作為跨平台的接口, 自然要處理這個轉換
// frameworks/native/opengl/include/EGL/eglplatform.h
#if defined(_WIN32) || defined(__VC32__) && 
   ...
#elif defined(__WINSCW__) || 
   ...
#elif defined(__ANDROID__) || defined(ANDROID)

struct ANativeWindow;
struct egl_native_pixmap_t;

typedef struct ANativeWindow*           EGLNativeWindowType;
typedef struct egl_native_pixmap_t*     EGLNativePixmapType;
typedef void*                           EGLNativeDisplayType;

#elif defined(__unix__)
   ...
#else
#error "Platform not recognized"
#endif

這裡可以看到 EGLNativeWindowType 實際上被定為了 ANativeWindow

我們試著展開 eglCreateWindowSurface看看:
// frameworks/native/opengl/libs/EGL/eglApi.cpp
EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
                                    NativeWindowType window,
                                    const EGLint *attrib_list)
{
    egl_connection_t* cnx = NULL;
    egl_display_ptr dp = validate_display_connection(dpy, cnx);
    if (dp) {
        EGLDisplay iDpy = dp->disp.dpy;

        int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);

        // 一些 format 的制定, 跳過
        ...

        if (format != 0) {
            int err = native_window_set_buffers_format(window, format);
            if (err != 0) {
                ALOGE("error setting native window pixel format: %s (%d)",
                        strerror(-err), err);
                native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
                return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
            }
        }

        if (dataSpace != 0) {
            int err = native_window_set_buffers_data_space(window, dataSpace);
            if (err != 0) {
                ALOGE("error setting native window pixel dataSpace: %s (%d)",
                        strerror(-err), err);
                native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
                return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
            }
        }

        // the EGL spec requires that a new EGLSurface default to swap interval
        // 1, so explicitly set that on the window here.
        ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window);
        anw->setSwapInterval(anw, 1);

        EGLSurface surface = cnx->egl.eglCreateWindowSurface(
                iDpy, config, window, attrib_list);
        if (surface != EGL_NO_SURFACE) {
            egl_surface_t* s = new egl_surface_t(dp.get(), config, window,
                    surface, cnx);
            return s;
        }

        // EGLSurface creation failed
        native_window_set_buffers_format(window, 0);
        native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
    }
    return EGL_NO_SURFACE;
}

呼叫了 native_window_api_connect,
可以在window.h (/system/core/include/system/window.h) 找到
/*
 * native_window_api_connect(..., int api)
 * connects an API to this window. only one API can be connected at a time.
 * Returns -EINVAL if for some reason the window cannot be connected, which
 * can happen if it's connected to some other API.
 */
static inline int native_window_api_connect(
        struct ANativeWindow* window, int api)
{
    return window->perform(window, NATIVE_WINDOW_API_CONNECT, api);
}

呼叫了 window->perform(window, NATIVE_WINDOW_API_CONNECT, api);
從前面我們可以知道現在的 window 就是 s.get(), 也就是 Surface 結構.
接下來把它全部展開:
int Surface::hook_perform(ANativeWindow* window, int operation, ...) {
    va_list args;
    va_start(args, operation);
    Surface* c = getSelf(window);
    return c->perform(operation, args);
}

int Surface::perform(int operation, va_list args)
{
    int res = NO_ERROR;
    switch (operation) {
    ...
    case NATIVE_WINDOW_API_CONNECT:
        res = dispatchConnect(args);
        break;
    ...
    default:
        res = NAME_NOT_FOUND;
        break;
    }
    return res;
}

int Surface::dispatchConnect(va_list args) {
    int api = va_arg(args, int);
    return connect(api);
}

int Surface::connect(int api) {
    static sp<IProducerListener> listener = new DummyProducerListener();
    return connect(api, listener);
}

int Surface::connect(int api, const sp<IProducerListener>& listener) {
    ATRACE_CALL();
    ALOGV("Surface::connect");
    Mutex::Autolock lock(mMutex);
    IGraphicBufferProducer::QueueBufferOutput output;
    int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
    if (err == NO_ERROR) {
        uint32_t numPendingBuffers = 0;
        uint32_t hint = 0;
        output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
                &numPendingBuffers);

        // Disable transform hint if sticky transform is set.
        if (mStickyTransform == 0) {
            mTransformHint = hint;
        }

        mConsumerRunningBehind = (numPendingBuffers >= 2);
    }
    if (!err && api == NATIVE_WINDOW_API_CPU) {
        mConnectedToCpu = true;
        // Clear the dirty region in case we're switching from a non-CPU API
        mDirtyRegion.clear();
    } else if (!err) {
        // Initialize the dirty region for tracking surface damage
        mDirtyRegion = Region::INVALID_REGION;
    }

    return err;
}

status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
        int api, bool producerControlledByApp, QueueBufferOutput *output) {
    ATRACE_CALL();
    Mutex::Autolock lock(mCore->mMutex);
    mConsumerName = mCore->mConsumerName;

    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->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
                    mCore->mTransformHint,
                    static_cast<uint32_t>(mCore->mQueue.size()));

            // Set up a death notification so that we can disconnect
            // automatically if the remote producer dies
            if (listener != NULL &&
                    IInterface::asBinder(listener)->remoteBinder() != NULL) {
                status = IInterface::asBinder(listener)->linkToDeath(
                        static_cast<IBinder::DeathRecipient*>(this));
                if (status != NO_ERROR) {
                    BQ_LOGE("connect(P): linkToDeath failed: %s (%d)",
                            strerror(-status), status);
                }
            }
            mCore->mConnectedProducerListener = listener;
            break;
        default:
            BQ_LOGE("connect(P): unknown API %d", api);
            status = BAD_VALUE;
            break;
    }

    mCore->mBufferHasBeenQueued = false;
    mCore->mDequeueBufferCannotBlock =
            mCore->mConsumerControlledByApp && producerControlledByApp;
    mCore->mAllowAllocation = true;

    return status;
}

沒有特別做甚麼事情, 只是將 mCore->mConnectedApi 設置為 api;
下面幾個 native_window_xxx 的 API 的 flow 同樣是走這個 flow.

我們回到 eglCreateWindowSurface, 最下面會去生成 EGLSurface
EGLSurface surface = cnx->egl.eglCreateWindowSurface(
        iDpy, config, window, attrib_list);

這個 cnx 是一個 function pointer, 它會透過 EGL interface 去控制 OpenGL,
根據你的 Loader 所讀取到不同的 dynamic library 而定.
當然, 你可以看看 Google Emulator 的 EGL 實作
(frameworks/native/opengl/libagl/egl.cpp)
static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config,
        NativeWindowType window, const EGLint* /*attrib_list*/)
{
    ...
    egl_surface_t* surface;
    surface = new egl_window_surface_v2_t(dpy, config, depthFormat,
            static_cast<ANativeWindow*>(window));
    ...
    return surface;
}

這邊生成了一個 egl_window_surface_v2_t 的結構,
並把我們的 Surface 作為 nativeWindow 變數儲存起來

接下來看看

EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
                            EGLSurface read, EGLContext ctx)
{
    ...
    EGLBoolean result = dp->makeCurrent(c, cur_c,
            draw, read, ctx,
            impl_draw, impl_read, impl_ctx);
   ...
}

EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c,
        EGLSurface draw, EGLSurface read, EGLContext /*ctx*/,
        EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx)
{
    EGLBoolean result;

    { // scope for the lock
        Mutex::Autolock _l(lock);
        if (c) {
            result = c->cnx->egl.eglMakeCurrent(
                    disp.dpy, impl_draw, impl_read, impl_ctx);
            if (result == EGL_TRUE) {
                c->onMakeCurrent(draw, read);
                if (!cur_c) {
                    mHibernation.incWakeCount(HibernationMachine::STRONG);
                }
            }
        } else {
            result = cur_c->cnx->egl.eglMakeCurrent(
                    disp.dpy, impl_draw, impl_read, impl_ctx);
            if (result == EGL_TRUE) {
                cur_c->onLooseCurrent();
                mHibernation.decWakeCount(HibernationMachine::STRONG);
            }
        }
    }

    return result;
}

EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
                            EGLSurface read, EGLContext ctx)
{
    ...
    ogles_context_t* gl = (ogles_context_t*)ctx;
    if (makeCurrent(gl) == 0) {
        if (ctx) {
            egl_context_t* c = egl_context_t::context(ctx);
            egl_surface_t* d = (egl_surface_t*)draw;
            egl_surface_t* r = (egl_surface_t*)read;

            if (c->draw) {
                egl_surface_t* s = reinterpret_cast<egl_surface_t*>(c->draw);
                s->disconnect();
                s->ctx = EGL_NO_CONTEXT;
                if (s->zombie)
                    delete s;
            }
            if (c->read) {
                // FIXME: unlock/disconnect the read surface too 
            }

            c->draw = draw;
            c->read = read;

            if (c->flags & egl_context_t::NEVER_CURRENT) {
                c->flags &= ~egl_context_t::NEVER_CURRENT;
                GLint w = 0;
                GLint h = 0;
                if (draw) {
                    w = d->getWidth();
                    h = d->getHeight();
                }
                ogles_surfaceport(gl, 0, 0);
                ogles_viewport(gl, 0, 0, w, h);
                ogles_scissor(gl, 0, 0, w, h);
            }
            if (d) {
                if (d->connect() == EGL_FALSE) {
                    return EGL_FALSE;
                }
                d->ctx = ctx;
                d->bindDrawSurface(gl);
            }
            if (r) {
                // FIXME: lock/connect the read surface too 
                r->ctx = ctx;
                r->bindReadSurface(gl);
            }
        return EGL_TRUE;
    }
    ...
    return setError(EGL_BAD_ACCESS, EGL_FALSE);
}

還有印象 d 就是 egl_window_surface_v2_t 嗎?
我們查看它的 connect function, 又見到了熟悉的好朋友 nativeWindow
(<ゝω・) Surface☆

EGLBoolean egl_window_surface_v2_t::connect()
{
    // we're intending to do software rendering
    native_window_set_usage(nativeWindow,
            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);

    // dequeue a buffer
    int fenceFd = -1;
    if (nativeWindow->dequeueBuffer(nativeWindow, &buffer,
            &fenceFd) != NO_ERROR) {
        return setError(EGL_BAD_ALLOC, EGL_FALSE);
    }

    // wait for the buffer
    sp<Fence> fence(new Fence(fenceFd));
    if (fence->wait(Fence::TIMEOUT_NEVER) != NO_ERROR) {
        nativeWindow->cancelBuffer(nativeWindow, buffer, fenceFd);
        return setError(EGL_BAD_ALLOC, EGL_FALSE);
    }

    // allocate a corresponding depth-buffer
    width = buffer->width;
    height = buffer->height;
    if (depth.format) {
        depth.width   = width;
        depth.height  = height;
        depth.stride  = depth.width; // use the width here
        uint64_t allocSize = static_cast<uint64_t>(depth.stride) *
                static_cast<uint64_t>(depth.height) * 2;
        if (depth.stride < 0 || depth.height > INT_MAX ||
                allocSize > UINT32_MAX) {
            return setError(EGL_BAD_ALLOC, EGL_FALSE);
        }
        depth.data    = (GGLubyte*)malloc(allocSize);
        if (depth.data == 0) {
            return setError(EGL_BAD_ALLOC, EGL_FALSE);
        }
    }
    // keep a reference on the buffer
    buffer->common.incRef(&buffer->common);

    // pin the buffer down
    if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN |
            GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
        ALOGE("connect() failed to lock buffer %p (%ux%u)",
                buffer, buffer->width, buffer->height);
        return setError(EGL_BAD_ACCESS, EGL_FALSE);
        // FIXME: we should make sure we're not accessing the buffer anymore
    }
    return EGL_TRUE;
}

再度展開 Surface::dequeueBuffer

int Surface::hook_dequeueBuffer(ANativeWindow* window,
        ANativeWindowBuffer** buffer, int* fenceFd) {
    Surface* c = getSelf(window);
    return c->dequeueBuffer(buffer, fenceFd);
}

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
    ATRACE_CALL();
    ALOGV("Surface::dequeueBuffer");

    uint32_t reqWidth;
    uint32_t reqHeight;
    bool swapIntervalZero;
    PixelFormat reqFormat;
    uint32_t reqUsage;

    {
        Mutex::Autolock lock(mMutex);

        reqWidth = mReqWidth ? mReqWidth : mUserWidth;
        reqHeight = mReqHeight ? mReqHeight : mUserHeight;

        swapIntervalZero = mSwapIntervalZero;
        reqFormat = mReqFormat;
        reqUsage = mReqUsage;
    } // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffer

    int buf = -1;
    sp<Fence> fence;
    status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, swapIntervalZero,
            reqWidth, reqHeight, reqFormat, reqUsage);

    if (result < 0) {
        ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer(%d, %d, %d, %d, %d)"
             "failed: %d", swapIntervalZero, reqWidth, reqHeight, reqFormat,
             reqUsage, result);
        return result;
    }

    Mutex::Autolock lock(mMutex);
    sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);

    // this should never happen
    ALOGE_IF(fence == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf);

    if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
        freeAllBuffers();
    }

    if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
        result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
        if (result != NO_ERROR) {
            ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);
            mGraphicBufferProducer->cancelBuffer(buf, fence);
            return result;
        }
    }

    if (fence->isValid()) {
        *fenceFd = fence->dup();
        if (*fenceFd == -1) {
            ALOGE("dequeueBuffer: error duping fence: %d", errno);
            // dup() should never fail; something is badly wrong. Soldier on
            // and hope for the best; the worst that should happen is some
            // visible corruption that lasts until the next frame.
        }
    } else {
        *fenceFd = -1;
    }

    *buffer = gbuf.get();
    return OK;
}

* mGraphicBufferProducer 就是 BufferQueueProducer

整個調用流程如下
eglMakeCurrent (EGL API)
  -> makeCurrent (egl_display)
    -> eglMakeCurrent (OEM EGL library)
      -> connect (EGL Surface)
        -> hook_dequeueBuffer (native_window = Surface)
          -> dequeueBuffer (Surface)
            -> dequeueBuffer (MonitoredProducer)
              -> dequeueBuffer (BufferQueueProducer)

這整個流程是 BufferQueueProducer 要開始運作的前哨站,
dequeueBuffer 函數將會承接到前面提過的 BufferQueue 裡面的各種 Slots

就從下一篇再開始描述吧 ⧸⎩⎠⎞͏(・∀・)⎛͏⎝⎭⧹

不定參數印 log

From the UNIXProcess_md.c #ifdef DEBUG_PROCESS   /* Debugging process code is difficult; where to write debug output? */ static void deb...