您的位置:首页 > 移动开发

webview同步合成

2015-10-26 18:04 197 查看
问题

Chrome合成器现在已经有了软件模式,但是只靠这一点还不能解决现有Android
Webview的使用/滥用问题。

目前Android
Webview系统的渲染情况如下:

● Nonmodal(非模态)

用户无法通过WebView API指定WebView是处于软件模式还是硬件模式。相反地,canvas是一层接一层的传递给draw调用的,而这个canvas的后端存储可以是硬的也可以是软的。尽管如此,只有在WebView连接到一颗硬件加速的View Tree时,硬件绘制才会发生,而软件绘制到一块side canvas上则随时都可以发生。

● 同步

绘制命令必须阻塞,直到一帧渲染(到canvas)完。

● 及时(just
in time)

在绘制命令到达之前,滑动偏移和缩放因子(也可能通过矩阵起作用)可能已经变化了几次。WebView也可能初始化的比屏幕大,每一帧只绘制当前裁剪区域。

● 软件模式下不会丢失内容

如果是绘制到软件canvas上,不会由丢失或者模糊的tile.

(但如果是硬件作为后端存储的canvas,checkerboardis ok.)

关于上述特性,有两个代表性的使用场景:

● 基于WebView的第三方浏览器,使用了一个onscreen
hardware canvas,允许用户来回滑动。现在它想取整个文档的一个缩略图。

为了做这件事,它会创建一个小的software
canvas,并将一个0.1缩放因子的矩阵应用到该canvas上,然后调用webView.draw(softwareCanvas).(

这个函数是取缩略图的推荐用法,代替已经半过时的capturePicture)。

● 一个应用使用underlying文档的大小初始化webview,高度是viewport的20倍。然后在完全忽略scroll
offset的情况下,使用一个矩阵移动这个webview.这个过程中应该做到没有任何的过度绘制或者其他的性能影响。

Chromium webview需要努力精确重现Android
WebView的这些特性,而不是只做到大致相似或者绕开它。否则向后兼容问题会很难解决。幸好,impl-side
painting开启了精确重现Android
WebView这些特性的实现方式。

解决大纲(更新)

下面是为为提出来的完成上述需求的一个最简单的计划,依赖impl-side
painting.

首先,我们可以利用WebView单进程的优势,将renderer compositor impl thread与browserui thread合并。

这样可以使我们的线程模型与Android Browser的线程模型(包含一个UI线程,一个WebKit线程,一个raster线程)更匹配。

我们的WebKit线程只和某个message loop对话,而不关心这个message loop是一个新建的线程还是封装的一个已有线程。

手势事件流:

1.一个手势来自与Android Java Code.

2.当需要发送IPC时,作为替换,我们发送到一个新的类SyncInputEventFilter.这个类会直接调用CC。有三种情况:

a. 如果event hit root layer,CC拒绝这个event,并让WebView-specific code 处理 scrolling.

b. 如果event hit sublayer,CC吸收这个event,并scroll sublayer.

c. 如果event hit slow-scroll区域,CC使用HandleOnMainThread拒绝这个event,SyncInputEventFilter发送IPC到render进程。

当WebView被要求绘制时:

1.在WebView.onDraw()中,留意当前的transform,scale,clip rect,最新的android矩阵都要考虑在内。

2.用1中的信息设置LayerTreeHostImpl的状态。

3.通过“BeginMainFrame”/"deadline"调用CC马上开始绘制。

4.计算layer的绘制属性,在CaclDrawProperties中给rootlayer添加额外的transform,clip rect.

5.以下是派生的方法:

○ 如果是softwarecanvas(由OutputSurface::ForcedSoftwareDraw()标识):

i.遍历layer tree只生产resource-free DrawQuads.

ii.为当前帧创建一个本地的SoftwareRenderer,将PictureDrawQuads光栅化到software canvas上。

○ 如果是hardwarecanvas:像平常一样产生GL命令,并推送到command buffer中。

6.WebView.onDraw()返回。

7.如果是硬件绘制,绘制函数将被调用,不改变线程的情况下,在system context下,使command
buffer立即执行整个GL流,执行完再清除GL state保证整个system context不变。

下面是此方法的一些实现:

● “Normal”软件合成只被部分使用一个临时的cc::SoftwareRenderer实例完成“tile free software compositing”模式。

● 我们没有browser compositor.

● renderer compositor不会自发调度frames.直到被drawfunctor调用,它才会开始绘制。

● 由于软件绘制不需要使用tiles,我们延迟到第一次收到硬件绘制命令时才创建Tiles.

○ 所以没有attach到硬件加速的view时,TileManager的memory-limit应该是0.

● 软件绘制命令存在如下的性能权衡:

○ scrolling/pinching比tiled compositing要慢,因为SkPicture的性能具有不确定性。

○ 新的失效区域绘制到最终canvas上的过程比tiled compositing要快,因为两者之间没有Tile.

鉴于软件绘制的Webview通常用在取缩略图/截图这类应用,这种权衡还是合理的。注意Android
Webview的软件绘制路径也是直接从SkPicture raster的,所以滑动体验不会衰退。

● 不允许在draw functor以外触发任何GL调用,因为我们没有权限在draw functor以外使用这个context.

尽管如此,我们可在任何时候使用为GL Tile创建和使用gralloc()Buffer。

● 对于capturePicture和通常的软件绘制,我们可以重用同样的软件绘制路径,因为SkPicture同样可以是绘制目标。

如果我们对canvas应用clip和矩阵,Skia的R tree优化需要做正确的事情。

Render Surfaces and alpha

对于硬件绘制,render surfaces和alpha混合可以正常工作.但是对于软件绘制,问题来了,

● 可能发生绘制到view
tree attachment外面的情况。这些情况下,如果我们创建一个render-surface资源并持有它,

我们可能永远都接收不到下一个绘制调用了,而且当需要销毁这个rendersurface时,我们也没有明显的时间。

● CC中进行的不同层的alpha混合,通常需要依赖Tile作为Buffer存在才能正确进行,但是我们没有Tile.

在这些场景中,我们可以创建一个per-frame single-shot rendersurface,或者忽略web平台规范,draw
incrorrectly.由于传统WebView也忽略了平台规范,第一个版本我们也采用后一种方式。

a.在高级的render-surface-based features的场景中,比如clipped 3d transformed layers和css
filters,简单忽略他们是ok的,因为现存的webview类应用都不能使用他们。

b.对与alpha我们需要在某种形式下提供支持。当前代码hacks SkPicture,在绘制的时候直接绘制到root
layer.由于alpha混合不是可交换的,导致层比较复杂时,颜色值不正确,但在简单场景下效果还ok.

对硬件渲染模式的改进

只有当WebView第一次连接到硬件加速的Android view上时,才可以使用GL Context.

在此之前,只能接收软件绘制命令。所以,我们必须在只有软件渲染模式的基准上初始化CC,并准备好突然升级到完全的GL渲染。

(需要注意,即使在转变为硬件渲染后,我们仍然需要随时支持软件绘制,---所以,完全的GL模式是软件模式的严格意义上的超类)。

当我们从硬件加速的view上分离,我们需要释放所有的GL资源,并降回到软件模式的基准。

当WebView连接到硬件加速的Android View树时,在UI/Compositor线程开始第一次绘制调用前,会有一个同步调用初始化CC到GL状态。硬件

初始化必须是同步的,不能等待主线程。

就资源分配而言,传统的Android WebView分配GL Tiles,但是从不分配Software Tiles.如上所述,我们想保留这种方式。所以,在纯软件渲染模式下,

我们将避免Tile分配,在初始化硬件阶段,创建TileManager,并开始光栅化任务。

同样的,如果一个在软件模式基准中不能显示的Layer,比如VideoLayer,在GL初始化之前就被attach了,我们也需要保证其正常显示。

重新创建output-surface的路径起初是为了lost-context的恢复设计的,在硬件渲染的升级和降级中,这条路径也为我们做了大部分工作。


问题列表:

●不能调用LayerTreeHost::DeleteContentsTexturesOnImplThread,因为PrioritizedResourceManager的清除需要阻塞主线程。

*注意,在Android系统渲染过程中,我们不会使用PrioritizedResourceManager。所以可以简单使用if()摒除这部分代码。

● 在主线程不能更新新的capabilities/setttings.

*List:allow_partial_texture_updates,MaxPartialTextureUpdates, using_accelerated_painting

*计划是:在初始化软件模式时,将setting设置正确,但直到硬件初始化完,才开始使用这些设置.

● 不管是否使用offscreencontext,硬件初始化时都会设置offscreen context.

*只在需要使用时,设置offscreen context,这需要将决定从主线程中移除。

● 在impltree中是否需要清除资源

*软件模式不需要分配资源,所以不清除资源也fine.但是需要确保this is the case with test/DCHECKS.

*或者,调用clean up,并且保证所有的Layer都清除了他们的资源,没有资源泄漏情况;

替换策略

下面的一些想法同样可以提供令人满意的同步行为,但是在性能或复杂度方面遇到了困难。

● 保持线程分离,在绘制时做同步IPC(这篇文档之前版本的选择方案)。Draw是我们考虑同步的主要位置。在绘制时我们可以在renderer
compositor impl线程阻塞BrowserUI线程,从而获得同步行为。

问题:A)由于GL必须在BrowserUI线程中执行,这将导致对delegated
rendering的依赖,这将使架构更复杂。

B)APPS也期望能够同步设置scroll
position,这一点我们无法做到精确的向后兼容。更通常的,Apps可能还会做出很多奇怪的同步假设,但这些假设在这种线程模型下是无法支持的。

● WebKit主线程上阻塞。如果在WebKit主线程做所有的软件绘制,我们的行为就是兼容的。对缩略图类的应用和scrolling-styles应用的软件合成使用WebKit-thread阻塞是很有吸引力的。

问题:A)我们也阻塞了embedder的UI线程,把它暴露给高一层的jank,这在老的WebView中是不会出现的。即使在取缩略图的场景中,janking
the embedder 500ms也是不可接受的。

B)没有明显的证据表明使用软件绘制在性能或准确性上更好。

● 使用tiles做软件合成。

在软件模式下,代替直接从SKPictures绘制,我们可以在最近的scroll和scale值下,发送tile绘制任务到光栅化线程。阻塞直到光栅化完成,然后在使用SoftwareRenderer绘制。这要求LayerTreeHostImpl同时持有SoftwareRenderer和GLRenderer.由于我们的Tile是由grallocs分配的这一优势,所以可以被软件渲染和硬件渲染使用。

问题:事实证明,读回gralloc非常不容易。在CC中同时管理两种类型的Buffer会比较复杂,而且很难避免内存过度使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: