Android Graphics Framework

Android系統圖形框架

Android系統圖形框架由下往上主要的包括HAL(HWComposer和Gralloc兩個moudle),SurfaceFlinger(BufferQueue的消費者),WindowManagerService(窗口管理者),View(BufferQueue的生產者)四大模塊。

  • HAL: 包括HWComposer和Gralloc兩個moudle,Android N上由SurfaceFlinger打開,因此在同一進程。 gralloc 用于BufferQueue的內存分配,同時也有fb的顯示接口,HWComposer作為合成SurfaceFlinger里面的Layer,并顯示(通過gralloc的post函數)
  • SurfaceFlinger也叫LayerFlinger,作為Layer的管理著,同是也是BufferQueue的消費者,當每個Layer的生產者draw完完整的一幀時,會通知SurfaceFlinger,通知的方式采用BufferQueue。
  • WindowManagerService: 作為Window的管理者,掌管著計算窗口大小,窗口切換等任務,同時也會將相應的參數設置給SurfaceFlinger,比如Window的在z-order,和窗口的大小。
  • View: 作為BufferQueue的生產者,沒當執行lockCanvas->draw->unlockCanvas,之后會存入一幀數據進入BufferQueue中。
    圖片.png

1、低級別組件

  • BufferQueue 和 gralloc。
    BufferQueue 將可生成圖形數據緩沖區的組件(生產者)連接到接受數據以便進行顯示或進一步處理的組件(消費者)。通過供應商專用 HAL 接口實現的 gralloc 內存分配器將用于執行緩沖區分配任務。
  • SurfaceFlinger、Hardware Composer 和虛擬顯示屏。
    SurfaceFlinger 接受來自多個源的數據緩沖區,然后將它們進行合成并發送到顯示屏。Hardware Composer HAL (HWC) 確定使用可用硬件合成緩沖區的最有效的方法,虛擬顯示屏使合成輸出可在系統內使用(錄制屏幕或通過網絡發送屏幕)。
  • Surface、Canvas 和 SurfaceHolder。
    Surface 可生成一個通常由 SurfaceFlinger 使用的緩沖區隊列。當渲染到 Surface 上時,結果最終將出現在傳送給消費者的緩沖區中。Canvas API 提供一種軟件實現方法(支持硬件加速),用于直接在 Surface 上繪圖(OpenGL ES 的低級別替代方案)。與視圖有關的任何內容均涉及到 SurfaceHolder,其 API 可用于獲取和設置 Surface 參數(如大小和格式)。
  • EGLSurface 和 OpenGL ES。
    OpenGL ES (GLES) 定義了用于與 EGL 結合使用的圖形渲染 API。EGI 是一個規定如何通過操作系統創建和訪問窗口的庫(要繪制紋理多邊形,請使用 GLES 調用;要將渲染放到屏幕上,請使用 EGL 調用)。此頁還介紹了 ANativeWindow,它是 Java Surface 類的 C/C++ 等價類,用于通過原生代碼創建 EGL 窗口表面。
  • Vulkan。
    Vulkan 是一種用于高性能 3D 圖形的低開銷、跨平臺 API。與 OpenGL ES 一樣,Vulkan 提供用于在應用中創建高質量實時圖形的工具。Vulkan 的優勢包括降低 CPU 開銷以及支持 SPIR-V 二進制中間語言。

1.1 Gralloc

gralloc 用于BufferQueue的內存分配,同時也有fb的顯示接口

  • gralloc_alloc:用于分配一個顯示顯示緩沖區,分配好只有會返回緩沖區的handle(緩沖區buffer不會拷貝,所有的操作都是傳handle)
static int gralloc_alloc_buffer(alloc_device_t* dev,
        size_t size, int /*usage*/, buffer_handle_t* pHandle)
{
    int err = 0;
    int fd = -1;
    size = roundUpToPageSize(size);

    fd = ashmem_create_region("gralloc-buffer", size);  // 創建匿名共享內存
    if (fd < 0) {
        ALOGE("couldn't create ashmem (%s)", strerror(-errno));
        err = -errno;
    }
    if (err == 0) {
        private_handle_t* hnd = new private_handle_t(fd, size, 0);
        gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(
                dev->common.module);
        err = mapBuffer(module, hnd); // 同時將創建好的內存映射進入當前進程,返回handle
        if (err == 0) {
            *pHandle = hnd;
        }
    }
    return err;
}
  • gralloc_free :釋放緩沖區內存
  • fb_post :更新fb顯示

1.2 BufferQueue

  • BufferQueue 類是 Android 中所有圖形處理操作的核心。它的作用很簡單:將生成圖形數據緩沖區的一方(生產方)連接到接受數據以進行顯示或進一步處理的一方(消耗方)。幾乎所有在系統中移動圖形數據緩沖區的內容都依賴于 BufferQueue。
  • BufferQueue 包含將圖像流生產方與圖像流消耗方結合在一起的邏輯。圖像生產方的一些示例包括由相機 HAL 或 OpenGL ES 游戲生成的相機預覽。圖像消耗方的一些示例包括 SurfaceFlinger 或顯示 OpenGL ES 流的另一個應用,如顯示相機取景器的相機應用。
  • BufferQueue 永遠不會復制緩沖區內容(移動如此多的數據是非常低效的操作)。相反,緩沖區始終通過句柄進行傳遞。
  • 生產方和消耗方可以存在于不同的進程中。目前,消耗方始終創建和擁有數據結構。在舊版本的 Android 中,只有生產方才進行 Binder 處理(即生產方可能在遠程進程中,但消耗方必須存在于創建隊列的進程中)。Android 4.4 和更高版本已發展為更常規的實現。


    圖片.png

1.3 BufferQueue生產者與消費者分析

當應用add一個window到WMS時,WMS會調用SurfaceFlinger為這個window創建一個BufferQueue,BufferQueue的消費者留在了SurfaceFlinger中,而生產者會返回到View中,View中采用Canvas或者GL繪圖,用于消費者

    // ViewRootImpl.java
    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
        int relayoutResult = mWindowSession.relayout(
                mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingConfiguration,
                mSurface);            
    }

其中的
mWindow即每個應用窗口所創建的window,和ActivityRecord、DecorView一一對應
mSurface即為當前這個Layer的BufferQueue生產者

void Layer::onFirstRef() {
    // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
    BufferQueue::createBufferQueue(&producer, &consumer);
    mProducer = new MonitoredProducer(producer, mFlinger);
    mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName,
            this);
    mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
    mSurfaceFlingerConsumer->setContentsChangedListener(this);
    mSurfaceFlingerConsumer->setName(mName);

#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
#warning "disabling triple buffering"
#else
    mProducer->setMaxDequeuedBufferCount(2);
#endif

    const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
    updateTransformHint(hw);
}

createBufferQueue即為創建BufferQueue的地方,同時通過setContentsChangedListener將this注冊到消費者中去,當生產者mSurface有新的幀生成后,會通知到Layer中的onFrameAvailable

// Layer.cpp
void Layer::onFrameAvailable(const BufferItem& item) {
    // Add this buffer from our internal queue tracker
    { // Autolock scope
        Mutex::Autolock lock(mQueueItemLock);

        mQueueItems.push_back(item);
        android_atomic_inc(&mQueuedFrames);

        // Wake up any pending callbacks
        mLastFrameNumberReceived = item.mFrameNumber;
        mQueueItemCondition.broadcast();
    }

    mFlinger->signalLayerUpdate();
}

2、 SurfaceFlinger

  • 管理Layer的創建與銷毀,同時單個Layer又要接收來自WMS的設置參數(setLayer, setPosition , setSize setAlpha, createLayer, removeLayer等)
  • Layer的可見區域計算,合成Layer
  • 渲染合成圖像到fb
  • 管理VSYNC信號
  • 創建BufferQueue用于SurfaceFlinger的緩沖區


    圖片.png

2.1 創建BufferQueue用于SurfaceFlinger中的緩沖區(非Layer中的BufferQueue)

void SurfaceFlinger::init() {
    for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {    
            wp<IBinder> token = mBuiltinDisplays[i];

            sp<IGraphicBufferProducer> producer;
            sp<IGraphicBufferConsumer> consumer;
            BufferQueue::createBufferQueue(&producer, &consumer,
                    new GraphicBufferAlloc());  // 創建BufferQueue

            sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i,
                    consumer);  // 消費者放入FramebufferSurface
            int32_t hwcId = allocateHwcDisplayId(type);
            sp<DisplayDevice> hw = new DisplayDevice(this,
                    type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
                    fbs, producer,
                    mRenderEngine->getEGLConfig()); // 生產者是DisplayDevice,本質是合成只有的圖像

            if (i > DisplayDevice::DISPLAY_PRIMARY) {
                ALOGD("marking display %zu as acquired/unblanked", i);
                hw->setPowerMode(HWC_POWER_MODE_NORMAL);
            }
            mDisplays.add(token, hw);    
    }
}

2.2 Layer更新

前面講BufferQueue時,每當Layer中有新的buffer準備好了,一定會通知相應Layer的onFrameAvailable,從而通過signalLayerUpdate通知SurfaceFlinger,這就是開始處理Layer的入口

void SurfaceFlinger::signalLayerUpdate() {
    mEventQueue.invalidate();
}
void MessageQueue::invalidate() {
    mEvents->requestNextVsync();
}

但是onMessageReceived并不會立即收到消息去執行handleMessageTransaction、handleMessageRefresh,而是等到下一個VSYNC信號到來

void SurfaceFlinger::onMessageReceived(int32_t what) {
    ATRACE_CALL();
    switch (what) {
        case MessageQueue::INVALIDATE: {
            bool refreshNeeded = handleMessageTransaction();
            refreshNeeded |= handleMessageInvalidate();
            refreshNeeded |= mRepaintEverything;
            if (refreshNeeded) {
                // Signal a refresh if a transaction modified the window state,
                // a new buffer was latched, or if HWC has requested a full
                // repaint
                signalRefresh();
            }
            break;
        }
        case MessageQueue::REFRESH: {
            handleMessageRefresh();
            break;
        }
    }
}

2.3 VSYNC信號

  • SurfaceFlinger負責在收到的硬件VSYNC信號,通知EventThread,EventThread存放著所有注冊過的Connection,只要有Connection存在,就會觸發相應的事件,Looper中會響應事件并回調


    圖片.png
  • Choreographer的VSYNC信號和SurfaceFlinger接收VSYNC信號原理一樣,都是通過注冊一個Connection監聽事件


    圖片.png

2.4 Layer可見性計算與合成顯示

void SurfaceFlinger::handleMessageRefresh() {
    ATRACE_CALL();

    nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);

    preComposition(); //
    rebuildLayerStacks();  //  計算可見區域,主要的依據就是z-order大的覆蓋有重疊區域z-order小的
    setUpHWComposer();
    doDebugFlashRegions();
    doComposition();  // 合成圖像,并調用swapBuffers,這樣消費者FramebufferSurface就會被回調onFrameAvailable
    postComposition(refreshStartTime);
}

消費者FramebufferSurface收到onFrameAvailable后就會將新的圖像post到fb(通過gralloc的post函數)

// Overrides ConsumerBase::onFrameAvailable(), does not call base class impl.
void FramebufferSurface::onFrameAvailable(const BufferItem& /* item */) {
    sp<GraphicBuffer> buf;
    sp<Fence> acquireFence;
    status_t err = nextBuffer(buf, acquireFence);
    if (err != NO_ERROR) {
        return;
    }
    err = mHwc.fbPost(mDisplayType, acquireFence, buf);  // 調用gralloc的post
}

3、WindowManagerService框架

圖片.png

職責

  • 計算窗口大小

  • 計算窗口Z軸位置

  • 管理輸入法窗口

  • 管理壁紙窗口

  • 執行窗口切換

3.1 addWindow

在Activity的啟動中當執行到performLaunchActivity

    // ActivityThread.java
    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        r = performResumeActivity(token, clearHide, reason);
        if (r != null) {
            final Activity a = r.activity;
            boolean willBeVisible = !a.mStartedActivity;
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow(); //  Window指的是PhoneWindow,在performLaunchActivity中attch函數中創建
                View decor = r.window.getDecorView();  // decor是在onCreate中用戶主動調用setContentView時創建
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();  // 實質是ViewManagerImpl
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor; 
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;  //APP的type都是TYPE_BASE_APPLICATION
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);   // 向WindowManagerGlobal中添加當前Activity的DecorView
                }
    }

decor是在onCreate中用戶主動調用setContentView時創建
Window指的是PhoneWindow,在performLaunchActivity中attch函數中創建
ActivityClientRecord是在App進程中的和AMS中的ActivityRecord一一對應的對象

WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }
        root.setView(view, wparams, panelParentView);
    }

可以看出WindowManagerGlobal是管理view(DecorView),ViewRootImpl,參數的管家,他們都是一對一的關系

ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } 
}

開始向WMS添加當前mWindow(W),mWindow繼承于IWindow.Stub,也是個binder的對象,WMS可以通過這個binder回調ViewRootImpl

    WindowManagerService.java
    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
    
            boolean addToken = false;
            WindowToken token = mTokenMap.get(attrs.token);
            AppWindowToken atoken = null;
            boolean addToastWindowRequiresToken = false;
            ...
            WindowState win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
                    mPolicy.adjustWindowParamsLw(win.mAttrs);   // new 一個新的WindowState
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));

            res = mPolicy.prepareAddWindowLw(win, attrs);
            if (res != WindowManagerGlobal.ADD_OKAY) {
                return res;
            }

            final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                // 創建一個Input的雙向進程間管道,并將read端的fd通過outInputChannel返回,用于ViewRootImpl中創建Input的監聽Receiver,管道的write端registerInputChannel注冊進入了InputDispatcher,然后加入了InputDispatcher線程的Looper,用于發送事件信號
                // 這就成功的建立了InputDispatcher與當前應用ViewRootImpl進程間雙向通訊
                win.openInputChannel(outInputChannel);                    
            }
                    
            if (addToken) {
                mTokenMap.put(attrs.token, token);
            }
            win.attach();
            mWindowMap.put(client.asBinder(), win); // 將創建的WindowState存入WMS
            
            if (focusChanged) {
                mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);   // 檢查焦點窗口是否發生變化,如果發生了變化就要通知InputDispatcher更新焦點窗口
            }
            mInputMonitor.updateInputWindowsLw(false /*force*/);  // 將WMS中所有的InputWindowHandles都更新到InputDispatcher中去,方便InputDispatcher根據觸摸坐標尋找相應的window

3.2 relayoutWindow


ViewRootImpl.java

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

scheduleTraversals()中利用Choreographer向SurfaceFlinger注冊一個vsync信號接收回調,收到回調后會執行performTraversals(),這個函數是對view進行measure,layout,draw的核心函數,當執行performTraversals時發生以下條件事會調用WMS的relayoutWindow,通知WMS window發生的變化

  • 第一次performTraversals調用的時候
  • 當window需要resize(改變尺寸)的時候
  • view可見性發生變化時
  • 其他一些條件
ViewRootImpl.java
private void performTraversals() {
        if (mFirst || windowShouldResize || insetsChanged ||
                viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
            relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
        }
}

3.3 SurfaceFlinger Z order順序確定

openGlobalTransaction --> setLayer --> closeGlobalTransaction -->closeGlobalTransactionImpl --> setTransactionState --> setClientStateLocked --> mCurrentState.layersSortedByZ.add
layersSortedByZ是一個LayerVector的結構,繼承于SortedVector,在add的同時會用二分法則排序插入
Z order 的值越大顯示越靠前,但是最終顯示的判定還要結合layer的可見區域Region visibleRegion來顯示,一般情況可見區域只有Navgation Bar + Status Bar + Activity,有些軟件播放視頻會全屏,那么Navgation Bar + Status Bar 也就不可見了


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

4、 窗口(Window)的結構

圖片.png

ViewRootImpl是一個虛擬根View,用來控制窗口的渲染,以及用來與WindowManagerService、SurfaceFlinger通信

DecorView是窗口的真正根View,用戶在Activity的onCreate中setContentView中創建

ContentView描述窗口的主題風格

4.1 ViewRootImpl

Window的虛擬根View,一個Aactivity對應一個DecorView,對應一個ActivityClientRecord,對應一個ViewRootImpl,ViewRootImpl的作用:

  • 與WMS交互:將window(W:繼承于IWindow.Stub)加入到WMS(addToDispaly)

  • 與SurfaceFlinger交互: 因為ViewRootImpl中layout,measure,draw是Surface的的生產者,因此當draw完成以后SurfaceFlinger回取出相應的buffer進行合成顯示

  • 注冊一個InputEventReceiver :基于WMS返回的InputChannel創建一個Callback,用于監聽輸入事件mInputEventReceiver一旦被創建,就已經在監聽輸入事件了

  • performTraversals() : 執行畫圖三個重要操作measure,layout,draw,主要是在scheduleTraversals()中用Choreographer注冊系統刷新下一幀回調。

  • Choreographer:當window有任何改變的時候就會利用Choreographer注冊一個vsync信號回調,比如尺寸,參數變化,可見性變化,范圍改變,保證顯示實時更新

5、參考

SurfaceFlinger Z order順序確定

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容