android4.0.1 webkit 软件渲染过程分析

时间:2022-03-27 13:35:05

android4.0.1 webkit 软件渲染过程可分为三部分。

一. webkit 将绘制网页的命令存储在SKPicture中

这个过程的详细顺序图如下:


顺序图下载地址

android4.0.1 webkit 软件渲染过程分析

                                                                                                                                              webkit render 记录网页绘制命令顺序图

以下是这个过程的详细说明。

webviewcore.cpp的contentInvalidate()会调用webviewcore.cpp的contentDraw(),这个函数会回调webviewcore.java中的contentDraw().

webviewcore.java contentDraw()  会发送消息EventHub.WEBKIT_DRAW。这个消息在webViewCore.java的EventHub的transferMessage()中处理,

调用WebViewCore.java的webkitDraw();
webkitDraw()中会先调用nativeRecordContent(),nativeRecordContent()会调用WebViewCore.cpp中recordContent().
recordContent()会调用recordPictureSet().
recordPictureSet()中会调用layoutIfNeededRecursive()以便得到最新的contentwidth和contentheight()
layoutIfNeededRecursive()会调用FrameView.cpp的Layout()从而触发整颗RenderTree的Layout.
 recordPictureSet()在调用layoutIfNeededRecursive()后,会接着调用rebuildPicture()。
 rebuildPicture()会创建一块新的SKPicture,
SkPicture 用来记录绘制命令,这些命令会在以后draw到一个指定的canvas上。
这个SKPicture将被保存在WebViewCore.cpp中的 m_content的WTF::Vector<Pictures> mPictures结构中。
SKPicture 的实例作为参数传给SkAutoPictureRecord的构造函数。
SKAutoPictureRecord的构造函数会调用SKPicture的beginRecording().
在SKPicture的beginRecording()函数中会创建一块SKBitmap,和一块SkPictureRecord。并将新创建的SKBitmap作为Device设置给SKPictureRecord.
SKPictureRecord继承自SKCanvas.
SKPictureRecord会作为参数传给PlatformGraphicsContext的构造函数,赋值给SKCanvas类型的变量mCanvas.
PlatformGraphicsContext会作为参数构造WebCore::GraphicsContext。
WebCore::GraphicsContext的构造函数会调用platformInit().
GraphicContext.cpp封装了平台相关的图形库信息,不同的平台会重新实现GraphicContext定义的接口。
Android平台的GraphicContext实现在文件GraphicContextAndroid.cpp中。
所以WebCore::GraphicsContext中调用的PlatformInit()的具体实现在GraphicContextAndroid.cpp中。
PlatformInit()会新创建一个GraphicsContextPlatformPrivate实例。这是典型的代理模式。
即GraphicsContext中含有一个与具体平台相关的私有成员变量GraphicsContextPlatformPrivate来处理平台相关的逻辑。
发送给GraphicsContext的请求都转发给这个代理类来执行。

传给WebCore::GraphicsContext构造函数的PlatformGraphicsContext实例变量的指针会保存在GraphicsContextPlatformPrivate实例变量的成员变量m_platformGfxCtx中。 

这样我们就清楚通过GraphicsContext类得到的SKCanvas就是我们前面提到的调用SKPicture的beginRecording()时新建的一块SKPictureRecord.

它的Device是同一函数中创建的一块SKBitmap。

WebCore::GraphicsContext作为参数传给webFrameView的draw()函数。

之所以调用的是WebFrameView的draw()函数是因为WebFrameView的构造函数会调用 frameview->setplatformwidget(this),将自身作为platformwidget 设置给FrameView.
 接下来就是WebFrameView的draw()函数触发的webkit的绘制过程,这个过程中的所有绘制命令都记录在上述新建的SKPicture中。
 WebFrameView会调用FrameView的paintContents()同时将WebCore::GraphicsContext作为参数传进去。
 FrameView的paintContents()会先调用needsLayout()判断下是否需要重新Layout,如果需要则进入Layout过程,停止Webkit的paint过程。
如果不需要重新Layout,paintContents()会调用RenderView对应的RenderLayer进行具体的paint过程。
 RenderView是DOM Tree的根节点Document对应的RenderTree上的节点,也是RenderTree的根节点。
RenderView对应的RenderLayer是RenderLayer Tree上的根节点。
 
Dom Tree,Render Tree与 RenderLayer Tree之间的关系

每一个RenderObject 都关联着一个RenderLayer.这种关联是通过祖先RenderObject 节点直接或间接地建立的。

分享同一坐标系的RenderObject(比如被同一CSS transform属性影响的元素)必然位于同一RenderLayer.

正是由于RenderLayer的存在,网页上的元素才可以按照正确的顺序合成,从而恰当的显示有交叠的内容,和半透明元素等效果。
像在RenderBoxModelObject::requiresLayer() 及RenderBoxModelObject的子类中重实现的requiresLayer()中定义的那样,有很多种条件可以
触发一个特定的RenderObject创建一个RenderLayer.
 通常来讲,满足下列条件之一时,RenderObject就会创建RenderLayer:
1.网页的root节点;
2.有明确的CSS position属性(relative,absolute,transform)
3.元素是透明的
4.overflow, alpha mask,或者reflection
5.有css filter(滤镜) 属性
6.有2D加速Context或者3D(webGL)context的 canvas 元素对应的RenderObject.
7.video元素对应的RenderObject
 需要注意的是RenderObject和RenderLayer之间并不是一一对应的。  
RenderObject 或者与它所创建的RenderLayer相关联(如果它创建了的话),或者与它的第一个拥有RenderLayer的祖先RenderObject创建的RenderLayer相关联。
RenderLayer 也会形成一个树型层次结构。这个树结构的根节点是与网页的根元素相对应的RenderLayer.每一个RenderLayer 节点的后代都是
包含在父亲RenderLayer内的可视化的RenderLayer.
每一个RenderLayer的子节点都被存储在两个按升序排列的有序表中。
negZOrderList 有序表中存储的子节点是z-index值为负的子RenderLayer,所以这些RenderLayer在当前RenderLayer的下面;
posZOrderList有序表中存储的子节点是z-index值为正的子RenderLayer,所以这些RenderLayer在当前RenderLayer的上面;

 

RenderLayer的创建过程:

RenderObject.cpp的setStyle()函数中会调用styleDidChange().RenderObject和RenderBoxModelObject及其所有子类都实现了styleDidChange()函数。
RenderBoxModelObject::styleDidChange()会调用RenderBoxModelObject的requiresLayer()来判断当前RenderObject是否需要创建RenderLayer.如果需要则创建RenderLayer.
 RenderObjectChildList::appendChildNode()中会调用RenderObject的addLayers()
RenderObject的addLayers()会调用WebCore::addLayers()这个函数中会RenderLayer的addChild().
从而将新创建的RenderLayer插入到当前的RenderLayer Tree中。
所以RenderLayer Tree的构建与Render Tree的构建是同步的。

 RenderLayer 继承自ScrollableArea。

RenderLayer Tree是Render Tree的稀疏表示,用来加速合成和滚动。
 
css中z-index 属性设置元素的堆叠顺序。拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面。
Z-index 仅能在定位元素上奏效(即position 属性为 absolute,fix,relative)
z-index 控制页面元素的分层显示。
 RenderLayer是用来实现页面分层显示的主要数据结构。
 整个网页的绘制过程是通过遍历RenderLayer Tree从而达到遍历Render Tree的过程,在遍历过程中将绘制命令都记录在SKPicture中,
遍历的具体步骤如下:
a.判断当前RenderLayer是否与damage rect 有交集;
b.调用paintLayer()递归绘制有序表negZOrderList中的各层RenderLayer;
c.请求与当前RenderLayer相关联的那些RenderObject 绘制它们自己;
d.这个绘制过程是通过遍历以创建当前RenderLayer的RenderObject为开始节点的Render Tree进行的。当遇到一个RenderObject所关联的RenderLayer与当前RenderLayer不同时,遍历过程便结束了。
e.调用paintLayer()递归绘制有序表posZOrderList中的各层RenderLayer;


由RenderObject和RenderLayer的关系,我们知道这个过程是树套树的遍历过程。

可以将RenderLayer tree看作是Render tree的索引树,RenderLayer tree中的每个节点都对应着Render tree的一颗子树。遍历
RenderLayer tree时,顺着RenderLayer tree的每个节点遍历Render tree.遍历过程中Render tree的每个节点都将绘制自己的命令记录在SKPicture中。
这样当RenderLayer tree遍历完成后,Render tree也遍历完了。
 这个过程结束后,SKPicture中就存储了绘制网页的一部分命令。
前面讲过,SKPicture会被存储在webviewcore.cpp中的一个PictureSet中。
 recordContent()最后会创建一个BaseLayerAndroid;同时将记录着网页绘制命令的PictureSet传给BaseLayerAndroid.
 recordContent()返回BaseLayerAndroid类型的指针,这个指针值会以整数的形式返回给webviewcore.java.
至此,webviewcore.java中的webkitDraw()中调用的nativeRecoredContent()过程就结束了,接下来webviewcore.java的webkitDraw()会调用Webviewcore.java的webkitDraw(DrawData)从而为记录着网页绘制命令的PictureSet绘制到Canvas上作准备。


二. 准备将保存有webkit绘制命令的SKPicture集合绘制到Canvas

这个过程是将上一过程中返回的BaseLayerAndroid类型的指针赋值给当前要渲染的m_baseLayer变量。
webviewcore.java的webkitDraw()在调用完nativeRecordContent()后,会调用Webviewcore.java的webkitDraw(DrawData).这个函数会发送消息
NEW_PICTURE_MSG_ID。
 webviewcore.java 的 webkitDraw(DrawData draw)会调用  
Message.obtain(mWebView.mPrivateHandler,WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
将消息发给webview.java。
webview.java::handler()处理消息 NEW_PICTURE_MSG_ID会调用setNewPicture().
webview.java::setNewPicture()会调用WebView.java::setBaseLayer()。
WebView.java::setBaseLayer()会调用 webview.cpp的nativeSetBaseLayer().
通过这个函数 将webviewcore.cpp中recordContent()中创建的BaseLayerAndroid传递到了webview.cpp中。

三.将保存有webkit绘制命令的SKPicture集合绘制到Canvas上

viewrootImpl.java 的draw() 会触发surface.java的lockCanvas()/unLockCanvas(),
从而触发surface.cpp的lockCanvas()/unlockAndPostCanvas().
viewRootImpl.java的draw()先调用surface.java的lockCanvas()得到一块Buffer,这块Buffer是由SurfaceFlinger负责管理的。
然后调用view的draw(canvas)当view draw完后,调用surface.java的unlockAndPostCanvas().
将包含有当前view内容的Buffer传给SurfaceFlinger,SurfaceFlinger将所有的Buffer混合后传给FrameBuffer.至此,我们才可以在屏幕上看到网页内容。
webview.java draw() 把m_baseLayer的内容绘制到canvas上。
webview.java onDraw(canvas)调用
webview.java DrawContent(canvas)会调用drawCoreAndCursorRing()
webview.java的nativeDraw() will call webview.cpp draw().这个函数是将webview.cpp中保存有网页绘制命令的m_baseLayer(webviewcore.cpp的recordContent()中创建的)的内容绘制到canvas上。
这个绘制过程完成后,viewrootImpl.java中会调用unLockCanvas()从而将含有网页内容的Canvas所对应的Buffer传给SurfaceFlinger.