SurfaceFlinger模块

news2024/11/15 17:35:34

SurfaceFlinger是一个系统服务,作用就是接受不同layer的buffer数据进行合成,然后发送到显示设备进行显示。

SurfaceFlinger进程是什么时候起来的?

在之前的Android低版本手机上,SurfaceFlinger进程是在init.rc中启动的,在最新的高版本上SurfaceFlinger进程并不是直接在init.rc文件中启动的,而是通过Android.bp去包含启动surfaceflinger.rc文件,然后在该文件中再去启动SurfaceFlinger进程。

/framework/native/services/surfaceflinger/surfaceflinger.rc

 service surfaceflinger /system/bin/surfaceflinger
     class core animation
     user system
     group graphics drmrpc readproc
     capabilities SYS_NICE
     onrestart restart 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

关于init.rc文件的解析和Android.bp的编译脚本执行(Soong编译系统)本文不做深入研究,SurfaceFlinger进程启动了,就会执行到main函数。

/services/surfaceflinger/Main_surfaceflinger.cpp

main函数中,有几个关键的点需要关注。

  1. ProcessState::self() 函数的调用,个人理解是同binder驱动建立链接,获取驱动的版本,通知驱动,可以同时启动15个线程来处理Client的请求。

总结:

(1)构建ProcessState全局对象gProcess

(2)打开binder驱动,建立链接

(3)在驱动内部创建该进程的binder_proc,binder_thread结构,保存该进程的进程信息和线程信息,并加入驱动的红黑树。

(4)获取驱动的版本信息

(5)把该进程最多可同时启动的线程告诉驱动,并保存到改进程的binder_proc结构中

(6)把设备文件/dev/binder映射到内存中

  1. 设置SurfaceFlinger进程的优先级

setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
set_sched_policy(0, SP_FOREGROUND);
if(cpusets_enabled()){
    set_cpuset_policy(0, SP_SYSTEM);
}

关于cpuset的使用,有一些简单的命令如下:

查看cpuset的所有分组

adb shell ls -l /dev/cpuset

查看system-background的cpuset的cpu

adb shell cat /dev/cpuset/system-background/cpus

查看system-background的应用

adb shell cat /dev/cpuset/system-background/tasks

查看SurfaceFlinger的cpuset

adb shell 'cat /proc/(pid of surfaceflinger)/cpuset'

我们也可以自定义cpuset,就是可以根据各自的需求,动态配置自定义的cpuset,例如SurfaceFlinger的线程默认跑到4个小核上,假如有个需求要把SurfaceFlinger的线程跑到大核上,就可以配置自定义cpuset,在进入某个场景的时候,把SurfaceFlinger进程pid配置到自定义的cpuset的tasks中。

3 初始化SurfaceFlinger

// 实例化 surfaceflinger
sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();

// 初始化 surfaceflinger
flinger->init();

// 将 flinger 添加到 ServiceManager 进程中
sp<IServiceManager> sm(defaultServiceManager());
sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false, IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);

//启动 DisplayService
startDisplayService();

// 启动 surfaceflinger
flinger->run();

实例化 surfaceflinger

有个SurfaceFlingerFactory.cpp,类似java中的工厂类,在头文件中定义了好多创建不同对象的函数。

/frameworks/native/services/surfaceflinger/SurfaceFlingerFactory.cpp

sp<SurfaceFlinger> createSurfaceFlinger() {
    static DefaultFactory factory;
    return new SurfaceFlinger(factory);
}
} // namespace android::surfaceflinger

通过SurfaceFlingerFactory.cpp创建了一个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的接口定义和实现,本文不做细节描述。

简单画了SurfaceFlinger的UML图。

  1. ISurfaceComposer 是对SurfaceFlinger进程的binder调用。

  1. HWC2::ComposerCallback,这个是面向底层硬件的回调,这个包含了三个很关键的回调函数,onComposerHotplug函数表示显示屏的热插拔通知给SurfaceFlinger进程的回调, onComposerHalRefresh函数表示底层硬件通知SurfaceFlinger进程HWC刷新的回调,onComposerHalVsync表示底层通知SurfaceFlinger进程同步信号的回调。

我们接下来看下SurfaceFlinger的构造函数。

在SurfaceFlinger中的构造方法中,初始化了大量的全局变量,有一些变量影响了整个代码的执行流程等等,而这些变量都可以在开发者模式中去更改它,就是SurfaceFlinger作为binder的服务端,设置页面中的开发者模式页面做为client端去调用更改,努比亚作为ROM厂商,SurfaceFlinger的代码中还包括了开关高通辅助功能的属性开关等等。

初始化 surfaceflinger

实例化surfaceflinger对象之后,调用该对象的init方法,这个方法里面有几个比较重要的代码。

  1. 构造SkiaRenderEngine渲染引擎

mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(
        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)
                .build()));

在Android S版本之前,这块的绘制流程都是opengles实现的,在Android S新版本上切换到skia库,mCompositionEngine这个类比较重要,主要负责layer的Client合成就是GPU合成,截图,录屏等核心功能都和这块有关系,Client合成有别于HWC合成,一个是GPU合成,一个是高通的硬件去合成。针对skia库的研究后面有单独的章节进行讲解。

  1. 构造Vsync

// Process any initial hotplug and resulting display changes.
processDisplayHotplugEventsLocked();

上面这个函数会被几个地方调用到,在init方法中,还有binder的调用中和ComposerCallback的回调中,我们重点看这个函数中调用了initScheduler方法,

// start the EventThread
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);

上面把initScheduler方法中几个关键的代码贴出来,这个和Vsync研究密切相关,当我们要研究SurfaceFlinger的合成流程,也就本章节要讲的最核心的流程,合其触发的条件是由Vsync控制的,Vsync很像一个节拍器,SurfaceFlinger中每一帧的合成都需要跟随节拍器,太快或者太慢都会导致屏幕显示异常,一般表现为画面卡顿,不流畅,本章的主要讲解SurfaceFlinger的合成流程,关于Vsync的研究后面有独立章节进行讲解。

  1. 举例讲解一个init函数中高通一个so库的加载

char layerExtProp[PROPERTY_VALUE_MAX];
property_get("vendor.display.use_layer_ext", layerExtProp, "0");
if(atoi(layerExtProp)){
    mUseLayerExt = true;
}

当我们把vendor.display.use_layer_ext中配置成1,就会把mUseLayerExt的变量置为true,这个变量代表SurfaceFlinger使用liblayerext.qti.so。这个so库是判断当前的layer是不是游戏的layer,所以当这个库加载成功之后,会在BufferLayer对象在构造函数中初始化一个layer的变量mLayerClass。

if(mFlinger->mLayerExt){
    mLayerClass = mFlinger -> mLayerExt ->GetLayerClass(mName);
}

当mLayerClass被初始化成功之后,第三方应用可以通过binder调用,查询SurfaceFlinger进程这个应用的layer是不是游戏的layer,如果是游戏的layer则可以处理自己的逻辑等等,在最新的Android S版本有个AppClassifier应用需要启动,如果被禁止会导致liblayerext.qti.so去判断layer的类型发生异常,这个apk是高通实现的,会监听应用的安装广播,所以会在数据库中存储一份不同应用对应的的类别信息。

  1. 将SurfaceFlinger添加到 ServiceManager进程中

这个逻辑主要是提供给app端通过binder来调用SurfaceFlinger的方法,SurfaceFlinger的onTransact函数会根据app端传递的code做不同的代码处理,下图是onTransact函数中一处code的处理。

case 1034: {
    switch (n = data.readInt32()) {
        case 0:
        case 1:
            enableRefreshRateOverlay(static_cast<bool>(n));
            break;
        default: {
            Mutex::Autolock lock(mStateLock);
            reply->writeBool(mRefreshRateOverlay != nullptr);
        }
    }
    return NO_ERROR;
}

这个code是1034的逻辑,那么是那个app端发出来的么,答案是设置应用中的开发者模式页面中的功能按钮,搜索了Settings应用中关于1034的逻辑,找到了一个ShowRefreshRatePreferenceController.java包含这段逻辑。

/Settings/src/com/android/settings/development/ShowRefreshRatePreferenceController.java

private static final String SHOW_REFRESH_RATE_KEY = "show_refresh_rate";
@VisibleForTesting
static final int SURFACE_FLINGER_CODE = 1034;
public ShowRefreshRatePreferenceController(Context context) {
    super(context);
    mSurfaceFlinger = ServiceManager.getService(SURFACE_FLINGER_SERVICE_KEY);
}
@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
     }
 }

从上面的代码可以看到,App端对SurfaceFlinger进程进行了binder通讯。

  1. 启动 surfaceflinger

voidSurfaceFlinger::run() {
    while (true) {
        mEventQueue->waitMessage();
    }
}
SurfaceFlinger onFirstRef方法
voidSurfaceFlinger::onFirstRef() {
    mEventQueue->init(this);
}

在前面的main函数,SurfaceFlinger指针对象实际上还是一个智能指针,sp强引用指针。这种智能指针在第一次引用的时候,会调用onFirstRef方法,进一步实例化内部需要的对象,这个方法调用了mEventQueue的init方法,而这个对象就是线程安全的MessageQueue对象。

SurfaceFlinger中的MessageQueue和Android应用层开发的MessageQueue设计非常相似,只是个别角色做的事情稍微有一点不同。

SurfaceFlinger的MessageQueue机制的角色:

  1. MessageQueue 同样做为消息队列向外暴露操作接口,不像应用层的MessageQueue一样作为Message链表的队列缓存,而是提供了相应的发送消息的接口以及等待消息方法。

  1. native的Looper是整个MessageQueue的核心,以epoll_event为核心,event_fd为辅助构建一套快速的消息回调机制。

  1. native的Handler则是实现handleMessage方法,当Looper回调的时候,将会调用Handler中的handleMessage方法处理回调函数。

MessageQueue init

/frameworks/native/services/surfaceflinger/Scheduler/MessageQueue.cpp

voidMessageQueue::init(constsp<SurfaceFlinger>&flinger) {
      mFlinger=flinger;
      mLooper=newLooper(true);
      mHandler=newHandler(*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的主线程进行合成刷新。

以上就是SurfaceFlinger进程初始化的过程,中间提到了一些比较重要的类,接下来会通过几个章节对SurfaceFlinger进程中比较核心的逻辑进行代码讲解。

  1. 第1章 Vsync研究

  1. 第2章 BufferQueue研究

  1. 第3章 SurfaceFlinger动画研究

  1. 第4章 SurfaceFlinger主流程研究(分析handleMessage方法的逻辑)

  1. 第5章 HWC研究

  1. 第6章 Android显示流程研究

  1. 第7章 Skia库研究

  1. 第8章 截图/录屏流程研究

关于SurfaceFlinger的两篇文章都在《努比亚技术团队》简书中的账号发表过,这两篇是本人发表的,由于努比亚技术团队后续的文章不再更新,关于SurfaceFlinger模块的后续文章研究会在本人的博客下持续更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/391208.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

.Net Core WebApi 在Linux系统Deepin上部署Nginx并使用(一)

前言&#xff1a; Deepin最初是基于Ubuntu的发行版 2015年脱离Ubuntu开发&#xff0c;开始基于Ubuntu上游Debian操作系统 2019年脱离Debian&#xff0c;直接基于Linux开发&#xff0c;真正属于自己的上游Linux系统发行版 2022年8月&#xff0c;新版《Deepin V23》我下载开始了我…

Registry与DGC的攻击利用

0x01 2022-02-03写的一篇文章。 0x02 Registry Registry指的是RMI的注册表&#xff0c;攻击的目标是注册表所在的机器&#xff0c;一般注册表和RMI Server在同一个机器上&#xff0c;特殊情况下也会在不同机器上。 在我们通过LocateRegistry#getRegistry获取到目标开启的注…

Win32:C++其实早已支持中文编程

我们以前学习C/C的时候&#xff0c;对于变量和标识符的命名都有如下规则&#xff1a; 变量名必须由字母、数字、下划线构成只能以字母、下划线开头 似乎对中文不太友善啊&#xff0c;于是后来出现了一些中文编程的呼声&#xff0c;甚至还真的出现了一些中文编程语言。 其实在…

【微信小程序-原生开发】实用教程15 - 列表的排序、搜索(含云数据库常用查询条件的使用方法,t-search 组件的使用)

请先完成列表数据的分页、触底加载 【微信小程序-原生开发】实用教程14 - 列表的分页加载&#xff0c;触底加载更多&#xff08;含无更多数据的提醒和显示&#xff0c;自定义组件&#xff09; https://blog.csdn.net/weixin_41192489/article/details/129355396 效果预览 核心…

嵌入式 Linux进程间的通信--信号

目录 信号 信号的概述 信号类型 信号发送 1、kill 函数 2、raise函数 3、pause函数 信号处理 可以结合上一篇文章一起看&#xff1a; 嵌入式 Linux进程之间的通信_丘比特惩罚陆的博客-CSDN博客 信号 信号的概述 软中断信号&#xff08;signal&#xff0c;又简称为…

软聚类算法:模糊聚类 (Fuzzy Clustering)

前言 如果你对这篇文章感兴趣&#xff0c;可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」&#xff0c;查看完整博客分类与对应链接。 在介绍模糊聚类之前&#xff0c;我们先简单地列举一下聚类算法的常见分类&#xff1a; 硬聚类 (Hard Clustering) Connec…

NGINX学习笔记 - 一篇了解NGINX的基本概念(一)

NGINX是什么&#xff1f; NGINX是一款由俄罗斯人伊戈尔赛索耶夫使用C语言开发的、支持热部署的、轻量级的WEB服务器/反向代理服务器/电子邮件代理服务器&#xff0c;因为占用内存较少&#xff0c;启动极快&#xff0c;高并发能力强&#xff0c;所以在互联网项目中广泛应用。可…

十二、面向切面编程AOP

IoC使软件组件松耦合。AOP让你能够捕捉系统中经常使用的功能&#xff0c;把它转化成组件。 AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;面向切面编程&#xff0c;面向方面编程。&#xff08;AOP是一种编程技术&#xff09; AOP是对OOP的补充延伸。 …

互联互通新里程,数字城轨助力城市繁荣!

轨道交通是城市间互通互联的命脉&#xff0c;是当下人们出行的首要选择之一&#xff0c;也是我国“新基建”的重点建设对象。将城轨交通各链路系统及多类型服务&#xff0c;与空间感知、移动互联、云计算等技术深度融合&#xff0c;集中实现城市空间、城轨分布、城轨运行动态的…

有哪些平价好用的台灯推荐?台灯买什么光的比较好

随着社会的发展&#xff0c;生产水平逐渐提高&#xff0c;很多东西都得到长足的发展&#xff0c;对咱们的生活水平的提高帮助也越来越大&#xff0c;台灯也不例外。台灯是现在各个年龄段人群都在使用的产品&#xff0c;晚上熬夜工作、学习、看书、休闲等等都很合适&#xff0c;…

Linux学习第十七节-Apache httpd的web服务

1.简介 适用于Unix/Linux下的web服务器软件。 Apache httpd&#xff08;开源且免费&#xff09;&#xff0c;虚拟主机&#xff0c;支持HTTPS协议&#xff0c;支持用户认证&#xff0c;支持单个目录的访问控制&#xff0c;支持URL地址重写&#xff0c;支持路径别名&#xff0c;…

北斗RTK高精度定位在AI领域的应用

随着北斗高精度定位技术越来越成熟&#xff0c;通过GNSS高精度定位与机器人结合&#xff0c;越来越多的智能机器人走进我们生活中。像驾培机器人、智能除草机器人、智能巡检机器人、北斗划线机器人等智能机器人已经广泛的投入使用。驾培机器人驾培机器人&#xff1a;通考车安装…

java基础——类加载机制

类加载机制一、背景知识补充二、类加载过程/机制1、浅层理解2、大致步骤3、具体步骤&#xff08;3.1&#xff09;装载loading&#xff1a;查找和导入相应的class文件&#xff08;3.2&#xff09;链接linking&#xff1a;把类的二进制数据合并到JRE中&#xff08;3.3&#xff09…

计算机的操作系统

目录 ❤ 什么是操作系统? ❤ 什么是文件? ❤ 什么是应用程序? ❤ 为什么要有操作系统? ​❤ 操作系统有什么用? ❤ 操作系统和应用程序的启动 python从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129328397?spm1001.201…

json-server的使用

流程 1.安装json-server的两个依赖 npm -g i json-server npm install -g json-server 2.安装axios依赖 npm i axios 3.全局导入axios使用src目录下main.js文件内 import axios from ‘axios’; 4.配置全局默认地址&#xff1a;src目录下main.js文件内 axios.defaults.bas…

十九、java虚拟机堆

堆的核心概述 1.一个JVM实例只存在一个堆内存&#xff0c;堆也是java内存管理的核心区域。 2.Java堆区子啊JVM启动的时候即被创建&#xff0c;其空间大小也就确定了&#xff0c;是jvm管理的最大一块内存空间&#xff0c; 1&#xff09;堆内存的大小是可以调节的。 3.《java虚拟…

运行时数据区及程序计数器

运行时数据区 概述 运行时数据区&#xff0c;也就是下图这部分&#xff0c;它是在类加载完成后的阶段 当我们通过前面的&#xff1a;类的加载-> 验证 -> 准备 -> 解析 -> 初始化 这几个阶段完成后&#xff0c;就会用到执行引擎对我们的类进行使用&#xff0c;同时…

记录Paint部分常用的方法

Paint部分常用的方法1、实例化之后Paint的基本配置2、shader 和 ShadowLayer3、pathEffect4、maskFilter5、colorFilter6、xfermode1、实例化之后Paint的基本配置 Paint.Align Align指定drawText如何将其文本相对于[x,y]坐标进行对齐。默认为LEFTPaint.Cap Cap指定了笔画线和路…

SpringBoot整合定时任务和邮件发送(邮箱 信息轰炸 整蛊)

SpringBoot整合定时任务和邮件发送&#xff08;邮箱 信息轰炸 整蛊&#xff09; 目录SpringBoot整合定时任务和邮件发送&#xff08;邮箱 信息轰炸 整蛊&#xff09;1.概述2.最佳实践2.1创建项目引入依赖(mail)2.2 修改yml配置文件2.3 启动类添加EnableScheduling注解2.4 执行的…

设计模式(三)--适配器模式(Adapter Pattern)

将一个接口转换成客户希望的另一个接口&#xff0c;适配器模式使接口不兼容的那些类可以一起工作.比如我们日常开发中使用到的slf4j就使用了适配器模式&#xff0c;slf4j提供了一系列打日志的api,底层调用的是log4j或者logback来打日志&#xff0c;而作为调用者&#xff0c;不需…