array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 Android 图形架构之一 ——概述 - 爱码网

前言

本系列的文章,可以让你明白,一个View最终是如何显示到屏幕上的,从应用层到硬件抽象层。对分析app的卡顿,掉帧等 有很大帮助。

由于图形架构的涉及到的代码量很大,所以本篇先来个总体的概述,有个纵观全局的感觉,再看每个细节,就能心中有数。战略上把握好了,战术有点失误,也能不影响大局

Android 图形架构 之一 ——概述
Android 图形架构 之二—— SurfaceFlinger 创建和连接
Android 图形架构 之三—— 创建Surface
Android 图形架构 之四——图形缓冲区GraphicBuffer
Android 图形架构 之五——Activity、WindowManagerService、Surface的关系
Android图形架构 之六——硬件VSync、VSync-app、Vsync-sf
Android图形架构 之七——SurfaceFlinger 合成Layer

App 的绘图方式

应用层可通过两种方式将图像绘制到屏幕上:使用 Canvas 或 OpenGL :

  • android.graphics.Canvas 是一个 2D 图形 API , Canvas API 通过一个名为 OpenGLRenderer 的绘制库实现硬件加速,该绘制库将 Canvas 运算转换为 OpenGL 运算,以便它们可以在 GPU 上执行。从 Android 4.0 开始,硬件加速的 Canvas 默认情况下处于启用状态

  • 使用 OpenGL ES 直接渲染到 Surface 。 Android 在 Android.opengl 软件包中提供了 OpenGL ES 接口

下面这张官方图片,提现了 图像流 从 Image stream producers 到Surface,再被 Image stream consumers 中的SurfaceFlinger(其中也有OpenGl ES的一些工作)消费掉,再到硬件抽象层,最后显示到屏幕上

Android 图形架构之一 ——概述

Activity 也是需要创建Surface的, Activity显示流程为:

  1. startActivity启动Activity;

  2. 为Activity创建一个window(PhoneWindow),并在WindowManagerService中注册这个window;

  3. WindowManagerService会要求SurfaceFlinger为这个window创建一个surface用来绘图。SurfaceFlinger创建一个Surface。(在SurfaceFlinger中Surface 是用Layer来体现的,也就是说App中的一个Surface ,对应SurfaceFlinger的一个Layer),这个layer的核心即是一个BufferQueue(),这时候app就可以在这个layer上render了;

  4. 将所有的layer进行合成,显示到屏幕上。

一般app而言,屏幕会有三个layer:屏幕顶端的status bar,屏幕下面的navigation bar,还有就是app的UI部分。app的layer可能多余或者少于3个,例如对全屏显示的app就没有status bar。status bar和navigation bar是由系统进行去render。而app的UI部分对应的layer 是由它自己去处理(通知SurfaceFlinger处理),最后需要把这些layer合成 。

Surface和SurfaceFlinger

无论开发者使用Canvas 或 OpenGLI,一切内容都会渲染到 Surface 。在 Android 平台上创建的每个窗口都由 Surface 提供支持。

SurfaceFlinger 会把系统中所有应用程序的最终的“绘图结果”进行“混合”,然后统一显示到物理屏幕上,

Android 图形架构之一 ——概述

GraphicBuffer、BufferQueue

Android 图形架构之一 ——概述

图像流生产者与图像流消费者 的数据传递就是通过 BufferQueue ,里面有很多的GraphicBuffer

这是生产者消费者模型。 一旦生产者 有新数据,就会通知 SurfaceFlinger 进行消费,最后显示到屏幕

生产者和消费者可能是在不同的进程,它们的通信不是用Binder,为了高效传输大块数据,使用匿名共享内存,BufferQueue 不会复制缓冲区内容;通过句柄进行传递。

下图是上图的细化

接下来具体说明客户端(producer)和服务端SurfaceFlinger(consumer)工作的模式:
首先这里的buffer是共享缓冲区,故肯定会涉及到互斥锁,所以buffer的状态也会有多种,一般的buffer大致会经过FREE->DEQUEUED->QUEUED->ACQUIRED->FREE这个流程,如下图:
Android 图形架构之一 ——概述

  • BufferQueue:Producer向BufferQueue中写数据,Consumer从BufferQueue中读数据。

  • Producer:生产者。因为应用程序不断地刷新UI,从而将产生的显示数据源源不断地写到BufferQueue:Producer中。当Producer需要使用一块buffer时,调用BufferQueue的dequeue函数,获得的buffer就只属于producer,当生产者认为一块buffer已经写入完成后,调用BufferQueue的queue函数,把buffer归还到BufferQueue的队列中。

  • Consumer:消费者 。当生产者准备好数据后,BufferQueue就会调用onFrameAvailable()来通知Consumer进行消费

图像生产者 例如:相机 HAL 或 OpenGL ES 游戏生成的相机预览。
图像消费者 例如: SurfaceFlinger 或显示 OpenGL ES 流的另一个应用,如显示相机取景器的相机应用。

BufferQueue 通常用于渲染到 Surface ,并且与 GL 消费者及其他任务一起消耗内容。 BufferQueue 可以在三种不同的模式下运行:

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

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

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

EGL OpenGL

Android 平台图形处理 API 的标准:

  • OpenGL
    是由 SGI 公司开发的一套 3D 图形软件接口标准,由于具有体系结构简单合理、使用方便、与操作平台无关等优点, OpenGL 迅速成为 3D 图形接口的工业标准,并陆续在各种平台上得以实现。

  • OpenGL ES
    是由 khronos 组织根据手持及移动平台的特点,对 OpenGL 3D 图形 API 标准进行裁剪定制而形成的。

  • Vulkan
    是由 khronos 组织在 2016 年正式发布的,是 OpenGL ES 的继任者。 API 是轻量级、更贴近底层硬件 close-to-the-metal 的接口,可使 GPU 驱动软件运用多核与多线程 CPU 性能。

OpenGL ES 是 Android 绘图 API ,但 OpenGL ES 是平台通用的,与系统无关的,在特定设备上使用需要一个中间层做适配, Android 中这个中间层就是 EGL 。

FrameBuffer

Linux 抽象出 FrameBuffer 这个设备来供用户态进程实现直接写屏。 FrameBuffer 机制模仿显卡的功能,是显卡硬件的抽象,可以通过 FrameBuffer 的读写直接对显存进行操作。

用户可以将 FrameBuffer 看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。

用户不必关心物理显存的位置、换页机制等等具体细节,这些都是由 FrameBuffer 设备驱动来完成的。但 FrameBuffer 本身不具备任何运算数据的能力。 CPU 将运算后的结果放到FrameBuffer,就会显示处理,中间不会对数据做处理。应用程序也可以直接读写FrameBuffer,尽管 FrameBuffer 需要真正的显卡驱动的支持,但所有显示任务都有 CPU 完成,因此 CPU 负担很重。

帧缓存可以在系统存储器(内存)的任意位置,视频控制器通过访问帧缓存来刷新屏幕。帧缓存也叫刷新缓存 FrameBuffer 或 RefreshBuffer ,这里的帧 Frame 是指整个屏幕范围。帧缓存有个地址,是在内存里。我们通过不停的向 FrameBuffer 中写入数据,显示控制器就自动的从 FrameBuffer 中取数据并显示出来。全部的图形都共享内存中同一个帧缓存。

FrameBuffer 帧缓冲实际上包括两个不同的方面:

  • Frame :帧,就是指一幅图像,在屏幕上看到的那幅图像就是一帧
  • Buffer :缓冲,就是一段存储区域,可这个区域存储的是帧

FrameBuffer 就是一个存储图形/图像帧数据的缓冲。Linux 内核提供了统一的 Framebuffer 显示驱动,设备节点 /dev/graphics/fb* 或者 /dev/fb* ,以 fb0 表示第一个 Monitor ,这个虚拟设备将不同硬件厂商实现的真实设备统一在一个框架下,这样应用层就可以通过标准的接口进行图形/图像的输入和输出了:

Gralloc

Gralloc 的含义为是 Graphics Alloc 图形分配 。 Android 系统在硬件抽象层中提供了一个 Gralloc 模块,封装了对 Framebuffer 的所有访问操作。

Gralloc 模块符合 Android 标准的 HAL 架构设计;它分为 fb 和 gralloc 两个设备:前者负责打开内核中的 Framebuffer 、初始化配置,以及提供 post, setSwapInterval 等操作;后者则管理帧缓冲区的分配和释放。上层只能通过 Gralloc 访问帧缓冲区,这样一来就实现了有序的封装保护。

Gralloc 分配器返回的句柄可以通过 Binder 在进程之间传递。

Hardware Composer

HWC(hwcomposer)是Android中进行窗口(Layer)合成和显示的HAL层模块,其实现是特定于设备的,而且通常由显示设备制造商 (OEM)完成,为SurfaceFlinger服务提供硬件支持。

SurfaceFlinger可以使用OpenGL ES合成Layer,这需要占用并消耗GPU资源。大多数GPU都没有针对图层合成进行优化,当SurfaceFlinger通过GPU合成图层时,应用程序无法使用GPU进行自己的渲染。而HWC通过硬件设备进行图层合成,可以减轻GPU的合成压力。
显示设备的能力千差万别,很难直接用API表示硬件设备支持合成的Layer数量,Layer是否可以进行旋转和混合模式操作,以及对图层定位和硬件合成的限制等。因此HWC描述上述信息的流程是这样的:

  1. SurfaceFlinger向HWC提供所有Layer的完整列表,让HWC根据其硬件能力,决定如何处理这些Layer。
  2. HWC会为每个Layer标注合成方式,是通过GPU还是通过HWC合成。
  3. SurfaceFlinger负责先把所有注明GPU合成的Layer合成到一个输出Buffer,然后把这个输出Buffer和其他Layer(注明HWC合成的Layer)一起交给HWC,让HWC完成剩余Layer的合成和显示。

至此,本系列涉及到的内容主要内容,都大概描述了一下,还有一些流程中涉及到的VSync,WMS等,这些不是很影响整体的逻辑,再后面的文章再具体分析。

相关文章: