Android 12(S) 影像顯示系統 - drm_hwcomposer 簡析(下)

二的次方發表於2022-07-06

必讀:

Android 12(S) 影像顯示系統 - 開篇


 

 

合成方式

合成型別的定義:/hardware/interfaces/graphics/composer/2.1/IComposerClient.hal

    /** Possible composition types for a given layer. */
    /** 建議去看原始碼中的註釋,可以理解每一個type的含義 */
    enum Composition : int32_t {
        INVALID = 0,
        CLIENT = 1, 
        DEVICE = 2,
        SOLID_COLOR = 3,
        CURSOR = 4,
        SIDEBAND = 5,
    };

 

後端的設計邏輯

有三個類定義

  1. Backend == 一個後端的實現,註冊為"generic",主要是定義了ValidateDisplay方法,這個方法用來設定可見的HwcLayer應該採用什麼合成方式

  2. BackendClient  ==  一個後端的實現,註冊為"client",主要是定義了ValidateDisplay方法,它把所有HwcLayer都設定成立Client合成方式

  3. BackendManager == 後端的管理器,用來根據Device name從已註冊的backend列表中選擇一個,設定給HwcDisplay;GetBackendByName就是通過Device name來從available_backends_中選擇一個匹配的Backend建構函式來構建後端物件。

 

HWC 中如何為每一個Layer選擇合成方式

[drm-hwcomposer/hwc2_device/HwcDisplay.cpp]
HWC2::Error HwcDisplay::ValidateDisplay(uint32_t *num_types,
                                        uint32_t *num_requests) {
    if (IsInHeadlessMode()) {
     *num_types = *num_requests = 0;
     return HWC2::Error::None;
  }
    return backend_->ValidateDisplay(this, num_types, num_requests); //呼叫backend的方法
}

去呼叫到後端的具體validate方法,我的平臺就是走到Backend::ValidateDisplay

[drm-hwcomposer/backend/Backend.cpp]
HWC2::Error Backend::ValidateDisplay(HwcDisplay *display, uint32_t *num_types,
                                     uint32_t *num_requests) {
  *num_types = 0;
  *num_requests = 0;
  auto layers = display->GetOrderLayersByZPos(); // 按Z-order順序排列的HwcLayer的集合
  int client_start = -1; // layers中,需要Client合成的layer的起始位置
  size_t client_size = 0; // layers中,需要Client合成的layer的個數
  if (display->ProcessClientFlatteningState(layers.size() <= 1)) {
    display->total_stats().frames_flattened_++;
    client_start = 0;
    client_size = layers.size();
    //設定合成型別,client_start到client_start+client_size之間的設定為Client,其它的設定為Device
    MarkValidated(layers, client_start, client_size);
  } else {
    std::tie(client_start, client_size) = GetClientLayers(display, layers);// 刷選哪些layer需要Client合成
    //設定合成型別,client_start到client_start+client_size之間的設定為Client,其它的設定為Device
    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();
      //設定合成型別,client_start到client_start+client_size之間的設定為Client,其它的設定為Device
      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;
}

Backend中還有幾個輔助方法,簡單介紹下

GetClientLayers:刷選出哪些layer需要Client合成,篩選是會經過兩層考核 IsClientLayer & GetExtraClientRange

IsClientLayer:判斷指定的Layer是否要Client合成,有幾個條件:1. HardwareSupportsLayerType硬體不支援的合成方式
                                                2. IsHandleUsable buffer handle無法轉為DRM要求的buffer object
                                                3. color_transform_hint !=HAL_COLOR_TRANSFORM_IDENTITY
                                                4. 需要scale or phase,但hwc強制GPU來處理

GetExtraClientRange: 進一步篩選client layer, 當layer的數量多於hwc支援的planes時,需要留出一個給 client target

 

合成顯示

PresentDisplay方法的作用就是把內容呈現到螢幕上去

[drm-hwcomposer/hwc2_device/HwcDisplay.cpp]
HWC2::Error HwcDisplay::PresentDisplay(int32_t *present_fence) {
  ...
  AtomicCommitArgs a_args{};
  ret = CreateComposition(a_args);// 呼叫
  ...
}

主要是去呼叫了CreateComposition這個方法

[drm-hwcomposer/hwc2_device/HwcDisplay.cpp]
HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) {
  if (IsInHeadlessMode()) { // 無頭模式,不做處理,返回
    ALOGE("%s: Display is in headless mode, should never reach here", __func__);
    return HWC2::Error::None;
  }
  int PrevModeVsyncPeriodNs = static_cast<int>(
      1E9 / GetPipe().connector->Get()->GetActiveMode().v_refresh());
  auto mode_update_commited_ = false; // 是否需要更新/提交
  if (staged_mode_ && // staged_mode_ 當前所處的顯示模式
      staged_mode_change_time_ <= ResourceManager::GetTimeMonotonicNs()) {
    client_layer_.SetLayerDisplayFrame( // 設定顯示的位置大小
        (hwc_rect_t){.left = 0,
                     .top = 0,
                     .right = static_cast<int>(staged_mode_->h_display()),
                     .bottom = static_cast<int>(staged_mode_->v_display())});
    configs_.active_config_id = staged_mode_config_id_;
    a_args.display_mode = *staged_mode_;
    if (!a_args.test_only) {
      mode_update_commited_ = true;
    }
  }
  // order the layers by z-order
  bool use_client_layer = false; // 是否有GPU合成的圖層
  uint32_t client_z_order = UINT32_MAX;
  std::map<uint32_t, HwcLayer *> z_map;
  for (std::pair<const hwc2_layer_t, HwcLayer> &l : layers_) {
    switch (l.second.GetValidatedType()) {
      case HWC2::Composition::Device:
        z_map.emplace(std::make_pair(l.second.GetZOrder(), &l.second)); // z_map中是按照z-order排序的,Device合成的圖層
        break;
      case HWC2::Composition::Client:
        // Place it at the z_order of the lowest client layer
        use_client_layer = true;
        client_z_order = std::min(client_z_order, l.second.GetZOrder()); // 找到GPU合成圖層中最小的z-order
        break;
      default:
        continue;
    }
  }
  if (use_client_layer)
    z_map.emplace(std::make_pair(client_z_order, &client_layer_)); // GPU合成的Client圖層加入z_map集合
  if (z_map.empty()) // 空集合,沒有要合成的圖層
    return HWC2::Error::BadLayer;
  std::vector<DrmHwcLayer> composition_layers;
  // now that they're ordered by z, add them to the composition
  for (std::pair<const uint32_t, HwcLayer *> &l : z_map) {
    DrmHwcLayer layer;
    l.second->PopulateDrmLayer(&layer); // 把HwcLayer轉為DrmHwcLayer,主要是一些資訊
    int ret = layer.ImportBuffer(GetPipe().device); // 1. 把buffer_handle_t轉為drm buffer object  
                                                    // 2. 做drmPrimeFDToHandle處理
    if (ret) {
      ALOGE("Failed to import layer, ret=%d", ret);
      return HWC2::Error::NoResources;
    }
    composition_layers.emplace_back(std::move(layer));
  }
  /* Store plan to ensure shared planes won't be stolen by other display
   * in between of ValidateDisplay() and PresentDisplay() calls
   */
  current_plan_ = DrmKmsPlan::CreateDrmKmsPlan(GetPipe(), // 建立一個計劃:合成顯示
                                               std::move(composition_layers));
  if (!current_plan_) {
    if (!a_args.test_only) {
      ALOGE("Failed to create DrmKmsPlan");
    }
    return HWC2::Error::BadConfig;
  }
  a_args.composition = current_plan_;
   // 提交/合成/顯示到螢幕 == >DrmAtomicStateManager::ExecuteAtomicCommit
  int ret = GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args); 
  if (ret) {
    if (!a_args.test_only)
      ALOGE("Failed to apply the frame composition ret=%d", ret);
    return HWC2::Error::BadParameter;
  }
  if (mode_update_commited_) {
    staged_mode_.reset();
    vsync_tracking_en_ = false;
    if (last_vsync_ts_ != 0) {
      hwc2_->SendVsyncPeriodTimingChangedEventToClient(
          handle_, last_vsync_ts_ + PrevModeVsyncPeriodNs);
    }
  }
  return HWC2::Error::None;
}

上面出現了一個新的型別

struct DrmHwcLayer {
  buffer_handle_t sf_handle = nullptr;
  hwc_drm_bo_t buffer_info{};
  std::shared_ptr<DrmFbIdHandle> fb_id_handle;
  int gralloc_buffer_usage = 0;
  DrmHwcTransform transform{};
  DrmHwcBlending blending = DrmHwcBlending::kNone;
  uint16_t alpha = UINT16_MAX;
  hwc_frect_t source_crop;
  hwc_rect_t display_frame;
  DrmHwcColorSpace color_space;
  DrmHwcSampleRange sample_range;
  UniqueFd acquire_fence;
  int ImportBuffer(DrmDevice *drm_device);
  bool IsProtected() const {
    return (gralloc_buffer_usage & GRALLOC_USAGE_PROTECTED) ==
           GRALLOC_USAGE_PROTECTED;
  }
};

ImportBuffer呼叫的流程:

int DrmHwcLayer::ImportBuffer(DrmDevice *drm_device) {
  buffer_info = hwc_drm_bo_t{};
  int ret = BufferInfoGetter::GetInstance()->ConvertBoInfo(sf_handle,
                                                           &buffer_info);
  if (ret != 0) {
    ALOGE("Failed to convert buffer info %d", ret);
    return ret;
  }
  fb_id_handle = drm_device->GetDrmFbImporter().GetOrCreateFbId(&buffer_info);
  if (!fb_id_handle) {
    ALOGE("Failed to import buffer");
    return -EINVAL;
  }
  return 0;
}

進而呼叫到相關方法
BufferInfoMapperMetadata::ConvertBoInfo
DrmFbImporter::GetOrCreateFbId

 

DrmAtomicStateManager::CommitFrame方法中應該是最終去顯示內容的邏輯。

 

看一下CommitFrame呼叫棧資訊:

PresentDisplay呼叫棧資訊

ValidateDisplay呼叫棧資訊

 

相關文章