前言
之前有写过一篇文章对卡顿情况进行分析,有兴趣的朋友可以去看看
那么我们从trace对CPU状态进行卡顿分析了,这篇文章我们对显示系统进行一个学习。
启动
读取文件
在Android13版本中,SurfaceFlinger是由Android.bp去启动init.rc文件,然后再解析文件去加载SurfaceFlinger
frameworks/native/services/surfaceflinger/SurfaceFlinger.rc
service surfaceflinger /system/bin/surfaceflinger
class core animation
user system
group graphics drmrpc readproc
capabilities SYS_NICE
onrestart restart --only-if-running zygote
task_profiles HighPerformance
socket pdx/system/vr/display/client stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
socket pdx/system/vr/display/manager stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0
socket pdx/system/vr/display/vsync stream 0666 system graphics u:object_r:pdx_display_vsync_endpoint_socket:s0
on property:vendor.debug.sf.restart=1
restart surfaceflinger
on property:init.svc.zygote=restarting && property:debug.sf.toomanylayers=1
restart surfaceflinger
setprop debug.sf.toomanylayers "0"
然后以此来调用main_SurfaceFlinger.cpp文件的main函数
在这里面有这几点需要关注,首先是main函数中的ProcessState::self() 函数的调用
- 构建ProcessState全局对象gProcess
- 打开binder驱动,建立链接
- 在驱动内部创建该进程的binder_proc,binder_thread结构,保存该进程的进程信息和线程信息,并加入驱动的红黑树队列中。
- 获取驱动的版本信息
- 把该进程最多可同时启动的线程告诉驱动,并保存到改进程的binder_proc结构中
- 把设备文件/dev/binder映射到内存中
于此同时,还会把SF的自身调用限制在4线程中并设置SurfaceFlinger进程的优先级
setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
set_sched_policy(0, SP_FOREGROUND);
//将大多数SurfaceFlinger线程放入系统后台cpuset防止我们不必要地使用大核心
//在绑定线程池初始化之后执行此操作
#ifdef MTK_SF_CPU_POLICY
if (SfCpuPolicyAdapter::isEnabled()
&& SfCpuPolicyAdapter::foregroundEnabled()){
//do nothing
}
else
#endif
if (cpusets_enabled()) set_cpuset_policy(0, SP_SYSTEM);
在初始化的时候,会把优先级设置成前台foreground,优先跑在4个小核上,关于这一点可以使用命令
adb shell ‘cat /proc/(pid of surfaceflinger)/cpuset’
进行查看。如果有需求定制,可以在某个场景配置到特定的cpuset中,提高或者降低它的优先级
初始化
// 实例化SF
sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
// 在客户端连接之前进行初始化
flinger->init();
// 将surfaceflinger放入servicemanager
sp<IServiceManager> sm(defaultServiceManager());
sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,
IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
// 运行surfaceflinger这个线程
flinger->run();
工厂实例化
聪明的小明肯定注意到了在surfaceflinger文件夹中有这样一个文件,那就是SurfaceFlingerFactory。很明显这就是使用工厂模式去对surfaceflinger进行一个实例化
sp<SurfaceFlinger> createSurfaceFlinger() {
static DefaultFactory factory;
return new SurfaceFlinger(factory);
}
通过createSurfaceFlinger()方法创建了一个SurfaceFlinger对象。
/frameworks/native/services/surfaceflinger/SurfaceFlinger.h
class SurfaceFlinger : public BnSurfaceComposer,
public PriorityDumper,
private IBinder::DeathRecipient,
private HWC2::ComposerCallback,
private ISchedulerCallback
SurfaceFlinger继承BnSurfaceComposer,实现ISurfaceComposer接口,实现ComposerCallback,PriorityDumper是一个辅助类,提供了SurfaceFlinger的dump信息。
- ISurfaceComposer 是Client端对SurfaceFlinger进程的binder接口调用。
- ComposerCallback,这个是HWC模块的回调,这个包含了三个很关键的回调函数,onComposerHotplug函数表示显示屏热插拔事件, onComposerHalRefresh函数表示Refresh事件,onComposerHalVsync表示Vsync信号事件。
在这些函数调用结束了之后,我们继续分析SurfaceFlinger的构造函数。在构造函数里,初始化了很多的全局变量,虽然我们可以在开发者模式中进行更改,但他们会影响代码的执行流程,如果不熟悉的话得先熟悉熟悉再进行修改哦。
初始化skia引擎
//当缓存大小被严格调整为单个显示时,发送maxFrameBufferAcquiredBuffers
auto builder = renderengine::RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
.setImageCacheSize(maxFrameBufferAcquiredBuffers)
.setUseColorManagerment(useColorManagement)
.setEnableProtectedContext(enable_protected_contents(false))
.setPrecacheToneMapperShaderOnly(false)
.setSupportsBackgroundBlur(mSupportsBlur)
.setContextPriority(
useContextPriority
? renderengine::RenderEngine::ContextPriority::REALTIME
: renderengine::RenderEngine::ContextPriority::MEDIUM);
if (auto type = chooseRenderEngineTypeViaSysProp()) {
builder.setRenderEngineType(type.value());
}
mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(builder.build()));
这里就是SurfaceFlinger初始化的第一段代码,可以看到是对renderengine进行初始化,并且设置一些参数。mCompositionEngine,也就是layer的Client合成引擎。这个类比较重要,Client也就是GPU合成。目前layer的合成方式有两种,一种是GPU合成,另外一种是HWC合成,也就是有些厂商所说的device合成。HWC合成一般用于游戏方面,性能会更加优异。
构造Vsync
// 处理任何初始热插拔和由此产生的显示更改
processDisplayHotplugEventsLocked();
在init方法中,重点看这个函数中调用了initScheduler方法,从字面意义上来看就是初始化调度器。
// 开始线程
mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
const auto configs = mVsyncConfiguration->getCurrentConfigs();
const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs();
mAppConnectionHandle =
mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
/*workDuration=*/configs.late.appWorkDuration,
/*readyDuration=*/configs.late.sfWorkDuration,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
/*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
/*readyDuration=*/configs.late.sfWorkDuration,
[this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
mEventQueue->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
configs.late.sfWorkDuration);
注意,Vsync是整个SurfaceFlinger最重要的组成部分,也是最核心的合成流程。整个SurfaceFlinger的合成节奏都是跟着Vsync信号走的,也就是说Vsync就像一个节拍器,控制着整个绘制帧流程的节拍。太快或者太慢都会导致屏幕显示异常,一般表现为画面卡顿,不流畅。
注入
SurfaceFlinger模块提供很多binder接口,在服务端的onTransact函数会根据Client端传递的code做不同的代码处理
case 1034: {
auto future = mScheduler->schedule([&] {
switch (n = data.readInt32()) {
case 0:
case 1:
FTL_FAKE_GUARD(mStateLock,
enableRefreshRateOverlay(static_cast<bool>(n)));
break;
default: {
reply->writeBool(
FTL_FAKE_GUARD(mStateLock, isRefreshRateOverlayEnabled()));
}
}
});
future.wait();
return NO_ERROR;
}
分析这一段,我们先不看这一段代码内部是在做什么。首先我们看到重点的case 1034,这明显是发送了一个信号去进行这一段逻辑代码的调用。那是从哪里发送的这个1034呢?我们在代码中进行检索发现在这里
在一级目录使用find . -iname “*.java” | xargs grep “1034” 命令
packages/app/Settings/src/com/android/settings/development/ShowRefreshRatePreferenceController.java
@VisibleForTesting
static final int SURFACE_FLINGER_CODE = 1034;
......
@VisibleForTesting
void updateShowRefreshRateSetting() {
// magic communication with surface flinger.
//魔法交🐂???
try {
if (mSurfaceFlinger != null) {
final Parcel data = Parcel.obtain();
final Parcel reply = Parcel.obtain();
data.writeInterfaceToken(SURFACE_COMPOSER_INTERFACE_KEY);
data.writeInt(SETTING_VALUE_QUERY);
mSurfaceFlinger.transact(SURFACE_FLINGER_CODE, data, reply, 0 /* flags */);
final boolean enabled = reply.readBoolean();
((SwitchPreference) mPreference).setChecked(enabled);
reply.recycle();
data.recycle();
}
} catch (RemoteException ex) {
// intentional no-op
}
}
@VisibleForTesting
void writeShowRefreshRateSetting(boolean isEnabled) {
try {
if (mSurfaceFlinger != null) {
final Parcel data = Parcel.obtain();
data.writeInterfaceToken(SURFACE_COMPOSER_INTERFACE_KEY);
final int showRefreshRate = isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF;
data.writeInt(showRefreshRate);
mSurfaceFlinger.transact(SURFACE_FLINGER_CODE, data,
null /* reply */, 0 /* flags */);
data.recycle();
}
} catch (RemoteException ex) {
// intentional no-op
}
updateShowRefreshRateSetting();
}
从上文可以看到,在刷新率配置菜单中去进行对应函数的调用,settings这个app对SurfaceFlinger进行了binder通信。
启动
void SurfaceFlinger::run() {
mScheduler->run();
}
void SurfaceFlinger::onFirstRef() {
mEventQueue->init(this);
}
SurfaceFlinger中的MessageQueue和Android应用层开发的MessageQueue设计非常相似,只是个别角色做的事情稍微有一点不同。
SurfaceFlinger的MessageQueue机制的角色:
- MessageQueue 同样做为消息队列向外暴露接口,不像应用层的MessageQueue一样作为Message链表的队列缓存,而是提供了相应的发送消息的接口以及等待消息方法。
- native的Looper是整个MessageQueue的核心,以epoll_event为核心,event_fd为辅助构建一套快速的消息回调机制。
- native的Handler则是实现handleMessage方法,当Looper回调的时候,将会调用Handler中的handleMessage方法处理回调函数。
MessageQueue init
/frameworks/native/services/surfaceflinger/Scheduler/MessageQueue.cpp
void MessageQueue::init(const sp<SurfaceFlinger>& flinger) {
mFlinger = flinger;
mLooper = new Looper(true);
mHandler = new Handler(*this);
}
该init方法中实例化了Looper和Handle。
void MessageQueue::Handler::handleMessage(const Message& message) {
switch (message.what) {
case INVALIDATE:
mEventMask.fetch_and(~eventMaskInvalidate);
mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
break;
case REFRESH:
mEventMask.fetch_and(~eventMaskRefresh);
mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
break;
}
}
在上面的回调函数,可以看到注册了两种不同的刷新监听,一个是invalidate刷新,一个是refresh刷新。它们最后都会回调到SurfaceFlinger中的onMessageReceived中,换句话说,每当我们需要图元刷新的时候,就会通过mEventQueue的post方法,回调到SurfaceFlinger的主线程进行合成刷新。