[096]圖解HWC的合成策略

背景

最近好幾個網(wǎng)友都來問我HWC是根據(jù)什么條件來決定client合成還是device区岗,作為之前一直"吹牛逼"略板,說自己如何好學(xué),如何積極回答網(wǎng)友問題的博主慈缔,這波必須安排叮称,看完這篇文章,絕對讓你明白藐鹤。

本文參考實現(xiàn)來源于external/drm_hwcomposer瓤檐,我相信別的廠家的思路也大差不差。

一教藻、獲取參與SF合成的所有l(wèi)ayers

假設(shè)我們的所有可見的layers按照z軸,圖中有7個layer這樣子排列右锨。


二括堤、根據(jù)該屏幕支持的Usable Planes數(shù)量算出client合成的layer數(shù)量

假設(shè)該屏幕支持4個Planes,但是由于layer的數(shù)量大于4,因為需要保留一個Planes悄窃,用于client合成讥电,也就是Framebuffersurface。
HWC只支持4-1=3個layer為device合成轧抗,需要找到連續(xù)7-3=4個layers作為client合成(代碼分析2_1)

2.1 選擇像素點之和最少的連續(xù)4個layers

如圖恩敌,我們有了四個備選方案,我們要選擇像素點個數(shù)之和最少的連續(xù)4個layers横媚,我覺得這樣子的策略纠炮,應(yīng)該是減少GPU合成的工作量(代碼分析2_2

2.2 最后的結(jié)果

假如第二個方案中,像素點個數(shù)之和最少灯蝴,HWC最后返回的策略如下圖恢口。


小結(jié)

這個就是最簡單的策略,但是接下來要在這個基礎(chǔ)穷躁,引申出更加復(fù)雜的策略耕肩。

三、假如有l(wèi)ayer指定要client合成

layer是可以指定client合成的问潭,例如layer有圓角猿诸,當(dāng)然還有很多情況,后面講代碼的時候會講(代碼分析3_0)狡忙。我們先假設(shè)指定client合成的layer為2梳虽,4。


第一個指定的client layer和最后一個指定的client layer之間的 layer 必須是變成client了(代碼分析3_1)去枷,如下圖:3就很慘的也被設(shè)置成client合成了

四怖辆、再次考慮Usable Planes數(shù)量算出client合成的layer的數(shù)量

按照中說的一樣,我們還是需要找到連續(xù)的4個layer作為client合成删顶,但是我們還的還得加個條件竖螃,包含中提到的2~4的三個layer

4.1 得到備選方案

這次我們只有兩個備選方案了,因為2.1中提到的第1個和第4個方案都沒有同時包含2~4的三個layer

4.2 選擇像素點之和最少的連續(xù)4個layer的方案逗余。

還是和中的策略一樣特咆,假如這次是2,3录粱,4腻格,5的像素點之和最少,最后得到的結(jié)果如下啥繁。

小結(jié)

到這里應(yīng)該看明白了菜职,其實圖解策略就很簡單了,再去結(jié)合代碼看就比較容易了旗闽。

代碼分析

代碼量不多酬核,我就全部貼上來了external/drm_hwcomposer/backend/Backend.cpp蜜另,文章中代碼分析X_X對應(yīng)下面的部分注釋,關(guān)鍵是看懂這個函數(shù)的實現(xiàn)

//關(guān)鍵代碼嫡意,返回client layer的start index和size举瑰。
std::tie(client_start, client_size) = GetClientLayers(display, layers);

帶著我前面講的策略去跟這個函數(shù)。

/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "Backend.h"

#include <climits>

#include "BackendManager.h"
#include "bufferinfo/BufferInfoGetter.h"

namespace android {

HWC2::Error Backend::ValidateDisplay(HwcDisplay *display, uint32_t *num_types,
                                     uint32_t *num_requests) {
  *num_types = 0;
  *num_requests = 0;

  auto layers = display->GetOrderLayersByZPos();

  int client_start = -1;
  size_t client_size = 0;

  if (display->ProcessClientFlatteningState(layers.size() <= 1)) {
    display->total_stats().frames_flattened_++;
    client_start = 0;
    client_size = layers.size();
    MarkValidated(layers, client_start, client_size);
  } else {
    std::tie(client_start, client_size) = GetClientLayers(display, layers);//關(guān)鍵代碼蔬螟,返回client layer的start id和size此迅。

    MarkValidated(layers, client_start, client_size);

    bool testing_needed = !(client_start == 0 && client_size == layers.size());

    AtomicCommitArgs a_args = {.test_only = true};

    if (testing_needed &&
        display->CreateComposition(a_args) != HWC2::Error::None) {
      ++display->total_stats().failed_kms_validate_;
      client_start = 0;
      client_size = layers.size();
      MarkValidated(layers, 0, client_size);
    }
  }

  *num_types = client_size;

  display->total_stats().gpu_pixops_ += CalcPixOps(layers, client_start,
                                                   client_size);
  display->total_stats().total_pixops_ += CalcPixOps(layers, 0, layers.size());

  return *num_types != 0 ? HWC2::Error::HasChanges : HWC2::Error::None;
}

std::tuple<int, size_t> Backend::GetClientLayers(
    HwcDisplay *display, const std::vector<HwcLayer *> &layers) {
  int client_start = -1;
  size_t client_size = 0;

  for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
    if (IsClientLayer(display, layers[z_order])) {
      if (client_start < 0)
        client_start = (int)z_order;
      client_size = (z_order - client_start) + 1;
    }
  }//代碼分析3_1

  return GetExtraClientRange(display, layers, client_start, client_size);//擴大額外的需要為client的layers
}

//代碼分析3_0
//判斷l(xiāng)ayer是不是必須為client合成或者已經(jīng)指定了client合成
bool Backend::IsClientLayer(HwcDisplay *display, HwcLayer *layer) {
  return !HardwareSupportsLayerType(layer->GetSfType()) ||
         !BufferInfoGetter::GetInstance()->IsHandleUsable(layer->GetBuffer()) ||
         display->color_transform_hint() != HAL_COLOR_TRANSFORM_IDENTITY ||
         (layer->RequireScalingOrPhasing() &&
          display->GetHwc2()->GetResMan().ForcedScalingWithGpu());
}

bool Backend::HardwareSupportsLayerType(HWC2::Composition comp_type) {
  return comp_type == HWC2::Composition::Device ||
         comp_type == HWC2::Composition::Cursor;
}

//計算連續(xù)幾個layer的像素點之和
uint32_t Backend::CalcPixOps(const std::vector<HwcLayer *> &layers,
                             size_t first_z, size_t size) {
  uint32_t pixops = 0;
  for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
    if (z_order >= first_z && z_order < first_z + size) {
      hwc_rect_t df = layers[z_order]->GetDisplayFrame();
      pixops += (df.right - df.left) * (df.bottom - df.top);
    }
  }
  return pixops;
}

//標記每個layer的合成策略,最后sf拿到的就是這個標記
void Backend::MarkValidated(std::vector<HwcLayer *> &layers,
                            size_t client_first_z, size_t client_size) {
  for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
    if (z_order >= client_first_z && z_order < client_first_z + client_size)
      layers[z_order]->SetValidatedType(HWC2::Composition::Client);
    else
      layers[z_order]->SetValidatedType(HWC2::Composition::Device);
  }
}

std::tuple<int, int> Backend::GetExtraClientRange(
    HwcDisplay *display, const std::vector<HwcLayer *> &layers,
    int client_start, size_t client_size) {
  auto planes = display->GetPipe().GetUsablePlanes();
  size_t avail_planes = planes.size();//獲取整個drm支持的planes的數(shù)量

  /*
   * If more layers then planes, save one plane
   * for client composited layers
   */
  if (avail_planes < display->layers().size())
    avail_planes--;//保留一個用于存放所有client合成的layer

  int extra_client = int(layers.size() - client_size) - int(avail_planes);//計算剩余需要成為client的數(shù)量
  //上面是代碼分析2_1

  if (extra_client > 0) {
    int start = 0;
    size_t steps = 0;
    if (client_size != 0) {//有l(wèi)ayer指定了client合成
      int prepend = std::min(client_start, extra_client);
      int append = std::min(int(layers.size()) -
                                int(client_start + client_size),
                            extra_client);
      start = client_start - (int)prepend;
      client_size += extra_client;
      steps = 1 + std::min(std::min(append, prepend),
                           int(layers.size()) - int(start + client_size));
    } else {//沒有l(wèi)ayer指定了client合成旧巾。代碼分析
      client_size = extra_client;//剩余的都是client
      steps = 1 + layers.size() - extra_client;//計算硬件合成步伐
    }

    //選擇像素之和最少的連續(xù)layers
    uint32_t gpu_pixops = UINT32_MAX;
    for (size_t i = 0; i < steps; i++) {
      uint32_t po = CalcPixOps(layers, start + i, client_size);
      if (po < gpu_pixops) {
        gpu_pixops = po;
        client_start = start + int(i);
      }
    } //代碼分析2_2
  }

  return std::make_tuple(client_start, client_size);
}

// clang-format off
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, cert-err58-cpp)
REGISTER_BACKEND("generic", Backend);
// clang-format on

}  // namespace android

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耸序,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子菠齿,更是在濱河造成了極大的恐慌佑吝,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绳匀,死亡現(xiàn)場離奇詭異芋忿,居然都是意外死亡,警方通過查閱死者的電腦和手機疾棵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門戈钢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人是尔,你說我怎么就攤上這事殉了。” “怎么了拟枚?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵薪铜,是天一觀的道長。 經(jīng)常有香客問我恩溅,道長隔箍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任脚乡,我火速辦了婚禮蜒滩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘奶稠。我一直安慰自己俯艰,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布锌订。 她就那樣靜靜地躺著竹握,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辆飘。 梳的紋絲不亂的頭發(fā)上啦辐,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天污秆,我揣著相機與錄音,去河邊找鬼昧甘。 笑死,一個胖子當(dāng)著我的面吹牛战得,可吹牛的內(nèi)容都是我干的充边。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼常侦,長吁一口氣:“原來是場噩夢啊……” “哼浇冰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起聋亡,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤肘习,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后坡倔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體漂佩,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年罪塔,在試婚紗的時候發(fā)現(xiàn)自己被綠了投蝉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡征堪,死狀恐怖瘩缆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情佃蚜,我是刑警寧澤庸娱,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站谐算,受9級特大地震影響熟尉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜氯夷,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一臣樱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腮考,春花似錦雇毫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至馅闽,卻和暖如春飘蚯,著一層夾襖步出監(jiān)牢的瞬間馍迄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工局骤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留攀圈,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓峦甩,卻偏偏與公主長得像赘来,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子凯傲,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

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