【Android渲染】为什么alpha渲染性能低?

开门见山

1. 多次写入像素

在渲染不透明的像素时,只需要写入一次

在渲染透明像素时,每个像素至少需要写入两次

  • 第一次是之前的像素值
  • 第二次把之前的颜色与设置的不透明度做混合,产生一个新的颜色值,写入这个新的颜色值。

我们看到的一层下面有另一层的半透明效果,对于屏幕来说就是一个混合后的具体的颜色值。


2. 指令重排无效

发给RenderThread的DisplayList,里面的内容是按照View层级来排列的,默认顺序执行,但这样性能并不好:

分治、归并的思路可以提高性能,大家都听过指令重排序,把相同的指令一起执行、合并成一次操作执行,这样性能肯定是更好的,经过优化,绘制的流程如下:

很多内容一次绘制成功,非常高效。只是有一个问题,其实上面的例子中,背景item并不是一次性渲染的,原因是因为出现了重叠(overlap),为了确保重叠后的alpha是正确的,这里的渲染只能串行执行,而不能归并。

我们发现,正确性还是Android默认方案的第一权重;在第1点中提到过,alpha渲染,底层是在原像素上做blending,要依次渲染。

我们看到,大部分的渲染操作都可以享受指令重排序、归并批处理的高性能,但alpha渲染被排除在外了。

3. 灾难情况: ViewGroup的alpha渲染

在动画中,经常需要更改ViewGroup的透明度实现淡入淡出效果。这时候,事情有了一些变化,如果沿用之前的绘制方式(每个子View逐个透明度绘制)会出现一个问题:

重叠的部分,不透明度会叠加(如下图右侧).

我们想要的效果是下图左侧,而不是右侧。

这个问题应该怎么解决呢?Android渲染团队当然也意识到这个问题,他们的解决方案是:

  • 绘制一帧未做alpha变换的数据到内存中
  • 然后把这一帧的数据做alpha处理之后,渲染到屏幕

这个方案确实可以解决上面的UI异常问题。只是,他导致了一次整个View Hierarchy的重新绘制,每次ViewGroup 的alpha变换都要重绘一帧数据,如果我在执行一个动画,那岂不是性能灾难?

于是问题就来了:

这一帧数据为什么要重新绘制?刚刚渲染的不就是这一帧吗?

同学你说的很对,这一帧确实生成过了,只是被丢弃了。毕竟它会占用不小的内存,如果不知道是否有用,默认在会之后丢弃。

我们优化的方向,就是使用缓存数据,空间换时间。具体的执行调用如下API:

//before API 16
setLayerType(View.LAYER_TYPE_HARDWARE, null);//before alpha animation
setLayerType(View.LAYER_TYPE_NONE, null);//after alpha animation

//from API 16
ViewPropertyAnimator.alpha(0.0f).withLayer();//manage layer type automatically

刚刚我们谈到,问题的出现是因为重叠部分的UI出现了异常,如果你的确信不会出现UI重叠,或者(为了更好的渲染性能)可以接受因此产生的UI问题,可以重写View的hasOverlappingRendering方法,结果返回false, 它会带来渲染性能的提升。

class MyView extends View {

    @Override
    public boolean hasOverlappingRendering() {
        return false;
    }
}
性能对比图

以上。


Reference

https://www.youtube.com/watch?v=zdQRIYOST64
https://www.youtube.com/watch?v=wIy8g8yNhNk&feature=emb_logo

你可能感兴趣的:(【Android渲染】为什么alpha渲染性能低?)