-
Window Positioning
中的WindowManager
主要是用来控制Window
对象 -
Window
对象是用来存放View
对象的容器,每个Window
对象都会关联Surface
对象 -
WindowManager
监视Window
对象的生命周期、输入和焦点事件、屏幕方向、转换、动画、位置、变换、z顺序等 -
然后将所有
Window
元数据发送给SurfaceFlinger
,SurfaceFlinger
利用这些元数据把自己管理的所有Surface
组合成layer
-
然后交给
Hardware Composer
做进一步处理 -
HAL
层中的Hardware Composer(HWC)
会根据当前硬件来进一步进行缓冲区的组合,它的具体实现依赖于特定的显示设备 -
官网关于
HWC
的数据流如下:
SurfaceFlinger
作为client
向HWC
提供一个完整的layer
列表,然后询问HWC
计划如何处理HWC
会将这些layer
标记为client合成
或者device合成
并告知SurfaceFlinger
SurfaceFlinger
将处理标记为client合成
的layer
,然后通过BufferQueue
传递给HWC
- 余下的
layer
由HWC
自行处理
网上一篇很有趣的渲染总结(文中有些错误,但瑕不掩瑜):Android渲染原理
VSYNC信号
前面提到Linux
使用Framebuffer
来用作显示输出。但是,如果在屏幕更新到一半时,用户进程更新了Framebuffer
中的数据,将导致屏幕上画面的上半部分是前一帧的画面,下半部分变成了新的画面。当然这种异常会在下次刷新时纠正过来,但是在用户感知上画面会出现闪烁感
- 针对这种情况,早期的解决方法是使用
双缓冲机制
,双缓冲就是提供两块Framebuffer
,一块用于显示,另一块用于数据更新,数据准备好后,通过ioctl
操作告诉显示设备切换用于显示的Framebuffer
,这样图像就能快速的显示出来了 - 但是双缓冲并没有完全解决问题,虽然双缓冲切换的速度很快,但是如果切换的时间点不对,在画面更新一半的时候进行切换,还是会出现单缓冲区遇到的闪烁问题
- 当然,可以在底层进行控制,当收到切换请求后内部并不马上执行,而是等到刷新完成后再切换,这样可以完全避免画面重叠的问题
- 但是,这样做会带来新的问题,如果
ioctl
操作完成后缓冲区没有切换,应用就不能确定何时可以再使用缓存区,只能通过ioctl
不停地查询缓冲区状态,直到切换完成。这种CPU
主动查询的方式很低效
为此Android
让底层固定地发送信号给用户进程,通知进程切换的时机,这样就避免了用户进程主动查询的操作。而这个信号就是VSYNC
信号
官方传送门:
VSYNC
官方描述如下:VSYNC
信号用来同步整个显示流程(Display Pipeline)
。显示流程
包括app
渲染、SurfaceFlinger
合成、HWC
(硬件渲染)组成
(这部分感觉原文更容易理解)VSYNC synchronizes the time apps wake up to start rendering, the time SurfaceFlinger wakes up to composite the screen, and the display refresh cycle.
VSYNC
信号应该由显示驱动产生,这样才能达到最佳效果。但是Android
为了能运行在不支持VSYNC
机制的设备上,也提供了用软件来模拟产生VSYNC
信号的手段
官网描述:通过
HWC
来产生VSYNC
信号,并通过接口回调将事件进行发送(主要是SurfaceFlinger
进行事件接收)
基础知识铺垫完成,我们先来看看Surface
Surface
官网对
Surface
的描述是:A surface is an interface for a producer to exchange buffers with a consumer.
上面描述的是一种生产者-消费者
的模式,而Surface
充当了中间衔接的角色。
以Activity
中UI
显示为例:
-
生产者
的任务就是把图形绘制在Surface
对象上 -
比较出名的生产者就是
SurfaceView
组件了 -
SurfaceFlinger
作为消费者
会把所有Surface
对应的图像层混合在一起 -
最后
消费者
将其输出到FrameBuffer
中,这样在屏幕上就看到最后合成的图像了
下面我们从Java层
开始分析Surface
应用中Surface
的创建过程
应用开发中很少直接使用Surface
,因为每个Activity
中都已经创建好了各自的Surface
对象,通常只有一些特殊的应用才需要在Activity
之外再去创建Surface
,例如相机、视频播放应用。
不过,通常这些应用也是通过创建SurfaceView
来使用Surface
需要注意的是,在应用中不能直接去创建一个可用的Surface
对象(也可以说直接创建出的对象没什么实际用途),因为这样创建出的Surface
对象和SurfaceFlinger
之间没有任何关联。
该如何创见一个可用的Surface
对象呢?
我们看下Surface
类的定义:
public class Surface implements Parcelable {
long mNativeObject;
// 一个无参构造,空实现
public Surface() {
}
public Surface(SurfaceTexture surfaceTexture) {
if (surfaceTexture == null) {
throw new IllegalArgumentException(“surfaceTexture must not be null”);
}
mIsSingleBuffered = surfaceTexture.isSingleBuffered();
synchronized (mLock) {
mName = surfaceTexture.toString();
setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
}
}
}
Surface
类对外有两个构造方法:
-
一个是无参构造,实现也是空的
-
注释中说这个主要是给
readFromParcel()
反序列化用的 -
那我们看下
readFromParcel()
方法
public void readFromParcel(Parcel source) {
if (source == null) {
throw new IllegalArgumentException(“source must not be null”);
}
synchronized (mLock) {
mName = source.readString();
mIsSingleBuffered = source.readInt() != 0;
setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
}
}
-
另一个需要传递
SurfaceTextur