前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[052]Q平台上setBrightness的巨坑

[052]Q平台上setBrightness的巨坑

作者头像
王小二
发布2020-06-08 12:11:23
9030
发布2020-06-08 12:11:23
举报

前言

最近解决了一个掉帧的问题,从应用层来看是buffer申请不到,最后发现是Q平台升级+高通的代码+我们自己驱动优化算法导致的,三者缺一不可,由于保密协议,我只能简单的原生代码和简单的图来描述这个问题,避免大家踩坑。

一、Q平台上setBrightness的升级

1.1 Android Q

代码语言:javascript
复制
@Override
public void setBrightness(int brightness, int brightnessMode) {
    synchronized (this) {
        // LOW_PERSISTENCE cannot be manually set
        if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
            Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId +
                    ": brightness=0x" + Integer.toHexString(brightness));
            return;
        }
        // Ideally, we'd like to set the brightness mode through the SF/HWC as well, but
        // right now we just fall back to the old path through Lights brightessMode is
        // anything but USER or the device shouldBeInLowPersistenceMode().
        if (brightnessMode == BRIGHTNESS_MODE_USER && !shouldBeInLowPersistenceMode()
                && mSurfaceControlMaximumBrightness == 255) {
            // TODO: the last check should be mSurfaceControlMaximumBrightness != 0; the
            // reason we enforce 255 right now is to stay consistent with the old path. In
            // the future, the framework should be refactored so that brightness is a float
            // between 0.0f and 1.0f, and the actual number of supported brightness levels
            // is determined in the device-specific implementation.
            if (DEBUG) {
                Slog.d(TAG, "Using new setBrightness path!");
            }
            SurfaceControl.setDisplayBrightness(mDisplayToken,
                    (float) brightness / mSurfaceControlMaximumBrightness);
        } else {
            int color = brightness & 0x000000ff;
            color = 0xff000000 | (color << 16) | (color << 8) | color;
            setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
        }
    }
}

1.2 Android P

代码语言:javascript
复制
@Override
public void setBrightness(int brightness, int brightnessMode) {
    synchronized (this) {
        // LOW_PERSISTENCE cannot be manually set
        if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
            Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId +
                    ": brightness=0x" + Integer.toHexString(brightness));
            return;
        }
        int color = brightness & 0x000000ff;
        color = 0xff000000 | (color << 16) | (color << 8) | color;
        setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
    }
}

1.3 两者的区别

we'd like to set the brightness mode through the SF/HWC as well 我们也希望通过SF/HWC设置屏幕亮度

就是一旦以下条件满足就会走Q版本上新的设置亮度流程

代码语言:javascript
复制
 if (brightnessMode == BRIGHTNESS_MODE_USER && !shouldBeInLowPersistenceMode()
                && mSurfaceControlMaximumBrightness == 255) 

Android Q上有两种方式设置屏幕亮度,如下图表示,导致掉帧的就是方式1

方式1:system_server->SF->HWC HAL->设备节点->背光驱动(Android Q)
方式2:system_server->Light HAL->设备节点->背光驱动(Android Q, P)

二、为什么会导致掉帧?

我在前言中已经说了是由Q平台升级+高通的代码+我们自己驱动优化算法导致的,三者缺一不可。

2.1 Q平台升级

出问题的项目就是走了方式1的流程,所以满足条件。

2.2 高通的代码

高通的HWC HAL层代码中对setDisplayBrightness接口实现中加了一个display的锁。也就意味这HWC其他的接口会和setDisplayBrightness产生锁的竞争关系。

2.3 我们自己驱动优化算法

我们在驱动中对背光设置有一些优化,在特定的情况下,会导致写设备节点的时间耗时200ms左右。

2.4 还原现场

首先lightsensor触发了自动背光调节,然后走SF-HWC去设置了亮度,持有了display的锁。

由于驱动的优化算法,导致这把锁持有了200ms。

这个200ms时间段里,SF绘制每一个帧的代码中也有和HWC的调用,因为拿不到锁导致也 block了,从而锁住了一个buffer。

应用申请一个buffer,完成绘制,交给sf,sf来不及使用。 应用又申请一个buffer,完成绘制,交给sf,sf来不及使用。 最后应用的三个buffer,一个处于lock,两个处于未用的状态(手机中bufferqueue设置的是3个) 应用再次申请buffer的时候,没有可用的buffer了,导致了主线程的block,最后导致了掉帧的问题的出现。

三、另外一个诡异的事情。

虽然问题基本已经解决,但是我无法解释,还有一个诡异的事情。

同一个手机,在驱动代码完全一样,只不过刷了不同高通基线的代码 竟然一个走方式1,一个走方式2。

日志发现的原因:

方式1的时候mSurfaceControlMaximumBrightness为255 方式2的时候mSurfaceControlMaximumBrightness为0

3.1 maximumBrightness为什么是0

基本可以猜到下面的代码在初始化的时候有异常,导致了maximumBrightness为0. 为什么会导致maximumBrightness为0,简单的说一下就是高通的基线升级导致了getDisplayBrightnessSupport返回了true,我就不展开讲了。

代码语言:javascript
复制
private LightImpl(Context context, int id) {
    mId = id;
    mDisplayToken = SurfaceControl.getInternalDisplayToken();
    final boolean brightnessSupport = SurfaceControl.getDisplayBrightnessSupport(
            mDisplayToken);
    if (DEBUG) {
        Slog.d(TAG, "Display brightness support: " + brightnessSupport);
    }
    int maximumBrightness = 0;
    if (brightnessSupport) {
        PowerManager pm = context.getSystemService(PowerManager.class);
        if (pm != null) {
            maximumBrightness = pm.getMaximumScreenBrightnessSetting();
        }
    }
    mSurfaceControlMaximumBrightness = maximumBrightness;
}

3.2 负负得正

这个就是典型的负负得正,基线没有升级导致了getDisplayBrightnessSupport为false,导致了mSurfaceControlMaximumBrightness为0,最后走方式2,掉帧问题也就消失。

总结

基本上整个问题的分析过程,我是通过trace分析出来的,然后结合特定关键点的log,把这个问题给解决了。这是一个很有意思的问题,不方便放trace的截图,无法和大家分享如何看trace。

但是除了学会看trace的技巧,你一定要清楚的知道每一个进程,每一个线程之前的通信的关系,然后在自己的脑海中去还原现场,抽丝剥茧,找到问题点。

整个问题牵涉到APP-Framework-Kernel,如果你想要在性能优化上更近一步,我个人认为打通APP-Framework-Kernel是非常重要的一步。

尾巴

为什么Android Q上要大费周章通过SF/HWC去设置屏幕亮度,我推测是谷歌希望将屏幕亮度调节和屏幕UI显示之间建立起一个关系,一起配合调整,让用户对屏幕的观感效果更好。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、Q平台上setBrightness的升级
    • 1.1 Android Q
      • 1.2 Android P
        • 1.3 两者的区别
        • 二、为什么会导致掉帧?
          • 2.1 Q平台升级
            • 2.2 高通的代码
              • 2.3 我们自己驱动优化算法
                • 2.4 还原现场
                • 三、另外一个诡异的事情。
                  • 日志发现的原因:
                    • 3.1 maximumBrightness为什么是0
                      • 3.2 负负得正
                      • 总结
                      • 尾巴
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档