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

硬件渲染也可以分为三个部分,其中前两个部分与软件渲染相同。
我们来看apk中开启硬件加速后,硬件渲染的第三部分:

将保存有webkit绘制命令的SKPicture集合显示到framebuffer上

是怎样具体实现的。

下面的图是webkit 硬件渲染过程涉及的主要类的类图。


类图下载地址


下图是硬件渲染过程中paint的详细顺序图。


顺序图下载地址

也是从webview.java::onDraw()开始,调用webview.java drawContent(),drawContent()接着调用drawCoreAndCursorRing().
drawCoreAndCursorRing()中调用nativeGetDrawGLFunction得到WebView.cpp中drawGL()的函数对象GLDrawFunctor的实例指针。
drawCoreAndrCursorRing()中接着调用((HardwareCanvas) canvas).callDrawGLFunction(functor).这个函数的作用就是使GLDrawFunctor指向的函数得到调用。
所以WebView.cpp中的drawGL()开始执行。
WebView.cpp的drawGL()第一次执行时候会创建GLWebViewState,并调用GLWebViewState的m_glWebViewState->setBaseLayer()。将含有网页绘制命令的BaseLayerAndroid实例指针传给GLWebViewState.
GLWebViewState的drawGL()先通过TilesManager调用TransferQueue的updateDirtyBaseTiles().这个过程我们稍候分析。
接着调用TilesManager的gatherLayerTextures()把当前可用的BaseTileTexture加锁。
接着调用setupDrawing()为即将开始的drawGL()作准备。
这些准备工作包括:
1.为TilesManger的ShaderProgram设置所需信息;
2.调用setViewport()计算水平和垂直方向所需要的BaseTile数,并调用TilesManager的setMaxTextureCount()分配所需的BaseTileTexture.分配好的BaseTileTexture(当前用的是SurfaceTextureMode)保存在TilesManager中。
在创建BaseTileTexture时,BaseTileTexture的构造函数会调用TransferQueue::initSharedSurfaceTextures().这个函数在第一次调用时会创建一块android::SurfaceTexture.
android::SurfaceTexture对应着一组GraphicBuffer和一个GL Texture. 这里的GraphicBuffer和GL Texture是在绘制过程中需要用到的两个重要的数据结构。这里要有个印象。
setupDrawing()完成后,调用BaseLayerAndroid的drawGL()启动绘制过程.
BaseLayerAndroid的drawGL()会调用drawBasePictureInGL().
drawBasePictureInGL()中我们先不考虑zoom的情况,先看基本的绘制过程,
渲染的核心工作都是在BaseLayerAndroid::drawBasePictureInGL()中完成的,这个函数完成以下两件事:
一.调用tiledPage->prepare()将网页内容上传给组成该TilePage的各个BaseTile所对应的GL Texture.为将TiledPage内容渲染到屏幕上做好准备。
二.调用tiledPage->draw()把TiledPage代表的网页内容渲染到屏幕上。

第一件事主要是通过四个步骤完成的,先概括下:
1.将记录网页绘制命令的PictureSet绘制到BaseRender中创建的一块SKCanvas上;
2.将步骤1中SKCanvas的device SKBitmap的内容拷贝到前文准备工作2中创建的android::SurfaceTexture对应的GraphicBuffer中。
3.将步骤2中含有网页内容的GraphicsBuffer上传给前文准备工作2中创建的GL Texture。
4.将步骤3中包含网页内容的GL Texture的部分内容传给BaseTile对应的GL Texture.
由上面的过程,可以看出Android::SurfaceTexture的作用相当于一个中转站,起缓冲作用,网页内容先汇集到这里再转给BaseTile的 GL Texture.

步骤1的具体实现过程:
tiledPage->prepare()首先计算覆盖BasyLayerAndroid的内容水平和垂直方向各需要多少个BaseTile。
根据ViewPort的长宽和TilesManager的tileWidth()和tileHeight()返回的每个BaseTile的长宽(android4.0.1都是256)。
接着按照垂直方向所需要的BaseTile数(即高度)逐行调用TiledPage的prepareRow().准备每一行所需要的各个BaseTile.
所以这个过程的主要工作是在TiledPage的prepareRow()中完成的.prepare()只是按照高度逐次调用这个函数。
prepareRow()的主要工作是准备某一行所需要的所有BaseTile,并把准备好的BaseTile封装成PaintTileOperation传给TilesManager等待进一步处理。
准备BaseTile的工作包括为这一行的每个BaseTile:
          1.setGLWebViewState
          2.setPage
          3.setContents
          4.reserveTexture()
其中reserveTexture的作用是为每个BaseTile分配一个BaseTileTexture,每个BaseTileTexture都关联着一个GL Texture.网页内容最终就是要绘在这个GL Texture上。
BaseTileTexture的分配是通过TilesManager的getAvailableTexture()完成的,分配是遵循一定规则的,具体可见函数中的注释。
需要注意的是此时GL Texture并没有被分配,GL Texture只有在用的时候才被分配。
接着看封装好的PaintTileOperation被传给TilesManager之后是怎样被进一步处理的:
TilesManager维护着一个线程TexturesGenerator,PaintTileOperation被放入这个线程维护的任务队列中,等待被调度执行。
当PaintTileOperation被调度执行时,BaseTile的paintBitmap()被调用,
paintBitmap()调用BaseRender的renderTiledContent().renderTiledContent()会创建一个SKCanvas,然后调用
RasterRenderer的setupCanvas(),在setupCanvas()中新建一块SkBitmap,并以这块bitmap为参数,新建一个SKDevice,将这块SKDevice设置给renderTiledContent()中创建的SKCanvas.这样renderTiledContent()中创建的SKCanvas,就对应了一块SKBitmap.
接下就可以将BaseLayerAndroid中记载网页绘制命令的PictureSet,绘制到这个SKCanvas上了。
renderTiledContent()通过调用TiledPage的paint()函数并将新建的SKCanvas传进去来触发绘制过程。
TiledPage会调用GLWebViewState::paintBaseLayerContent()。
paintBaseLayerContent()会调用BaseLayerAndroid的drawCanvas().
drawCanvas()会调用pictureSet的draw().
PictureSet的draw()把自身记录的网页绘制命令实实在在的绘制到BaseRender的renderTiledContent()函数中创建的SKCanvas对应的SKBitmap上。
这一步与软件渲染路径下的最后一步将PictureSet记录的网页绘制命令绘制到surfaceflinger中分配的一块SKCanvas上是相同的。
至此步骤1就完成了。此时网页的内容保存在了SKCanvas对应的一块SKBitmap上。

步骤2的具体实现过程:
步骤1的绘制过程完成后,BaseRender的renderTiledContent()函数会接着调用RasterRenderer(BaseRender的子类)的renderingComplete().
RasterRender的renderingComplete()调用GLUtils::paintTextureWithBitmap().同时将步骤1的SKCanvas传入。paintTextureWithBitmap()
调用GLUtils::updateSharedSurfaceTextureWithBitmap()这个函数会通过TilesManager调用TransferQueue::updateQueueWithBitmap().
这个函数调用TransferQueue::tryUpdateQueueWithBitmap().
tryUpdateQueueWithBitmap()中主要作两件事:
a)将步骤1传过来的存储网页内容的Bitmap, copy 到前文准备工作2中新建的Android::SurfaceTexture对应的GraphicBuffer中。
具体过程分为三步:
1.得到Android::SurfaceTexture对应的GraphicBuffer。
具体调用过程如下:
ANativeWindow_lock(surfacetextureclient)
这个函数会调用SurfaceTextureClient->perform(NATIVE_WINDOW_LOCK)
SurfaceTextureClient::dispatchLock()
SurfaceTextureClient::lock()
SurfaceTextureClient::dequeueBuffer()
SurfaceTexture::dequeueBuffer()
2.得到GraphicBuffer后,将包含网页内容的Bitmap copy 到Graphic Buffer中。
3.将Graphic Buffer放回Android::SurfaceTexture的GraphicBuffer队列中。
具体调用过程如下:
ANativeWindow_unlockAndPost()
这个函数会调用SurfaceTextureClient->perform(NATIVE_WINDOW_UNLOCK_AND_POST)
SurfaceTextureClient::dispatchUnlockAndPost()
SurfaceTextureClient::unlockAndPost()
SurfaceTextureClient::queueBuffer()
SurfaceTexture::queueBuffer()
b)更新transfer queue 的信息.
通过调用addItemInTransferQueue(renderInfo, currentUploadType, &bitmap)将当前需要渲染的BaseTile的信息存入TransferQueue的一个队列m_transferQueue中
至此步骤2就完成了,网页内容被保存在android::SurfaceTexture对应的GraphicBuffer中。

步骤3的具体实现过程:
TransferQueue::updateDirtyBaseTiles()
调用m_sharedSurfaceTexture->updateTexImage()
updateTexImage()调用glEGLImageTargetTexture2DOES()将含有网页内容的Buffer上传给了TransferQueue::initSharedSurfaceTextures()中创建的GL Texture.
至此步骤3就完成了,网页内容通过GL ES操作被上传给了前文准备工作2中调用TransferQueue::initSharedSurfaceTextures()创建的GL Texture。

步骤4的具体实现过程:
TransferQueue::updateDirtyBaseTiles()在完成步骤3后,
会根据m_transferQueue中存放的BaseTileTexture的信息,调用
BaseTileTexture::requireGLTexture()接着调用
GLUtils::createBaseTileGLTexture()
这时,BaseTile所对应的GL Texture才真正的被创建。
有了BaseTile的GL Texture 后,下一步就是将步骤3中包含网页内容的GL Texture blit到BaseTile的GL Texture。
这个过程是通过TransferQueue::updateDirtyBaseTiles()调用TransferQueue::blitTileFromQueue()完成的。
至此步骤4就完成了,网页内容保存在BaseTile对应的GL Texture中。

第二件事是调用tiledPage->draw()把TiledPage代表的网页内容渲染到屏幕上。
这个过程的具体实现如下:
tiledPage->draw()函数遍历组成这个Page的BaseTile,并依次调用这个BaseTile的draw()。
BaseTile::draw()会调用TilesManager::instance()->shader()->drawQuad()。
drawQuad()会调用ShaderProgram::drawQuadInternal().
ShaderProgram::drawQuadInternal()将BaseTile的包含网页内容的Texture绘制到缓冲区。
这块缓冲区在ShaderProgram::init()中创建。
glDrawArrays()之后,渲染应该就完成了。


你可能感兴趣的:(android4.0.1 webkit 硬件渲染过程分析)