OpenHarmony——Graphic子系统之开机动画 原创 精华

深开鸿
发布于 2022-3-17 14:29
浏览
4收藏

作者:曹璀

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开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-3-17 14:29:49修改
5
收藏 4
回复
举报
1条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

图形子系统的讲解必须支持一波

回复
2022-3-17 18:23:55
回复
    相关推荐