OpenHarmony—Graphic子系统之开机动画

系统 OpenHarmony
Graphic子系统 提供了图形接口能力和窗口管理接口能力, 支持应用程序框架子系统和ACE等子系统使用。支持所有运行标准系统的设备使用。

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

简介

标准系统提供了图形接口能力和窗口管理接口能力, 支持应用程序框架子系统和ACE等子系统使用。故可以根据不同硬件系统选择编译不同模块,选择适配轻量系统或者标准系统。图形子系统主要包括UI组件、布局、动画、字体、输入事件、窗口管理、渲染绘制等模块。

以下主要分析标准系统能力。代码版本是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等子系统使用。支持所有运行标准系统的设备使用。

其主要的结构如下图所示:

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

创建画布

说明:GPU渲染时,获取buffer没有经过render service server,而是在client端用skia完成,在用egl做完显示窗口的初始化动作后,开始绘制图像。

源码分析

启动

服务启动配置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端完成送显。

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-10-12 15:14:08

开机动画鸿蒙

2023-07-10 16:06:50

鸿蒙检测锁屏应用

2011-09-09 14:49:31

Windows7开机动画

2023-07-18 14:05:30

鸿蒙

2023-04-12 15:31:11

系统服务管理鸿蒙

2022-02-17 20:57:07

OpenHarmon操作系统鸿蒙

2021-09-18 14:40:37

鸿蒙HarmonyOS应用

2021-11-08 15:04:47

鸿蒙HarmonyOS应用

2022-01-06 16:17:58

鸿蒙HarmonyOS应用

2021-12-17 16:42:09

鸿蒙HarmonyOS应用

2022-01-10 15:30:11

鸿蒙HarmonyOS应用

2021-11-18 10:28:03

鸿蒙HarmonyOS应用

2022-05-10 11:17:27

电话子系统数据服务模块

2023-06-28 15:00:02

开源鸿蒙输入系统架构

2022-05-24 15:46:51

Wi-FiSTA模式

2022-01-13 10:11:59

鸿蒙HarmonyOS应用

2022-01-20 11:04:31

Linux DRMOpenHarmon鸿蒙

2021-09-13 15:15:18

鸿蒙HarmonyOS应用

2023-04-06 09:14:11

多模输入子系统鸿蒙

2022-02-23 15:27:46

鸿蒙开机动画环境搭建
点赞
收藏

51CTO技术栈公众号