[053]一条评论引发的思考

前言

昨天在IT之家留言说如果应用无法满足120hz的绘制,假设如果绘制一帧的时间如果大于1/120秒,哪怕是多了1毫秒,就会导致应用在120hz的手机上也就变成了60hz。

后来仔细想想这句话说的并不是特别严谨,为什么这么说呢?

一、证明我的观点

首先我写一个demo来证明我的观点意思

1.1 满帧的应用

ublic class MyTextView extends TextView {

    int i = 0;

    public MyTextView(Context context) {
        super(context);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (i < 6) {
            this.post(new Runnable() {
                @Override
                public void run() {
                    MyTextView.this.setText(i++ + "");
                }
            });
        }
    }
}

我写了一个只绘制6帧的一个界面,activity中包含这个TextView,通过抓trace可以看到,下面这个漂亮6帧绘制,都是在一个vsync周期(图中黑白背景,一个白色或者黑色就是一个vsync周期)绘制一帧。

为什么第一帧会有点延迟是因为主线程在干其他事情,好在绘制花不了太多时间。
大概绘制也就3毫秒左右,毕竟界面简单。


1.2 帧数减半的应用

public class MyTextView extends TextView {

    int i = 0;

    public MyTextView(Context context) {
        super(context);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        try {
            Thread.sleep(16);
        } catch (Exception e) {
        }
        if (i < 6) {
            this.post(new Runnable() {
                @Override
                public void run() {
                    MyTextView.this.setText(i++ + "");
                }
            });
        }
    }
}

我人为在MyTextView.onDraw方法中sleep了16毫秒,来模拟很多应用写的不好,例如View层级厉害复杂,甚至在onDraw方法中进行文件读写。通过抓trace可以看到,原本1.1中6帧6个时间周期绘制完成,现在变成了6帧12个时间周期完成了。


1.3 总结

这就是我一直想说的一个观点,120hz的手机体验不一定有90hz的手机好的原因,一旦主线程一次traversal(一次ViewRootImpl的ondraw的遍历)的事情超出了vsync的时间周期1/120s,就会导致帧率直接砍半,降为60帧,90hz可以让这个折半降帧率的现象减少。

二、绘制一帧指的是什么

为什么我的观点不准确的原因,就是我太笼统的定义了绘制一帧这个词。

在硬件加速开启的情况下:
如果我将绘制一帧的时间定义成:主线程一次traversal,那我说的话是正确的。
如果我将绘制一帧的时间定义成:主线程一次traversal + renderthread一次渲染,那我说的话是不严谨的。

2.1 引发我思考的trace

因为要让主线程一次traversal + renderthread超时不是特别好写demo,我就用一个昨天引发我思考的一个trace给大家讲解一下。



大家从第一个黄色F的圆圈开始看,UI Thread的doFrame + RenderThread的DrawFrame的时间超出了一个Vsync周期,接下来每一帧的情况都是按照第一帧绘制的情况一样的情况运行,最好造成的现象就是虽然帧数是满帧,但是每一帧其实都是延迟显示的。

用一句哲学的话来概括就是:你眼睛看到任何事物,其实都是事物过去的样子
假如主线程一次traversal + renderthread一次渲染时间超出了vsync周期不多,这样子的应用大概率是可以满帧运行(满帧不代表显示正常)。

我上面一句话中用了不多,大概率这种模糊的文字,因为这个情况下能否满帧的临界点比较难定义。

2.2 如何定义这个120变成60帧的临界点

继续下面这个图,你会发现UI Thread的doFrame会非常happy的按照vsync信号执行,但是下面renderthread已经忙的不可开交了。

其实第一帧已经延迟了,假如运气不好导致了drawframe的时间横跨了2个vsync周期,还是会导致丢帧,这个时候就从120帧降为60帧。

三、总结

3.1 理想中的结论

假设开启硬件加速,一帧绘制的时间 = UI线程(T1)+ RenderThread线程(T2)
Vsync时间的周期是1/120s,约等于8.3ms
如果 16.6ms > T1 > 8.3ms,会导致从120帧降为60帧。
如果T1 < 8.3ms,16.6ms > T1 + T2 > 8.3ms,虽然还是满帧,但是你看到的永远是前一帧的画面。
如果 T1 + T2 > 16.6ms,毫无疑问会导致从120帧降为60帧。

上面理论看起来是很完美的结论,千万别把这个当做定理去记忆,因为其实现实会比这些情况复杂的多。
3.2 显示中的结论

可能会有人说现在CPU,GPU那么强,怎么可能会超时呢?
举个微信在865的手机的滑动消息列表的例子,T1 约等于6ms,T1 + T2约等于8.2ms。
这还是不怎么复杂的界面,大家会发现,6ms已经很接近8.3ms了。

3.3 给开发者的建议

对于要适配120hz手机的应用的工程师,你们要注意以下事情。
1.避免T1 > 8.3ms,也就是要减少主线程干的活,要减少一次draw的时间。千万别再draw的流程进行文件访问或者sleep。
2.避免在滑动响应的时候T1+T2 > 8.3ms,虽然界面在显示的时候,推迟一帧用户是察觉不了的,但是在滑动的响应中如果推迟一帧,可能会有跟手性的问题出现了。
3.千万别关闭硬件加速,一旦关闭硬件加速T1+T2 > 8.3ms就会导致120降为60帧。

四、尾巴

说到这里,如果听得懂我在说什么的人,应该能懂我想表达的意思。
如果不懂我说的什么的人,希望你去补一下以下知识点,再回过头来看这个文章。

知识点1:

TextView.setText到屏幕显示文字,整个过程发生了什么。

知识点2:

硬件加速之后,主线程(UI Thread)和RenderThread之间的协同工作的方式。

知识点3:

APP界面和SurfaceFlinger之间的关系

可能会有人说,你揪那么细致干嘛,对于用户来说,掉了几帧的影响也不大,,但是对于性能优化工程师来说,有时候就是要纠结那几个丢帧的情况,只有做好了每一帧的完美绘制,才能给用户带来最完美的用户体验。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343