天天看點

OpenHarmony——Graphic子系統之開機動畫

作者:曹璀

1 簡介

标準系統提供了圖形接口能力和視窗管理接口能力, 支援應用程式架構子系統和ACE等子系統使用。故可以根據不同硬體系統選擇編譯不同子產品,選擇适配輕量系統或者标準系統。圖形子系統主要包括UI元件、布局、動畫、字型、輸入事件、視窗管理、渲染繪制等子產品。

OpenHarmony——Graphic子系統之開機動畫

以下主要分析标準系統能力。代碼版本是OpenHarmony3.1版本。

開機動畫是鴻蒙系統啟動後,運作的第一個和圖形渲染相關的程序,相關依賴相對獨立便于分析,是分析圖形子系統比較好的切入點。圖形子系統主要依賴視窗、surface、render service。

目錄

./foundation/graphic
|-- standard
|   |-- figures
|   |-- frameworks						# 架構代碼目錄
|   |   |-- animation_server
|   |   |-- bootanimation				# 開機動畫
|   |   |-- dumper
|   |   |-- fence
|   |   |-- surface						# 渲染表面
|   |   |-- vsync
|   |   |-- wm
|   |   |-- wmserver
|   |   |-- wmservice
|   |-- interfaces
|   |   |-- innerkits
|   |   `-- kits
|   |-- rosen
|   |   |-- build
|   |   |-- doc
|   |   |-- include
|   |   |-- lib
|   |   |-- modules
|   |   |   |-- 2d_graphics				# 2維圖形
|   |   |   |-- animation				# 動畫
|   |   |   |-- composer				# 渲染合成器
|   |   |   |-- effect
|   |   |   |-- render_service			# 渲染服務端
|   |   |   |-- render_service_base		# 渲染基礎能力
|   |   |   |-- render_service_client	# 渲染用戶端
|   |   |   `-- utils
|   |   `-- tools
|   `-- utils
|-- surface
|-- ui
           

Graphic子系統 提供了圖形接口能力和視窗管理接口能力, 支援應用程式架構子系統和ACE等子系統使用。支援所有運作标準系統的裝置使用。

其主要的結構如下圖所示:

OpenHarmony——Graphic子系統之開機動畫
  • Surface

    圖形緩沖區管理接口,負責管理圖形緩沖區和高效便捷的輪轉緩沖區。依賴Display driver開辟buffer及buffer管理。

  • Vsync Client

    垂直同步信号管理接口,負責管理所有垂直同步信号注冊和響應。

  • WindowManager

    視窗管理器接口,負責建立和管理視窗。

  • IPC/RPC通信

    程序間通信協定,支援渲染用戶端和服務端建立連接配接、申請buffer、重新整理buffer等

  • Render Service ohos

    render service适配ohos的部分,屬于render service的基礎能力,其中适配了ohos的render service client及IPC代理能力

  • Compositor

    圖像合成送顯,依賴Display driver和Surface,管理buffer及送顯。

  • Input Manager

    多模輸入子產品,負責接收事件輸入

  • Skia

    第三方渲染接口,支援CPU和GPU渲染情況下的畫布繪制

  • Render Service Base

    render service的基礎能力,包含Render Service ohos

主要流程

CPU渲染

申請buffer

OpenHarmony——Graphic子系統之開機動畫

建立畫布

OpenHarmony——Graphic子系統之開機動畫

說明:GPU渲染時,擷取buffer沒有經過render service server,而是在client端用skia完成,在用egl做完顯示視窗的初始化動作後,開始繪制圖像。可參考

OpenGLES 與 EGL 基礎概念 - 知乎 (zhihu.com)

源碼分析

啟動

服務啟動配置graphic.cfg在foundation/graphic/standard/graphic.cfg目錄,分别啟動了bootanimation和render_service程序。

{
    "jobs" : [{
            "name" : "post-fs-data",
            "cmds" : [
                "start render_service",
                "start bootanimation"
            ]
        }, {
            "name" : "init",
            "cmds" : [
                "chmod 666 /dev/mali0",
                "chown system graphics /dev/mali0"
            ]
        }
    ],
    "services" : [{
            "name" : "render_service",						# 渲染服務端
            "path" : ["/system/bin/render_service"],
            "uid" : "root",
            "gid" : ["system", "shell", "uhid", "root"]
        }, {
            "name" : "bootanimation",						# 開機啟動程序
            "path" : ["/system/bin/bootanimation"],
            "once" : 1,
            "uid" : "root",
            "gid" : ["system", "shell", "uhid", "root"]
        }
    ]
}

           

初始化

void BootAnimation::Init(int32_t width, int32_t height)
{
    windowWidth_ = width;
    windowHeight_ = height;
	
    InitBootWindow();	// 建立啟動視窗
    InitRsSurface();	// 初始化surface
    InitPicCoordinates();

    std::vector<uint32_t> freqs;
    VsyncHelper::Current()->GetSupportedVsyncFrequencys(freqs);
    if (freqs.size() >= 0x2) {
        freq_ = freqs[1];
    }

    UnzipFile(BOOT_PIC_ZIP, DST_FILE_PATH);	// 解壓動畫壓縮包
    CountPicNum(DST_FILE_PATH.c_str(), maxPicNum_); // 計算圖檔數量

    Draw();	// 開始繪制
    PostTask(std::bind(&BootAnimation::CheckExitAnimation, this), EXIT_TIME);
}

           

說明:

  • Init函數會初始化surface。
  • InitBootWindow建立啟動視窗,通過WindowScene調用WindowImpl建立RSSurfaceNode對象。
  • RSSurfaceNode對象可在InitRsSurface中建立surface。
  • UnzipFile輸入參數都是固定的,分别為zip包和輸出目錄。
  • CountPicNum會對輸出目錄下的圖檔進行統計。
  • Draw對解壓出來的開機圖檔進行繪制渲染。
  • PostTask設定開機動畫結束退出回調。
void BootAnimation::InitRsSurface()
{
    rsSurface_ = OHOS::Rosen::RSSurfaceExtractor::ExtractRSSurface(window_->GetSurfaceNode());
    if (rsSurface_ == nullptr) {
        LOG("rsSurface is nullptr");
        return;
    }
#ifdef ACE_ENABLE_GL
    rc_ = OHOS::Rosen::RenderContextFactory::GetInstance().CreateEngine();
    rc_->InitializeEglContext();
    rsSurface_->SetRenderContext(rc_);
#endif
}
           

說明:

  • ExtractRSSurface則是在InitBootWindow獲得的RSSurfaceNode的基礎上擷取surface。

繪制流程

1.擷取RSSurface,當是CPU渲染的時候擷取的是個RSSurfaceOhosRaster對象,GPU渲染時是個RSSurfaceOhosGl對象,這個地方擷取RSRenderServiceConnectionProxy就是IPC機制的應用。

bool RSSurfaceNode::CreateNodeAndSurface(const RSSurfaceRenderNodeConfig& config)
{
    // RSIRenderClient::CreateRenderServiceClient()擷取了一個RSRenderServiceClient對象
    surface_ = std::static_pointer_cast<RSRenderServiceClient>(RSIRenderClient::CreateRenderServiceClient())
                   ->CreateNodeAndSurface(config);
    return (surface_ != nullptr);
}
           
std::shared_ptr<RSSurface> RSRenderServiceClient::CreateNodeAndSurface(const RSSurfaceRenderNodeConfig& config)
{
    // 得到RSRenderServiceConnectionProxy
    auto renderService = RSRenderServiceConnectHub::GetRenderService();
    if (renderService == nullptr) {
        return nullptr;
    }
    // 用得到的RSRenderServiceConnectionProxy建立ProducerSurface對象
    sptr<Surface> surface = renderService->CreateNodeAndSurface(config);

#ifdef ACE_ENABLE_GL
    // GPU render
    std::shared_ptr<RSSurface> producer = std::make_shared<RSSurfaceOhosGl>(surface);
#else
    // CPU render
    std::shared_ptr<RSSurface> producer = std::make_shared<RSSurfaceOhosRaster>(surface);
#endif
    return producer;
}
           

2.繪制開機圖檔,逐個圖檔加載渲染,渲染時flush的過程參考擷取buffer的過程,flush會把buffer發送到render service server端合成送顯。

void BootAnimation::OnDraw(SkCanvas* canvas)
{
    std::string imgPath = BOOT_PIC_DIR + std::to_string(bootPicCurNo_) + ".jpg";
    // pic is named from 0
    if (bootPicCurNo_ != (maxPicNum_ - 1)) {
        bootPicCurNo_ = bootPicCurNo_ + 1;
    }
    std::unique_ptr<FILE, decltype(&fclose)> file(fopen(imgPath.c_str(), "rb"), fclose);

    auto skData = SkData::MakeFromFILE(file.get());

    auto codec = SkCodec::MakeFromData(skData);
    sk_sp<SkImage> image = SkImage::MakeFromEncoded(skData); // 通過skia轉換圖像資料
	// 在畫布上繪制
    SkPaint backPaint;
    backPaint.setColor(SK_ColorBLACK);
    canvas->drawRect(SkRect::MakeXYWH(0.0, 0.0, windowWidth_, windowHeight_), backPaint);
    SkPaint paint;
    SkRect rect;
    rect.setXYWH(pointX_, pointY_, realWidth_, realHeight_);
    canvas->drawImageRect(image.get(), rect, &paint);
	// 把畫布資料發送到render service server端,并在server端送顯
    rsSurface_->FlushFrame(framePtr_);
}
           

說明:

  • MakeFromFILE加載圖檔。
  • MakeFromEncoded對加載圖檔資料進行轉化。
  • SkCanvas通過drawRect和drawImageRect繪制圖像。
  • FlushFrame把畫布資料送顯,經過IPC通信會把buffer資訊傳到server端BufferQueue。總體過程參考申請buffer的過程。
GSError BufferQueue::FlushBuffer(int32_t sequence, const BufferExtraData &bedata,
    int32_t fence, const BufferFlushConfig &config)
{
    ScopedBytrace bufferIPCSend("BufferIPCSend");
    sret = DoFlushBuffer(sequence, bedata, fence, config);
    if (sret == GSERROR_OK) {
        if (listener_ != nullptr) {
            ScopedBytrace bufferIPCSend("OnBufferAvailable");
            listener_->OnBufferAvailable();
        } else if (listenerClazz_ != nullptr) {
            ScopedBytrace bufferIPCSend("OnBufferAvailable");
            listenerClazz_->OnBufferAvailable();
        }
    }
    return sret;
}
           

說明:

  • DoFlushBuffer會調用display驅動FlushCache。
  • OnBufferAvailable調用的是RSRenderServiceListener::OnBufferAvailable,進行可用buffer計量,同時會通知Vsync可以同步。

3.遞歸重新整理圖檔,把BootAnimation::Draw注冊成回調函數,在DispatchMain中循環調用,達到逐個圖檔渲染的效果。

void BootAnimation::RequestNextVsync()
{
    if (needCheckExit) {
        CheckExitAnimation();
    }
    struct FrameCallback cb = {
        .frequency_ = freq_,
        .timestamp_ = 0,
        .userdata_ = nullptr,
        .callback_ = std::bind(&BootAnimation::Draw, this),
    };
    // 注冊回調
    GSError ret = VsyncHelper::Current()->RequestFrameCallback(cb);
}
           

說明:

  • 會通過VsyncHelper注冊回調,定時調用Draw。

總結

開機動畫的CPU渲染過程是從render_service擷取buffer,在client端用buffer+skia建立canvas,進行繪制。逐個圖檔flush到render service server端,在server端完成送顯。

更多原創内容請關注:深開鴻技術團隊

入門到精通、技巧到案例,系統化分享HarmonyOS開發技術,歡迎投稿和訂閱,讓我們一起攜手前行共建鴻蒙生态。

想了解更多關于鴻蒙的内容,請通路:

51CTO和華為官方合作共建的鴻蒙技術社群

https://ost.51cto.com/#bkwz

繼續閱讀