Androidt图形绘制原理从底层到上层学习(二) Android 图形组件Surface、SurfaceFlinger、Window相关

时间:2024-04-04 17:52:48

下面内容转载自知乎:Android的UI底层是用CPU绘图的还是GPU绘图的?以及surfaceview,window,普通view是如何实现的?

https://www.zhihu.com/question/25811504

 首先亮图,然后再给出定义。

Androidt图形绘制原理从底层到上层学习(二) Android 图形组件Surface、SurfaceFlinger、Window相关

  1. 表面(Surface): Surface就是指向显存的一个物体,用来被绘制到屏幕上,所有你能看见的Window都拥有可以在上面绘制的Surface,在安卓中,系统使用Surface Flinger服务来把Surface按照正确的深度信息渲染到最终的屏幕上。一个Surface一般拥有超过一个(通常是2个) buffer 单元(back buffer 与 front buffer),系统通常在back buffer中进行渲染,完成之后与front buffer交换,这样显示到屏幕上,以实现流畅显示的效果。
  2. 窗口(Window): Window拥有一个Surface,在Surface里绘制Window里的内容。一个application通过Windows Manager来创建窗口,Windows Manager为每一个窗口创建Surface来让application在上面绘制各种物体。
  3. 视图(View):视图就窗口里的UI元素,一个窗口只拥有一个View Hierarchy,这些View Hierarchy提供了窗口里的所有表现。当一个窗口需要重新绘制时(比如一个View invalidate自己),锁定Surface,并返回一个Canvas用来在上面绘制,如上图所示,在view hierarchy树向下传递Canvas,来绘制每个view。这一切都完成后,Surface被解锁,并通过Surface Flinger交换前后Buffer来显示到屏幕上。
  4. Canvas:Canvas是Surface绘图时返回的一个接口,并提供一些绘图api,用来进行实际的绘图操作。目前Canvas可以绘制在bitmap或者openGL container上。
  5. SurfaceView: SurfaceView是View的一个特殊子类,它拥有专有的Surface,使application可以直接在上面绘制(普通的view hierarchy必须共享窗口唯一的surface)。这其中的原理很简单,SurfaceView就是请求Window Manager创建一个新窗口,并改变窗口之间的深度信息来显示。如果SurfaceView的Window显示在主窗口的后面,surfaceview将主窗口相应的位置设置成透明来使可见。

总结一下,一个activity拥有一个window(用来绘制它的UI), 并且一个Window只有一个Surface和View hierarchy来绘制,SurfaceView实质上是创建了一个新的窗口,所以拥有自己独立的Surface,可以直接绘制在上面。


    下面的内容来自google教程中的图片,梳理下canvas/OpenGL -> Surface -> SurfaceFlinger -> HAL(Hardware Composer / Gralloc)的渲染流程


图形

Androidt图形绘制原理从底层到上层学习(二) Android 图形组件Surface、SurfaceFlinger、Window相关

Android 框架提供了各种用于 2D 和 3D 图形渲染的 API,可与制造商的图形驱动程序实现方法交互,因此,务必充分了解这些 API 如何在更高的级别工作。本页介绍了在其上构建这些驱动程序的图形硬件抽象层 (HAL)。

应用开发者可通过两种方式将图像绘制到屏幕上:使用 Canvas 或 OpenGL。有关 Android 图形组件的详细说明,请参阅系统级图形架构

android.graphics.Canvas 是一个 2D 图形 API,而且是最受开发者群体青睐的图形 API。Canvas 运算会在 Android 中绘制所有原生和自定义 android.view.View。在 Android 中,Canvas API 通过一个名为 OpenGLRenderer 的绘制库实现硬件加速,该绘制库将 Canvas 运算转换为 OpenGL 运算,以便它们可以在 GPU 上执行。

从 Android 4.0 开始,硬件加速的 Canvas 默认情况下处于启用状态。因此,支持 OpenGL ES 2.0 的硬件 GPU 对于 Android 4.0 及更高版本的设备来说是强制要求。有关硬件加速绘制路径的工作原理及其行为与软件绘制路径行为之间的差异的说明,请参阅硬件加速指南

除了 Canvas,开发者渲染图形的另一个主要方式是使用 OpenGL ES 直接渲染到 Surface。Android 在 Android.opengl 软件包中提供 OpenGL ES 接口,开发者可以使用这些接口通过 SDK 或原生 API(在 Android NDK 中提供)来调用其 GL 实现。

Android 实现人员可以使用 drawElements 质量计划(也称为 deqp)来测试 OpenGL ES 功能。

Android 图形组件

无论开发者使用什么渲染 API,一切内容都会渲染到“Surface”。Surface 表示缓冲队列中的生产方,而缓冲队列通常会被 SurfaceFlinger 消耗。在 Android 平台上创建的每个窗口都由 Surface 提供支持。所有被渲染的可见 Surface 都被 SurfaceFlinger 合成到显示部分。

下图显示了关键组件如何协同工作:

Androidt图形绘制原理从底层到上层学习(二) Android 图形组件Surface、SurfaceFlinger、Window相关

图 1. Surface 如何被渲染

主要组件如下所述:

图像流生产方

图像流生产方可以是生成图形缓冲区以供消耗的任何内容。例如 OpenGL ES、Canvas 2D 和 mediaserver 视频解码器。

图像流消耗方

图像流的最常见消耗方是 SurfaceFlinger,该系统服务会消耗当前可见的 Surface,并使用窗口管理器中提供的信息将它们合成到显示部分。SurfaceFlinger 是可以修改所显示部分内容的唯一服务。SurfaceFlinger 使用 OpenGL 和 Hardware Composer 来合成一组 Surface。

其他 OpenGL ES 应用也可以消耗图像流,例如相机应用会消耗相机预览图像流。非 GL 应用也可以是消耗方,例如 ImageReader 类。

窗口管理器

控制窗口的 Android 系统服务,它是视图容器。窗口总是由 Surface 提供支持。该服务会监督生命周期、输入和聚焦事件、屏幕方向、转换、动画、位置、变形、Z-Order 以及窗口的其他许多方面。窗口管理器会将所有窗口元数据发送到 SurfaceFlinger,以便 SurfaceFlinger 可以使用该数据在显示部分合成 Surface。

硬件混合渲染器

显示子系统的硬件抽象实现。SurfaceFlinger 可以将某些合成工作委托给 Hardware Composer,以分担 OpenGL 和 GPU 上的工作量。SurfaceFlinger 只是充当另一个 OpenGL ES 客户端。因此,在 SurfaceFlinger 将一个或两个缓冲区合成到第三个缓冲区中的过程中,它会使用 OpenGL ES。这样使合成的功耗比通过 GPU 执行所有计算更低。

Hardware Composer HAL 则进行另一半的工作,并且是所有 Android 图形渲染的核心。Hardware Composer 必须支持事件,其中之一是 VSYNC(另一个是支持即插即用 HDMI 的热插拔)。

Gralloc

需要使用图形内存分配器 (Gralloc) 来分配图像生产方请求的内存。有关详情,请参阅 Gralloc HAL

数据流

有关 Android 图形管道的描述,请参见下图:

Androidt图形绘制原理从底层到上层学习(二) Android 图形组件Surface、SurfaceFlinger、Window相关

图 2. 流经 Android 的图形数据流

左侧的对象是生成图形缓冲区的渲染器,如主屏幕、状态栏和系统界面。SurfaceFlinger 是合成器,而硬件混合渲染器是制作器。

BufferQueue

BufferQueues 是 Android 图形组件之间的粘合剂。它们是一对队列,可以调解缓冲区从生产方到消耗方的固定周期。一旦生产方移交其缓冲区,SurfaceFlinger 便会负责将所有内容合成到显示部分。

有关 BufferQueue 通信过程,请参见下图。

Androidt图形绘制原理从底层到上层学习(二) Android 图形组件Surface、SurfaceFlinger、Window相关

图 3. BufferQueue 通信过程

BufferQueue 包含将图像流生产方与图像流消耗方结合在一起的逻辑。图像生产方的一些示例包括由相机 HAL 或 OpenGL ES 游戏生成的相机预览。图像消耗方的一些示例包括 SurfaceFlinger 或显示 OpenGL ES 流的另一个应用,如显示相机取景器的相机应用。

BufferQueue 是将缓冲区池与队列相结合的数据结构,它使用 Binder IPC 在进程之间传递缓冲区。生产方接口,或者您传递给想要生成图形缓冲区的某个人的内容,即是 IGraphicBufferProducer(SurfaceTexture 的一部分)。BufferQueue 通常用于渲染到 Surface,并且与 GL 消耗方及其他任务一起消耗内容。BufferQueue 可以在三种不同的模式下运行:

类同步模式 - 默认情况下,BufferQueue 在类同步模式下运行,在该模式下,从生产方进入的每个缓冲区都在消耗方那退出。在此模式下不会舍弃任何缓冲区。如果生产方速度太快,创建缓冲区的速度比消耗缓冲区的速度更快,它将阻塞并等待可用的缓冲区。

非阻塞模式 - BufferQueue 还可以在非阻塞模式下运行,在此类情况下,它会生成错误,而不是等待缓冲区。在此模式下也不会舍弃缓冲区。这有助于避免可能不了解图形框架的复杂依赖项的应用软件出现潜在死锁现象。

舍弃模式 - 最后,BufferQueue 可以配置为丢弃旧缓冲区,而不是生成错误或进行等待。例如,如果对纹理视图执行 GL 渲染并尽快绘制,则必须丢弃缓冲区。

为了执行这项工作的大部分环节,SurfaceFlinger 就像另一个 OpenGL ES 客户端一样工作。例如,当 SurfaceFlinger 正在积极地将一个缓冲区或两个缓冲区合成到第三个缓冲区中时,它使用的是 OpenGL ES。

Hardware Composer HAL 执行另一半工作。该 HAL 充当所有 Android 图形渲染的中心点。

来源:

https://source.android.google.cn/devices/graphics/


更加深入的学习:

Android有两种绘制模式—— 依赖CPU实现的软件渲染,依赖GPU实现的硬件渲染。

先回答第1个疑问:UI底层是GPU绘制  

Android绘制时可采用两种方式:Canvas和OpenGL。android.graphics.Canvas 是Android中的2D绘制API,如果不开硬件加速,就是软件渲染,如果开了硬件加速,就是硬件渲染,内部是通过OpenGLRenderer这个类将Canvas的绘制过程交给GPU的。而Android 4.0之后默认开启了硬件加速,不过目前4.0之前的手机已经占有率已经很低。  OPenGL用于3D绘图,底层是GPU渲染。   

第2个疑问:Surfaceview以及其他的Textview,Imageview在底层实现上究竟有什么区别?  

第3个疑问:在自定义view时有个canvas接口,这个又和surface有什么关系?

    先说Android中有3类窗口:  应用窗口(Application Window):对应一个Activity,Activity中Window的实现类是PhoneWindow,这也是目前Window类的唯一实现;  

    子窗口(Sub Window):必须依附于任何类型的父窗口,例如PopupWinow、Dialog等;  

    系统窗口(System Window): 系统设计的,不依附于任何应用的窗口,比如说,状态栏(Status Bar), 导航栏(Navigation Bar), 壁纸(Wallpaper), 来电显示窗口(Phone),锁屏窗口(KeyGuard), 信息提示窗口(Toast), 音量调整窗口,鼠标光标等等。  

    各种Textview,Imageview等控件构成view hierarchy,view hierarchy会交给一个组件,例如将布局文件设置到Activity,view hierarchy通过这个组件对应了一个Window(例如Activity中的PhoneWindow),而Window中持有一个Surface对象,就是真实绘制表面。这个Suface对象最终作为数据元,通过匿名共享内存机制进行跨进程通讯,交给SurfaceFlinger。 

    这里再挖2个图:

Android应用程序与SurfaceFlinger服务的关系概述和学习计划https://blog.csdn.net/luoshengyang/article/details/7846923

Androidt图形绘制原理从底层到上层学习(二) Android 图形组件Surface、SurfaceFlinger、Window相关


Androidt图形绘制原理从底层到上层学习(二) Android 图形组件Surface、SurfaceFlinger、Window相关


整个绘制流程都是在主线程即UI线程中跑的,各种measure和draw函数都是在主线程中。  而Suraceview虽然也嵌入了view hierarchy,但是却不是在主线程绘制,而是在子线程中绘制,为什么要提供Suraceview呢?主要用于耗时绘制的场景,例如视频、动画等