Web页面渲染及合成加速(二)

来源:互联网 发布:排课软件 免费 编辑:程序博客网 时间:2024/05/16 14:52

 

原文出处:http://blog.csdn.net/qq295445028/article/details/8244598

 

 

1.     LayersDraw to Backing Store.

该步骤是将WebCore渲染的内容绘制到后端存储的过程,这里的后端存储有两种。一种是BaseLayerAndroid类的PictureSet,一种是LayerAndroid类的SkPicture。SkPicture记录了一系列的绘制命令,而PictureSet是SkPicture的集合。

事实上这一步可以看做将Render树绘制到Layer树中的SkPicture和PictureSet中存储起来。下一步再按照Layer树中存储的步骤绘制到屏幕上(第四节)。

后端存储有两种方式,它们的实现步骤也不相同。写到BaseLayerAndroid的PictureSet的步骤称为Page Backing Store,而写到LayerAndroid则称为Layer Backing Store。下面分别介绍这两种方式的实现过程。

1.1  PageBacking Store

该过程可以从android::WebViewCore::webkitDraw开始看起。下面给出一个精简的调用堆栈(省略了一些中间步骤):

在android::WebViewCore中recordContent函数会先调用recordPictureSet,再调用createBaseLayer函数。

其中recordPictureSet有一个输出参数,它获取PictureSet。而createBaseLayer则创建一个BaseLayerAndroid对象,并将前者获取的PictureSet设置给该对象(setContent)。这就完成了整个PageBacking Store的过程。

而在rebuildPicture中会通过SkPicture来构造GraphicsContext类。然后作为参数传给RenderLayer,在遍历RenderLayer树的过程中,由GraphicsContext来进行最终的绘制工作。

1.2  LayerBacking Store

LayerAndroid类在WebCore线程中创建,通常由GraphicsLayer::create调用GraphicsLayerAndroid构造函数,进而创建。

在Sync的时候,GraphicsLayerAndroid将对应的RenderLayer的内容记录到LayerAndroid的SkPicture里面(GraphicsLayerAndroid::syncCompositingState)。LayerAndroids将内部的SkPicture预先绘制在Texture上,然后再绘制在WebView上(硬件加速)(详情见下一节)。

2.     CompositingBacking Store to Window

下面先放一张硬件加速渲染所涉及的类的关系图,本文的内容可以与该图相互印证。


2.1  合成流程.

2.1.1 流程

从WebKit::WebView::onDraw函数开始,WebView控件尝试在屏幕上绘制出其内容,该函数会调用drawContent,调用到nativeGetDrawGLFunction来获取drawGL的函数对象。然后再将函数对象传递给GLES20Canvas->callDrawGLFunction,该函数将调用到PDK中的内容。从PDK中一路调用到android::WebView::drawGL,然后调用WebCore::GLWebViewState::drawGL,就开始进入WebCore中的处理。

WebView被重绘时,进行合成(UI线程)

–先绘制对应当前Page ViewPort的Root Layer的Tiled Texture

–再依次绘制其它带Backing的Layers的Tiled Texture

如果Root Layer(BaseLayerAndroid)的TiledTexture和其它带Backing的Layers(LayerAndroid)的Tiled Texture还没有生成,它们需要在TextureGenerator线程生成。

目前TiledTexture生成的Code Path同时包括使用CPU和GPU两种方式,但是在Android4.0浏览器默认还是使用CPU的方式,估计GPU的方式还没有优化好(速度更慢并且存在渲染错误)。

使用CPU的方式生成TileTexture包括以下步骤:

–使用一个全局的SkBitmap,每次先清空(大小为一个Tile的大小)

–将SkPicture绘制在全局的SkBitmap上

–将全局的SkBitmap直接通过memcpy拷贝到为这个Tile分配的Graphics Buffer上

 

2.1.2 TextureGenerator线程

硬件加速的情况下,所有真正的渲染都发生在TextureGenerator线程,包括Page的Vector BackingStore生成Texure,Layer生成Texture。Compositing发生在UI线程,软件渲染的方式下,只是依次绘制Root Layer的Vector和其它Layer的Vector,硬件加速的方式下,绘制它们预先生成的Texture。

TexturesGenerator线程,在TexturesGenerator中定义,在TilesManager类的构造函数中被初始化,并被启动。其会在TexturesGenerator::threadLoop函数中进行业务处理,没有事情时会wait,并等待唤醒。而唤醒该线程通常是外界通过调用TexturesGenerator::scheduleOperation函数来进行。该线程最终会调用到BaseLayerAndroid::drawCanvas函数来将PictureSet画到SkCanvas上。

2.2  渲染模型

合成大体可以分为2个过程,paint和draw。paint过程是按照BackingStore来绘制canvas(包括渲染Texture)。而draw过程则是将canvas显示到屏幕上。以paint过程为主。

 

2.2.1       Layer树和瓦片

关于Layer树及BaseLayerAndroid和LayerAndroid在前几节都介绍过了。

一个BaseLayerAndroid由一组tile构成,就类似于被很多瓷砖覆盖的地面。 BaseLayerAndroid可能很大,一般策略是这一组tile仅覆盖viewport的区域,并且将webview 的内容(绘制步骤存储在BaseLayerAndroid的PictureSet中)绘制在tile上,最后将tile显示在屏幕上. 因此每次都要检查需要使用哪些tile。

 类TiledPage代表了上述的,覆盖viewport的一组tile(由BaseTile类表示). 当渲染的时候,需调用TiledPage::prepare()来准备,然后它自己绘制到屏幕上。

这里需要插一句,每一个GLWebViewState会含有2个TiledPage(front andbackground), 一个在当前比例显示页面, 另一个在后台绘制不同比例下的页面.  例如, 当我们缩放一个TiledPage, 它的tiles会有某种程度的失真. 为了解决这个问题,当用户停止缩放,我们会在新的比例下绘制background TilePage. 当 background TilePage准备就绪,我们把它和当前正在显示的TilePage交换。

TilePage的prepare()函数决定了哪些tile需要绘制以及以什么样的顺序绘制。当覆盖viewport的tiles准备好时,就可以开始绘制。prepare的过程比较重要,如果prepare不能成功,则整个渲染过程也不能成功。prepare过程中会调用到TiledPage::prepareRow,在此函数中才是真正的准备过程。此函数会判断本行中哪些tile落在当前viewport内,并且该BaseTile被标记为dirty,则未该BaseTile获取渲染纹理。然后调用TexturesGenerator线程来为该BaseTile进行绘制。

瓦片失效:如果tile是最新的,则不会被重绘.Tile只有被认为是dirty的时候才需要重绘:

- tile获取新的纹理

- webkit使tile的全部或部分内容失效

瓦片生命周期

1. 每一个tile在主GL线程中被创建,并被分配到TiledPage的特定位置.

2. TexturesGenerator线程为tile生成TileTexture,并上传至GPU。

3. bitmap被上传到GPU之后,主GL线程将调用tile的draw()函数,来将tile显示在屏幕上

4. 重复2-3步

5. 当用户切换到新页面时tile被销毁

 

2.2.2    纹理

很显然不能让每个BaseTile都有一个GL纹理 – 我们需要从一个纹理池中获取GL纹理,然后重复使用它们。获取纹理(BaseTile::reserveTexture())是一个比较重要的过程, 在TiledPage::prepare()函数中, 需要组织起所需的tile(viewport范围内且标记为dirty) 到一个 TilesSet,并调用BaseTile::reserveTexture() 为每一个tile分配一个专门的后台纹理。它会调用TilesManager::getAvailableTexture来获取纹理。

TilesManager类是一个管理类型的类,它负责管理各种纹理。该类中有一个可用纹理池,纹理池中的纹理数量由之前计算得来,通常是viewport中所能包含的最多tile数的两倍。

为何是两倍?因为每个BaseTile中有2个纹理,m_frontTexture和m_backTexture。与TiledPage道理一个,一个是当前显示的纹理,另一个在后台进行绘制,然后再交换两者。

 reserveTexture()向TilesManager类来请求纹理。分配机制如下:

 - 优先分配与前一次相同的纹理

 - 优先分配远离viewport的纹理

 - 优先分配不同TiledPage的纹理

3. 后记

    整个硬件加速的渲染机制还是比较复杂的,一共涉及一百多个文件,几万行代码。其中的细节更是杂乱无章。本文只是略做探究,有不足不正确之处还请大家批评指正。