Android Display System --- Surface Flinger
SurfaceFlinger 是Android multimedia 的一个部分,在Android 的实现中它是一个service ,提供系统范围内的surface composer 功能,它能够将各种[url=]应用[/url]程序的2D 、3D surface 进行组合。在具体讲SurfaceFlinger 之前,我们先来看一下有关显示方面的一些[url=]基础[/url][url=]知识[/url]。 1 、[url=]原理[/url]分析 让我们首先看一下下面的屏幕简略图: 每个应用程序可能对应着一个或者多个图形界面,而每个界面我们就称之为一个surface ,或者说是window ,在上面的图中我们能看到4 个surface ,一个是home 界面,还有就是红、绿、蓝分别代表的3 个surface ,而两个button 实际是home surface 里面的内容。在这里我们能看到我们进行图形显示所需要[url=]解决[/url]的问题:
b 、然后就各个surface 之间可能有重叠,比如说在上面的简略图中,绿色覆盖了蓝色,而红色又覆盖了绿色和蓝色以及下面的home ,而且还具有一定透明度。这种层之间的关系应该如何描述?
在IBinder 左边的就是客户端部分,也就是需要窗口显示的应用程序,而右边就是我们的Surface Flinger service 。 创建一个surface 分为两个过程,一个是在SurfaceFlinger 这边为每个应用程序(Client) 创建一个[url=]管理[/url]结构,另一个就是创建存储内容的buffer ,以及在这个buffer 上的一系列画图之类的操作。 因为SurfaceFlinger 要管理多个应用程序的多个窗口界面,为了进行管理它提供了一个Client 类,每个来请求服务的应用程序就对应了一个Client 。因为surface 是在SurfaceFlinger 创建的,必须返回一个结构让应用程序知道自己申请的surface 信息,因此SurfaceFlinger 将Client 创建的控制结构per_client_cblk_t 经过BClient 的封装以后返回给SurfaceComposerClient ,并向应用程序提供了一组创建和销毁surface 的操作:
创建 Layer 的过程,首先是由这个应用程序的 Client 根据应用程序的 pid 生成一个唯一的 layer ID ,然后根据大小,位置,格式啊之类的信息创建出 Layer 。在 Layer 里面有一个嵌套的 Surface 类,它主要包含一个ISurfaceFlingerClient::Surface_data_t ,包含了这个 Surace 的统一标识符以及 buffer 信息等,提供给应用程序使用。最后应用程序会根据返回来的 ISurface 信息等创建自己的一个 Surface 。 Android 提供了 4 种类型的 layer 供选择,每个 layer 对应一种类型的窗口,并对应这种窗口相应的操作: Layer ,LayerBlur , LayerBuffer , LayerDim 。不得不说再说 Android 起的乱七八糟的名字, LayerBuffer 很容易让人理解成是Layer 的 Buffer ,它实际上是一种 Layer 类型。各个 Layer 的效果大家可以参考 Surface.java 里面的描述:/frameworks/base/core/java/android/view/surface.java 。这里要重点说一下两种 Layer ,一个是 Layer (norm layer) ,另一个是 LayerBuffer 。 Norm Layer 是 Android 种使用最多的一种 Layer ,一般的应用程序在创建 surface 的时候都是采用的这样的 layer ,了解 Normal Layer 可以让我们知道 Android 进行 display 过程中的一些基础原理。 Normal Layer 为每个 Surface 分配两个 buffer : front buffer 和 back buffer ,这个前后是相对的概念,他们是可以进行 Flip 的。 Front buffer 用于 SurfaceFlinger 进行显示,而 Back buffer 用于应用程序进行画图,当 Back buffer 填满数据 (dirty) 以后,就会 flip , back buffer 就变成了 front buffer 用于显示,而 front buffer 就变成了 back buffer 用来画图,这两个 buffer 的大小是根据 surface 的大小格式动态变化的。这个动态变化的实现我没仔细看,可以参照 : /frameworks/base/lib/surfaceflinger/layer.cpp 中的 setbuffers() 。 两个 buffer flip 的方式是 Android display 中的一个重要实现方式,不只是每个 Surface 这么实现,最后写入 FB 的 main surface 也是采用的这种方式。 LayerBuffer 也是将来必定会用到的一个 Layer ,个人觉得也是最复杂的一个 layer ,它不具备 render buffer ,主要用在 camera preview / video playback 上。它提供了两种实现方式,一种就是 post buffer ,另外一种就是我们前面提到的 overlay , Overlay 的接口实际上就是在这个 layer 上实现的。不管是 overlay 还是 post buffer 都是指这个 layer 的数据来源自其他地方,只是 post buffer 是通过软件的方式最后还是将这个 layer merge 主的 FB ,而 overlay 则是通过硬件 merge 的方式来实现。与这个 layer 紧密联系在一起的是 ISurface 这个接口,通过它来注册数据来源,下面我举个例子来说明这两种方式的使用方法: 前面几个步骤是通用的: // 要使用 Surfaceflinger 的服务必须先创建一个 client sp<SurfaceComposerClient> client = new SurfaceComposerClient(); // 然后向 Surfaceflinger 申请一个 Surface , surface 类型为 PushBuffers sp<Surface> surface = client->createSurface(getpid(), 0, 320, 240, PIXEL_FORMAT_UNKNOWN, ISurfaceComposer::ePushBuffers); // 然后取得 ISurface 这个接口, getISurface() 这个函数的调用时具有权限限制的,必须在 Surface.h 中打开: /framewoks/base/include/ui/Surface.h sp<ISurface> isurface = Test::getISurface(surface); //overlay 方式下就创建 overlay ,然后就可以使用 overlay 的接口了 sp<OverlayRef> ref = isurface->createOverlay(320, 240, PIXEL_FORMAT_RGB_565); sp<Overlay> verlay = new Overlay(ref); //post buffer 方式下,首先要创建一个 buffer ,然后将 buffer 注册到 ISurface 上 ISurface::BufferHeap buffers(w, h, w, h, PIXEL_FORMAT_YCbCr_420_SP, transform, 0, mHardware->getPreviewHeap()); mSurface->registerBuffers(buffers); 3.2 、[url=]应用 [/url]程序对窗口的控制和画图 [url=]Surface [/url]创建以后,应用程序就可以在 buffer 中画图了,这里就面对着两个问题了,一个是怎么知道在哪个 buffer 上来画图,还一个就是画图以后如何通知 SurfaceFlinger 来进行 flip 。除了画图之外,如果我们移动窗口以及改变窗口大小的时候,如何告诉 SurfaceFlinger 来进行处理呢 ?在明白这些问题之前,首先我们要了解 SurfaceFlinger 这个[url=]服务 [/url]是如何运作的: 从类图中可以看到 SurfaceFlinger 是一个线程类,它继承了 Thread 类。当创建 SurfaceFlinger 这个服务的时候会启动一个 SurfaceFlinger 监听线程,这个线程会一直等待事件的发生,比如说需要进行 sruface flip ,或者说窗口位置大小发生了变化等等,一旦产生这些事件, SurfaceComposerClient 就会通过 IBinder 发出信号,这个线程就会结束等待处理这些事件,处理完成以后会继续等待,如此循环。 SurfaceComposerClient 和 SurfaceFlinger 是通过 SurfaceFlingerSynchro 这个类来同步信号的,其实说穿了就是一个条件变量。监听线程等待条件的值变成 OPEN ,一旦变成 OPEN 就结束等待并将条件置成 CLOSE 然后进行事件处理,处理完成以后再继续等待条件的值变成 OPEN ,而 Client 的 Surface 一旦改变就通过 IBinder 通知 SurfaceFlinger 将条件变量的值变成 OPEN ,并唤醒等待的线程,这样就通过线程类和条件变量实现了一个动态处理机制。 了解了 SurfaceFlinger 的事件机制我们再回头看看前面提到的问题了。首先在对 Surface 进行画图之前必须锁定 Surface 的 layer ,实际上就是锁定了 Layer_cblk_t 里的 swapstate 这个变量。 SurfaceComposerClient 通过 swapsate 的值来确定要使用哪个 buffer 画图,如果 swapstate 是下面的值就会阻塞 Client ,就不翻译了直接 copy 过来: // We block the client if: // eNextFlipPending: we've used both buffers already, so we need to // wait for one to become availlable. // eResizeRequested: the buffer we're going to acquire is being // resized. Block until it is done. // eFlipRequested && eBusy: the buffer we're going to acquire is // currently in use by the server. // eInvalidSurface: this is a special case, we don't block in this // case, we just return an error. 所以应用程序先调用 lockSurface() 锁定 layer 的 swapstate ,并获得画图的 buffer 然后就可以在上面进行画图了,完成以后就会调用 unlockSurfaceAndPost() 来通知 SurfaceFlinger 进行 Flip 。或者仅仅调用 unlockSurface() 而不通知 SurfaceFlinger 。 一般来说画图的过程需要重绘 Surface 上的所有像素,因为一般情况下显示过后的像素是不做保存的,不过也可以通过设定来保存一些像素,而只绘制部分像素,这里就涉及到像素的拷贝了,需要将 Front buffer 的内容拷贝到 Back buffer 。在 SurfaceFlinger 服务实现中像素的拷贝是经常需要进行的操作,而且还可能涉及拷贝过程的转换,比如说屏幕的旋转,翻转等一系列操作。因此 [url=]Android [/url]提供了拷贝像素的 hal ,这个也可能是我们将来需要实现的,因为用硬件完成像素的拷贝,以及拷贝过程中可能的矩阵变换等操作,比用 memcpy 要有效率而且节省资源。这个 HAL 头[url=]文件 [/url]在: /hardware/libhardware/hardware/include/copybit.h 窗口状态变化的处理是一个很复杂的过程,首先要说明一下, SurfaceFlinger 只是执行 [url=]Windows [/url]manager 的指令,由 Windows manager 来决定什么是偶改变大小,位置,[url=]设置 [/url]透明度,以及如何调整 layer 之间的顺序, SurfaceFlinger 仅仅只是执行它的指令。 PS : Windows Manager 是 [url=]java [/url]层的一个服务,提供对所有窗口的[url=]管理 [/url]功能,这部分的内容我没细看过,觉得是将来需要了解的内容。 窗口状态的变化包括位置的移动,窗口大小,透明度, z-order 等等,首先我们来了解一下 SurfaceComposerClient 是如何和 SurfaceFlinger 来交互这些信息的。当应用程序需要改变窗口状态的时候它将所有的状态改变信息打包,然后一起发送给 SurfaceFlinger , SurfaceFlinger 改变这些状态信息以后,就会唤醒等待的监听线程,并设置一个标志位告诉监听线程窗口的状态已经改变了,必须要进行处理,在 Android 的实现中,这个打包的过程就是一个 Transaction ,所有对窗口状态 (layer_state_t) 的改变都必须在一个 Transaction 中。 到这里应用程序客户端的处理过程已经说完了,基本分为两个部分,一个就是在窗口画图,还一个就是窗口状态改变的处理。 4 、 SurfaceFlinger 的处理过程 了解了 Flinger 和客户端的交互,我们再来仔细看看 SurfaceFlinger 的处理过程,前面已经说过了 SurfaceFlinger 这个服务在创建的时候会启动一个监听的线程,这个线程负责每次窗口更新时候的处理,下面我们来仔细看看这个线程的事件的处理,大致就是下面的这个图: [/url] 先大致讲一下 Android 组合各个窗口的[url=]原理 : Android 实际上是通过计算每一个窗口的可见区域,就是我们在屏幕上可见的窗口区域 ( 用 Android 的词汇来说就是 visibleRegionScreen ) ,然后将各个窗口的可见区域画到一个主 layer 的相应部分,最后就拼接成了一个完整的屏幕,然后将主 layer 输送到 FB 显示。在将各个窗口可见区域画到主 layer 过程中涉及到一个硬件实现和一个软件实现的问题,如果是软件实现则通过 Opengl 重新画图,其中还包括存在透明度的 alpha 计算;如果实现了 copybit hal 的话,可以直接将窗口的这部分[url=]数据 [/url]直接拷贝过来,并完成可能的旋转,翻转,以及 alhpa 计算等。 下面来看看 Android 组合各个 layer 并送到 FB 显示的具体过程: 4.1 、 handleConsoleEvent 当接收到 signal 或者 singalEvent 事件以后,线程就停止等待开始对 Client 的请求进行处理,第一个步骤是handleConsoleEvent ,这个步骤我看了下和 /dev/console 这个设备有关,它会取得屏幕或者释放屏幕,只有取得屏幕的时候才能够在屏幕上画图。 4.2 、 handleTransaction 前面提到过,窗口状态的改变只能在一个 Transaction 中进行。因为窗口状态的改变可能造成本窗口和其他窗口的可见区域变化,所以就必须重新来计算窗口的可见区域。在这个处理子过程中 Android 会根据标志位来对所有 layer 进行遍历,一旦发现哪个窗口的状态发生了变化就设置标志位以在将来重新计算这个窗口的可见区域。在完成所有子 layer 的遍历以后, Android 还会根据标志位来处理主 layer ,举个例子,比如说传感器感应到手机横过来了,会将窗口横向显示,此时就要重新设置主 layer 的方向。 4.3 、 handlePageFlip
从 Z 值最大的 layer 开始计算,也就是说从最上层的 layer 计算,去掉本身的透明区域和覆盖在它上面的不透明区域,得到的就是这个 layer 的可见区域。然后这个 layer 的不透明区域就会累加到不透明覆盖区域,这个 layer 的可见区域会放入到主 layer 的可见区域,然后计算下一个 layer ,直到计算完所有的 layer 的可见区域。这中间的计算是通过定义在 skia 中的一种与或非的图形逻辑运算实现的,类似我们数学中的与或非逻辑图。 4.4 、 handleRepaint 计算出每个 layer 的可见区域以后,这一步就是将所有可见区域的内容画到主 layer 的相应部分了,也就是说将各个 surface buffer 里面相应的内容拷贝到主 layer 相应的 buffer ,其中可能还涉及到 alpha 运算,像素的翻转,旋转等等操作,这里就像我前面说的可以用硬件来实现也可以用软件来实现。在使用软件的 opengl 做计算的过程中还会用到 PixFlinger 来做像素的合成,这部分内容我还没时间来细看。 4.5 、 postFrameBuffer 最后的任务就是翻转主 layer 的两个 buffer ,将刚刚写入的内容放入 FB 内显示了。 |
转载于:https://blog.51cto.com/cfy10/1258438
相关文章:
最新组合式模型量化方法,实现FPGA最高硬件利用率,准确率-推理速度达到SOTA...
作者 | 王言治来源 | AI科技大本营(ID:rgznai100)深度神经网络(DNN)在图像、语言处理等领域获得了巨大成功,而如何将这些网络部署在ASIC、FPGA等嵌入式设备仍是热门研究方向。结构搜索,以及传统的剪枝、量化…

消息服务发送短信,手机接收不到短信解决思路
阿里云使用消息服务,发送注册码给手机。测试几次发现手机都接收不到,后台也没报错!今天我提交自己的工单,售后工程师已经帮我解决了,非常感谢他!官方代码:https://help.aliyun.com/document_det…

Asp.net中具体的日期格式化用法
1.绑定时格式化日期方法: <ASP:BOUNDCOLUMN DATAFIELD "JoinTime " DATAFORMATSTRING "{0:yyyy-MM-dd} " > <ITEMSTYLE WIDTH "18% " > </ITEMSTYLE > </ASP:BOUNDCOLUMN > 2.数据控件如DataGrid/DataList等的件格式…
日本「AI 鱼脸识别」项目,每分钟识别 100 条
来源 | HyperAI超神经头图 | 视觉中国近日,日本的一个 AI 分拣鱼类项目进入实验阶段。这将有望改善日本渔业劳动力老龄化及短缺的社会现状。日本作为岛国,其独特的地理位置,让国民自古以来就跟鱼结下了不解之缘,甚至形成了其独特的…

使用Spring实现邮件发送
2019独角兽企业重金招聘Python工程师标准>>> 这两天写个小程序需要使用邮件发送的功能,在网上搜索了一帮子文章,感觉还是使用Spring的邮件发送功能比较方便,哈哈,懒人就这样子了,不想再动了。整好了&#x…

GZip压缩与解压缩
GZIP的压缩与解压缩代码: public static class CompressionHelper{/// <summary> /// Compress the byte[] /// </summary> /// <param name"input"></param> /// <returns></returns> public static byte[] Compres…

String.Format()方法
String.Format方法是我们在.Net应用开发时经常使用到的,它的灵活使用有时能够达到事半功倍的效果,下面我们就借用MSDN上的一个示例来向大家展示String.Format的各种用法。 该示例展示了Numeric、DateTime和Enumeration标准格式的使用,另外&am…

Brian 的 Perl 问题之万能指南
为什么80%的码农都做不了架构师?>>> PerlChina brians Guide to Solving Any Perl Problem Home Page | All Pages | Recently Revised | Authors | Feeds | Export | 原 名:Brian’s Guide to Solving Any Perl Problem 中 文: Brian 的 Pe…

联手小米,雀巢中国推出健康管家Nesfinity,满足个性化生活需求管理
1月20日,雀巢中国携手小米战略合作正式开启,双方共同打造的雀巢健康管家“Nesfinity”正式发布。这项综合性智能健康生活新生态的合作,不仅开启了雀巢中国在智能物联网和大数据应用的新历程,更是双方由品牌合作跨入战略合作的开端…

使用joda-time工具类 计算时间相差多少 天,小时,分钟,秒
下面程序使用了两种方法计算两个时间相差 天,小时,分钟,秒 package jodotest;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;import org.joda.time.DateTime;import org.joda.time.Days;import …

C语言格式控制符和转义字符
1. 格式控制符 格式输出printf 作用是向终端输出若干个类型任意的数据。 格式:printf (格式控制符,输出列表) 1) 格式控制符 l % 格式说明引导符。 l - 指定…

面试高频题:单链表的逆置操作/链表逆序
函数内对形参的操作并不能影响实参,函数内修改的是实参的副本。要想在函数内部修改输入参数,要么传入的是实参的引用,要么传入的是实参的地址。 #include <iostream> #include <cstdlib> #include <cstring>//strlen using…
猖狂!微软、思科源码惨遭黑客 100 万美元打包出售
【编者按】SolarWinds 黑客攻击事件又延伸出新的危害了:微软、思科、FireEye 等公司的源代码在一网站公开出售,明码标价,甚至打包价为一百万,究竟是什么情况?整理 | 郑丽媛出品 | CSDN(ID:CSDNn…

vmstart的用法
vmstat命令是最常见的Linux/Unix监控工具,可以展现给定时间间隔的服务器的状态值,包括服务器的CPU使用率,内存使用,虚拟内存交换情况,IO读写情况。这个命令是我查看Linux/Unix最喜爱的命令,一个是Linux/Unix都支持,二是…

配置.net 3.0开发环境
开发.net 3.0 应用程序,需要配置开发环境。配置步骤如下:1. 开发.net 3.0,首先当然要安装.NET Framework 3.0 了安装前使用windowsupdate安装好最新的更新(Windows XP SP2 和Windows 2003 SP1一定要安装),下…

杭电 hdu 2096
小明AB:#include<iostream> using namespace std; int main(){int n;cin>>n;cin.ignore();while(n--){int a,b;cin>>a>>b;cout<<(a%100b%100)%100<<endl;}return 0; }转载于:https://blog.51cto.com/beyond316/1261849

关于2021年及未来,人工智能的5大趋势预测
吴恩达教授(美国斯坦福大学计算机科学系和电子工程系副教授)曾反复强调一句名言:"人工智能是新电力。" 我们正跟随着人工智能发展的脚步,走向第四次工业革命的浪潮之巅。 毋庸置疑,人工智能已经成为社会进步…

京东618:智能机器人JIMI的进击之路
ArchSummit全球架构师峰会深圳站将于2017年7月7日~8日在深圳华侨城洲际酒店召开,大会设置了相关专题来深入解读电商大促背后的技术故事,大会还邀请了eBay、WalmartLabs等国外顶尖技术专家,分享AI促销、搜索引擎、异地多活、库存物流等核心架构…
读懂深度迁移学习,看这文就够了 | 赠书
百度前首席科学家、斯坦福大学副教授吴恩达(Andrew Ng)曾经说过:迁移学习将是继监督学习之后的下一个促使机器学习成功商业化的驱动力。本文选自《深度学习500问:AI工程师面试宝典》,将重点介绍目前最热门的深度迁移学…

Visual Studio 2005 IDE 技巧和窍门
发布日期: 2007-02-26 | 更新日期: 2007-02-26James Lau Microsoft 项目经理 适用于: Microsoft Visual Studio 2005 摘要:Visual Studio 2005 是目前业内一流的开发工具,我想在此与大家分享一些使用技巧和窍门&#x…

ZOJ 1025 Wooden Sticks(快排+贪心)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId25 题目大意:机器运送n个木条,每个木条有一个长度和重量。运送第一根木条需要1分钟的准备时间,接下来如果后一根木条的长度和重量都大于等于前一根木条&…
Swift:UIKit中Demo(一)
关于Swift的基本概念及语法知识。我在前面的章节中已经介绍了非常多。这一节和下一节主要有针对性的解说Swift在实际UIKit开发中的使用场景及注意点。先来看看Demo的终于效果图。Demo分析: 1. 界面上面有三个button,他们的宽度不一致。 2. 点击每一个but…

jdbc封装与多并发的共鸣
欢迎来到:http://observer.blog.51cto.com代码的封装是一门艺术,封装得好,不但给自己便利,还可以给自己的维护提供帮助;同时,封装得好,还可以给看自己代码的人以赏心悦目的感觉,团队…

计算机视觉怎样实现自我超越?更大规模更精准的数据
最新发布的《2021中国人工智能应用趋势报告》强调,数据、算力和算法是支撑人工智能发展的"三驾马车",为模型训练提供基本资料的「数据」,是人工智能的根基。 随着互联网、社交媒体、移动设备和传感器的大量普及,其产生…

Visual Studio 2005 Web Deployment Projects版本不同引发的问题
为了方便Visual Studio 2005发布为单一dll,微软发布了一个Visual Studio 2005 插件,Visual Studio 2005 Web Deployment Projects,在微软的不同文档里,这个插件提供了两个下载地址,分别是: 下载地址一&…

【书籍下载链接】_2_第二轮_计算机专业书籍
各位朋友,下面是我收集的书籍,介绍给大家,有需要可以分享给大家,如果看的还可以,请购买纸质版的图书。 驱动器 J 中的卷是 Elements 卷的序列号是 8AAF-3206 j:\ 的目录 2014/01/20 20:00 1,533,385 WinCE.pdf2010/09/…

VS2005发布、生成网站时如何设置固定的dll文件名?
在用VS2005发布网站项目时,默认生成bin目录下的.dll文件名是随机命名的; 如果要固定生成文件名如何固定呢?有以下两种方案: 一、每个页面的程序集分别生成对应的dll; 方法:在“发布网站”的选项中,勾选“使用固定命名和单页程…

android 广播机制
1:首先说andoid 广播分为系统的和 自定义的 2:注册方式呢,也是两种,1:静态注册,在manifest.xml 文件中注册的 2:动态注册,用filter 区分 不说了 占代码 首先是动态注册:…
2021第一融!第四范式完成D轮7亿美元融资
来源丨第四范式头图丨来源于第四范式近日,第四范式宣布完成D轮融资,融资金额7亿美元。本轮融资由春华资本、博裕资本、厚朴投资领投,并引入国家制造业转型基金、国开、国新、中国建投、中信建投、海通证券等战略股东,红杉中国、中…

springboot-26-springboot 集成rabbitmq
rabbitmq是基于AMQP规范的一个消息代理, 它可以兼容jms, 支持其他语言, 并且可以跨平台 1, 安装 1) 普通安装 度娘: 2) docker 安装 sudo docker run -d -p 5672:5672 -p 15672:15672 rabbitmq:3-management 安装成功后: 使用 guest/guest 用户登录 2 使用: 1) 添加 rabbi…