HWComper介紹

HWComposer介紹

HWComposer類的作用:
1:打開了Gralloc模塊FB設(shè)備室埋,負責將圖像緩沖區(qū)的內(nèi)容渲染到FB顯示緩沖區(qū)
Gralloc模塊可以打開兩種設(shè)備显沈,一種負責分配圖形緩沖區(qū)两芳,另一種打開FB設(shè)備驯鳖,負責渲染FB緩沖區(qū)。
2:負責管理硬件HWComposer設(shè)備
HWC(hwcomposer)是Android中進行窗口(Layer)合成和顯示的HAL層模塊袍祖,其實現(xiàn)是特定于設(shè)備的底瓣,而且通常由顯示設(shè)備制造商 (OEM)完成,為SurfaceFlinger服務(wù)提供硬件支持盲泛。
SurfaceFlinger可以使用OpenGL ES合成Layer,這需要占用并消耗GPU資源键耕。大多數(shù)GPU都沒有針對圖層合成進行優(yōu)化寺滚,當SurfaceFlinger通過GPU合成圖層時,應(yīng)用程序無法使用GPU進行自己的渲染屈雄。而HWC通過硬件設(shè)備進行圖層合成村视,可以減輕GPU的合成壓力。
3:硬件HWComposer負責產(chǎn)生硬件VSync信號
HWC也提供了VSync事件酒奶,用于管理渲染和圖層合成時機蚁孔。

HWComposer初始化過程

在SurfaceFlinger的init方法中會初始化HWComposer對象

    // Initialize the H/W composer object.  There may or may not be an
    // actual hardware composer underneath.
    mHwc = new HWComposer(this,
            *static_cast<HWComposer::EventHandler *>(this));

init方法中直接創(chuàng)建了一個HWComposer對象奶赔,保存在SurfaceFlinger中。


HWComposer::HWComposer(
        const sp<SurfaceFlinger>& flinger,
        EventHandler& handler)
    : mFlinger(flinger),
      mFbDev(0), mHwc(0), mNumDisplays(1),
      mCBContext(new cb_context),
      mEventHandler(handler),
      mDebugForceFakeVSync(false)
{
    ......

    bool needVSyncThread = true;

    // 加載Gralloc HAL模塊杠氢,并打開FB設(shè)備站刑,保存在mFbDev中
    int fberr = loadFbHalModule();、
    // 加載HWComposer HAL模塊鼻百,并打開HWC設(shè)備绞旅,保存在mHWC中
    loadHwcModule();

    ......

    // 保留前三個設(shè)備ID
    for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
        mAllocatedDisplayIDs.markBit(i);
    }

    //如果打開HWC成功,則向HWC HAL注冊回調(diào)函數(shù)
    if (mHwc) {
        if (mHwc->registerProcs) {
            mCBContext->hwc = this;
            //注冊HWC硬件強制刷新的回調(diào)函數(shù)
            mCBContext->procs.invalidate = &hook_invalidate;
            //注冊硬件Vsync回調(diào)函數(shù)
            mCBContext->procs.vsync = &hook_vsync;
            //注冊熱插拔回調(diào)函數(shù)
            if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
                mCBContext->procs.hotplug = &hook_hotplug;
            else
                mCBContext->procs.hotplug = NULL;
            memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
            mHwc->registerProcs(mHwc, &mCBContext->procs);
        }

        // 是否需要軟件模擬VSYNC信號
        needVSyncThread = false;
        // 硬件HWC VSYNC 開始的時候默認關(guān)閉
        eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);

        //根據(jù)HWC的版本好温艇,決定支持顯示設(shè)備的數(shù)量
        if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
            // 1.3 adds support for virtual displays
            mNumDisplays = MAX_HWC_DISPLAYS;
        } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
            // 1.1 adds support for multiple displays
            mNumDisplays = NUM_BUILTIN_DISPLAYS;
        } else {
            mNumDisplays = 1;
        }
    }

    //如果FB設(shè)備打開成功因悲,則獲取FB顯示設(shè)備的基本信息,保存在對應(yīng)的DisplayData對象中.
    if (mFbDev) {
        DisplayData& disp(mDisplayData[HWC_DISPLAY_PRIMARY]);
        disp.connected = true;
        disp.format = mFbDev->format;
        DisplayConfig config = DisplayConfig();
        config.width = mFbDev->width;
        config.height = mFbDev->height;
        config.xdpi = mFbDev->xdpi;
        config.ydpi = mFbDev->ydpi;
        config.refresh = nsecs_t(1e9 / mFbDev->fps);
        disp.configs.push_back(config);
        disp.currentConfig = 0;
    } else if (mHwc) {
        // here we're guaranteed to have at least HWC 1.1
        for (size_t i =0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
            queryDisplayProperties(i);
        }
    }

    //如果沒有HWC硬件勺爱,則需要創(chuàng)建一個VSyncThread軟件模擬VSYNC信號
    if (needVSyncThread) {
        mVSyncThread = new VSyncThread(*this);
    }
}

HWComposer的初始化函數(shù)做了那些內(nèi)容晃琳?
1:加載Gralloc模塊,打開FB設(shè)備琐鲁,用于渲染顯示緩沖區(qū)
2:加載HWC HAL模塊卫旱,打開HWC設(shè)備,用于硬件合成Layer及產(chǎn)生Vsync信號
3:注冊HWC Vsync, 熱插拔绣否,強制刷新的回調(diào)函數(shù)
4:獲取FB顯示驅(qū)動相關(guān)的參數(shù)誊涯,存放到DisplayData中

HWComposer 的DisplayData

  struct DisplayData {
        DisplayData();
        ~DisplayData();
        Vector<DisplayConfig> configs; //設(shè)備配置信息
        size_t currentConfig;
        uint32_t format;    //FB Hal的數(shù)據(jù)格式
        bool connected; //設(shè)備時候連接
        bool hasFbComp; //是否需要GL合成
        bool hasOvComp; //是否需要HWC河涌
        size_t capacity; //列表的最大容量
        hwc_display_contents_1* list; //要合成的Layer的列表,用于傳遞個HWC HAL
        hwc_layer_1* framebufferTarget; //HWComposer將列表中的Layer合成后最終存放到這個Layer中
        buffer_handle_t fbTargetHandle; //最終合成Layer對應(yīng)的圖像緩沖區(qū)
        ......
        buffer_handle_t outbufHandle;
        sp<Fence> outbufAcquireFence;

        // protected by mEventControlLock
        int32_t events;
    };

SurfaceFlinger中有DisplayDevice來描述一個顯示設(shè)備蒜撮, HWComposer中DisplayData來描述一個顯示設(shè)備需要顯示的數(shù)據(jù)信息暴构。詳細注釋已經(jīng)在代碼中

HWComposer 硬件合成相關(guān)接口分析

SurfaceFlinger合成圖像的時候會調(diào)用setHWComposer函數(shù),來設(shè)置HWComposer硬件合成相關(guān)的參數(shù)

    //創(chuàng)建HWComposer任務(wù)列表段磨,并初始化基本的屬性信息
    HWComposer& hwc(getHwComposer());
    if (hwc.initCheck() == NO_ERROR) {
        // 創(chuàng)建HWC 合成的工作列表
        if (CC_UNLIKELY(mHwWorkListDirty)) {
            mHwWorkListDirty = false;
            //變量所有的顯示設(shè)備列表取逾,為每個顯示設(shè)備都創(chuàng)建工作任務(wù)列表
            for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
                sp<const DisplayDevice> hw(mDisplays[dpy]);
                const int32_t id = hw->getHwcDisplayId();
                if (id >= 0) {
                    //獲取當前顯示設(shè)備需要顯示的Layer列表,存放在VisibleLayersSortedByZ列表中
                    const Vector< sp<Layer> >& currentLayers(
                        hw->getVisibleLayersSortedByZ());
                    const size_t count = currentLayers.size();
                    //調(diào)用HWComposer的createWorkList創(chuàng)建工作列表苹支,指定顯示設(shè)備ID和Layer數(shù)量
                    if (hwc.createWorkList(id, count) == NO_ERROR) {
                        HWComposer::LayerListIterator cur = hwc.begin(id);
                        const HWComposer::LayerListIterator end = hwc.end(id);
                        for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) {
                            const sp<Layer>& layer(currentLayers[i]);
                            //遍歷通過SurfaceFlinger的Layer設(shè)置HWC Layer的混合模式砾隅,裁剪區(qū)域等屬性
                            layer->setGeometry(hw, *cur);
                            if (mDebugDisableHWC || mDebugRegion || mDaltonize || mHasColorMatrix) {
                                cur->setSkip(true);
                            }
                        }
                    }
                }
            }
        }

        // 為HWComposer的每個Layer設(shè)置圖形數(shù)據(jù)

        // 遍歷所有的顯示設(shè)備,為每個設(shè)備的工作列表中的Layer設(shè)置數(shù)據(jù)
        for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
            sp<const DisplayDevice> hw(mDisplays[dpy]);
            const int32_t id = hw->getHwcDisplayId();
            if (id >= 0) {
                //獲取當前顯示設(shè)備中需要顯示的Layer列表
                const Vector< sp<Layer> >& currentLayers(
                    hw->getVisibleLayersSortedByZ());
                const size_t count = currentLayers.size();
                HWComposer::LayerListIterator cur = hwc.begin(id);
                const HWComposer::LayerListIterator end = hwc.end(id);
                for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) {
                    //通過SF中Layer的setPerFrameData方法债蜜,設(shè)置HWC Layer的圖形數(shù)據(jù)晴埂,將Buffer指給HWC 的Layer
                    const sp<Layer>& layer(currentLayers[i]);
                    layer->setPerFrameData(hw, *cur);
                }
            }
        }   

        //調(diào)用HWC的prepare,做硬件合成準備
        status_t err = hwc.prepare();
        ALOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));

SF設(shè)置HWComposer的順序如下:
1:調(diào)用HWComposer的createWorkList創(chuàng)建合成任務(wù)列表
2:遍歷所有的Layer為HWComposer的Layer設(shè)置圖形數(shù)據(jù)
3:調(diào)用HWComposer的prepare做合成準備

HWComposer創(chuàng)建任務(wù)列表

HWComposer用于合成Layer圖像數(shù)據(jù)寻定,HWComposer硬件合成第一步是創(chuàng)建HWComposer任務(wù)列表

//參數(shù)id 表示當前為哪個顯示設(shè)備創(chuàng)建任務(wù)列表儒洛, numLayers表示要創(chuàng)建多大的列表,也就是SurfaceFlinger中有多少個Layer要顯示在這個屏幕上狼速。
status_t HWComposer::createWorkList(int32_t id, size_t numLayers) {
    if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) {
        return BAD_INDEX;
    }

    if (mHwc) {
        //找到當前ID對應(yīng)的顯示設(shè)備的信息
        DisplayData& disp(mDisplayData[id]);
        //如果版本大于HWC_DEVICE_API_VERSION_1_1琅锻, 則需要多創(chuàng)建一個Layer, 這個Layer用來存放那些需要軟件合成的Layer GL合成后的圖像數(shù)據(jù),這些Layer合成后的數(shù)據(jù)會放到任務(wù)列表中和其他的Layer一起參與硬件合成
        if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
            numLayers++;
        }
        //如果對應(yīng)顯示設(shè)備的列表還沒有分配內(nèi)存,或者容量較小恼蓬,則釋放掉原來的列表惊完,重新分配內(nèi)存
        if (disp.capacity < numLayers || disp.list == NULL) {
            size_t size = sizeof(hwc_display_contents_1_t)
                    + numLayers * sizeof(hwc_layer_1_t);
            free(disp.list);
            disp.list = (hwc_display_contents_1_t*)malloc(size);
            disp.capacity = numLayers;
        }
        //如果版本大于HWC_DEVICE_API_VERSION_1_1
        if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
            //將列表中最后一個Layer作為GL軟件合成最后的存放Layer
            disp.framebufferTarget = &disp.list->hwLayers[numLayers - 1];
            memset(disp.framebufferTarget, 0, sizeof(hwc_layer_1_t));
            const DisplayConfig& currentConfig =
                    disp.configs[disp.currentConfig];
            const hwc_rect_t r = { 0, 0,
                    (int) currentConfig.width, (int) currentConfig.height };
            //設(shè)置最后一個Layer的合成方式為HWC_FRAMEBUFFER_TARGET
            disp.framebufferTarget->compositionType = HWC_FRAMEBUFFER_TARGET;
            disp.framebufferTarget->hints = 0;
            disp.framebufferTarget->flags = 0;
            disp.framebufferTarget->handle = disp.fbTargetHandle;
            disp.framebufferTarget->transform = 0;
            disp.framebufferTarget->blending = HWC_BLENDING_PREMULT;
            //設(shè)置最終GL合成的區(qū)域
            if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
                disp.framebufferTarget->sourceCropf.left = 0;
                disp.framebufferTarget->sourceCropf.top = 0;
                disp.framebufferTarget->sourceCropf.right =
                        currentConfig.width;
                disp.framebufferTarget->sourceCropf.bottom =
                        currentConfig.height;
            } else {
                disp.framebufferTarget->sourceCrop = r;
            }
            disp.framebufferTarget->displayFrame = r;
            disp.framebufferTarget->visibleRegionScreen.numRects = 1;
            disp.framebufferTarget->visibleRegionScreen.rects =
                &disp.framebufferTarget->displayFrame;
            disp.framebufferTarget->acquireFenceFd = -1;
            disp.framebufferTarget->releaseFenceFd = -1;
            disp.framebufferTarget->planeAlpha = 0xFF;
        }
        disp.list->retireFenceFd = -1;
        disp.list->flags = HWC_GEOMETRY_CHANGED;
        //設(shè)置列表中Layer的數(shù)量為numHwLayers
        disp.list->numHwLayers = numLayers;
    }
    return NO_ERROR;
}

DisplayData描述了一個顯示設(shè)備要合成的數(shù)據(jù)信息,其中有一個Layer列表处硬,存放SufaceFlinger設(shè)置的該顯示設(shè)備即將參加合成的Layer列表小槐。
創(chuàng)建任務(wù)列表的時候首先需要判斷列表的容量是否足以容納當前設(shè)置的Layer數(shù)量,如果存放不了則需要重新分配列表的內(nèi)存空間郁油。
當我們把Layer列表交個硬件進行合成的時候本股,需要多指定一個Layer,該Layer用來存放桐腌,軟件GL合成Layer的最終圖像數(shù)據(jù)拄显,這些Layer被軟件合成的后放到任務(wù)列表最后一個Layer中,一起參與HWC硬件合成案站,最后一起顯示到屏幕上躬审,所以在申請列表空間的時候需要多申請一個Layer的空間。在列表中這個Layer的位置為列表最后一個蟆盐,最終輸出的這個Layer是需要openGL進行合成操作的承边,所以將最后的Layer的合成類型設(shè)置為HWC_FRAMEBUFFER_TARGET,表示這個Layer最終需要渲染到FB顯示緩沖區(qū)石挂。

HWComposer數(shù)據(jù)合成準備

status_t HWComposer::prepare() {
    Mutex::Autolock _l(mDisplayLock);
    ......

    //調(diào)用硬件HWC prepare 即將開始合成數(shù)據(jù)
    int err = mHwc->prepare(mHwc, mNumDisplays, mLists);
    ALOGE_IF(err, "HWComposer: prepare failed (%s)", strerror(-err));

    if (err == NO_ERROR) {
        //遍歷每一個顯示設(shè)備博助,確定該顯示設(shè)備是否需要HWC和GL合成
        for (size_t i=0 ; i<mNumDisplays ; i++) {
            DisplayData& disp(mDisplayData[i]);
            disp.hasFbComp = false;
            disp.hasOvComp = false;
            //遍歷該設(shè)備中的Layer
            if (disp.list) {
                for (size_t i=0 ; i<disp.list->numHwLayers ; i++) {
                    hwc_layer_1_t& l = disp.list->hwLayers[i];
                    //如果該Layer的flag為HWC_SKIP_LAYER, 則需要GL方式合成
                    if (l.flags & HWC_SKIP_LAYER) {
                        l.compositionType = HWC_FRAMEBUFFER;
                    }
                    //如果Layer合成方式為HWC_FRAMEBUFFER痹愚,設(shè)置該設(shè)備hasFbComp為true
                    if (l.compositionType == HWC_FRAMEBUFFER) {
                        disp.hasFbComp = true;
                    }
                    //如果Layer合成方式為HWC_OVERLAY富岳, 則設(shè)置該設(shè)備hasOvComp為true
                    if (l.compositionType == HWC_OVERLAY) {
                        disp.hasOvComp = true;
                    }
                    //如果Layer的合成凡是為HWC_CURSOR_OVERLAY, 也設(shè)置hasOvComp為ture
                    if (l.compositionType == HWC_CURSOR_OVERLAY) {
                        disp.hasOvComp = true;
                    }
                }
                //如果該Layer為openGL合成輸出的Layer則設(shè)置hasFbComp為true
                if (disp.list->numHwLayers == (disp.framebufferTarget ? 1 : 0)) {
                    disp.hasFbComp = true;
                }
            } else {
                //如果list為空拯腮,則默認為GL方式合成
                disp.hasFbComp = true;
            }
        }
    }
    return (status_t)err;
}

HWComposer的prepare方法決定了該顯示設(shè)備支持那種合成方式窖式,合成方式主要分為兩種
HWC硬件合成和GL方式的軟件合成
有以下類型的Layer,該顯示設(shè)備需要支持HWC硬件合成
1:Layer的合成類型為 HWC_OVERLAY
2:Layer的合成類型為 HWC_CURSOR_OVERLAY 光標類型的Layer
什么情況下顯示設(shè)備需要支持軟件合成呢?
1:如果HWComposer的任務(wù)列表為空动壤,則默認該設(shè)備需要GL來進行軟件合成
2:如果有Layer的flag為HWC_SKIP_LAYER萝喘,則需要支持GL合成
3:如果該Layer是HWC指定的OpenGL軟件合成后輸出Layer,則需要支持GL軟件合成

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末琼懊,一起剝皮案震驚了整個濱河市阁簸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哼丈,老刑警劉巖启妹,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異削祈,居然都是意外死亡翅溺,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門髓抑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咙崎,“玉大人惫皱,你說我怎么就攤上這事甥温“俅遥” “怎么了兜喻?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵匙姜,是天一觀的道長蔫敲。 經(jīng)常有香客問我柬姚,道長郁轻,這世上最難降的妖魔是什么队秩? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任笑旺,我火速辦了婚禮,結(jié)果婚禮上馍资,老公的妹妹穿的比我還像新娘筒主。我一直安慰自己,他們只是感情好鸟蟹,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布乌妙。 她就那樣靜靜地躺著,像睡著了一般建钥。 火紅的嫁衣襯著肌膚如雪藤韵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天熊经,我揣著相機與錄音泽艘,去河邊找鬼。 笑死奈搜,一個胖子當著我的面吹牛悉盆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播馋吗,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼焕盟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宏粤?” 一聲冷哼從身側(cè)響起脚翘,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绍哎,沒想到半個月后来农,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡崇堰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年沃于,在試婚紗的時候發(fā)現(xiàn)自己被綠了涩咖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡繁莹,死狀恐怖檩互,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情咨演,我是刑警寧澤闸昨,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站薄风,受9級特大地震影響饵较,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜遭赂,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一循诉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧撇他,春花似錦打洼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至僻弹,卻和暖如春阿浓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蹋绽。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工芭毙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卸耘。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓退敦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蚣抗。 傳聞我的和親對象是個殘疾皇子侈百,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容