本文作者:江苏润和软件股份有限公司 郎建中
一、总体介绍 由AppSpawn进程Fork出来的AbilityMain进程 是JS FA应用native的实现载体。其中包含了Ability的生命周期管理、JerryScript引擎初始化和注册内部JS类、JS APP文件读取和在Jerry里运行、以及调用Graphic UI控件进行窗口绘制。架构图如下:
本文主要专注在窗口的创建、Surface创建使用和在WMS中进行合成送显的过程。对于BufferQueue、BufferQueueProducer、BufferQueueConsumer的关系在另一篇文章《Camera中的Surface使用》有介绍。 约束:本文中所有场景介绍都是指轻量设备侧的鸿蒙系统。
二、代码目录 涉及的代码目录: foundation/ace :JS应用开发框架代码,包括了JerryScipt引擎等 foundation/graphic/lite :图形框架子系统,包含了ui和WMS、IMS等 foundation/aafwk/frameworks/ability_lite :AbilityMain框架和主程序
三、代码分析 3.1 AbilityMain进程创建Window和Surface的过程
上面时序图中,蓝色部分是运行在AbilityMain进程中,绿色部分是运行在wms_server进程中。AbilityMain进程就是JS应用的本地实现,wms_server进程就是窗口管理服务(负责窗口管理和合成、IMS)。 在AbilityMain进程中通过调用Window::CreateWindow()函数来创建窗口。代码如下: Window类是WindowImpl的基类,只实现了CreateWindow和DestoryWindow两个静态方法。 在CreateWindow中new出来WindowImp实例,并且调用了Create()方法,在Create()完成后,将这个实例通过AddToDisplay()方法加入到RenderManager中,这里不多解析。我们来看看Create()做了什么。
IWindowsManager::GetInstance()得到的就是LiteProxyWindowsManager,这个类负责AbilityMain进程内的窗口管理,负责创建IWindow实例。
上面的代码可以看出:LiteProxyWindowManager通过LiteWMRequestor创建出来 LiteWinRequestor对象,然后用这个对象构造了LiteProxyWindow对象(也就是IWindow的实现)。 我们往下看LiteWMRequestor::CreateWindow()的代码:
这个Invoke最终在WMS进程中被LiteWMS::WMSRequestHandle()处理:
注意上面的代码,在创建成功后,会把 LiteWindow的窗口ID通过IPC返回给客户进程,这个ID将被LiteWinRequestor所持有。
在LiteWM中,CreateWindow方法构造了LiteWindow对象,并把这个对象放入了winList_链表中。LiteWM负责管理窗口,后续会解释通过winList_链表进行窗口的合成和送显。 我们在看下LiteWindow::CreateSurface()过程:
上面的代码看到调用了Surface::CreateSurface()方法,这个方法的过程在《Camera中的Surface使用过程》中有详细介绍,这里不多说了,主要是以下几点: 1、通过BufferManager调用Gralloc HAL进行初始化函数指针 2、构造BufferQueue对象 3、通过BufferQueue对象构造BufferQueueProducer和BufferQueueConsumer 在创建Surface后,设置BufferQueue的size为2,并立即请求了Surface内存,并且保存在backBuf_中,后续会用到。这里Queue Size为2表示一个Surface给backBuf_,另一个Surface留给AbilityMain进程的窗口绘制用,下面会介绍。
我们回头看下LiteWMRequestor::CreateWindow函数在调用Invoke的时候设置了回调函数,如下: –>
在Callback()回调函数中,通过IPC拿到远程创建的LiteWindow的ID,然后构造了LiteWinRequestor对象,这个对象将被LiteProxyWindow所持有(见LiteProxyWindowManager::CreateWindow()函数)。
这里总结下几个类的关系:
- LiteProxyWindowManager 负责创建IWindow(也就是LiteProxyWindow)
- LiteWMRequestor 负责与WMS通讯,其对应的类是wms_server进程中的LiteWMS类
- LiteWinRequestor 负责与WMS通讯,其对应的类是wms_server进程中的LiteWM类
- LiteProxyWindow 就是IWindow的实例化类,IWindow最终被WindowImpl持有,是WindowImpl实现窗口操作的代理。LiteProxyWindow本身持有了LiteWinRequestor类,通过LiteWinRequestor与LiteWM通讯。LiteWinRequestor持有了远端LiteWindow的ID。
- LiteWindow 就是WMS进程中创建出来的窗口类,与客户进程中的WindowImpl对应。
综上所述: 1、在AbilityMain进程中的WindowImpl代表一个窗口,其对应的WMS服务中是LiteWindow类。他们之间是通过LiteWinRequestor类来通讯的。在WMS进程中,LiteWM负责窗口管理,管理的对象就是LiteWindow。 2、LiteWindow被创建后,立即创建了BufferQueue、BufferQueueProducer,BufferQueueConsumer用以管理窗口的显存。 3、LiteWindow被创建后,立即申请了Surface显存,这个Surface将被用作后续Window里面的控件的绘制内存。
3.2 WindowImpl渲染中获取Surface内存地址的过程 在上面的创建窗口的过程中,窗口创建成果后,会把自己加入到RenderManager类中,WindowImpl::AddToDisplay()的代码如下: 这个RenderManager类就是负责所有的窗口的绘制Task的管理,它会启动一个RenderTask绘制所有注册的WindowImpl窗口。绘制的方法是调用WindowImpl::Render()方法,我们从这里开始分析:
上面的代码分两部分: 1、执行UpdateHalDisplayBuffer()。
这里iWindow_就是上节提到的LiteProxyWindow类对象,持有了LiteWinRequestor类对象,通过IPC与WMS进程中的LiteWM通讯。
–>
上面的代码通过IPC调用跟LiteWM类通讯,通过传递的Window ID,最终调用到LiteWindow::GetSurface(),并且返回了Surface就是第一节中创建的Surface。这里我们主要看下Callback函数。
–>
在Callback中,我们看到根据IPC返回值构建了SurfaceImpl本地对象。这个对象是通过SurfaceImpl::GenericSurfaceByIpcIo()函数构建的。这个方式与上一节中构建的SurfaceImpl对象不同,是构建的SurfaceImpl的远程代理对象。这与《Camera中的Surface使用》中的Surface使用方法不同。 我们看下SurfaceImpl::GenericSurfaceByIpcIo()函数实现:
我们看到通过GenericSurfaceByIpcIo()函数构建的SurfaceImpl的构造函数是带参数的,这种方式将赋值IsConsumer_为false,导致SurfaceImpl::Init()函数的走向不同。我们看下Init函数:
在IsConsumer_为false的情况下,走了else的逻辑,因此创建了 BufferClientProducer类。这个类是远程的WMS进程中的 BufferQueueProducer的代理类。因此创建的SurfaceImpl只是持有了远程代理类BuferClientProducer对象,其本身就是运行在WMS中的LiteWindow所持有的SurfaceImpl的远程代理。 我们回到LiteProxyWindow::GetSurface()函数:
在winRequestor_->GetSurface()调用后,我们知道surface就是SurfaceImpl对象并且是远程的WMS进程中的Surface对象的代理。然后用这个对象构造了LiteProxySurface对象返回给WindowImpl::UpdateHalDisplayBuffer()函数作为ISurface对象。下面我们看看WindowImpl::UpdateHalDisplayBuffer()函数中的ISurface->Lock()的实现:
上面代码中的surface_就是 SurfaceImpl对象,也是WMS进程中的LiteWindow持有的SurfaceImpl的远程代理。因此RequestBuffer()调用最终会走到WMS进程中,从窗口绑定的Surface中申请SurfaceBuffer,最终会走到BufferQueue去申请,这里不再详细描述。 因此buufer_就是从WMS进程中分配到的SurfaceBuffer,类型是SurfaceBufferImpl。通过获取这块buffer的虚拟地址、物理地址等信息返回到WindowImpl::UpdateHalDisplayBuffer()函数。 我们再回到WindowImpl::UpdateHalDisplayBuffer()函数:
在Lock调用获取buffer的地址后,下面的红框中的代码把这个地址信息保存在了ScreenDeviceProxy类的gfxAlloc_成员中。ScreenDeviceProxy采用了单例模式,因此可以认为保存在了ScreenDeviceProxy::gfxAlloc_ 这个全局变量中了。
2、从rootView_->Render()开始渲染。 我们再回到WindowImpl::Render()下半部分的rootview->Render()调用: –>
RootView::Render()函数主要做三件事: 1、调用RenderRect绘制窗口:根据UIView的嵌套关系,一层层的调用onDraw函数进行绘制。这里不做详细分析了。 2、调用WindowImpl::Flush():推送画好的Buffer到BufferQueue中去,等待消费者取用。 3、调用WindowImpl::Update():通知LiteWM更新。最终调用到LiteWM::UpdateWindow()。这里不再详细分析,有兴趣同学自己跟一下。 我们来看看WindowImpl::Flush()函数的实现:
这里的surface就是LiteProxySurface,我们来看看LiteProxySurface::Unlock()函数:
终于看到FlushBuffer()的调用。这里的surface_就是AbilityMain进程中的SurfaceImpl对象,也是远程Surface对象的代理,前面有过解释。FlushBuffer()会把绘制好的buffer推送到BufferQueue中去,这个过程在《Camera中的Surface使用》中有详细解析,这里不再分析。 看到这里,大家可能有个疑问,Window中的这些UIView类的控件是怎么画到SurfaceImpl对象持有的Buffer内存中去的?下面一节解析一下这个问题。
3.3 UI控件绘制到Surface内存的过程 由于UIView的子类很多,我们不一一分析了,主要分析下UIView::onDraw()函数的实现: –>
这个函数的注释还是比较清楚的。函数分两种情况有边界和无边界。每种情况下又分宽>=高和宽DrawColorArea(),代码如下:
–>
DrawUtils::DrawColorArea()函数中的DRAW_UTILS_PREPROCESS是一段宏定义(在其他绘制函数都有使用)。这个宏定义中获取的screenbuffer 是通过ScreenDeviceProxy类的GetBuffer()函数。我们看看这个函数:
而gfxAlloc_这个成员变量就是上一节在WindowImpl::UpdateHalDisplayBuffer()函数中通过LiteProxySurface::Lock()获取的虚拟地址所赋值的。到这里我们就可以知道UIView控件绘制的内存就是WindowImpl持有的LiteProxyWindow持有的LiteProxySurface持有的SurfaceImpl(远程代理)所申请的,而分配者是运行在WMS进程中的LiteWindow持有的SurfaceImpl所持有的BufferQueueProducer对象。
3.4 WMS的合成送显过程 wms_server进程主程序启动后进入死循环,执行LiteWM::MainTaskHandler()函数: –>
–>
在ProcessUpdates()函数中分三个部分,首先进行窗口合成,然后画光标,最后送显。函数中的updates_是UpdateRegions类型,表示更新的区域。这个更新的区域是通过RootView::Render()函数中调用WindowImpl::Update()函数通知WMS后计算得到的,这里不详细分析了。我们主要看下DrawRegion()函数:
在这个函数中分别调用了LiteWindow::UpdateBackBuf()和LiteWindow::Flush()函数,我们看看这两个函数干什么了。
在LiteWindow::UpdateBackBuf()函数中,我们看到首先是通过SurfaceImpl::AcquireBuffer()获取推送到BufferQueue中的Window图像,然后把这帧图像复制到backBuf_中,最后释放AcquireBuffer得到的buffer到BufferQueue中(这样LiteProxySurface::Lock()又可以RequestBuffer了,因为Queue Size是2,如果不归还到BufferQueue就没有free buffer可用了) 我们再来看看LiteWindow::Flush()函数实现:
ENABLE_HARDWARE_ACCELERATION宏里面的代码与下面红框中代码作用一样的,都是做图像内存拷贝,上面是用硬件加速引擎做,这里不做分析了。 下面红框中的代码中 srcBuf 表示拷贝图像的源数据,是在backBuf_中,也就是前面提到的窗口绘制的图形,而 dstBuf 是目的图像内存,是在 layerData中的,后面 dy*layerData->stride是计算图像起点的内存位置, layerData->stride表示图像一行的字节数,这里不详细分析下面的计算过程。那么layerData->virAddr是什么地址呢?这个layerData是函数传入参数,是LiteWM的成员变量layerData_,在LiteWM的构造函数中,我们看到:
–>
那么g_devSurfaceData怎么来的呢? 我们看到在wms的main函数中:
–>
g_devSurfaceData 是在HiFbDevInit()函数中被初始化的,有g_display的内存地址来赋值的。
终于看到 g_display的地址是通过调用 display_hal模块的GetLayerBuffer获取的。 所以LiteWindow::Flush()最终是把backBuf_的图像拷贝到一块由display_hal分配的显存里面了。这里我们可以认为WMS进程只有一块g_display对应的显存,也是所有窗口合成的目的地,而每一个窗口对应2个Surface Buffer,一块用于backBuf_合成,一块用于窗口绘制。 最后我们看看LiteWM::ProcessUpdates()函数中送显LckFlush()函数:
上面函数中调用了display_hal中的Flush函数,将g_display中指定的显存进行输出到显示器。 总结: 1、在AbilityMain中的Surface是作为Producer来使用,并且通过IPC方式与运行在WMS中的BufferQueueProducer、BufferQueue、BufferQueueConsumer进行交互,申请Surface Buffer。如下架构图:
2、在AbilityMain中调用RequestBuffer 申请内存,结束绘画后通过FlushBuffer将Buffer推送到BufferQueue。 3、WMS进程通过AcquireBuffer获取图像,并将图像保存到backBuf_,然后调用ReleaseBuffer归还到BufferQueue。 4、在WMS启动的时候通过graphic_hals中的函数GetLayerBuffer()获取整个屏幕的显存LayerData。 5、LiteWM类负责窗口合成,类的构造中得到2步骤中拿到的显存信息LayerData。 6、每一个窗口WindowImpl对应一个Surface,通过SurfaceImpl::RequestBuffer可以远程调用到WMS,获取BufferQueue中的一块Buffer。WindowImpl对应的在WMS进程中的LiteWindow持有一个backBuf_的Surface,用途是在合成窗口过程中做合成的备份。 7、LiteWM在窗口合成的过程中,把窗口Surface Buffer的数据拷贝到 LayerData指定的显存空间,然后调用graphic_hals中的LcdFlush()函数,将LayerData显存送显到底层。