android Gui系統之SurfaceFlinger(1)

来源:http://www.cnblogs.com/deman/archive/2016/06/14/5584198.html
-Advertisement-
Play Games

GUI 是任何系統都很重要的一塊。 android GUI大體分為4大塊。 1)SurfaceFlinger 2)WMS 3)View機制 4)InputMethod 這塊內容非常之多,但是理解後,可以觸類旁通,其實現在主流的系統,包括andorid,ios在構架上,都是有很多相識之處。 我們先來講 ...


GUI 是任何系統都很重要的一塊。

android GUI大體分為4大塊。

1)SurfaceFlinger

2)WMS

3)View機制

4)InputMethod

這塊內容非常之多,但是理解後,可以觸類旁通,其實現在主流的系統,包括andorid,ios在構架上,都是有很多相識之處。

我們先來講SurfaceFlinger

 

1.OpenGL & OpenGL ES

OPenGL ES 是android系統繪畫的基礎。關於OpenGL部分,可以百度瞭解下。

先來看一個OpenGL & SurfaceFlinger之間的框架圖:

從底層往上看: 1)linux內核提供統一的設備驅動,/dev/graphics/fb* 2) Android HAL 提供2個介面 Gralloc & fb fb 負責打開framebuffer,提供介面操作。gralloc負責管理幀緩衝區的分配和釋放。 composer是HAL中另一個重要的功能,它主要是給廠商定製UI合成。SurfaceFlinger中負責HWComposer會用到這個功能。 而且關鍵是HWComposer還負責產生VSync信號,這是本期SurfaceFlinger的重點。 3)由於OpenGL是一套通用的庫(大部分就是介面),所以它需要一個本地的實現。andorid平臺OpenGL有2個本地視窗,FrameBufferNativeWindow & Surface。 4)OpenGL可以有軟體 或者依托於硬體實現,具體的運行狀態,就是由EGL來配置。 5)SurfaceFlinger持有一個成員數組mDisplays來支持各種顯示設備。DisplayDevices在初始化的時候調用EGL來搭建OpenGL的環境。  

2.Android的硬體介面HAL

HAL需要滿足android系統和廠商的要求

2.1硬體介面的抽象

從面向對象角度來講,介面的概念就是由C++非常容易實現,但是HAL很多代碼是C語言描述的。 這就需要一種技巧來實現面向對象。 定義一種結構,子類的成員變數第一個類型是父類的結構就可以了。抽象方法可以用函數指針來實現。 其實這個就是C++多態實現的基本原理,具體可參考《深入理解C++對象模型》

2.2介面的穩定性

Android已經把各個硬體都介面都統一定義在:

libhardware/include/hardware/ 具體代碼可以參考:https://github.com/CyanogenMod/android_hardware_libhardware/tree/cm-12.0/include/hardware

 

3.Android顯示設備:Gralloc &  FrameBuffer

FrameBuffer是linux環境下顯示設備的統一介面。從而讓用戶設備不需要做太多的操作,就可以適配多種顯示設備。 FramwBuffer本質上就是一套介面。android系統不會直接操作顯示驅動,而通過HAL層來封裝。而HAL中操作驅動的模塊就是 gralloc。

3.1Gralloc模塊的載入

gralloc通過FrameBufferNativeWindow 來載入的:
FramebufferNativeWindow::FramebufferNativeWindow() 
    : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false)
{
    hw_module_t const* module;
    if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {
        int stride;
        int err;
        int i;
        err = framebuffer_open(module, &fbDev);
        ALOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
        
        err = gralloc_open(module, &grDev);
        ALOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err));

        // bail out if we can't initialize the modules
        if (!fbDev || !grDev)
            return;
        
        mUpdateOnDemand = (fbDev->setUpdateRect != 0);
        
        // initialize the buffer FIFO
        if(fbDev->numFramebuffers >= MIN_NUM_FRAME_BUFFERS &&
           fbDev->numFramebuffers <= MAX_NUM_FRAME_BUFFERS){
            mNumBuffers = fbDev->numFramebuffers;
        } else {
            mNumBuffers = MIN_NUM_FRAME_BUFFERS;
        }
        mNumFreeBuffers = mNumBuffers;
        mBufferHead = mNumBuffers-1;

        /*
         * This does not actually change the framebuffer format. It merely
         * fakes this format to surfaceflinger so that when it creates
         * framebuffer surfaces it will use this format. It's really a giant
         * HACK to allow interworking with buggy gralloc+GPU driver
         * implementations. You should *NEVER* need to set this for shipping
         * devices.
         */
#ifdef FRAMEBUFFER_FORCE_FORMAT
        *((uint32_t *)&fbDev->format) = FRAMEBUFFER_FORCE_FORMAT;
#endif

        for (i = 0; i < mNumBuffers; i++)
        {
                buffers[i] = new NativeBuffer(
                        fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);
        }

        for (i = 0; i < mNumBuffers; i++)
        {
                err = grDev->alloc(grDev,
                        fbDev->width, fbDev->height, fbDev->format,
                        GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride);

                ALOGE_IF(err, "fb buffer %d allocation failed w=%d, h=%d, err=%s",
                        i, fbDev->width, fbDev->height, strerror(-err));

                if (err)
                {
                        mNumBuffers = i;
                        mNumFreeBuffers = i;
                        mBufferHead = mNumBuffers-1;
                        break;
                }
        }

        const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags; 
        const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;
        const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi;
        const_cast<int&>(ANativeWindow::minSwapInterval) = 
            fbDev->minSwapInterval;
        const_cast<int&>(ANativeWindow::maxSwapInterval) = 
            fbDev->maxSwapInterval;
    } else {
        ALOGE("Couldn't get gralloc module");
    }

    ANativeWindow::setSwapInterval = setSwapInterval;
    ANativeWindow::dequeueBuffer = dequeueBuffer;
    ANativeWindow::queueBuffer = queueBuffer;
    ANativeWindow::query = query;
    ANativeWindow::perform = perform;

    ANativeWindow::dequeueBuffer_DEPRECATED = dequeueBuffer_DEPRECATED;
    ANativeWindow::lockBuffer_DEPRECATED = lockBuffer_DEPRECATED;
    ANativeWindow::queueBuffer_DEPRECATED = queueBuffer_DEPRECATED;
}
FramebufferNativeWindow

我們繼續深入看:

galloc的父類,最終是:

libhardware\include\hardware\hardware.h

typedef struct hw_module_methods_t {
    /** Open a specific device */
    int (*open)(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device);

} hw_module_methods_t;

只有一個open方法,也就是所有的廠商都需要實現開啟設備的方法。

看下fb的打開的代碼:

libhardware\modules\gralloc\framebuffer.cpp

int fb_device_open(hw_module_t const* module, const char* name,
        hw_device_t** device)
{
    int status = -EINVAL;
    if (!strcmp(name, GRALLOC_HARDWARE_FB0)) {
        /* initialize our state here */
        fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev));
        memset(dev, 0, sizeof(*dev));

        /* initialize the procs */
        dev->device.common.tag = HARDWARE_DEVICE_TAG;
        dev->device.common.version = 0;
        dev->device.common.module = const_cast<hw_module_t*>(module);
        dev->device.common.close = fb_close;
        dev->device.setSwapInterval = fb_setSwapInterval;
        dev->device.post            = fb_post;
        dev->device.setUpdateRect = 0;

        private_module_t* m = (private_module_t*)module;
        status = mapFrameBuffer(m);
        if (status >= 0) {
            int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);
            int format = (m->info.bits_per_pixel == 32)
                         ? (m->info.red.offset ? HAL_PIXEL_FORMAT_BGRA_8888 : HAL_PIXEL_FORMAT_RGBX_8888)
                         : HAL_PIXEL_FORMAT_RGB_565;
            const_cast<uint32_t&>(dev->device.flags) = 0;
            const_cast<uint32_t&>(dev->device.width) = m->info.xres;
            const_cast<uint32_t&>(dev->device.height) = m->info.yres;
            const_cast<int&>(dev->device.stride) = stride;
            const_cast<int&>(dev->device.format) = format;
            const_cast<float&>(dev->device.xdpi) = m->xdpi;
            const_cast<float&>(dev->device.ydpi) = m->ydpi;
            const_cast<float&>(dev->device.fps) = m->fps;
            const_cast<int&>(dev->device.minSwapInterval) = 1;
            const_cast<int&>(dev->device.maxSwapInterval) = 1;
            *device = &dev->device.common;
        }
    }
    return status;
}

首先check設備名是否正確。

分配dev的空間,這是一個殼。 然後初始化dev。 提供fb的核心介面 記憶體映射
status = mapFrameBuffer(m);

然後是建立殼 & 核心間的關係。

這樣就打開了fb設備。

在回到FrameBufferNativeWindow 可以看到:

        err = framebuffer_open(module, &fbDev);
        ALOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
        
        err = gralloc_open(module, &grDev);
        ALOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err));

fb打開的驅動信息在fbDev,gralloc打開的信息在grDev中。

fbDev負責的是主屏幕,grDev負責圖形緩衝去的分配和釋放。

所以FrameBufferNativeWindow控制這SurfaceFlinger的基礎。

 

4.FrameBufferNativeWindow

4.1FramebufferNativeWindow

在OpenGL中,我們不斷提及本地視窗的概念,在Android中,native window一共由2個。 一個是面向管理者(SurfaceFlinger)的 FramebufferNativeWindow 另一個是面像APP的,surface。 先來看第一種:
首先看下定義的地方:
class FramebufferNativeWindow 
    : public ANativeObjectBase<
        ANativeWindow, 
        FramebufferNativeWindow, 
        LightRefBase<FramebufferNativeWindow> >
{

ANativeWindow是什麼東西?

ANativeWindow是OpenGL 在android平臺的顯示類型。

所以FramebufferNativeWindow就是一種Open GL可以顯示的類型。

FramebufferNativeWindow的構造函數上面已經貼出來了,進一步分析如下:

1)載入module,上面已經分析過了。

2)打開fb & gralloc,也已經分析過了。

3)根據fb的設備屬性,獲得buffer數。這個buffer後面會解釋。

4)給每個buffer初始化,並分配空間。這裡new NativeBuffer只是指定buffer的類型,或者分配了一個指針,但是沒有分配記憶體,所以還需要alloc操作。

5)為本地視窗屬性賦值。

目前buffer預設值是在2~3,後面會介紹3緩衝技術,就會用到3個buffer。

雙緩衝技術:

把一組圖畫,畫到屏幕上,畫圖是需要時間的,如果時間間隔比較長,圖片就是一個一個的畫在屏幕的,看上去就會卡。

如果先把圖片放在一個緩衝buffer中,待全部畫好後,把buffer直接顯示在屏幕上,這就是雙緩衝技術。

 

4.2dequeuebuffer

int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, 
        ANativeWindowBuffer** buffer, int* fenceFd)
{
    FramebufferNativeWindow* self = getSelf(window);
    Mutex::Autolock _l(self->mutex);
    framebuffer_device_t* fb = self->fbDev;

    int index = self->mBufferHead++;
    if (self->mBufferHead >= self->mNumBuffers)
        self->mBufferHead = 0;

    // wait for a free non-front buffer
    while (self->mNumFreeBuffers < 2) {
        self->mCondition.wait(self->mutex);
    }
    ALOG_ASSERT(self->buffers[index] != self->front);

    // get this buffer
    self->mNumFreeBuffers--;
    self->mCurrentBufferIndex = index;

    *buffer = self->buffers[index].get();
    *fenceFd = -1;

    return 0;
}

代碼不多,但是卻是核心功能,通過它來獲取一塊可渲染的buffer。

1)獲取FramebufferNativeWindow對象。為什麼沒有使用this 而是使用了傳入ANativeWindow的方式,此處我們並不關心。

2)獲得一個Autolock的鎖,函數結束,自動解鎖。

3)獲取mBufferHead變數,這裡自增,也就是使用下一個buffer,一共只有3個,(原因上面已經解釋),所以迴圈取值。

4)如果沒有可用的緩衝區,等待bufferqueue釋放。一旦獲取後,可用buffer就自減

 

5.Surface

Surface是另一個本地視窗,主要和app這邊交互。註意:app層java代碼無法直接調用surface,只是概念上surface屬於app這一層的。

首先Surface是ANativeWindow的一個子類。

可以推測,surface需要解決如下幾個問題:

1)面向上層(java層)提供畫板。由誰來分配這塊記憶體

2)與SurfaceFlinger是什麼關係

Surface::Surface(
        const sp<IGraphicBufferProducer>& bufferProducer,
        bool controlledByApp)

 

sp<IGraphicBufferProducer>& bufferProducer 是分配surface記憶體的。它到底是什麼呢?


 先來看看從ViewRootImpl到獲取surface的過程。 ViewRootImpl持有一個java層的surface對象,開始是空的。 後續的流程見上面的流程圖。也就是-說ViewRootImpl持有的surface對象,最終是對SurfaceComposerClient的創建的surface的一個“引用”。 由此分析可以看到 一個ISurfaceClient->ISurfaceComposerClient->IGraphicBufferProducer.當然binder需要一個實名的server來註冊。 在ServiceManager中可以看到,這些服務查詢的是“SurfaceFlinger”。 也就是,這些東東都是SurfaceFlinger的內容。
SurfaceFlinger::SurfaceFlinger()
    :   BnSurfaceComposer(),

SurfaceFlinger是BnSurfaceComposer的一個子類。也就是ISurfaceComposer的一個實現。

surface雖然是為app層服務的,但是本質上還是由SurfaceFlinger來管理的。

SurfaceFlinger怎麼創建和管理surface,需要通過BufferQueue,將在下一篇討論。

參考:

《深入理解android內核設計思想》 林學森

 




 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • command+shift+0會出現如下圖 然後輸入你想找的API 記得找帶Reference這種標記的文檔 ...
  • ARC已經出來很久了,自動釋放記憶體的確很方便,但是並非絕對安全絕對不會產生記憶體泄露。導致iOS對象無法按預期釋放的一個無形殺手是——迴圈引用。迴圈引用可以簡單理解為A引用了B,而B又引用了A,雙方都同時保持對方的一個引用,導致任何時候引用計數都不為0,始終無法釋放。若當前對象是一個ViewContr ...
  • 剛接手了公司iOS的兩個APP, 現在碰到了這樣一個問題: 有一臺iPhone在一個APP中使用了微信支付,支付成功後,點擊返回到該APP,結果卻跳到了另外一個APP去了。 這兩個APP都是公司開發的,然後並不是在所有的手機上出現這種情況,只是在其中某一臺iPhone手機上出現這種情況。 支付過程大 ...
  • 前一陣子做了一個小項目,關於android的,想記錄一下學到的一些知識,做成一個小系列吧,算是對自己這一個多月來的見證。首先說明,這些知識也都是從網上各處學習來的,我自己做了一些小整理。 1.SQLite資料庫 之前用的資料庫是MySQL和SQLServer,還用過oracle,雖然不是很精通,這次 ...
  • 之前用一張圖分析了Google給出的MVP架構,但是在Google給出的所有案例裡面除了基本的MVP架構還有其它幾種架構,今天就來分析其中的Clean架構。同樣的,網上介紹Clean架構的文章很多,我也就不用文字過多敘述了,還是用一張類圖來分析一下Clean架構的這個案例吧。好了,先直接上圖! 上完 ...
  • 1、進程 什麼是進程 進程是指在系統中正在運行的一個應用程式 每個進程之間是獨立的,每個進程均運行在其專用且受保護的記憶體空間內 比如同時打開迅雷、Xcode,系統就會分別啟動2個進程 2、線程 什麼是線程 1個進程要想執行任務,必須得有線程(每1個進程至少要有1條線程) 一個進程(程式)的所有任務都 ...
  • 在《Android 常用數據適配器ArrayAdapter》中介紹了ArrayAdapter數據適配器。但是存在一個缺陷,那就是條目的圖標都固定相同,要顯示每個條目的圖標都不相同,那麼使用SimpleAdapter 新建項目後,在layout文件夾下新建list_item.xml文件,接著編輯佈局, ...
  • android:nextFoucsUp padding是站在父view的角度描述問題,它規定它裡面的內容必須與這個父view邊界的距離。margin則是站在自己的角度描述問題,規定自己和其他(上下左右)的view之間的距離,如果同一級只有一個view,那麼它的效果基本上就和padding一樣了 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...