您当前的位置: 首页 >  android
  • 0浏览

    0关注

    674博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Android硬件加速、图形绘制相关

沙漠一只雕得儿得儿 发布时间:2021-04-01 15:35:24 ,浏览量:0

Android硬件加速原理与实现简介

https://tech.meituan.com/2017/01/19/hardware-accelerate.html

 

SurfaceFinger工作流程

https://juejin.cn/post/6898525503960186887

详细文章见 Android-SurfaceFlinger启动与工作原理

surfaceflinger 是在 Android 系统启动时解析 init.rc 文件启动的守护进程,在 SurfaceFlinger 的启动流程中:

  1. 首先会创建 SurfaceFlinger 对象,在构造器中创建了 DispSync 同步模型对象;
  2. 然后执行初始化 SurfaceFlinger 的逻辑:
    • 注册监听,接收 HWC 的相关事件。
    • 启动 APP 和 SF 的 EventThread 线程,用来管理基于 DispSync 创建的两个 DispSyncSource 延时源对象,分别是用于绘制(app--mEventThreadSource)和合成(SurfaceFlinger--mSfEventThreadSource)。启动了 EventThread 线程后,会一直阻塞在 waitForEventLocked 方法中(期间会根据需要设置监听器),直到接收到 Vsync 信号且至少有一个连接正在等待 Vsync 信号才会继续执行线程逻辑,即通知监听者;
    • 通过 MessageQueue.setEventThread 方法创建了一个连接,并通过 Looper.addFd 方法监听 BitTube 数据。
    • 创建 HWComposer 对象(通过 HAL 层的 HWComposer 硬件模块 或 软件模拟产生 Vsync 信号),现在的 Android 系统基本上都可以看成是通过硬件 HWComposer 产生 Vsync 信号,而不使用软件模拟,所以下面解析都只谈及硬件 HWComposer 的 Vsync 信号;
    • 初始化非虚拟的显示屏;
    • 启动开机动画服务;
  3. 最后执行 SurfaceFlinger.run 逻辑,该方法会在 SurfaceFlinger 主线程通过死循环执行 MessageQueue.waitMessage 方法等待消息的到来,其内部调用了 Looper.pollOnce 方法,该方法会从 Looper.addFd 方法监听的 BitTube 中读取数据,当有数据到来时执行对应的回调方法。

当硬件或软件模拟发出 Vsync 信号时:

  1. 回调 SF 相关方法,SF 调用 DispSync 同步模型的方法处理 Vsync 信号(统计和计算模型的偏移和周期),并根据返回值判断是否使能/关闭 HWC Vsync 信号的发出。
  2. DispSync 根据计算的偏移和周期计算下次 Vsync 信号发生时间,并通知监听者 Vsync 信号到达的事件,传递给 DispSyncSource 延时源,延时源通过 EventThread 来管理 Vsync 信号的收发。
  3. EventThread 调用连接 Connection 对象向 BitTube 发送数据,触发 addFd 函数中设置的回调方法,回调方法进而调用 SF.onMessageReceived 函数,然后进行图像的合成等工作。

另一方面,Choreographer 会通过上面创建的 APP 延时源 mEventThreadSource 对象及其对应的 EventThread 线程来监听同步模拟发出的 Vsync 信号,然后进行绘制(measure/layout/draw)操作。具体逻辑见 Android-Choreographer工作原理。

将 SurfaceFlinger 的工作流程总结如下图:

SurfaceFlinger工作流程

Choreographer工作流程

详细文章见 Android-Choreographer工作原理

  • Choreographer: 使 CPU/GPU 的绘制是在 VSYNC 到来时开始。Choreographer 初始化时会创建一个表示对 Vsync 信号感兴趣的连接,当有绘制请求时通过 postCallback 方法请求下一次 Vsync 信号,当信号到来后才开始执行绘制任务。
  • 只有当 App 注册监听下一个 Vsync 信号后才能接收到 Vsync 到来的回调。如果界面一直保持不变,那么 App 不会去接收每隔 16.6ms 一次的 Vsync 事件,但底层依旧会以这个频率来切换每一帧的画面(也是通过监听 Vsync 信号实现)。即当界面不变时屏幕也会固定每 16.6ms 刷新,但 CPU/GPU 不走绘制流程。
  • 当 View 请求刷新时,这个任务并不会马上开始,而是需要等到下一个 Vsync 信号到来时才开始;measure/layout/draw 流程运行完后,界面也不会立刻刷新,而会等到下一个 VSync 信号到来时才进行缓存交换和显示。
  • 造成丢帧主要有两个原因:一是遍历绘制 View 树以及计算屏幕数据超过了16.6ms;二是主线程一直在处理其他耗时消息,导致绘制任务迟迟不能开始(同步屏障不能完全解决这个问题)。
  • 可通过Choreographer.getInstance().postFrameCallback()来监听帧率情况。

阅读这篇文章建议先阅读 Android-SurfaceFlinger启动与工作原理 这篇文章,然后结合 Choreographer 的工作流程,可以对 Vsync 信号是怎么协调 App 端的绘制任务以及 SurfaceFlinger 的合成任务有一个比较清晰的认识。

用一张图总结一下 Choreographer 的工作流程:

Choreographer工作流程

View绘制流程

详细文章见 Android-Window机制源码解读 和 Android-View绘制原理

在 Choreographer 接收到 Vsync 信号后便开始 View 的绘制过程,即老生常谈的 measure, layout, draw 三大步骤。

Surface工作流程及软硬件绘制

详细文章见 Android-Surface之创建流程及软硬件绘制 和 Android-Surface之双缓冲及SurfaceView解析

在 View 的 draw 过程中会接触到 Surface 的逻辑,通过它可以向 SurfaceFlinger 申请一块缓存区用来绘制。绘制任务可以分为软件绘制与硬件绘制两种。

  • Java 层的 Surface 对象中 mNativeObject 属性指向 native 层中创建的 Surface 对象。
  • Surface 对应 SurfaceFlinger 中的 Layer 对象,它持有 Layer 中的 BufferQueueProducer 指针(生产者),通过这个生产者对象可以在绘制时向 BufferQueue 申请一块空闲的图形缓存区 GraphicBuffer,在 Surface 上绘制的内容会存入该缓存区内。
  • SurfaceFlinger 通过 BufferQueueConsumer 消费者从 BufferQueue 中取出 GraphicBuffer 中的数据进行合成渲染并送到显示器显示。

软件绘制

软件绘制可能会绘制到不需要重绘的视图,且其绘制过程在主线程进行的,可能会造成卡顿等情况。它把要绘制的内容写进一个 Bitmap 位图,其实就是填充到了 Surface 申请的图形缓存区里。

软件绘制可分为三个步骤:

  1. Surface.lockCanvas -- dequeueBuffer 从 BufferQueue 中出队列一块缓存区。
  2. View.draw -- 绘制内容。
  3. Surface.unlockCanvasAndPost -- queueBuffer 将填充了数据的缓存区存入 BufferQueue 队列中,然后通知给 SurfaceFlinger 进行合成(请求 Vsync 信号)。

硬件绘制

硬件绘制会将绘制函数作为绘制指令(DrawOp)记录在一个列表(DisplayList)中,然后交给单独的 Render 线程使用 GPU 进行硬件加速渲染。它只需要针对需要更新的 View 对象的脏区进行记录或更新,无需更新的 View 对象则能重用先前 DisplayList 中记录的指令。

硬件绘制可分为两个阶段:

  1. 构建阶段:将 View 的绘制操作(drawLine...)抽象成 DrawOp 操作并存入 DisplayList 中。
  2. 绘制阶段:首先分配缓存区(同软件绘制),然后将 Surface 绑定到 Render 线程,最后通过 GPU 渲染 DrawOp 数据。

硬件加速的内存申请跟软件绘制一样都是借助 Layer 中的 BufferQueueProducer 生产者从 BufferQueue 中出队列一块空闲缓存区 GraphicBuffer 用来渲染数据的,之后也都会通知 SurfaceFlinger 进行合成。不一样的地方在于硬件加速相比软件绘制而言算法可能更加合理,同时采用了一个单独的 Render 线程,减轻了主线程的负担。

双缓冲

一般来说将双缓冲用到的两块缓冲区称为 -- 前缓冲区(front buffer) 和 后缓冲区(back buffer)。显示器显示的数据来源于 front buffer 前缓存区,而每一帧的数据都绘制到 back buffer 后缓存区,在 Vsync 信号到来后会交互缓存区的数据(指针指向),这时 front buffer 和 back buffer 的称呼及功能倒转。

在 View 的绘制过程中 Surface 使用了双缓冲技术。

SurfaceView

SurfaceView 就是一块拥有自己独立 Surface 的特殊 View 视图,由于这个特性,它一般用来实现比较复杂的图像或动画/视频的显示。

用一张图总结一下 Android 软硬件绘制的流程:

Android-软硬件绘制流程

图形系统工作流程总结

到这里总算将 Android 图形系统的工作流程串联起来了,在阅读了源码之后感觉对这个流程有了更清晰的理解,而不是跟着网上的结论人云亦云,感觉每个人说的都有道理,但是总有些地方觉得又有冲突,自己阅读源码跟看别人阅读源码之后给出的结论,感觉确实不一样,Read The Fucking Source Code 是一种很好的解惑手段,接下来剩下的就是这些流程中的某些细节了,比如说 requestLayout 和 invalidate 方法的区别之类的,这些相关的问题以后有时间也会慢慢记录一下。文中内容如有错误欢迎指出,共同进步!觉得不错的留个赞再走哈~

用一张图总结一下图形系统工作的整体流程:

Android图形系统工作流程

 

关注
打赏
1657159701
查看更多评论
立即登录/注册

微信扫码登录

0.0400s