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

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

必讀:

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


 

前言

Android原始碼中有包含drm_hwcomposer:/external/drm_hwcomposer/

drm_hwcomposer 這個過程下的程式碼架構變化還是很頻繁的,我這裡分析直接去 drm_hwcomposer 的官方地址抓取最新的code來做分析了

 

解析

這個工程編譯後會產生 shared library :/vendor/lib/hw/hwcomposer.drm.so

drm_hwcomposer作為一個HAL module,其寫作實現還是遵循了舊有的Android HAL Module的介面實現規則。

 

看看一些結構體的定義以及他們之間的關係:

結構體hw_device_t的定義

[/hardware/libhardware/include/hardware/hardware.h]
typedef struct hw_device_t {
    tag; /** tag must be initialized to HARDWARE_DEVICE_TAG */
    uint32_t version;
    struct hw_module_t* module;
    uint64_t reserved[12];
    int (*close)(struct hw_device_t* device);
} hw_device_t;

結構體hwc2_device_t的定義

[/hardware/libhardware/include/hardware/hwcomposer2.h]
typedef struct hwc2_device {
/* Must be the first member of this struct, since a pointer to this struct
     * will be generated by casting from a hw_device_t* */
    struct hw_device_t common;
    void (*getCapabilities)(struct hwc2_device* device, uint32_t* outCount,
            int32_t* /*hwc2_capability_t*/ outCapabilities);
    hwc2_function_pointer_t (*getFunction)(struct hwc2_device* device,
            int32_t /*hwc2_function_descriptor_t*/ descriptor);
} hwc2_device_t;

結構體DrmHwc2Device的定義

[drm-hwcomposer/hwc2_device/hwc2_device.cpp]

struct Drmhwc2Device : hwc2_device {
    DrmHwcTwo drmhwctwo;
};

按照結構體定義的理解,我們可以認為三個型別,具有如下繼承關係

本文作者@二的次方  2022-07-05 釋出於部落格園

看一個關鍵的static方法 HookDevOpen,該方法中會去例項化一個Drmhwc2Device物件,其中去建立了一個DrmHwcTwo物件

[drm-hwcomposer/hwc2_device/hwc2_device.cpp]
static int HookDevOpen(const struct hw_module_t *module, const char *name,
                       struct hw_device_t **dev) {
  ...
  auto ctx = std::make_unique<Drmhwc2Device>();
  if (!ctx) {
    ALOGE("Failed to allocate DrmHwcTwo");
    return -ENOMEM;
  }

  ctx->common.tag = HARDWARE_DEVICE_TAG;
  ctx->common.version = HWC_DEVICE_API_VERSION_2_0;
  ctx->common.close = HookDevClose;
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
  ctx->common.module = (hw_module_t *)module;
  ctx->getCapabilities = HookDevGetCapabilities;
  ctx->getFunction = HookDevGetFunction;

  *dev = &ctx.release()->common;

  return 0;
}

在HWC HAL Service啟動時,初始化階段openDeviceWithAdapter中去呼叫了open函式,就是call到了HookDevOpen可以參見:

/hardware/interfaces/graphics/composer/2.1/utils/passthrough/include/composer-passthrough/2.1/HwcLoader.h

 

DrmHwcTwo構造時做了什麼工作?

[drm-hwcomposer/hwc2_device/DrmHwcTwo.cpp]
DrmHwcTwo::DrmHwcTwo() : resource_manager_(this){}; // DrmHwcTwo的建構函式定義

[drm-hwcomposer/hwc2_device/DrmHwcTwo.h]
ResourceManager resource_manager_; // DrmHwcTwo類中的成員

很簡單,就是去例項化一個ResourceManager物件,其建構函式中處理初始化了uevent_listener等成員,也沒啥了
frontend_interface_指向DrmHwcTwo物件

[drm-hwcomposer/drm/ResourceManager.cpp]
ResourceManager::ResourceManager(
    PipelineToFrontendBindingInterface *p2f_bind_interface)
    : frontend_interface_(p2f_bind_interface) {
  if (uevent_listener_.Init() != 0) {
    ALOGE("Can't initialize event listener");
  }
}

 

到這裡,我大概可以看到ResourceManager是個非常重要的核心類,他應該管理著DRM的資源。
他的定義中也定義了void Init();函式,那這個初始化函式是什麼時候呼叫的呢? 

 

 在這篇博文中:Android 12(S) 影像顯示系統 - SurfaceFlinger的啟動和訊息佇列處理機制(四)
講解SurfaceFlinger的初始化過程時,設定callback給HWC,層層傳遞後就會呼叫到DrmHwcTwo::RegisterCallback
進而呼叫到了 resource_manager_.Init();

 

ResourceManager 初始化到底初始化了什麼呢?

 本文作者@二的次方  2022-07-05 釋出於部落格園

 

[drm-hwcomposer/drm/ResourceManager.cpp]
void ResourceManager::Init() {
  if (initialized_) {
    ALOGE("Already initialized"); // 已經初始化了,避免重複初始化
    return;
  }
  char path_pattern[PROPERTY_VALUE_MAX];
  // Could be a valid path or it can have at the end of it the wildcard %
  // which means that it will try open all devices until an error is met.
  int path_len = property_get("vendor.hwc.drm.device", path_pattern,
                              "/dev/dri/card%");
  if (path_pattern[path_len - 1] != '%') {
    AddDrmDevice(std::string(path_pattern));
  } else {
    path_pattern[path_len - 1] = '\0';
    for (int idx = 0;; ++idx) {
      std::ostringstream path;
      path << path_pattern << idx;
      struct stat buf {};
      if (stat(path.str().c_str(), &buf) != 0)
        break;
      if (DrmDevice::IsKMSDev(path.str().c_str())) {
        AddDrmDevice(path.str());
      }
    }
  }
    /**上面一大坨程式碼,簡單理解就是找到DRM的裝置節點,然後開啟它,在我的裝置上是/dev/dri/card0 */
    /** AddDrmDevice中去初始化DRM各種各樣的資源 **/

  char scale_with_gpu[PROPERTY_VALUE_MAX];
  property_get("vendor.hwc.drm.scale_with_gpu", scale_with_gpu, "0");
  scale_with_gpu_ = bool(strncmp(scale_with_gpu, "0", 1));// 使用GPU縮放的標誌
  if (BufferInfoGetter::GetInstance() == nullptr) {
    ALOGE("Failed to initialize BufferInfoGetter"); 
       // 初始化BufferInfoGetter,用於從Gralloc Mapper中獲取buffer的屬性資訊
    return;
  }
  uevent_listener_.RegisterHotplugHandler([this] {// 註冊熱插拔的回撥
    const std::lock_guard<std::mutex> lock(GetMainLock());
    UpdateFrontendDisplays();
  });
  UpdateFrontendDisplays();//這裡會Send Hotplug Event To Client,SF會收到一次onComposerHalHotplug
                                                  // attached_pipelines_的初始化、更新
  initialized_ = true; // 設定標記,表明已經初始化過了
}

重點看幾個函式

 

AddDrmDevice 

[drm-hwcomposer/drm/ResourceManager.cpp]
int ResourceManager::AddDrmDevice(const std::string &path) {
    auto drm = std::make_unique<DrmDevice>();// 建立DrmDevice物件
    int ret = drm->Init(path.c_str());//初始化DrmDevice,path一般就是/dev/dri/card0
    drms_.push_back(std::move(drm));// 儲存到drms_這個vector中
    return ret;
}

一個重要的角色登場:DrmDevice,如下其定義

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

 

DrmDevice的建構函式中建立一個 DrmFbImporter 物件

[drm-hwcomposer/drm/DrmDevice.cpp]
DrmDevice::DrmDevice() {
  drm_fb_importer_ = std::make_unique<DrmFbImporter>(*this);
}

 

DrmDevice::Init
完成了獲取DRM資源的初始化,CRTC、Encoder、Connector、Plane這些資源都獲取到了 

 

[drm-hwcomposer/drm/DrmDevice.cpp]
auto DrmDevice::Init(const char *path) -> int {
    /* TODO: Use drmOpenControl here instead */
    fd_ = UniqueFd(open(path, O_RDWR | O_CLOEXEC)); //開啟裝置,一般是/dev/dri/card0
    if (!fd_) {
        // NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme
        ALOGE("Failed to open dri %s: %s", path, strerror(errno));//開啟失敗,返回錯誤
        return -ENODEV;
    }
    // 設定DRM_CLIENT_CAP_UNIVERSAL_PLANES,獲取所有支援的Plane資源
    int ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
    if (ret != 0) {
        ALOGE("Failed to set universal plane cap %d", ret);
        return ret;
    }
    // 設定DRM_CLIENT_CAP_ATOMIC,告知DRM驅動該應用程式支援Atomic操作
    ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_ATOMIC, 1);
    if (ret != 0) {
        ALOGE("Failed to set atomic cap %d", ret);
        return ret;
    }
    // 設定開啟 writeback
#ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS
    ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
    if (ret != 0) {
        ALOGI("Failed to set writeback cap %d", ret);
    }
#endif
    uint64_t cap_value = 0;
    if (drmGetCap(GetFd(), DRM_CAP_ADDFB2_MODIFIERS, &cap_value) != 0) {
        ALOGW("drmGetCap failed. Fallback to no modifier support.");
        cap_value = 0;
    }
    HasAddFb2ModifiersSupport_ = cap_value != 0;//是否支援Add Fb2 Modifiers
    // 設定master mode
    drmSetMaster(GetFd());
    if (drmIsMaster(GetFd()) == 0) {
        ALOGE("DRM/KMS master access required");
        return -EACCES;
    }
    // 獲取 drmModeRes
    auto res = MakeDrmModeResUnique(GetFd());
    if (!res) {
        ALOGE("Failed to get DrmDevice resources");
        return -ENODEV;
    }
    // 最小和最大的解析度
    min_resolution_ = std::pair<uint32_t, uint32_t>(res->min_width,
                                                    res->min_height);
    max_resolution_ = std::pair<uint32_t, uint32_t>(res->max_width,
                                                    res->max_height);
    // 獲取所有的CRTC,建立DrmCrtc物件,並加入crtcs_這個vector<unique_ptr<DrmCrtc>>
    for (int i = 0; i < res->count_crtcs; ++i) {
        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
        auto crtc = DrmCrtc::CreateInstance(*this, res->crtcs[i], i);
        if (crtc) {
            crtcs_.emplace_back(std::move(crtc));
        }
    }
    // 獲取所有的Encoder,建立DrmEncoder物件,並加入encoders_這個vector<unique_ptr<DrmEncoder>> 
    for (int i = 0; i < res->count_encoders; ++i) {
        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
        auto enc = DrmEncoder::CreateInstance(*this, res->encoders[i], i);
        if (enc) {
            encoders_.emplace_back(std::move(enc));
        }
    }
    // 獲取所有的Connector,建立DrmConnector物件,並加入connectors_這個vector<unique_ptr<DrmConnector>>
       // 或放入writeback_connectors_這個vector中
    for (int i = 0; i < res->count_connectors; ++i) {
        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
        auto conn = DrmConnector::CreateInstance(*this, res->connectors[i], i);
        if (!conn) {
            continue;
        }
        // wirteback如何理解?
        if (conn->IsWriteback()) {
            writeback_connectors_.emplace_back(std::move(conn));
        } else {
            connectors_.emplace_back(std::move(conn));
        }
    }
    // 獲取drmModePlaneRes
    auto plane_res = MakeDrmModePlaneResUnique(GetFd());
    if (!plane_res) {
        ALOGE("Failed to get plane resources");
        return -ENOENT;
    }
    // 獲取所有的Plane,建立DrmPlane物件,並加入planes_這個vector<unique_ptr<DrmPlane>> 
    for (uint32_t i = 0; i < plane_res->count_planes; ++i) {
        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
        auto plane = DrmPlane::CreateInstance(*this, plane_res->planes[i]);
        if (plane) {
            planes_.emplace_back(std::move(plane));
        }
    }
    return 0;
}

 

 回到ResourceManager::Init()中,最後呼叫了一次UpdateFrontendDisplays()

 

[drm-hwcomposer/drm/ResourceManager.cpp]
void ResourceManager::UpdateFrontendDisplays() {
    // internal displays放前面,external放後面的排序connectors
    auto ordered_connectors = GetOrderedConnectors();
    for (auto *conn : ordered_connectors) {
        conn->UpdateModes();
        bool connected = conn->IsConnected();
        bool attached = attached_pipelines_.count(conn) != 0; // 判斷map中是否存在key為conn的元素
        if (connected != attached) {
            ALOGI("%s connector %s", connected ? "Attaching" : "Detaching",
                        conn->GetName().c_str());
            if (connected) {// connected==true and attached == false,繫結資源
                auto pipeline = DrmDisplayPipeline::CreatePipeline(*conn);
                if (pipeline) {
                    //frontend_interface_指向DrmHwcTwo物件
                    frontend_interface_->BindDisplay(pipeline.get());
                    attached_pipelines_[conn] = std::move(pipeline);//存入map
                }
            } else { // connected==false and attached == true,解綁資源
                auto &pipeline = attached_pipelines_[conn];
                frontend_interface_->UnbindDisplay(pipeline.get());
                attached_pipelines_.erase(conn);// map中刪除
            }
        }
    }
    frontend_interface_->FinalizeDisplayBinding();
}

 


DrmHwcTwo中的兩個成員: 

[drm-hwcomposer/hwc2_device/DrmHwcTwo.h]  

std::map<hwc2_display_t, std::unique_ptr<HwcDisplay>> displays_;
std::map<DrmDisplayPipeline *, hwc2_display_t> display_handles_;

 

出現了三個函式:
DrmHwcTwo::BindDisplay
主要是建立HwcDisplay,
DrmHwcTwo::UnbindDisplay
刪除HwcDisplay
DrmHwcTwo::FinalizeDisplayBinding
完成顯示繫結,大概看是Creating null-display for headless mode , send hotplug events to the client,displays_for_removal_list_

本文作者@二的次方  2022-07-05 釋出於部落格園

重點看一看建立HwcDisplay和SetPipeline做了啥子吧

HwcDisplay的建構函式很簡單,就是初始化一些成員

[drm-hwcomposer/hwc2_device/HwcDisplay.cpp]
HwcDisplay::HwcDisplay(hwc2_display_t handle, HWC2::DisplayType type,
                       DrmHwcTwo *hwc2)
    : hwc2_(hwc2), // 關聯的DrmHwcTwo物件
      handle_(handle),     // typedef uint64_t hwc2_display_t;   handle本質就是一個uint64_t整數值
      type_(type), // Physical 物理螢幕
      color_transform_hint_(HAL_COLOR_TRANSFORM_IDENTITY) {
  // clang-format off
  color_transform_matrix_ = {1.0, 0.0, 0.0, 0.0,
                             0.0, 1.0, 0.0, 0.0,
                             0.0, 0.0, 1.0, 0.0,
                             0.0, 0.0, 0.0, 1.0};
  // clang-format on
}

HwcDisplay::SetPipeline

[drm-hwcomposer/hwc2_device/HwcDisplay.cpp]
void HwcDisplay::SetPipeline(DrmDisplayPipeline *pipeline) {
    Deinit(); 
    pipeline_ = pipeline;
    if (pipeline != nullptr || handle_ == kPrimaryDisplay) {
        Init(); // 初始化
        hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ true);
    } else {
        hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ false);
    }
}

 

再看HwcDisplay::Init

[drm-hwcomposer/hwc2_device/HwcDisplay.cpp]
HWC2::Error HwcDisplay::Init() {
    ChosePreferredConfig(); //選擇一個最佳的config,然後SetActiveConfig
    // VSYNC相關的程式碼省略不看

    if (!IsInHeadlessMode()) {//設定後端 backend
        ret = BackendManager::GetInstance().SetBackendForDisplay(this);
        if (ret) {
            ALOGE("Failed to set backend for d=%d %d\n", int(handle_), ret);
            return HWC2::Error::BadDisplay;
        }
    }

    client_layer_.SetLayerBlendMode(HWC2_BLEND_MODE_PREMULTIPLIED);

    return HWC2::Error::None;
}

 

又出現了新的名詞: Backend

誰是 front end ? 誰是back end ?  扮演的角色功能分別是什麼? 

初步看起來貌似是:
front end 對外提供呼叫的介面,外部使用者呼叫 front end 暴漏出的介面來呼叫某一功能;
back end 內部的實現邏輯,是前端介面功能的內部實現,是真正做事的地方;

本文作者@二的次方  2022-07-05 釋出於部落格園

HwcDisplay類中有成員  == HwcLayer client_layer_,有個疑問 這個client layer 是如何與SF中的GPU合成的圖層關聯起來的?

他是一個特例,特殊的專門的的layer,轉用於處理顯示 CLIENT -- GPU 合成的 buffer,  SetClientTarget傳遞buffer資料給他

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

 

小結


以上內容,主要講述分析的是開機階段,DRM HWC的初始化的一些流程。大概就是獲取DRM的資源,建立並初始化必要模組。

 

相關文章