android Gui系統之SurfaceFlinger(3)

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

7.SurfaceFlinger SurfaceFlinger在前面的篇幅了,多有涉及。 SurfaceFlinger是GUI刷新UI的核心,所以任何關於SurfaceFlinger的改進都會對android UI系統有重大影響。 SurfaceFlinger主要分為4個部分 1)黃油計劃 proj ...


7.SurfaceFlinger

SurfaceFlinger在前面的篇幅了,多有涉及。

SurfaceFlinger是GUI刷新UI的核心,所以任何關於SurfaceFlinger的改進都會對android UI系統有重大影響。

SurfaceFlinger主要分為4個部分

1)黃油計劃---project butter

2)啟動過程

3)SurfaceFlinger & BufferQueue的關係

4)Vsync信號的處理

7.1黃油計劃

就是給android系統,圖上一層“黃油”。我們來看看andorid是怎麼給SurfaceFlinger塗上這層黃油的。

butter 由2個組成部分,Vsync & Triple buffer。

Triple buffer:

上面講到雙緩衝區技術,也提到FrameBufferNativeWindow 在申請buffer的時候,可以是2,或者是3.

這個3 就是馬上要講到的Triple Buffer技術。

我們先會過來看看雙緩衝技術。

之前說 雙緩衝,是把一個buffer放在bitmap上,等到這個所有元素都準備好以後,在把bitmap刷到屏幕上。

這樣會解決卡頓的感覺。

我們考慮一種情況,假設屏幕刷新頻率是66Hz,CPU頻率是100Hz.

之前已經講了雙緩衝技術,這裡簡單過一下。

如上面的假設,UI的刷新是0.015s,而buffer的準備是0.01s

一個Frame Buffer代表一幀圖像。

0.01s:

此時,buffer已經準備好數據,而顯示器只顯示了圖像的2/3

0.015s

顯示器顯示了第一幀圖像,而buffer已經填充了第二幀的1/3

0.02s

Buffer已經準備好了第二幀,而顯示器出現了問題,1/3的內容屬於第二幀,2/3的內容屬於第一幀。

這就是android引入雙緩衝技術的原因。

如果buffer準備的時間,比屏幕刷新圖像的速度慢呢?

顯示屏的每一次刷新,就是對顯示器屏幕的掃描,但是它是有間隔的(物理設備嘛,肯定有這個間隔)。

典型的PC顯示器屏幕刷新頻率是60Hz,這是因為一秒60幀,從人的角度看,就會覺得很流暢。

所以間隔1/60秒,也就是16ms 如果我們準備時間<=16ms,那就可以做到“無縫連接”。畫面就很流程。

這段空隙稱為VBI。 這個時間就是交換緩衝區最佳的時間。而這個交換的動作就是Vsync 也是SurfaceFlinger的重點。

如果我們圖像準備時間<=16ms. OK,畫面是很流暢的,但是我們無法保證設備性能一定很very good。所以也有可能畫面準備時間超過16ms


我們看看這張圖。 剛開始buffer裡面有數據A,這時候,可以直接顯示在屏幕上。 過了16ms以後,數據B還沒準備好,屏幕只能繼續顯示A。這樣就浪費了依次交換的機會。 到下一次交換,B被顯示在屏幕上。 這裡有段時間被浪費了。 等到下一次A的時候,過了16ms,還是沒有準備好,繼續浪費。所以雙緩衝區技術,也有很大浪費。 有沒有辦法規避呢, 比如上圖 B & A之間的這段時間,如果我增加一個buffer,C。 這樣B準備好以後,雖然C沒有好,但是B可以顯示在屏幕上,等到下一次16ms到了以後,C已經準備好了,這樣可以很大程度上減少CPU時間的浪費。 也就是空間換時間的一種思想。 所以多緩衝區就是,就是可以根據系統的實際記憶體情況,來判斷buffer的數量。  

7.2 SurfaceFlinger的啟動

SurfaceFlinger 我們前面已經說了,它其實就是一個service。

void SurfaceFlinger::onFirstRef()
{
    mEventQueue.init(this);
}

初始化事件隊列。

void MessageQueue::init(const sp<SurfaceFlinger>& flinger)
{
    mFlinger = flinger;
    mLooper = new Looper(true);
    mHandler = new Handler(*this);
}

創建了looper & Handler

但是這個looper什麼時候起來的呢?

void MessageQueue::waitMessage() {
    do {
        IPCThreadState::self()->flushCommands();
        int32_t ret = mLooper->pollOnce(-1);
        switch (ret) {
            case Looper::POLL_WAKE:
            case Looper::POLL_CALLBACK:
                continue;
            case Looper::POLL_ERROR:
                ALOGE("Looper::POLL_ERROR");
            case Looper::POLL_TIMEOUT:
                // timeout (should not happen)
                continue;
            default:
                // should not happen
                ALOGE("Looper::pollOnce() returned unknown status %d", ret);
                continue;
        }
    } while (true);
}

可以看到最終會調用looper啟動函數。可以看到Looper::POLL_TIMEOUT: android什麼都沒做,儘管它們不應該發生。

其實handler兜了一圈,發現最後還是回到surfaceflinger來處理:

void SurfaceFlinger::onMessageReceived(int32_t what) {
    ATRACE_CALL();
    switch (what) {
        case MessageQueue::TRANSACTION: {
            handleMessageTransaction();
            break;
        }
        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;
        }
    }
}

7.3 client

任何有UI界面App都在surfaceflinger裡面有client。

所以是一個app對應一個surfaceflinger裡面的client(ISurfaceComposerClient)。

 

 

下麵我們來分析surfaceflinger的2個重要函數:

sp<ISurfaceComposerClient> SurfaceFlinger::createConnection()
{
    sp<ISurfaceComposerClient> bclient;
    sp<Client> client(new Client(this));
    status_t err = client->initCheck();
    if (err == NO_ERROR) {
        bclient = client;
    }
    return bclient;
}

返回ISurfaceComposerClient,也就是client的bind對象實體。

其實就上面標紅的一句,進行必要的有效性檢查,現在代碼:

status_t Client::initCheck() const {
    return NO_ERROR;
}

有了clinet以後,看下surface的產生。

status_t Client::createSurface(
        const String8& name,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        sp<IBinder>* handle,
        sp<IGraphicBufferProducer>* gbp)
{
    /*
     * createSurface must be called from the GL thread so that it can
     * have access to the GL context.
     */

    class MessageCreateLayer : public MessageBase {
        SurfaceFlinger* flinger;
        Client* client;
        sp<IBinder>* handle;
        sp<IGraphicBufferProducer>* gbp;
        status_t result;
        const String8& name;
        uint32_t w, h;
        PixelFormat format;
        uint32_t flags;
    public:
        MessageCreateLayer(SurfaceFlinger* flinger,
                const String8& name, Client* client,
                uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
                sp<IBinder>* handle,
                sp<IGraphicBufferProducer>* gbp)
            : flinger(flinger), client(client),
              handle(handle), gbp(gbp),
              name(name), w(w), h(h), format(format), flags(flags) {
        }
        status_t getResult() const { return result; }
        virtual bool handler() {
            result = flinger->createLayer(name, client, w, h, format, flags,
                    handle, gbp);
            return true;
        }
    };

    sp<MessageBase> msg = new MessageCreateLayer(mFlinger.get(),
            name, this, w, h, format, flags, handle, gbp);
    mFlinger->postMessageSync(msg);
    return static_cast<MessageCreateLayer*>( msg.get() )->getResult();
}

到來到去,其實就2句話:

postMessageSync,其實就是一開始不會直接創建surface,然後放入surfaceflinger隊列里,這樣不會打斷現在的操作。

然後啟動createlayer方法。這個方法之前已經分析過了。

 

參考:

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

 

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

-Advertisement-
Play Games
更多相關文章
  • 從jQuery API 文檔中可以知道,jQuery自定義動畫的函數.animate( properties [, duration] [, easing] [, complete] )有四個參數: properties:一組包含作為動畫屬性和終值的樣式屬性和及其值的集合 duration(可選): ...
  • 還記得之前的javascript入門裡的講的confirm 消息對話框嗎?不記得也沒關係,我們先來回顧一下,然後在詳細講它。 複習: confirm 消息對話框通常用於允許用戶做選擇的動作,如:“你對嗎?”等。彈出對話框(包括一個確定按鈕和一個取消按鈕)。 語法: 參數說明: str:在消息對話框中 ...
  • 當初始化SpaceShip原型時,我們尚未創建任何能作為第一個參數來傳遞的場景。並且SpaceShip的原型加入到場景的註冊表中,而這絕不是我們想做的。這是一種使用子類時常用的方法。應當僅僅在子類構造函數中調用父類構造函數,而不是當創建子類原型時調用它。 一旦創建了SpaceShip的原型對象,我們... ...
  • 當頁面使用 UITabBarController + UINavigationController 框架的時候,當跳轉到詳情頁面的時候,如果 UITabBar 仍然存在的話就會造成邏輯混亂,用戶體驗也會下降,因此我們就有一個在詳情頁將 UITabBar 隱藏的需求,當然,在其他的一些情況也可能有隱藏 ...
  • 用類對象作為ArrayAdapter綁定的基本數據類型(和SimpleAdater效果類似) 一般ArrayAdapter綁定的基本數據類型是String,接下來介紹一下類對象作為基本數據類型; 首先,新建一個類News,這個類作為基本的數據類型 接下來先把listView的item的佈局確定下來, ...
  • 最近因為項目需要對聲音進行變聲,所以邊學習邊做,發現音頻的處理思路並不難,但是做起來還是有些繁瑣的(比預期的) 趁著腦子還發熱,趕緊把思路總結一下,記錄下來。 主要講三個部分 1,如何變聲2,安卓實現變聲3,ios實現變聲 1. 要想自己寫一個變聲的函數或者庫出來,談何容易,所以採用了大家普遍採用的 ...
  • 從寫第一篇Swift文章的時候到現在Swift已經從1.2發展到了今天的3.0,這期間由於Swift目前還在發展階段並不能向下相容,因此第一篇文章中的部分代碼在當前的Xcode環境中已經無法運行。在WWDC16上Apple公佈了Swift3.0,從中可以看出Apple對Swift的重視,以及Swif... ...
  • 上篇文章講到《Android 簡訊的備份》,本文主要實現Android 簡訊的還原,即是將一條 佈局文件: 添加代碼: 添加許可權: 運行項目,搞定 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...