Surface黑花闪

Alpha 限制在 [0.01, 0.99] 的原因

对应代码(L420):

val alpha = (1 - progress).coerceIn(0.01f, 0.99f)


问题一:alpha = 0f 会导致 Layer 被 hide

这是 SurfaceFlinger 的内部行为:当一个 Layer 的 alpha 被设为 0f(完全透明),SF 会认为”这个 Layer 没有任何可见内容”,自动将其标记为 hidden,从合成管线中移除。

后果:

  动画进度 progress = 1.0f alpha = 1 - 1.0 = 0.0f


                                SurfaceFlinger 自动 hide Layer


                             Layer 状态变为 hidden,但代码中
                             mSurfaceVisible 仍然是 true


                          状态不一致 下次 show/hide 逻辑出错

设 0.01f 时,Layer 保持 visible 状态在合成管线中,但人眼完全看不出来(不透明度只有 1%)。代码自己掌控 hide 时机(动画结束回调里主动调用 hideSurface())。

问题二:alpha = 1f 会花屏(BUGOS2-139460)

这是一个硬件/驱动层的 bug。当 ColorLayer 的 alpha 精确等于 1.0f 时,某些 GPU/显示驱动在合成路径上会出现异常渲染(花屏)。

可能的底层原因(常见于 Qualcomm/MTK HWC 实现):

  • alpha = 1.0f 的 ColorLayer 触发了 HWC(Hardware Composer)的特殊优化路径——直接覆盖而不做 alpha blending
  • 该优化路径在特定 shader/ColorLayer 组合下存在 bug,导致花屏
  • alpha = 0.99f 走的是正常 alpha blending 路径,绕开了有问题的优化分支

设 0.99f 时,视觉上与全黑无差异(只差 1% 透明度),但强制走 blending 路径,规避驱动 bug。

总结

这是 Android 系统开发中典型的 workaround 模式——用肉眼不可感知的微小偏移,绕开底层组件的边界条件 bug。

源码中的应用

 private fun showScreenFadeSurface(progress: Float, needShow: Boolean,
                                      coverBlack :Boolean, onWakingUp:Boolean = false): Boolean {
        synchronized(mScreenFadeLocked) {
            if (!ensureSurfaceControlCreated()) {
                if (onWakingUp) {
                    mCoverBlackScreenWakingUp = false
                }
                return false
            }
            if (onWakingUp) {
                mCoverBlackScreenWakingUp = true
            }
            if (coverBlack && mSurfaceVisible) {
                if (DEBUG) {
                    Log.d(TAG, "ScreenFade has visible progress $mSurfaceProgress")
                }
                return true
            }
            if (!mSurfaceVisible || mSurfaceProgress != progress) {
                mScreenFadeSurfaceControl?.let {
                    // alpha 设置为0f layer就回直接hide 不能设0f,设0.01f
                    // alpha 设置为1f 会有花屏问题 不能设1f,设0.99f workaround:BUGOS2-139460
                    val alpha = (1 - progress).coerceIn(0.01f, 0.99f)
                    mScreenFadeTransaction.setAlpha(it, alpha)
                    setScreenFadeToSf(it, progress)
                    if (needShow) {
                        Log.d(TAG, "ScreenFade show ScreenFade layer progress: $progress")
                        mScreenFadeTransaction.show(mScreenFadeSurfaceControl)
                    }
                    if (!mMergeWithNextTransaction) {
                        mScreenFadeTransaction.applyAsyncUnsafe()
                    }
                    mSurfaceVisible = true
                    mSurfaceProgress = progress
                }
            }
            return true
        }
    }