[转]Android图形合成和显示系统基于高通MSM8kMDP4平台znw5566

介绍了AndroidSurfaceFlinger层次以下的图形合成和显示系统,主要基于高通MSM8kMDP4x平台。

SurfaceFinger按英文翻译过来就是Surface投递者。SufaceFlinger的构成并不是太复杂,复杂的是他的客户端建构。SufaceFlinger主要功能是:

1)将Layers(Surfaces)内容的刷新到屏幕上

2)维持Layer的Zorder序列,并对Layer最终输出做出裁剪计算。

3)响应Client要求,创建Layer与客户端的Surface建立连接

4)接收Client要求,修改Layer属性(输出大小,Alpha等设定)

但是作为投递者的实际意义,我们首先需要知道的是如何投递,投掷物,投递路线,投递目的地。

1SurfaceFlinger的基本组成框架

SurfaceFlinger管理对象为:

mClientsMap:管理客户端与服务端的连接。

ISurface,IsurfaceComposer:AIDL调用接口实例

mLayerMap:服务端的Surface的管理对象。

mCurrentState.layersSortedByZ:以Surface的Z-order序列排列的Layer数组。

graphicPlane缓冲区输出管理

OpenGLES:图形计算,图像合成等图形库。

pmemDevice:提供共享内存,在这里只是在gralloc.xxx.so可见,在上层被gralloc.xxx.so抽象了。

2SurfaceFingerClient和服务端对象关系图

Client端与SurfaceFlinger连接图:

Client对象:一般的在客户端都是通过SurfaceComposerClient来跟SurfaceFlinger打交道。

3主要对象说明

3.1DisplayHardware&FrameBuffer

首先SurfaceFlinger需要操作到屏幕,需要建立一个屏幕硬件缓冲区管理框架。Android在设计支持时,考虑多个屏幕的情况,引入了graphicPlane的概念。在SurfaceFlinger上有一个graphicPlane数组,每一个graphicPlane对象都对应一个DisplayHardware.在当前的Android(2.1)版本的设计中,系统支持一个graphicPlane,所以也就支持一个DisplayHardware。

SurfaceFlinger,Hardware硬件缓冲区的数据结构关系图。

3.2Layer

method:setBuffer在SurfaceFlinger端建立显示缓冲区。这里的缓冲区是指的HW性质的,PMEM设备文件映射的内存。

1)layer的绘制

voidLayer::onDraw(constRegion&clip)const

{

intindex=mFrontBufferIndex;

GLuinttextureName=mTextures[index].name;

drawWithOpenGL(clip,mTextures[index]);

}

3.2mCurrentState.layersSortedByZ

以Surface的Z-order序列排列的LayerBase数组,该数组是层显示遮挡的依据。在每个层计算自己的可见区域时,从Z-order顶层开始计算,是考虑到遮挡区域的裁减,自己之前层的可见区域就是自己的不可见区域。而绘制Layer时,则从Z-order底层开始绘制,这个考虑到透明层的叠加。

4SurfaceFlinger的运行框架

我们从前面的章节的基本原理可以知道,SurfaceFlinger的运行框架存在于:threadLoop,他是SurfaceFlinger的主循环体。SurfaceFlinger在进入主体循环之前会首先运行:SurfaceFlinger::readyToRun()。

4.1SurfaceFlinger::readyToRun()

(1)建立GraphicPanle

(2)建立FrameBufferHardware(确定输出目标)

初始化:OpenGLES

建立兼容的mainSurface.利用eglCreateWindowSurface。

建立OpenGLES进程上下文。

建立主Surface(OpenGLES)。DisplayHardware的Init()@DisplayHardware.cpp函数对OpenGL做了初始化,并创建立主Surface。为什么叫主Surface,因为所有的Layer在绘制时,都需要先绘制在这个主Surface上,最后系统才将主Surface的内容”投掷”到真正的屏幕上。

(3)主Surface的绑定

1)在DisplayHandware初始完毕后,hw.makeCurrent()将主Surface,OpenGLES进程上下文绑定到SurfaceFlinger的上下文中,

2)之后所有的SurfaceFlinger进程中使用EGL的所有的操作目的地都是mSurface@DisplayHardware。

4.2ThreadLoop

(1)handleTransaction(…):主要计算每个Layer有无属性修改,如果有修改着内用需要重画。

(2)handlePageFlip()

computeVisibleRegions:根据Z-Order序列计算每个Layer的可见区域和被覆盖区域。裁剪输出范围计算-

在生成裁剪区域的时候,根据Z_order依次,每个Layer在计算自己在屏幕的可显示区域时,需要经历如下步骤:

1)以自己的W,H给出自己初始的可见区域

2)减去自己上面窗口所覆盖的区域

在绘制时,Layer将根据自己的可将区域做相应的区域数据Copy。

(3)handleRepaint()

composeSurfaces(需要刷新区域):

根据每个Layer的可见区域与需要刷新区域的交集区域从Z-Order序列从底部开始绘制到主Surface上。

(4)postFramebuffer()

(DisplayHardware)hw.flip(mInvalidRegion);

eglSwapBuffers(display,mSurface):将mSruface投递到屏幕。

5总结

现在SurfaceFlinger干的事情利用下面的示意图表示出来:

更详细地,参考

AndroidGUI之SurfaceFlinger系列

SurfaceFlinger使用的各组件,参考

*******************************************************************************

CopybitHALIntroduction

SurfaceFlingerlayer的compositionType有三种:

HWC_FRAMEBUFFER的使用OpenGLES来绘制;

HWC_OVERLAY的使用OverlayEngine来合成;

HWC_USE_COPYBIT的使用Copybit硬件加速绘制;

MSM8xxx平台Jellybean代码中没有发现使用HWC_USE_COPYBIT的layer,该平台下Copybit硬件加速主要有两种:

PPP:vpe模块的PPP,directcopy;

C2D:可能是2DGPUOpenVG之类的。

PPP驱动实现是做为Framebuffer设备的一个命令MSMFB_BLIT,C2D是使用c2dhal库;

MSM7627平台下hwcomposer还是使用copybit的。

可能早期系统没有HardwareComposer,又没有GPU的时候,layerdraw就要使用Copybit去一层一层一Rect一Rect的拷贝了。

Copybit的代码在display/libcopybit下,硬件合成器使用Copybit做的封装代码在display/libhwcomposer/copybit和copybit_c2d中,前者对应PPP,后者对应C2D。

主要介绍Gralloc/FramebufferHAL设备,可以籍此考察显示Buffer(Ashmem、Pmem)的拥有者和传递。

平台中内存有ashmen、PMEM等多种内存类型,为了Video、Graphics、GPU内存访问的需要,android引入Gralloc模块实现内存的管理。Gralloc把FrameBuffer的分配也纳入了其中,并且新引入ION做为Gralloc的非FrameBuffer内存的分配器。ION对于内核态内存在用户进程之间的访问和硬件平台模块之间数据流转提供了高效的解决方案。

Android中lcd是一个帧缓冲设备,驱动程序通过处理器的lcd控制器将物理内存的一段区域设置为显存,如果向这段内存区域写入数据就会马上在lcd上显示出来。Android在HAL中提供了gralloc模块,封装了用户层对帧缓冲设备的所有操作接口,并通过SurfaceFlinger服务向应用提供显示支持。在启动过程中系统会加载gralloc模块,然后打开帧缓冲设备,获取设备的各种参数并完成gralloc模块的初始化。当应用程序需要把内容显示到lcd上时,需要通过gralloc模块申请一块图形缓冲区,然后将这块图形缓冲区映射到自己的地址空间并写入内容即可。当应用程序不再需要这块图形缓冲区时需要通过gralloc模块释放掉,然后解除对缓冲区的映射。

1、基础数据结构

gralloc模块通过structprivate_module_t来描述,该结构定义如下:

1.structprivate_module_t{

2.gralloc_module_tbase;

3.

4.private_handle_t*framebuffer;/*指向图形缓冲区的句柄*/

5.uint32_tflags;/*用来标志系统帧缓冲区是否支持双缓冲*/

6.uint32_tnumBuffers;/*表示系统帧缓冲的个数*/

7.uint32_tbufferMask;/*记录系统帧缓冲的使用情况*/

8.pthread_mutex_tlock;/*保护结构体private_module_t的并行访问*/

9.buffer_handle_tcurrentBuffer;/*描述当前正在被渲染的图形缓冲区*/

10.intpmem_master;/*pmem设备节点的描述符*/

11.void*pmem_master_base;/*pmem的起始虚拟地址*/

12.

13.structfb_var_screeninfoinfo;/*lcd的可变参数*/

14.structfb_fix_screeninfofinfo;/*lcd的固定参数*/

15.floatxdpi;/*x方向上每英寸的像素数量*/

16.floatydpi;/*y方向上每英寸的像素数量*/

17.floatfps;/*lcd的刷新率*/

18.

19.intorientation;/*显示方向*/

20.

21.enum{

22.PRIV_USAGE_LOCKED_FOR_POST=0x80000000/*flagtoindicatewe'llpostthisbuffer*/

23.};

24.};

该结构的成员记录了gralloc模块的各种参数,主要为模块自己使用,应用程序操作的图形缓冲区的数据结构是structprivate_handle_t,定义如下:

1.>#ifdef__cplusplus

2.structprivate_handle_t:publicnative_handle{

3.#else

4.structprivate_handle_t{

5.structnative_handlenativeHandle;/*用来描述一个本地句柄值*/

6.#endif

7.

8.enum{

9.PRIV_FLAGS_FRAMEBUFFER=0x00000001,

10.PRIV_FLAGS_USES_PMEM=0x00000002,

11.PRIV_FLAGS_USES_MMEM=0x00000004,

12.PRIV_FLAGS_NEEDS_FLUSH=0x00000008,

13.};

14.

15.enum{

16.LOCK_STATE_WRITE=1<<31,

17.LOCK_STATE_MAPPED=1<<30,

18.LOCK_STATE_READ_MASK=0x3FFFFFFF

19.};

21./*指向一个文件描述符,这个文件描述符要么指向帧缓冲区设备,要么指向一块匿名共享内存

22.*取决于private_handle_t描述的图形缓冲区是在帧缓冲区分配的,还是在内存中分配的*/

23.intfd;

24./*指向一个魔数,它的值由静态成员变量sMagic来指定,用来标识一个private_handle_t结构体*/

25.intmagic;

26./*用来描述一个图形缓冲区的标志,它的值要么等于0,要么等于PRIV_FLAGS_FRAMEBUFFER

27.*当一个图形缓冲区的标志值等于PRIV_FLAGS_FRAMEBUFFER的时候,就表示它是在帧缓冲区中分配的*/

28.intflags;

29.intsize;/*描述一个图形缓冲区的大小*/

30.intoffset;/*描述一个图形缓冲区的偏移地址*/

31.

32.intphys;/*图形缓冲区或帧缓冲的起始物理地址*/

33.intbase;/*图形缓冲区或帧缓冲的起始虚拟地址*/

34.intlockState;

35.intwriteOwner;

36.intpid;/*描述一个图形缓冲区的创建者的PID*/

37.

38.#ifdef__cplusplus

39.staticconstintsNumInts=9;/*有9个整数变量*/

40.staticconstintsNumFds=1;/*有1个文件描述符*/

41.staticconstintsMagic=0x3141592;

42.

43.private_handle_t(intfd,intsize,intflags):

44.fd(fd),magic(sMagic),flags(flags),size(size),offset(0),

45.phys(0),base(0),lockState(0),writeOwner(0),pid(getpid())

46.{

47.version=sizeof(native_handle);

48.numInts=sNumInts;

49.numFds=sNumFds;

50.}

51.~private_handle_t(){

52.magic=0;

53.}

54.

55.boolusesPhysicallyContiguousMemory(){

56.return(flags&PRIV_FLAGS_USES_PMEM)!=0;

57.}

58.

59./*用来验证一个native_handle_t指针是否指向了一个private_handle_t结构体*/

60.staticintvalidate(constnative_handle*h){

61.constprivate_handle_t*hnd=(constprivate_handle_t*)h;

62.if(!h||h->version!=sizeof(native_handle)||

63.h->numInts!=sNumInts||h->numFds!=sNumFds||

64.hnd->magic!=sMagic)

65.{

66.LOGE("invalidgrallochandle(at%p)",h);

67.return-EINVAL;

68.}

69.return0;

70.}

71.

72.staticprivate_handle_t*dynamicCast(constnative_handle*in){

73.if(validate(in)==0){

74.return(private_handle_t*)in;

75.}

76.returnNULL;

77.}

78.#endif

79.};

图形缓冲区的操作接口由结构structgralloc_module_t定义:

80.typedefstructgralloc_module_t{

81.structhw_module_tcommon;

82.

83./*注册一个图形缓冲区,这个指定的图形缓冲区使用一个buffer_handle_t句柄来描述*/

84.int(*registerBuffer)(structgralloc_module_tconst*module,

85.buffer_handle_thandle);

86.

87./*注销一个图形缓冲区*/

88.int(*unregisterBuffer)(structgralloc_module_tconst*module,

89.buffer_handle_thandle);

90.

91./*用来锁定一个图形缓冲区并将缓冲区映射到用户进程

92.*在锁定一块图形缓冲区的时候,可以指定要锁定的图形绘冲区的位置以及大小

93.*这是通过参数l、t、w和h来指定的,其中,参数l和t指定的是要访问的图形缓冲区的左上角位置

94.*而参数w和h指定的是要访问的图形缓冲区的宽度和长度

95.*锁定之后,就可以获得由参数参数l、t、w和h所圈定的一块缓冲区的起始地址,保存在输出参数vaddr中

96.*另一方面,在访问完成一块图形缓冲区之后,需要解除这块图形缓冲区的锁定*/

97.int(*lock)(structgralloc_module_tconst*module,

98.buffer_handle_thandle,intusage,

99.intl,intt,intw,inth,

100.void**vaddr);

101.

102.int(*unlock)(structgralloc_module_tconst*module,

103.buffer_handle_thandle);

104.

105.int(*perform)(structgralloc_module_tconst*module,

106.intoperation,...);

107.

108./*reservedforfutureuse*/

109.void*reserved_proc[7];

110.}gralloc_module_t;

gralloc设备则用结构structalloc_device_t来描述,其定义如下:

111.typedefstructalloc_device_t{

112.structhw_device_tcommon;

113.

114./*申请图形缓冲区的内存空间*/

115.int(*alloc)(structalloc_device_t*dev,intw,inth,intformat,intusage,buffer_handle_t*handle,int*stride);

116.

117./*释放图形缓冲区的内存空间*/

118.int(*free)(structalloc_device_t*dev,buffer_handle_thandle);

119.}alloc_device_t;

帧缓冲设备则采用结构structframebuffer_device_t描述:

120.typedefstructframebuffer_device_t{

121.structhw_device_tcommon;

122.

123.constuint32_tflags;/*用来记录系统帧缓冲区的标志*/

124.

125.constuint32_twidth;/*lcd显示区域的像素点数*/

126.constuint32_theight;

127.

128.constintstride;/*描述设备显示屏的一行有多少个像素点*/

129.

130./*描述系统帧缓冲区的像素格式,主要有HAL_PIXEL_FORMAT_RGBX_8888和HAL_PIXEL_FORMAT_RGB_565两种*/

131.constintformat;

132.

133.constfloatxdpi;

134.constfloatydpi;

135.constfloatfps;/*lcd刷新率*/

138.

139.intreserved[8];

140.

141./*设置帧交换间隔*/

142.int(*setSwapInterval)(structframebuffer_device_t*window,intinterval);

143.

144./*设置帧缓冲区的更新区域*/

145.int(*setUpdateRect)(structframebuffer_device_t*window,intleft,inttop,intwidth,intheight);

146.

147./*用来将图形缓冲区buffer的内容渲染到帧缓冲区中去,即显示在设备的显示屏中去*/

148.int(*post)(structframebuffer_device_t*dev,buffer_handle_tbuffer);

149.

150./*用来通知fb设备device,图形缓冲区的组合工作已经完成*/

151.int(*compositionComplete)(structframebuffer_device_t*dev);

152.

153.void*reserved_proc[8];

154.}framebuffer_device_t;

其中成员函数post对应用程序来说是最重要的接口,它将完成数据写入显存的工作:

2、gralloc模块

HAL中通过hw_get_module接口加载指定id的模块,并获得一个hw_module_t结构来打开设备,流程如下:

1.>#defineHAL_LIBRARY_PATH1"/system/lib/hw"

2.#defineHAL_LIBRARY_PATH2"/vendor/lib/hw"

4.staticconstchar*variant_keys[]={

5."ro.hardware",/*Thisgoesfirstsothatitcanpickupadifferentfileontheemulator.*/

6."ro.product.board",

7."ro.board.platform",

8."ro.arch"

9.};

10.

11.staticconstintHAL_VARIANT_KEYS_COUNT=

12.(sizeof(variant_keys)/sizeof(variant_keys[0]));

13.

14.inthw_get_module(constchar*id,conststructhw_module_t**module)

15.{

16.intstatus;

17.inti;

18.conststructhw_module_t*hmi=NULL;

19.charprop[PATH_MAX];

20.charpath[PATH_MAX];

21.

22./*

23.*Herewerelyonthefactthatcallingdlopenmultipletimeson

24.*thesame.sowillsimplyincrementarefcount(andnotload

25.*anewcopyofthelibrary).

26.*Wealsoassumethatdlopen()isthread-safe.

27.*/

28.

29./*Loopthroughtheconfigurationvariantslookingforamodule*/

30.for(i=0;i

31.if(i

32.if(property_get(variant_keys[i],prop,NULL)==0){/*读取variant_keys数组指定的属性值*/

33.continue;

34.}

35.snprintf(path,sizeof(path),"%s/%s.%s.so",/*格式化模块名和路径,如:/system/lib/hw/gralloc.xxx.so*/

36.HAL_LIBRARY_PATH1,id,prop);

37.if(access(path,R_OK)==0)break;

38.

39.snprintf(path,sizeof(path),"%s/%s.%s.so",

40.HAL_LIBRARY_PATH2,id,prop);

41.if(access(path,R_OK)==0)break;

42.}else{

43.snprintf(path,sizeof(path),"%s/%s.default.so",

44.HAL_LIBRARY_PATH1,id);

45.if(access(path,R_OK)==0)break;

46.}

47.}

48.

49.status=-ENOENT;

50.if(i

51./*loadthemodule,ifthisfails,we'redoomed,andweshouldnottrytoloadadifferentvariant.*/

52.status=load(id,path,module);/*加载模块*/

55.returnstatus;

56.}

可以看出,是使用id和系统平台的名字组合出so的文件名,去设定的目录动态加载该库文件然后解析特定符号,找到hw_module_tobject。函数会在/system/lib/hw或者/vendor/lib/hw目录中去寻找gralloc.xxx.so文件,如果找到了就调用load接口完成加载。最终会调用gralloc_device_open完成gralloc设备成员的初始化:

1.intgralloc_device_open(consthw_module_t*module,constchar*name,

2.hw_device_t**device)

3.{

4.98intstatus=-EINVAL;

5.99if(!strcmp(name,GRALLOC_HARDWARE_GPU0)){

6.100constprivate_module_t*m=reinterpret_cast(

7.101module);

8.102gpu_context_t*dev;

9.103IAllocController*alloc_ctrl=IAllocController::getInstance();

10.104dev=newgpu_context_t(m,alloc_ctrl);

11.105*device=&dev->common;

12.106status=0;

13.}else{

14.status=fb_device_open(module,name,device);

15.}

16.

17.returnstatus;

18.}

可以认为Grallocmodule中有两个设备gpu_alloc_device和fb_device,前者用于分配GPU0使用的内存和FB内存,GPU0内存管理使用IONallocator;后者用于获取分配FramebufferInfo并操作fb。

在android系统中,所有的图形缓冲区都是由SurfaceFlinger服务分配的,在系统帧缓冲区中分配的图形缓冲区只在SurfaceFlinger服务中使用,而在内存中分配的图形缓冲区既可以在SurfaceFlinger服务中使用,也可以在其它的应用程序中使用,当应用程序请求SurfaceFlinger服务分配图形缓冲区时会发生两次映射:服务所在的进程首先会将申请到的缓冲区映射至服务的地址空间,然后应用程序使用这个图形缓冲时再将其映射至应用程序的地址空间。分配函数的实现如下:

1.staticintgralloc_alloc_framebuffer(alloc_device_t*dev,size_tsize,intusage,buffer_handle_t*pHandle)

2.{

3.private_module_t*m=reinterpret_cast(

4.dev->common.module);

5.pthread_mutex_lock(&m->lock);

6.interr=gralloc_alloc_framebuffer_locked(dev,size,usage,pHandle);

7.pthread_mutex_unlock(&m->lock);

8.returnerr;

9.}

11.staticintgralloc_alloc_buffer(alloc_device_t*dev,size_tsize,intusage,buffer_handle_t*pHandle)

12.{

13.127interr=0;

14.128intflags=0;

15.129size=roundUpToPageSize(size);

16.130alloc_datadata;

17.131data.offset=0;

18.132data.fd=-1;

19.133data.base=0;

20.134data.size=size;

21.135if(format==HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED)

22.136data.align=8192;

23.137else

24.138data.align=getpagesize();

25.139data.pHandle=(unsignedint)pHandle;

26.140err=mAllocCtrl->allocate(data,usage);

27.141

28.142if(!err){

29.143/*allocatememoryforenhancementdata*/

30.144alloc_dataeData;

31.145eData.fd=-1;

32.146eData.base=0;

33.147eData.offset=0;

34.148eData.size=ROUND_UP_PAGESIZE(sizeof(MetaData_t));

35.149eData.pHandle=data.pHandle;

36.150eData.align=getpagesize();

37.151inteDataUsage=GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP;

38.152inteDataErr=mAllocCtrl->allocate(eData,eDataUsage);

39.153ALOGE_IF(eDataErr,"grallocfailedforeDataerr=%s",strerror(-err));

40.154

41.155if(usage&GRALLOC_USAGE_PRIVATE_UNSYNCHRONIZED){

42.156flags|=private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED;

43.157}

44.158

45.159if(usage&GRALLOC_USAGE_PRIVATE_EXTERNAL_ONLY){

46.160flags|=private_handle_t::PRIV_FLAGS_EXTERNAL_ONLY;

47.161//TheEXTERNAL_BLOCKflagisalwaysanadd-on

48.162if(usage&GRALLOC_USAGE_PRIVATE_EXTERNAL_BLOCK){

49.163flags|=private_handle_t::PRIV_FLAGS_EXTERNAL_BLOCK;

50.164}

51.165if(usage&GRALLOC_USAGE_PRIVATE_EXTERNAL_CC){

52.166flags|=private_handle_t::PRIV_FLAGS_EXTERNAL_CC;

53.167}

54.168}

55.169

56.170flags|=data.allocType;

57.171inteBaseAddr=int(eData.base)+eData.offset;

58.172private_handle_t*hnd=newprivate_handle_t(data.fd,size,flags,

59.173bufferType,format,width,height,eData.fd,eData.offset,

60.174eBaseAddr);

61.175

62.176hnd->offset=data.offset;

63.177hnd->base=int(data.base)+data.offset;

64.178*pHandle=hnd;

65.179}

66.180

67.181ALOGE_IF(err,"grallocfailederr=%s",strerror(-err));

68.182

69.183returnerr;

70.184}

72./*****************************************************************************/

73.

74.staticintgralloc_alloc(alloc_device_t*dev,intw,inth,intformat,intusage,

75.buffer_handle_t*pHandle,int*pStride)

76.{

77.if(!pHandle||!pStride)

78.return-EINVAL;

79.

80.size_tsize,stride;

81.

82.intalign=4;

83.intbpp=0;

84.switch(format){/*一个像素点占用的字节数*/

85.caseHAL_PIXEL_FORMAT_RGBA_8888:

86.caseHAL_PIXEL_FORMAT_RGBX_8888:

87.caseHAL_PIXEL_FORMAT_BGRA_8888:

88.bpp=4;

89.break;

90.caseHAL_PIXEL_FORMAT_RGB_888:

91.bpp=3;

92.break;

93.caseHAL_PIXEL_FORMAT_RGB_565:

94.caseHAL_PIXEL_FORMAT_RGBA_5551:

95.caseHAL_PIXEL_FORMAT_RGBA_4444:

96.bpp=2;

97.break;

98.default:

99.return-EINVAL;

100.}

101.size_tbpr=(w*bpp+(align-1))&~(align-1);

102.size=bpr*h;

103.stride=bpr/bpp;

105.interr;

106.if(usage&GRALLOC_USAGE_HW_FB){

107.err=gralloc_alloc_framebuffer(dev,size,usage,pHandle);/*在系统帧缓冲中分配图形缓冲区*/

108.}else{

109.err=gralloc_alloc_buffer(dev,size,usage,pHandle);/*在内存中分配图形缓冲区*/

110.}

111.

112.if(err<0){

113.returnerr;

114.}

115.

116.*pStride=stride;

117.return0;

118.}

3、gpu_alloc模块

gpu0内存即非HW_FB内存使用ION分配器进行分配,此文不做详述。

4、fb模块

gralloc_device_open中会根据传递的参数分别初始化两个设备,定义如下:

1.>#defineGRALLOC_HARDWARE_FB0"fb0"

2.#defineGRALLOC_HARDWARE_GPU0"gpu0"

如果参数不是"gpu0",那么是"fb%u"的形式,则会调用fb_device_open初始化fb设备,主要流程和打开gralloc基本一致,在函数中会通过调用mapFrameBuffer->mapFrameBufferLocked获取帧缓存设备的参数并将其设备节点映射到用户空间,流程如下(大致如此,msm8960平台代码有所变化,msm平台上fb设备文件名是/dev/graphics/fb%u):

1.intmapFrameBufferLocked(structprivate_module_t*module)

3.if(module->framebuffer){

4.return0;

5.}

6.

7.charconst*constdevice_template[]={

8."/dev/graphics/fb%u",

9."/dev/fb%u",

10.0};

11.

12.intfd=-1;

13.inti=0;

14.charname[64];

15.

16.while((fd==-1)&&device_template[i]){

17.snprintf(name,64,device_template[i],0);

18.fd=open(name,O_RDWR,0);

19.i++;

20.}

21.if(fd<0)

22.return-errno;

23.

24.structfb_fix_screeninfofinfo;

25.if(ioctl(fd,FBIOGET_FSCREENINFO,&finfo)==-1)/*获取帧缓冲的固定参数*/

26.return-errno;

27.

28.structfb_var_screeninfoinfo;

29.if(ioctl(fd,FBIOGET_VSCREENINFO,&info)==-1)/*获取帧缓冲的可变参数*/

30.return-errno;

32.info.reserved[0]=0;

33.info.reserved[1]=0;

34.info.reserved[2]=0;

35.info.xoffset=0;

36.info.yoffset=0;

37.info.activate=FB_ACTIVATE_NOW;

39.info.bits_per_pixel=32;

40.info.red.offset=16;

41.info.red.length=8;

42.info.green.offset=8;

43.info.green.length=8;

44.info.blue.offset=0;

45.info.blue.length=8;

46.info.transp.offset=24;

47.info.transp.length=8;

49./*

50.*RequestNUM_BUFFERSscreens(atlest2forpageflipping)

51.*/

52.info.yres_virtual=info.yres*NUM_BUFFERS;/*帧缓冲总长度*/

53.

55.uint32_tflags=PAGE_FLIP;/*支持缓冲交换*/

56.if(ioctl(fd,FBIOPAN_DISPLAY,&info)==-1){

57.info.yres_virtual=info.yres;

58.flags&=~PAGE_FLIP;

59.LOGW("FBIOPAN_DISPLAYfailed,pageflippingnotsupported");

60.}

61.

62.if(info.yres_virtual

63./*weneedatleast2forpage-flipping*/

64.info.yres_virtual=info.yres;

65.flags&=~PAGE_FLIP;

66.LOGW("pageflippingnotsupported(yres_virtual=%d,requested=%d)",

67.info.yres_virtual,info.yres*2);

69.

70.if(ioctl(fd,FBIOGET_VSCREENINFO,&info)==-1)

71.return-errno;

72.

73.intrefreshRate=1000000000000000LLU/

74.(

75.uint64_t(info.upper_margin+info.lower_margin+info.yres)

76.*(info.left_margin+info.right_margin+info.xres)

77.*info.pixclock

78.);/*计算lcd刷新率*/

80.if(refreshRate==0){

81./*bleagh,badinfofromthedriver*/

82.refreshRate=60*1000;//60Hz

83.}

84.

85.if(int(info.width)<=0||int(info.height)<=0){

86./*thedriverdoesn'treturnthatinformation,defaultto160dpi*/

87.info.width=((info.xres*25.4f)/160.0f+0.5f);

88.info.height=((info.yres*25.4f)/160.0f+0.5f);

89.}

91.floatxdpi=(info.xres*25.4f)/info.width;

92.floatydpi=(info.yres*25.4f)/info.height;

93.floatfps=refreshRate/1000.0f;

94.

95.LOGI("using(fd=%d)\n"

96."id=%s\n"

97."xres=%dpx\n"

98."yres=%dpx\n"

99."xres_virtual=%dpx\n"

100."yres_virtual=%dpx\n"

101."bpp=%d\n"

102."r=%2u:%u\n"

103."g=%2u:%u\n"

104."b=%2u:%u\n",

105.fd,

106.finfo.id,

107.info.xres,

108.info.yres,

109.info.xres_virtual,

110.info.yres_virtual,

111.info.bits_per_pixel,

112.info.red.offset,info.red.length,

113.info.green.offset,info.green.length,

114.info.blue.offset,info.blue.length

115.);

117.LOGI("width=%dmm(%fdpi)\n"

118."height=%dmm(%fdpi)\n"

119."refreshrate=%.2fHz\n",

120.info.width,xdpi,

121.info.height,ydpi,

122.fps

123.);

125.if(ioctl(fd,FBIOGET_FSCREENINFO,&finfo)==-1)

126.return-errno;

128.if(finfo.smem_len<=0)

129.return-errno;

130.

131.module->flags=flags;

132.module->info=info;

133.module->finfo=finfo;

134.module->xdpi=xdpi;

135.module->ydpi=ydpi;

136.module->fps=fps;

137.

138./*

139.*maptheframebuffer

140.*/

141.

142.interr;

143.size_tfbSize=roundUpToPageSize(finfo.line_length*info.yres_virtual);/*帧缓冲大小*/

144.module->framebuffer=newprivate_handle_t(dup(fd),fbSize,

145.private_handle_t::PRIV_FLAGS_USES_PMEM);

147.module->numBuffers=info.yres_virtual/info.yres;/*计算系统帧缓冲的个数*/

148.module->bufferMask=0;

150.void*vaddr=mmap(0,fbSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);/*将fb映射到用户空间*/

151.if(vaddr==MAP_FAILED){

152.LOGE("Errormappingtheframebuffer(%s)",strerror(errno));

153.return-errno;

154.}

155.module->framebuffer->base=intptr_t(vaddr);/*帧缓冲的起始虚拟地址*/

156.memset(vaddr,0,fbSize);

157.return0;

158.}

关于fb设备的打开和HW_FB内存的分配,是在FrameBufferNativeWindow的构造代码中,可以看到打开fb0设备获取FramebufferInfo,然后使用gralloc为该FrameBufferNativeWindow分配两个HW_FB内存即Framebuffer,即每个Window,doublebuffer。代码如下:

1.62/*

2.63*Thisimplementsthe(main)framebuffermanagement.Thisclassisused

3.64*mostlybySurfaceFlinger,butalsobycommandlineGLapplication.

4.65*

5.66*InfactthisisanimplementationofANativeWindowontopof

6.67*theframebuffer.

7.68*

8.69*Currentlyitisprettysimple,itmanagesonlytwobuffers(thefrontand

9.70*backbuffer).

10.71*

11.72*/

12.73

13.74FramebufferNativeWindow::FramebufferNativeWindow()

14.75:BASE(),fbDev(0),grDev(0),mUpdateOnDemand(false)

15.76{

16.77hw_module_tconst*module;

17.78if(hw_get_module(GRALLOC_HARDWARE_MODULE_ID,&module)==0){

18.79intstride;

19.80interr;

20.81inti;

21.82err=framebuffer_open(module,&fbDev);

22.83ALOGE_IF(err,"couldn'topenframebufferHAL(%s)",strerror(-err));

23.84

24.85err=gralloc_open(module,&grDev);

25.86ALOGE_IF(err,"couldn'topengrallocHAL(%s)",strerror(-err));

26.87

27.88//bailoutifwecan'tinitializethemodules

28.89if(!fbDev||!grDev)

29.90return;

30.91

31.92mUpdateOnDemand=(fbDev->setUpdateRect!=0);

32.93

33.94//initializethebufferFIFO

34.95if(fbDev->numFramebuffers>=MIN_NUM_FRAME_BUFFERS&&

35.96fbDev->numFramebuffers<=MAX_NUM_FRAME_BUFFERS){

36.97mNumBuffers=fbDev->numFramebuffers;

37.98}else{

38.99mNumBuffers=MIN_NUM_FRAME_BUFFERS;

39.100}

40.101mNumFreeBuffers=mNumBuffers;

41.102mBufferHead=mNumBuffers-1;

42.103

43.104/*

44.105*Thisdoesnotactuallychangetheframebufferformat.Itmerely

45.106*fakesthisformattosurfaceflingersothatwhenitcreates

46.107*framebuffersurfacesitwillusethisformat.It'sreallyagiant

47.108*HACKtoallowinterworkingwithbuggygralloc+GPUdriver

48.109*implementations.Youshould*NEVER*needtosetthisforshipping

49.110*devices.

50.111*/

51.112#ifdefFRAMEBUFFER_FORCE_FORMAT

52.113*((uint32_t*)&fbDev->format)=FRAMEBUFFER_FORCE_FORMAT;

53.114#endif

54.115

55.116for(i=0;i

56.117{

57.118buffers[i]=newNativeBuffer(

58.119fbDev->width,fbDev->height,fbDev->format,GRALLOC_USAGE_HW_FB);

59.120}

60.121

61.122for(i=0;i

62.123{

63.124err=grDev->alloc(grDev,

64.125fbDev->width,fbDev->height,fbDev->format,

65.126GRALLOC_USAGE_HW_FB,&buffers[i]->handle,&buffers[i]->stride);

66.127

67.128ALOGE_IF(err,"fbbuffer%dallocationfailedw=%d,h=%d,err=%s",

68.129i,fbDev->width,fbDev->height,strerror(-err));

69.130

70.131if(err)

71.132{

72.133mNumBuffers=i;

73.134mNumFreeBuffers=i;

74.135mBufferHead=mNumBuffers-1;

75.136break;

76.137}

77.138}

78.139

79.140const_cast(ANativeWindow::flags)=fbDev->flags;

80.141const_cast(ANativeWindow::xdpi)=fbDev->xdpi;

81.142const_cast(ANativeWindow::ydpi)=fbDev->ydpi;

82.143const_cast(ANativeWindow::minSwapInterval)=

83.144fbDev->minSwapInterval;

84.145const_cast(ANativeWindow::maxSwapInterval)=

85.146fbDev->maxSwapInterval;

86.147}else{

87.148ALOGE("Couldn'tgetgrallocmodule");

88.149}

89.150

90.151ANativeWindow::setSwapInterval=setSwapInterval;

91.152ANativeWindow::dequeueBuffer=dequeueBuffer;

92.153ANativeWindow::lockBuffer=lockBuffer;

93.154ANativeWindow::queueBuffer=queueBuffer;

94.155ANativeWindow::query=query;

95.156ANativeWindow::perform=perform;

96.157ANativeWindow::cancelBuffer=NULL;

97.158}

创建FrameBufferNativeWindow仅发生在DisplayHardware的构造后初始化中,代码片段如下:

1.150voidDisplayHardware::init(uint32_tdpy)

2.151{

3.152mNativeWindow=newFramebufferNativeWindow();//******

4.153framebuffer_device_tconst*fbDev=mNativeWindow->getDevice();

5.154if(!fbDev){

6.155ALOGE("Displaysubsystemfailedtoinitialize.checklogs.exiting...");

7.156exit(0);

8.157}

9.158

10.159intformat;

11.160ANativeWindowconst*constwindow=mNativeWindow.get();

12.161window->query(window,NATIVE_WINDOW_FORMAT,&format);

13.162mDpiX=mNativeWindow->xdpi;

14.163mDpiY=mNativeWindow->ydpi;

15.164mRefreshRate=fbDev->fps;

16.....

17.}

1.217status_tSurfaceFlinger::readyToRun()

2.218{

3.219ALOGI("SurfaceFlinger'smainthreadreadytorun."

4.220"InitializinggraphicsH/W...");

5.221

6.222//weonlysupportonedisplaycurrently

7.223intdpy=0;

8.224

9.225{

10.226//initializethemaindisplay

11.227GraphicPlane&plane(graphicPlane(dpy));

12.228DisplayHardware*consthw=newDisplayHardware(this,dpy);//*******

13.229plane.setDisplayHardware(hw);

14.230}

15.231

16.232//createthesharedcontrol-block

17.233mServerHeap=newMemoryHeapBase(4096,

18.234MemoryHeapBase::READ_ONLY,"SurfaceFlingerread-onlyheap");

19.235ALOGE_IF(mServerHeap==0,"can'tcreatesharedmemorydealer");

20.236

21.237mServerCblk=static_cast(mServerHeap->getBase());

22.238ALOGE_IF(mServerCblk==0,"can'tgettosharedcontrolblock'saddress");

23.239

24.240new(mServerCblk)surface_flinger_cblk_t;

25.241

26.242//initializeprimaryscreen

27.243//(otherdisplayshouldbeinitializedinthesamemanner,but

28.244//asynchronously,astheycouldcomeandgo.Noneofthisissupported

29.245//yet).

30.246constGraphicPlane&plane(graphicPlane(dpy));

31.247constDisplayHardware&hw=plane.displayHardware();

32.248constuint32_tw=hw.getWidth();

33.249constuint32_th=hw.getHeight();

34.250constuint32_tf=hw.getFormat();

35.251hw.makeCurrent();

36......

37.}

fb模块最重要的工作就是将应用程序指定的内容写入显存中,是通过函数fb_post完成的,流程如下(msm8960代码大致如此,不过使用的是FBIOPUT_VSCREENINFOIOCTL_CODE):

1./*将图形缓冲区buffer的内容渲染到帧缓冲区中去*/

2.staticintfb_post(structframebuffer_device_t*dev,buffer_handle_tbuffer)

4.unsignedintphys;

5.void*virt;

6.intpitch;

7.intformat;

8.

9./*首先验证参数handle指向的一块图形缓冲区的确是由Gralloc模块分配的*/

10.if(private_handle_t::validate(buffer)<0)

11.return-EINVAL;

13.fb_context_t*ctx=(fb_context_t*)dev;

15.private_handle_tconst*hnd=reinterpret_cast(buffer);/*图形缓冲区*/

16.private_module_t*m=reinterpret_cast(dev->common.module);/*帧缓冲区*/

17.

18.if(m->currentBuffer){/*当前正在渲染的图形缓冲区*/

19.m->base.unlock(&m->base,m->currentBuffer);

20.m->currentBuffer=0;

21.}

22.

23.if(hnd->flags&private_handle_t::PRIV_FLAGS_FRAMEBUFFER){/*如果图形缓冲区是在系统帧缓冲中分配的*/

24.m->base.lock(&m->base,buffer,/*锁定图像缓冲区*/

25.private_module_t::PRIV_USAGE_LOCKED_FOR_POST,

26.0,0,m->info.xres,m->info.yres,NULL);

28.constsize_toffset=hnd->base-m->framebuffer->base;/*计算图形缓冲区与帧缓冲的偏移*/

29./*将作为参数的fb_var_screeninfo结构体的成员变量activate的值设置FB_ACTIVATE_VBL

30.*表示要等到下一个垂直同步事件出现时,再将当前要渲染的图形缓冲区的内容绘制出来

31.*这样做的目的是避免出现屏幕闪烁,即避免前后两个图形缓冲区的内容各有一部分同时出现屏幕中*/

32.m->info.activate=FB_ACTIVATE_VBL;

33.m->info.yoffset=offset/m->finfo.line_length;/*得到偏移的起始行*/

34.

35.if(ioctl(m->framebuffer->fd,FBIOPAN_DISPLAY,&m->info)==-1){/*刷新显示内容*/

36.LOGE("FBIOPAN_DISPLAYfailed");

37.m->base.unlock(&m->base,buffer);

38.return-errno;

39.}

40.

41.if(UNLIKELY(mDebugFps)){

42.debugShowFPS();

43.}

44.

45.m->currentBuffer=buffer;/*设置当前图形缓冲区*/

46.return0;

5、Grallocmap/unmap、register/unregister

当GPU内存filedescriptor从一个进程A传递到另一个进程B后,进程B做gralloc_register_buffer就是使用allocator此时是ION将该buffer在本进程映射一下,用于访问。这些功能做为gralloc的mapper功能。

关于内存filedescriptor、binder传递该内存fd的具体机制参见ION的功能。

SurfaceFlingerLayerClip&Draw

/*

*收到VSYNC后REFRESH显示

*/

413voidSurfaceFlinger::onMessageReceived(int32_twhat)

419//ifwe'reinaglobaltransaction,don'tdoanything.

420constuint32_tmask=eTransactionNeeded|eTraversalNeeded;

421uint32_ttransactionFlags=peekTransactionFlags(mask);

422if(CC_UNLIKELY(transactionFlags)){

423handleTransaction(transactionFlags);

424}

425

426//postsurfaces(ifneeded)

427handlePageFlip();

428

435handleRefresh();

436

437constDisplayHardware&hw(graphicPlane(0).displayHardware());

438

443if(CC_UNLIKELY(mHwWorkListDirty)){

444//buildtheh/wworklist

445handleWorkList();

446}

447

448if(CC_LIKELY(hw.canDraw())){

449//repainttheframebuffer(ifneeded)

450handleRepaint();

451//informtheh/wthatwe'redonecompositing

452hw.compositionComplete();

453postFramebuffer();

454}else{

455//pretendwedidthepost

456hw.compositionComplete();

457}

511voidSurfaceFlinger::handleTransactionLocked(uint32_ttransactionFlags)

512{

513constLayerVector¤tLayers(mCurrentState.layersSortedByZ);

514constsize_tcount=currentLayers.size();

515

516/*

517*Traversalofthechildren

518*(performthetransactionforeachofthemifneeded)

519*/

520

*针对每个Layer,提交其所做的状态变化

521constboollayersNeedTransaction=transactionFlags&eTraversalNeeded;

522if(layersNeedTransaction){

523for(size_ti=0;i

524constsp&layer=currentLayers[i];

525uint32_ttrFlags=layer->getTransactionFlags(eTransactionNeeded);

526if(!trFlags)continue;

527

528constuint32_tflags=layer->doTransaction(0);

529if(flags&Layer::eVisibleRegion)

530mVisibleRegionsDirty=true;

531}

532}

533

534/*

535*Performourowntransactionifneeded

536*/

537/*

*处理SurfaceFlinger全局状态变化

538if(transactionFlags&eTransactionNeeded){

*如果屏幕发生旋转,则设置mDirtyRegion为整个屏幕范围,更新mServerCblk

*客户端可以访问到,通知HWC屏幕位向改变重新设置其参数。

539if(mCurrentState.orientation!=mDrawingState.orientation){

540//theorientationhaschanged,recomputeallvisibleregions

541//andinvalidateeverything.

542

543constintdpy=0;

544constintorientation=mCurrentState.orientation;

545//Currentlyunused:constuint32_tflags=mCurrentState.orientationFlags;

546GraphicPlane&plane(graphicPlane(dpy));

547plane.setOrientation(orientation);

548constTransform&planeTransform(plane.transform());

549

550//updatethesharedcontrolblock

551constDisplayHardware&hw(plane.displayHardware());

552volatiledisplay_cblk_t*dcblk=mServerCblk->displays+dpy;

553dcblk->orientation=orientation;

554dcblk->w=plane.getWidth();

555dcblk->h=plane.getHeight();

556

557mVisibleRegionsDirty=true;

558mDirtyRegion.set(hw.bounds());

559

560//settheneworientationtoHWC

561HWComposer&hwc(graphicPlane(0).displayHardware().getHwComposer());

562hwc.eventControl(DisplayHardware::EVENT_ORIENTATION,

563planeTransform.getOrientation());

564

565}

566

*如果有Layer增加,设置赃区域标志,此时mDirtyRegion还为空,

*每次Repaint后mDirtyRegion就清空了。

*此处的判断条件使用Layer个数比较,需要与下面mLayersRemoved结合看。

*如果Layer有减少,即使增加的个数小于减少的个数,

*那么mVisibleRegionsDirty一定会被设置。

*如果没有减少,增加Layer后数目一定会增多。可读性不好。

567if(currentLayers.size()>mDrawingState.layersSortedByZ.size()){

568//layershavebeenadded

569mVisibleRegionsDirty=true;

570}

571

*有减少的Layer,那么其下Layer可能会暴露出来,需要Invalidate该Layer

*暴露出来的区域,所以需要记录这块区域。

*所有移除layer暴露出来的区域累积,记录在mDirtyRegionRemovedLayer中。

*Invalidate的效果是在lockPageFlip后,将mDirtyRegionRemovedLayer加到

*mDirtyRegion中。

*用户绘图后Post的赃区域在unlockPageFlip时做。

572//somelayersmighthavebeenremoved,so

573//weneedtoupdatetheregionsthey'reexposing.

574if(mLayersRemoved){

575mLayersRemoved=false;

576mVisibleRegionsDirty=true;

577constLayerVector&previousLayers(mDrawingState.layersSortedByZ);

578constsize_tcount=previousLayers.size();

579for(size_ti=0;i

580constsp&layer(previousLayers[i]);

581if(currentLayers.indexOf(layer)<0){

582//thislayerisnotvisibleanymore

583mDirtyRegionRemovedLayer.orSelf(layer->visibleRegionScreen);

584}

585}

586}

587}

588

*复制CurrentState到DrawingState中,即提交,下面代码处理Repaint时使用DrawingState

589commitTransaction();

590}

735voidSurfaceFlinger::handlePageFlip()

736{

737ATRACE_CALL();

738constDisplayHardware&hw=graphicPlane(0).displayHardware();

739constRegionscreenRegion(hw.bounds());

740

*更新每个Layer的脏区域,获取每Layer这次重绘所需要的GraphicBuffer,

*为每Layer生成纹理供GPUrender使用。

741constLayerVector¤tLayers(mDrawingState.layersSortedByZ);

742constboolvisibleRegions=lockPageFlip(currentLayers);

743

744if(visibleRegions||mVisibleRegionsDirty){

745RegionopaqueRegion;

*计算更新mDirtyRegion,得到所有OpaquedLayers的总的Region。

746computeVisibleRegions(currentLayers,mDirtyRegion,opaqueRegion);

747

748/*

749*rebuildthevisiblelayerlist;重建mVisibleLayersSortedByZ

750*/

751constsize_tcount=currentLayers.size();

752mVisibleLayersSortedByZ.clear();

753mVisibleLayersSortedByZ.setCapacity(count);

754for(size_ti=0;i

755if(!currentLayers[i]->visibleRegionScreen.isEmpty())

756mVisibleLayersSortedByZ.add(currentLayers[i]);

757}

758

*opaqueRegion区域外的区域绘制“虫洞”,记录该区域

759mWormholeRegion=screenRegion.subtract(opaqueRegion);

*本轮处理中mVisibleRegionsDirty标志使用完毕,重置。

760mVisibleRegionsDirty=false;

761invalidateHwcGeometry();

762}

763

*主要是将每个Layer用户Post的区域并到赃区域上

764unlockPageFlip(currentLayers);

765

766mDirtyRegion.orSelf(getAndClearInvalidateRegion());

767mDirtyRegion.andSelf(screenRegion);

768}

775boolSurfaceFlinger::lockPageFlip(constLayerVector¤tLayers)

776{

777boolrecomputeVisibleRegions=false;

778size_tcount=currentLayers.size();

779spconst*layers=currentLayers.array();

780for(size_ti=0;i

781constsp&layer(layers[i]);

782layer->lockPageFlip(recomputeVisibleRegions);

783}

784returnrecomputeVisibleRegions;

785}

527voidLayer::lockPageFlip(bool&recomputeVisibleRegions)

528{

529ATRACE_CALL();

530

*本Layer有新QueuedBuffer才需要更新纹理。

531if(mQueuedFrames>0){

532

533//ifwe'vealreadycalledupdateTexImage()withoutgoingthrough

534//acompositionstep,wehavetoskipthislayeratthispoint

535//becausewecannotcallupdateTeximage()withoutacorresponding

536//compositionComplete()call.

537//we'lltriggeranupdateinonPreComposition().

*如果上次的重绘还没有显示,本轮又要显示了,直接返回。

538if(mRefreshPending){

539mPostedDirtyRegion.clear();

540return;

541}

543//Capturetheoldstateofthelayerforcomparisonslater

544constboololdOpacity=isOpaque();

545spoldActiveBuffer=mActiveBuffer;

546

*因为有mRefreshPending时导致直接return,所有需要“引发”下个Frame的显示;

*signalLayerUpdate()即是requestNextVsync(),因为setRefreshRate(0)时,

*不接收VSYNC,所以需要显式要求下一个VSYNC发过来,“引发”下帧显示

547//signalanothereventifwehavemoreframespending

548if(android_atomic_dec(&mQueuedFrames)>1){

549mFlinger->signalLayerUpdate();

550}

551

*内部类用于检验Queued过来的Buffer是否符合该Layer的显示要求,

*不符合则reject,不予显示。

*如Camera或Video图像buffer的大小,格式等要符合。

552structReject:publicSurfaceTexture::BufferRejecter{

553Layer::State&front;

554Layer::State¤t;

555bool&recomputeVisibleRegions;

556Reject(Layer::State&front,Layer::State¤t,

557bool&recomputeVisibleRegions)

558:front(front),current(current),

559recomputeVisibleRegions(recomputeVisibleRegions){

560}

561

562virtualboolreject(constsp&buf,

563constBufferQueue::BufferItem&item){

564if(buf==NULL){

565returnfalse;

566}

567

568uint32_tbufWidth=buf->getWidth();

569uint32_tbufHeight=buf->getHeight();

570

571//checkthatwereceivedabufferoftherightsize

572//(Takethebuffer'sorientationintoaccount)

573if(item.mTransform&Transform::ROT_90){

574swap(bufWidth,bufHeight);

575}

576

577

578boolisFixedSize=item.mScalingMode!=NATIVE_WINDOW_SCALING_MODE_FREEZE;

579if(front.active!=front.requested){

580

581if(isFixedSize||

582(bufWidth==front.requested.w&&

583bufHeight==front.requested.h))

584{

585//Herewepretendthetransactionhappenedbyupdatingthe

586//currentanddrawingstates.Drawingstateisonlyaccessed

587//inthisthread,noneedtohaveitlocked

588front.active=front.requested;

589

590//Wealsoneedtoupdatethecurrentstatesothat

591//wedon'tend-upoverwritingthedrawingstatewith

592//thisstalecurrentstateduringthenexttransaction

593//

594//NOTE:Wedon'tneedtoholdthetransactionlockhere

595//becauseState::activeisonlyaccessedfromthisthread.

596current.active=front.active;

597

598//recomputevisibleregion

599recomputeVisibleRegions=true;

600}

601

622

623if(!isFixedSize){

624if(front.active.w!=bufWidth||

625front.active.h!=bufHeight){

626//rejectthisbuffer

627returntrue;

628}

629}

630returnfalse;

631}

632};

633

634

635Rejectr(mDrawingState,currentState(),recomputeVisibleRegions);

636

*使用该Layer的mActiveBuffer生成SurfaceTexture,用于OpenGL/3DGPUrender。

*图像格式不符合时,Reject::reject()被回调。

637if(mSurfaceTexture->updateTexImage(&r)

638//somethinghappened!

639recomputeVisibleRegions=true;

640return;

641}

/*************

*updateTexImage会释放上轮该Layer使用的GraphicBuffer;

*也即本轮使用的GraphicBuffer持续到下次需要重绘时释放。

*记得其申请是在lockPageFlip中记录在mActiveBuffer。

642

*记录或更新当前使用的即mActiveBuffer字段

*注意该buffer直到该Layer下轮重绘Repaint时才Release,

*期间SurfaceTexture对该Buffer是不可用的。

643//updatetheactivebuffer

644mActiveBuffer=mSurfaceTexture->getCurrentBuffer();

645if(mActiveBuffer==NULL){

646//thiscanonlyhappeniftheveryfirstbufferwasrejected.

647return;

648}

649

*设置mRefreshPending标志了,如果本轮还没有Paint而下次又来了,直接返回。

650mRefreshPending=true;

651mFrameLatencyNeeded=true;

652if(oldActiveBuffer==NULL){

653//thefirsttimewereceiveabuffer,weneedtotriggera

654//geometryinvalidation.

655mFlinger->invalidateHwcGeometry();

656}

657

*如果Crop&Transform&Scale改变,重设HWC参数

658Rectcrop(mSurfaceTexture->getCurrentCrop());

659constuint32_ttransform(mSurfaceTexture->getCurrentTransform());

660constuint32_tscalingMode(mSurfaceTexture->getCurrentScalingMode());

661if((crop!=mCurrentCrop)||

662(transform!=mCurrentTransform)||

663(scalingMode!=mCurrentScalingMode))

664{

665mCurrentCrop=crop;

666mCurrentTransform=transform;

667mCurrentScalingMode=scalingMode;

668mFlinger->invalidateHwcGeometry();

669}

670

*比较GraphicBuffer的维度是否有改变,用于更新HWC的维度参数,

*从而使HWC知道该准备多大的buffer空间,和图像参数用于合成。

671if(oldActiveBuffer!=NULL){

672uint32_tbufWidth=mActiveBuffer->getWidth();

673uint32_tbufHeight=mActiveBuffer->getHeight();

674if(bufWidth!=uint32_t(oldActiveBuffer->width)||

675bufHeight!=uint32_t(oldActiveBuffer->height)){

676mFlinger->invalidateHwcGeometry();

677}

678}

679

680mCurrentOpacity=getOpacityForFormat(mActiveBuffer->format);

681if(oldOpacity!=isOpaque()){

682recomputeVisibleRegions=true;

683}

684

*FIXME每个layer的dirty是在后面调用的computeVisibleRegions()中计算出来的,

*可以在彼时设置给Layer,记录脏区域是个很好的优化。

*但是RegionmPostedDirtyRegion是classLayer而不是classLayerBase的成员,

*慢慢FIX!此dirty非computeVisibleRegions中的dirty

685//FIXME:mPostedDirtyRegion=dirty&bounds

686constLayer::State&front(drawingState());

687mPostedDirtyRegion.set(front.active.w,front.active.h);

688

689glTexParameterx(GL_TEXTURE_EXTERNAL_OES,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);

690glTexParameterx(GL_TEXTURE_EXTERNAL_OES,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);

691}

692}

LayerClip精髓所在----

592voidSurfaceFlinger::computeVisibleRegions(

593constLayerVector¤tLayers,Region&dirtyRegion,Region&opaqueRegion)

594{

595ATRACE_CALL();

596

597constGraphicPlane&plane(graphicPlane(0));

598constTransform&planeTransform(plane.transform());

599constDisplayHardware&hw(plane.displayHardware());

600constRegionscreenRegion(hw.bounds());

602RegionaboveOpaqueLayers;

603RegionaboveCoveredLayers;

604Regiondirty;

605

606boolsecureFrameBuffer=false;

607

608size_ti=currentLayers.size();

*Clip不就是计算遮挡吗?z-order从顶向底,合乎逻辑。

609while(i--){

610constsp&layer=currentLayers[i];

611layer->validateVisibility(planeTransform);

612

613//startwiththewholesurfaceatitscurrentlocation

614constLayer::State&s(layer->drawingState());

615

616/*

617*opaqueRegion:areaofasurfacethatisfullyopaque.

618*/

619RegionopaqueRegion;

620

621/*

622*visibleRegion:areaofasurfacethatisvisibleonscreen

623*andnotfullytransparent.Thisisessentiallythelayer's

624*footprintminustheopaqueregionsaboveit.

625*Areascoveredbyatranslucentsurfaceareconsideredvisible.

626*/

627RegionvisibleRegion;

628

629/*

630*coveredRegion:areaofasurfacethatiscoveredbyall

631*visibleregionsaboveit(whichincludesthetranslucentareas).

632*/

633RegioncoveredRegion;

635

636//handlehiddensurfacesbysettingthevisibleregiontoempty

637if(CC_LIKELY(!(s.flags&ISurfaceComposer::eLayerHidden)&&s.alpha)){

//该Layer是否半透

638constbooltranslucent=!layer->isOpaque();

//该Layer可见范围

639constRectbounds(layer->visibleBounds());

640visibleRegion.set(bounds);

641visibleRegion.andSelf(screenRegion);

642if(!visibleRegion.isEmpty()){

*如果本layer具有全透明区域(全透明子窗口),如Video或Camera,

*本Layer该区域一定是不可见的,visibleRegion应该减去全透区域,

*translucent的判断条件并不表示该Layer为半透,而是有全透区域时,

*该Layer的Opaque属性应该设置为false,表并非FullOpaque。

*setTransparentRegion/setTransparentRegionWindow

*=>setTransparentRegionHint设置透明的。

*那半透明子窗口如何呢?因为Layer的地位相当于该应用的Parentmostwindow,

*所以半透子窗口下的区域也一定是本Layer的子窗口,而不可能是别的Layer,

*从而该半透子窗口在本Layer范围内部就做Alpha混叠了,对于本Layer来说是

*Opaque的,所以不需要半透部分区域。半透属性是针对整个Layer的。

643//Removethetransparentareafromthevisibleregion

644if(translucent){

645visibleRegion.subtractSelf(layer->transparentRegionScreen);

646}

647

648//computetheopaqueregion

649constint32_tlayerOrientation=layer->getOrientation();

*如果该Layer是Opaque的,那么其整个可见区域一定是遮挡下面的层的。

*记录,累积到aboveOpaqueLayers,供计算下面层的遮挡之用。

650if(s.alpha==255&&!translucent&&

651((layerOrientation&Transform::ROT_INVALID)==false)){

652//theopaqueregionisthelayer'sfootprint

653opaqueRegion=visibleRegion;

654}

655}

*coveredRegion本层被覆盖的区域,包括被上面层半透覆盖的区域,覆盖并非遮挡。

*本层可见区域总对下面层构成覆盖,累积到aboveCoveredLayers

658//Clipthecoveredregiontothevisibleregion

659coveredRegion=aboveCoveredLayers.intersect(visibleRegion);

660

661//UpdateaboveCoveredLayersfornext(lower)layer

662aboveCoveredLayers.orSelf(visibleRegion);

663

*减去本层被上面层遮挡的区域

664//subtracttheopaqueregioncoveredbythelayersaboveus

665visibleRegion.subtractSelf(aboveOpaqueLayers);

666

*计算本层的脏区域,分内容是否为脏(size是否变化)两种情形。

*如果是用内容脏,即size变化,那么认为整个区域都是脏的;

*如果是移除上层的Layer暴露出本Layer区域,则计算可见的最小的脏区域;

*此时赃区域是屏幕坐标系统?

*这么有用的个dirty为什么不保存给各个Layer

*此时的dirty还不包括用户Posted的真正意义上的脏区域!

*为什么不直接地处理为visbleRegion.andSelf(mDirtyRegionRemovedLayer)

*因为mDirtyRegionRemovedLayer仅是个区域,并没有记录层的透明属性。

667//computethislayer'sdirtyregion

668if(layer->contentDirty){

669//weneedtoinvalidatethewholeregion

670dirty=visibleRegion;

671//aswell,astheoldvisibleregion

672dirty.orSelf(layer->visibleRegionScreen);

673layer->contentDirty=false;

674}else{

675/*computetheexposedregion:

676*theexposedregionconsistsoftwocomponents:

677*1)what'sVISIBLEnowandwasCOVEREDbefore

678*2)what'sEXPOSEDnowlesswhatwasEXPOSEDbefore

679*

680*notethat(1)isconservative,westartwiththewhole

681*visibleregionbutonlykeepwhatusedtobecoveredby

682*something--whichmeanitmayhavebeenexposed.

683*

684*(2)handlesareasthatwerenotcoveredbyanythingbutgot

685*exposedbecauseofaresize.

686*/

687constRegionnewExposed=visibleRegion-coveredRegion;

688constRegionoldVisibleRegion=layer->visibleRegionScreen;

689constRegionoldCoveredRegion=layer->coveredRegionScreen;

690constRegionoldExposed=oldVisibleRegion-oldCoveredRegion;

691dirty=(visibleRegion&oldCoveredRegion)|(newExposed-oldExposed);

*被遮挡区域不需要重绘,从脏区域里除去

693dirty.subtractSelf(aboveOpaqueLayers);

694

*累积总的脏区域

695//accumulatetothescreendirtyregion

696dirtyRegion.orSelf(dirty);

697

*累积遮挡区域

698//UpdateaboveOpaqueLayersfornext(lower)layer

699aboveOpaqueLayers.orSelf(opaqueRegion);

700

*为每个Layer记录其mVisibleRegion和mCoveredRegion

701//Storethevisibleregionisscreenspace

702layer->setVisibleRegion(visibleRegion);

703layer->setCoveredRegion(coveredRegion);

704

705//Ifasecurelayerispartiallyvisible,lock-downthescreen!

706if(layer->isSecure()&&!visibleRegion.isEmpty()){

707secureFrameBuffer=true;

708}

709}

710

*把mDirtyRegionRemovedLayer并到mDirtyRegion中去。

*有移除Layers暴露出来的区域需要其下的Layers重绘,其实这个在dirty计算时已处理

*关键的是移除的Layer是最底层Layer的时候则直接露出屏底色,所以要此处要或上。

711//invalidatetheareaswherealayerwasremoved

712dirtyRegion.orSelf(mDirtyRegionRemovedLayer);

713mDirtyRegionRemovedLayer.clear();

714

715mSecureFrameBuffer=secureFrameBuffer;

*传出遮挡区域,OpaqueRegion为所有OpaqueLayerArea之和

716opaqueRegion=aboveOpaqueLayers;

717}

787voidSurfaceFlinger::unlockPageFlip(constLayerVector¤tLayers)

788{

789constGraphicPlane&plane(graphicPlane(0));

790constTransform&planeTransform(plane.transform());

791constsize_tcount=currentLayers.size();

792spconst*layers=currentLayers.array();

793for(size_ti=0;i

794constsp&layer(layers[i]);

795layer->unlockPageFlip(planeTransform,mDirtyRegion);

796}

797}

694voidLayer::unlockPageFlip(

695constTransform&planeTransform,Region&outDirtyRegion)

696{

697ATRACE_CALL();

698

699RegionpostedRegion(mPostedDirtyRegion);

700if(!postedRegion.isEmpty()){

701mPostedDirtyRegion.clear();

702if(!visibleRegionScreen.isEmpty()){

703//Thedirtyregionisgiveninthelayer'scoordinatespace

704//transformthedirtyregionbythesurface'stransformation

705//andtheglobaltransformation.

706constLayer::State&s(drawingState());

707constTransformtr(planeTransform*s.transform);

708postedRegion=tr.transform(postedRegion);

709

*computeVisibleRegions处理的赃区域是size变化和上层遮挡移除的情况,需要重绘;

*而用户修改提交的区域也需要重绘,还未加到赃区域中,此时并进来。

710//Atthispoint,thedirtyregionisinscreenspace.

711//Makesureit'sconstrainedbythevisibleregion(which

712//isinscreenspaceaswell).

713postedRegion.andSelf(visibleRegionScreen);

714outDirtyRegion.orSelf(postedRegion);

715}

716}

handleRefresh()没什么作用了。

handleWorkList()是为HWComposer分配缓冲工作集,用于硬件合成,暂不考察。

DisplayHardware.canDraw()用于判断当前是否处于draw过程中,屏是否已经关闭等,得出需要Repaint的判断。

835voidSurfaceFlinger::handleRepaint()

836{

837ATRACE_CALL();

838

839//computetheinvalidregion

840mSwapRegion.orSelf(mDirtyRegion);

841

842if(CC_UNLIKELY(mDebugRegion)){

843debugFlashRegions();

844}

845

846//settheframebuffer

847constDisplayHardware&hw(graphicPlane(0).displayHardware());

848glMatrixMode(GL_MODELVIEW);

849glLoadIdentity();

850

*针对DisplayHardware是支持RECT更新还是RECTS更新,做相应处理。

851uint32_tflags=hw.getFlags();

852if(flags&DisplayHardware::SWAP_RECTANGLE){

853//wecanredrawonlywhat'sdirty,butsinceSWAP_RECTANGLEonly

854//takesarectangle,wemustmakesuretoupdatethatwhole

855//rectangleinthatcase

856mDirtyRegion.set(mSwapRegion.bounds());

857}else{

858if(flags&DisplayHardware::PARTIAL_UPDATES){

859//Weneedtoredrawtherectanglethatwillbeupdated

860//(pushedtotheframebuffer).

861//ThisisneededbecausePARTIAL_UPDATESonlytakesone

862//rectangleinsteadofaregion(seeDisplayHardware::flip())

863mDirtyRegion.set(mSwapRegion.bounds());

864}else{

865//weneedtoredraweverything(thewholescreen)

866mDirtyRegion.set(hw.bounds());

867mSwapRegion=mDirtyRegion;

868}

869}

870

*此处先不考虑HWC,HWC另见后续MIMOdisplay–Overlay&HWComposer

*只考虑使用OpenGL/3DGPUrender的情况,即各layer->draw。

871setupHardwareComposer();

872composeSurfaces(mDirtyRegion);

873

874//updatetheswapregionandclearthedirtyregion

875mSwapRegion.orSelf(mDirtyRegion);

876mDirtyRegion.clear();

877}

912voidSurfaceFlinger::composeSurfaces(constRegion&dirty)

913{

914constDisplayHardware&hw(graphicPlane(0).displayHardware());

915HWComposer&hwc(hw.getHwComposer());

916hwc_layer_t*constcur(hwc.getLayers());

917

*不考虑HWC或者除HWC_OVERLAY有HWC_FB或者Layer数目超出HWC管道数时,

*使用GPUrender。

918constsize_tfbLayerCount=hwc.getLayerCount(HWC_FRAMEBUFFER);

919if(!cur||fbLayerCount){

920//Nevertouchtheframebufferifwedon'thaveanyframebufferlayers

921

*有HWC_FB有HWC_OVERLAY共存的情形,如Camera或Video

*暂时认为0不表,详见后续MIMOdisplay–Overlay&HWComposer

922if(hwc.getLayerCount(HWC_OVERLAY)){

923//whenusingoverlays,weassumeafullytransparentframebuffer

924//NOTE:wecouldreducehowmuchweneedtoclear,forinstance

925//removewherethereareopaqueFBlayers.however,onsome

926//GPUsdoinga"cleanslate"glClearmightbemoreefficient.

927//We'llrevisitlaterifneeded.

928constRegionregion(hw.bounds());

*为HWC先清原来的画面

929#ifdefQCOMHW

930if(0!=qdutils::CBUtils::qcomuiClearRegion(region,

931hw.getEGLDisplay()))

932#endif

933{

934glClearColor(0,0,0,0);

935glClear(GL_COLOR_BUFFER_BIT);

936}

937}else{

*看来FB的时候不需要清屏;如果需要画虫洞,则为该区域清屏以显示虫洞

938//screenisalreadyclearedhere

939if(!mWormholeRegion.isEmpty()){

940//canhappenwithSurfaceView

941#ifdefQCOMHW

942if(0!=qdutils::CBUtils::qcomuiClearRegion(mWormholeRegion,

943hw.getEGLDisplay()))

944#endif

945drawWormhole();

946}

947}

948

*真正开画

949/*

950*andthen,renderthelayerstargetedattheframebuffer

951*/

952

953constVector>&layers(mVisibleLayersSortedByZ);

954constsize_tcount=layers.size();

955

*显然的,从底层向顶层画

956for(size_ti=0;i

957constsp&layer(layers[i]);

*各Layer的赃区域

958constRegionclip(dirty.intersect(layer->visibleRegionScreen));

959if(!clip.isEmpty()){

960if(cur&&(cur[i].compositionType==HWC_OVERLAY)){

961if(i&&(cur[i].hints&HWC_HINT_CLEAR_FB)

962&&layer->isOpaque()){

963//nevercleartheveryfirstlayersincewe're

964//guaranteedtheFBisalreadycleared

965#ifdefQCOMHW

966if(0!=qdutils::CBUtils::qcomuiClearRegion(clip,

967hw.getEGLDisplay()))

968#endif

969layer->clearWithOpenGL(clip);

970}

971continue;

972}

*如果是HWC_OVERLAY的,不需要Layer自己画

973#ifdefQCOMHW

974if(cur&&(cur[i].compositionType!=HWC_FRAMEBUFFER))

975continue;

976#endif

977

*HWC_FBLayer,自己开画

978//renderthelayer

979layer->draw(clip);

980}

981}

982}elseif(cur&&!mWormholeRegion.isEmpty()){

983constRegionregion(mWormholeRegion.intersect(mDirtyRegion));

984if(!region.isEmpty()){

985#ifdefQCOMHW

986if(0!=qdutils::CBUtils::qcomuiClearRegion(region,

987hw.getEGLDisplay()))

988#endif

989drawWormhole();

990}

991}

992}

344voidLayerBase::draw(constRegion&clip)const

345{

346//DontdrawExternal-onlylayers

347if(isLayerExternalOnly(getLayer())){

348return;

349}

350onDraw(clip);

351}

*BindTextureandDrawusingOpenGLforthislayerontheBACKFramebuffer.

321voidLayer::onDraw(constRegion&clip)const

322{

323ATRACE_CALL();

324

325if(CC_UNLIKELY(mActiveBuffer==0)){

326//thetexturehasnotbeencreatedyet,thisLayerhas

327//infactneverbeendrawninto.Thishappensfrequentlywith

328//SurfaceViewbecausetheWindowManagercan'tknowwhentheclient

329//hasdrawnthefirsttime.

330

331//Ifthereisnothingunderus,wepaintthescreeninblack,otherwise

332//wejustskipthisupdate.

333

334//figureoutifthereissomethingbelowus

335Regionunder;

336constSurfaceFlinger::LayerVector&drawingLayers(

337mFlinger->mDrawingState.layersSortedByZ);

338constsize_tcount=drawingLayers.size();

339for(size_ti=0;i

340constsp&layer(drawingLayers[i]);

341if(layer.get()==static_cast(this))

342break;

343under.orSelf(layer->visibleRegionScreen);

344}

345//ifnoteverythingbelowusiscovered,weplugtheholes!

346Regionholes(clip.subtract(under));

347if(!holes.isEmpty()){

348clearWithOpenGL(holes,0,0,0,1);

350return;

352#ifdefQCOMHW

353if(!qdutils::isGPUSupportedFormat(mActiveBuffer->format)){

354clearWithOpenGL(clip,0,0,0,1);

355return;

356}

357#endif

358if(!isProtected()){

359//TODO:wecouldbemoresubtlewithisFixedSize()

360constbooluseFiltering=getFiltering()||needsFiltering()||isFixedSize();

361

362//Querythetexturematrixgivenourcurrentfilteringmode.

363floattextureMatrix[16];

364mSurfaceTexture->setFilteringEnabled(useFiltering);

365mSurfaceTexture->getTransformMatrix(textureMatrix);

366

367//Setthingsupfortexturing.

368glBindTexture(GL_TEXTURE_EXTERNAL_OES,mTextureName);

369GLenumfilter=GL_NEAREST;

370if(useFiltering){

371filter=GL_LINEAR;

372}

373glTexParameterx(GL_TEXTURE_EXTERNAL_OES,GL_TEXTURE_MAG_FILTER,filter);

374glTexParameterx(GL_TEXTURE_EXTERNAL_OES,GL_TEXTURE_MIN_FILTER,filter);

375glMatrixMode(GL_TEXTURE);

376glLoadMatrixf(textureMatrix);

377glMatrixMode(GL_MODELVIEW);

378glDisable(GL_TEXTURE_2D);

379glEnable(GL_TEXTURE_EXTERNAL_OES);

380}else{

381glBindTexture(GL_TEXTURE_2D,mFlinger->getProtectedTexName());

382glMatrixMode(GL_TEXTURE);

383glLoadIdentity();

384glMatrixMode(GL_MODELVIEW);

385glDisable(GL_TEXTURE_EXTERNAL_OES);

386glEnable(GL_TEXTURE_2D);

387}

388

389drawWithOpenGL(clip);

390

391glDisable(GL_TEXTURE_EXTERNAL_OES);

392glDisable(GL_TEXTURE_2D);

393}

*提交FrameBuffer

*对于使用GPU的情况,composeSurfaces已经将所有Surface都合成到BACKFramebuffer上了;

*对于HWComposer的情况,此时还没有启动硬件合成,是在DisplayHardware::flip=>

*HWComposer::commit中合成到BACKFramebuffer上并交换Framebuffer的。

*为了统一,应该把HWC合成的功能也放到composeSurfaces类似的composeLayers里面去,

*不用Surface字样是因为Surface是GPU使用的,MDPHWComposer并不具有

*操作Surface/Texture的能力,而只能操作支持格式的GraphicBuffer。

463voidSurfaceFlinger::postFramebuffer()

464{

465ATRACE_CALL();

466//mSwapRegioncanbeemptyhereissomecases,forinstanceifahidden

467//orfullytransparentwindowisupdating.

468//inthatcase,weneedtoflipanywaystonotriskadeadlockwith

469//h/wcomposer.

470

471constDisplayHardware&hw(graphicPlane(0).displayHardware());

472constnsecs_tnow=systemTime();

473mDebugInSwapBuffers=now;

474hw.flip(mSwapRegion);

475

476size_tnumLayers=mVisibleLayersSortedByZ.size();

477for(size_ti=0;i

478mVisibleLayersSortedByZ[i]->onLayerDisplayed();

479}

480

481

482mLastSwapBufferTime=systemTime()-now;

483mDebugInSwapBuffers=0;

484mSwapRegion.clear();

485}

435voidDisplayHardware::flip(constRegion&dirty)const

436{

437checkGLErrors();

439EGLDisplaydpy=mDisplay;

440EGLSurfacesurface=mSurface;

441

442#ifdefEGL_ANDROID_swap_rectangle

443if(mFlags&SWAP_RECTANGLE){

444constRegionnewDirty(dirty.intersect(bounds()));

445constRectb(newDirty.getBounds());

446eglSetSwapRectangleANDROID(dpy,surface,

447b.left,b.top,b.width(),b.height());

448}

449#endif

450

451if(mFlags&PARTIAL_UPDATES){

452mNativeWindow->setUpdateRectangle(dirty.getBounds());

453}

454

455mPageFlipCount++;

456

*mHwc->commit中也会调用eglSwapBuffers,因为不管用什么方式,

*EGL是FrameBufferNativeWindow的管理者,实现bufferswap,避免竞争。

457if(mHwc->initCheck()==NO_ERROR){

458mHwc->commit();

459}else{

460eglSwapBuffers(dpy,surface);

461}

462checkEGLErrors("eglSwapBuffers");

463

464//fordebugging

465//glClearColor(1,0,0,0);

466//glClear(GL_COLOR_BUFFER_BIT);

467}

没有GPU的eglSwapBuffers实现,就看看软的吧。

484EGLBooleanegl_window_surface_v2_t::swapBuffers()

485{

486if(!buffer){

487returnsetError(EGL_BAD_ACCESS,EGL_FALSE);

488}

489

490/*

491*HandleeglSetSwapRectangleANDROID()

492*Wecopybackfromthefrontbuffer

493*/

494if(!dirtyRegion.isEmpty()){

495dirtyRegion.andSelf(Rect(buffer->width,buffer->height));

496if(previousBuffer){

497//ThiswasconstRegioncopyBack,butthatcausesan

498//internalcompileerroronsimulatorbuilds

*frontbuffer和backbuffer的赃区域的差值拷贝回backbuffer中;

*因为frontbuffer的赃区域(即其重绘的区域)并未更新到backbuffer中,

*所以需要考回来;但是backbuffer的赃区域已经重绘了,是不能覆盖掉的,

*所以两个赃区域相减。

*这仅是doublebuffer的时候的实现,triplebuffer两个dirtyRegion就无法保证了。

499/*const*/RegioncopyBack(Region::subtract(oldDirtyRegion,dirtyRegion));

500if(!copyBack.isEmpty()){

501void*prevBits;

502if(lock(previousBuffer,

503GRALLOC_USAGE_SW_READ_OFTEN,&prevBits)==NO_ERROR){

504//copyfrompreviousBuffertobuffer

505copyBlt(buffer,bits,previousBuffer,prevBits,copyBack);

506unlock(previousBuffer);

507}

508}

509}

510oldDirtyRegion=dirtyRegion;

511}

512/*

*释放frontframebuffer

513if(previousBuffer){

514previousBuffer->common.decRef(&previousBuffer->common);

515previousBuffer=0;

516}

517

*解锁backframebuffer,表用户用完,要queue了。queueBuffer()

518unlock(buffer);

519previousBuffer=buffer;

520nativeWindow->queueBuffer(nativeWindow,buffer);

521buffer=0;

522

*backframebuffer已经提交了,需要再dequeue下一个framebuffer来做backframebuffer

*并且锁住,供使用;

523//dequeueanewbuffer

524if(nativeWindow->dequeueBuffer(nativeWindow,&buffer)==NO_ERROR){

525

526//TODO:lockBuffershouldratherbeexecutedwhentheveryfirst

527//directrenderingoccurs.

528nativeWindow->lockBuffer(nativeWindow,buffer);

529

530//reallocatethedepth-bufferifneeded

531if((width!=buffer->width)||(height!=buffer->height)){

532//TODO:weprobablyshouldresettheswaprecthere

533//ifthewindowsizehaschanged

534width=buffer->width;

535height=buffer->height;

536if(depth.data){

537free(depth.data);

538depth.width=width;

539depth.height=height;

540depth.stride=buffer->stride;

541depth.data=(GGLubyte*)malloc(depth.stride*depth.height*2);

542if(depth.data==0){

543setError(EGL_BAD_ALLOC,EGL_FALSE);

544returnEGL_FALSE;

545}

546}

547}

548

549//keepareferenceonthebuffer

550buffer->common.incRef(&buffer->common);

*lock/unlock

552//finallypinthebufferdown

553if(lock(buffer,GRALLOC_USAGE_SW_READ_OFTEN|

554GRALLOC_USAGE_SW_WRITE_OFTEN,&bits)!=NO_ERROR){

555ALOGE("eglSwapBuffers()failedtolockbuffer%p(%ux%u)",

556buffer,buffer->width,buffer->height);

557returnsetError(EGL_BAD_ACCESS,EGL_FALSE);

558//FIXME:weshouldmakesurewe'renotaccessingthebufferanymore

559}

560}else{

561returnsetError(EGL_BAD_CURRENT_SURFACE,EGL_FALSE);

562}

563

564returnEGL_TRUE;

*****************************************************************************

FileOrgnization

目录/hardware/qcom/display/liboverlay/

Android.mk

mdpRotator.cppOverlayRotatorWrpper

mdpWrapper.hMDPNormalandOverlayFrameBufferIOCTLWrapper

mdssRotator.cppOverlayMDSSRotatorWrapper

overlay.cppOverlayToplevelimplementationfile

overlay.hOverlayTopleveldeclarationfile

overlayCtrl.cppOverlayCtrlimplementationfile

overlayCtrlData.hOverlayCtrlandOverlayDatadeclarationfileincludingOverlayDataimplementation

overlayImpl.hOverlayimplementationwhichoperatesoverlaypipespair(LayerMixer)

overlayMdp.cppOverlayimplementationonMDP,usedbyOverlayCtrlData

overlayMdp.hOverlayonMDP

overlayMem.hOverlayVGpipeinputkernelmemoryfiledescriptor,maybegraphicbufferorrotatoroutputbuffer

overlayRotator.cppOverlayRotatortoplevelimplementation

overlayRotator.hOverlayRotatortopleveldeclaration

overlayState.hOverlaystatemachine

overlayUtils.cppOverlayUtils

overlayUtils.hOverlayUtils

pipes/OverlayPipes,thatisOverlaychannel.ItisaVGandRGBpipepaironMDP.

Platformarchitecture

MDP中每个VG和RGBpipepair作为一个LayerMixer的输入,由LayerMixer完成Overlay功能,作为一个Overlaychannel。

注意,RGB和VG并不固定配对做为某个LayerMixer的输入。如MDP4.2的LayerMixer0可以合成一个Border或BaseLayer和4个layer,即可以多达5个pipe输入到LayerMixer0,从而用作HardwareComposer。

当使用Overlay功能时:

RGBpipe的输入是普通的Framebuffer,是Surfaceflinger的合成输出;

VG的输入是video或graphics或camera图像等,是内核空间内存buffer,其owner一般是Video、Graphics或V4L2等。当其前端是Rotator时,Rotator的输入是这些buffer,Rotator的输出Overlayrotatorframebuffer作为VG的输入。

每个OverlayChannel结构如下图所示

关于OverlayBuffer(FrameBufferRotatorBufferOverlayBuffer)这些名称并不特别明确,只要明白OverlayChannel数据流路上的各输入输出Buffer的位置和作用即可。

下面以Layermixer1(对应/dev/graphics/fb0)为参考详述各buffer:

只UI显示时,

Framebuffer是fb0的framebuffer,是从启动时预留出的bootmem中的分配出来的。LayerMixer1处于BLT模式,Layermixer1和DMA_P(Primarydisplaydriver)分离,可以由软件完全控制。该Framebuffer做为DMA_P的输入,经MIPI_DSI输出到主屏上。

启用Overlay时,

上述Framebuffer做为RGB1pipe的输入,而视频或图像的内核buffer做为VGpipe的输入,二者经Layermixer1合成;此时LayerMixer1工作在非BLT模式,LayerMixer1和DMA_Pattach在一起,LayerMixer1输出控制参数直接提供给DMA_P使用。此时LayerMixer1仍有两种工作模式,FrameBuffer模式和DIRECT_OUT模式,前者时LayerMixer1和DMA_P之间使用一个overlaydoublebuffer做缓冲,输出给DMA_P;DIRECT_OUT模式下不使用该ovldoublebuffer,LayerMixer1直接输出给DMA_P。

一般VG和RGB的输入都可以是doublebuffer,ping-pang;LayerMixer的输出也是doublebuffer。DMA_P/S/E做为displaydriver传输前端buffer作为后端接口控制器的输入。

下面两图是QCMDPUImirror和Videomirror时的两结构图,并没有明确画出LayerMix1的Overlay流程路径,个别buffer的owner可能也有所差错,buffer也并不全,仅是大致描述Overlay及其部分buffer。

MDP和DSI和后端显示控制器和接口的连接结构如下图。

Layerarchitecture

Overlay->OverlayImpl

OverlayCtrlData

OverlayMDPCtrlData

MDPWrapper

FrameBuffer

KeyPoint

Ctrl用来设置overlaychannel的参数,Data用来提交buffer到Overlaychannelqueue。其实使用overlay本质上就是设置好pin路由,设置好通道工作参数,然后不停的提交数据让OverlayEnginee工作。MDP的OverlayChannel并没有别的特殊的编程接口,都是使用控制、状态和数据寄存器来访问。其实MDP提供了远比AndroidOverlay实现强得多的Overlay功能。

Someflow

Ctrl::commit()->MDPCtrl::set()->mdp_wrapper::setOverlay()->ioctl(fd,MSMFB_OVERLAY_SET,&ov)

->msm_fb->mdp4_overlay设置Overlay工作参数。

Data::queueBuffer->MDPData::play->mdp_wrapper::play()->ioctl(fd,MSMFB_OVERLAY_PLAY,&od)

->msm_fb->mdp4_overlay进行Overlay合成。注意queueBuffer第一参数fd是memFd,是内核空间的buffer,并不在用户空间和内核空间拷贝buffer数据。作用与framebuffer类似的是提交内核空间的该buffer到OverlayEngineeQueue。

Overlay&HWConMDP--MIMODisplay软硬整合

概述

Android显示系统SurfaceFlinger使用Overlay和HWC(Hardwarecomposer)完成SurfaceLayer的硬件合成。Overlay和HWC表现为两个HAL,为芯片方案制造商留了实现余地。

因为Overlay也时常被称为hardwarecomposition,为了避免混淆,本文中Overlay专指AndroidDisplayOverlayHAL,对应liboverlay.so;HWC专指SurfaceFlinger使用的硬件合成器HAL,对应hwcomposer.msm8xxxx.so。

QualcommMSM8k系列平台的MDP4.x(MobileDisplayPlatform)提供了硬件Overlay的功能,AndroidOverlayHAL与这个Overlay硬件相对应;当然,为了与上段照应,需要说明的是,MDP并不存在一个实现HWC的专门硬件,HWC也是在Overlay上实现的;体现在Android软件中,HWCHAL是建立在OverlayHAL基础上的,是使用OverlayHAL来实现的。

因此,再次,准确地说,提供硬件合成功能的是模块是Overlay,是MDPOverlay硬件的体现;而HWC则是SurfaceFlinger使用OverlayHAL的一个桥梁。

Overlay并非仅仅由SurfaceFlinger得到Surface后就不再经过SurfaceFlinger而使用硬件直接输出;从函数调用上看似乎是这样的,但是实际情形却非如此。硬件合成仅是合成的一种手段,合成的结果输出destination还是必须受控,因而也必须纳入SurfaceFlinger的输出管理范畴。

Overlay硬件平台

MSM8k上目前有MDP4.0,MDP4.1,MDP4.2三个硬件版本,主要是合成能力和管道数目的区别。通常,一个RGBpipe和一个VGpipe组成一个pipepair,前者是对应UIRGB图像数据,后者对应Camera或Video的RGB或YUV数据,两个pipe输入到一个LayerMixer0用于合成;额外的,LayerMixer可能还有BF(BorderFill)pipe,用于视频按比例显示时屏幕多余边框填充,这个多用于ExtTV或HDMI输出的时候。MDP4有7个pipe,3个LayerMixer,其中LayerMix0可以配置有多达两个RGBpipe,两个VGpipe,一个BFpipe输入,完成5Layer合成。

上述pipe是LayerMixer的输入元素,LayerMixer的输出对应到LCD,TV,HDMI等,当然不是直接对应,而是由DMAchannel和上述模块的控制器相连。三个LayerMixer对应的三个输出使用一般是约定的,当然,软件层面上MDPdriver中对每个管道的目标LayerMixer也做了固定的配置。三个输出一般标为Primary,Seconday,Extra,对应的DMA通道为DMA_P,DMA_S,DMA_E。

由于LayerMixer0提供了多达5个Layer的合成能力,所以当没有Camera或Video优先使用它的时候,它被充分利用来做Layer的Hardwarecomposition。提前且概要地,这儿必须清楚说明的是,LayerMixer0使用的两种情景:

当有Camera或Video的时候,SurfaceView对应的Layer为HWC_OVERLAY,这时该Layer对应一个VGpipe,而其余HWC_FRAMEBUFFERLayer经3DGPUrender到Framebuffer后,该Framebuffer输入一个pipe(RGB1--baselayer),和VGpipe经LayerMixer0合成输出。

当没有Camera或Video的时候,如果UILayer即HWC_FRAMEBUFFERLayer小于等于3个且都满足图像格式条件,那么这些Layer的CompositionType属性会被修改为HWC_OVERLAY,为每个Layer分配pipe,经LayerMixer0合成经DMA_P输出,这就是HWC。由于BFpipe能力条件的限制,不使用其做HWC,而RGB1做预留的baselayerFramebuffer使用,所以标榜的4-layermdpcompositionsupport实际只能接受SurfceFlinger的3个Layer做合成,也就是SurfaceFlinger的属性debug.mdpcomp.maxlayer=3。当超过3个Layer的时候,由于管道数量的限制,不能够再使用LayerMixer0,就使用GPUrender,也即每个Layer->draw。当然GPUrender也算是一个HardwareComposition,CompositionType方式的其中之一就是GPU。

OverlayHAL结构

MIMO输入输出代码结构设计,简单地讲,分为流路控制和数据流动两部分;Overlay及构建在其上的hwcomposer都是采用这种流路控制和数据流动的结构。

Overlay有多种应用场景,具体设计上描述为OverlayState。就是说如果允许管道任意灵活组合使用的话,可以有很多种花样的应用,但是这儿预先设计好这么些应用场景,应用场景一定,需要的管道类型和数目随之确定,管道连接的LayerMixer及其输出Video驱动控制器也确定。

OverlayState具体实现使用模板类OverlayImpl,P0,P1,P2是3个pipe,最多可以使用3个pipe。每个pipe对应的类是模板类GenericPipe,PANEL指定该pipe合成后图像输出目的,从而也决定其使用的LayerMixer。另外地,每个OverlayImpl模板类的3个管道可能需要图像旋转,所以有可能使用3个Rotator。此处的P0,P1,P2只是逻辑索引,并不具体是MDP的管道索引,根据使用规格需求,从MDP管道中为之动态分配。

hwcomposer也设计了几个封装类与Overlay应用场景对应,屏蔽了场景功能的具体实现。hwcomposer中定义的应用场景比Overlay提供的场景还要少,仅是有限几个。大致结构图如下:

由于overlay场景太多,因此只示意了四个,OV_BYPASS_x_LAYER可以是1,2,3个,分别对应1个,2个或3个HWC_FRAMEBUFFERLayer和baselayerFramebuffer合成的情形。

hwc_prepare的设计逻辑过于固化和简单,成了一种基于if-else判断的优先配置方式,宽阔的城堡顶部建了个鸟笼似的小阁楼;应该设计成Policy或RouteState的形式更具备可扩展性。

mdp4_overlay_set()

msmfb_overlay_set()

ioctl(fb,MSMFB_OVERLAY_SET)

overlay::mdp_wrapper::setOverlay()

MdpCtrl::set()

Ctrl::commit()

pipe::commit()->GernericPipe::commit()

OverlayImplBase::commit()->OverlayImpl<>::commit()

Overlay::commit(overlay::utils::eDest)

mdp4_overlay_play()

msmfb_overlay_play()

ioctl(fd,MSMFB_OVERLAY_PLAY,&od)msmfb_overlay_data.idismdp_overlay.idisthekernelpipeindexinkerneldbreturnedbyMdpCtrl.commit()

overlay::mdp_wrapper::play()

MdpData::play(intfd,uint32_toffset)

Data::play()

GenericPipe::queueBuffer(intfd,uint32_toffset)

OverlayImplBase::queueBuffer()->OverlayImpl<>::(intfd,uint32_toffset,utils::eDestdest)

Overlay::queueBuffer(intfd,uint32_toffset,utils::eDestdest)

Camera或VideoOverlay

CameraOverlay一般有两种实现模式,一种模式是V4L2不提供Overlay接口,在用户态把V4L2bufferhandle传递给Framebuffer,由FramebufferOverlayImplementation完成合成;另一种模式是V4L2提供Overlay接口,可以把Videobuffer在V4L2内核驱动中提交给FramebufferOverlay内核驱动完成合成。QCMSM8kOverlay是采用的第一种模式实现,但是V4L2和Framebuffer驱动也提供了第二种模式的实现;第二种模式可参见Freescale处理器上AndroidOverlay的实现。

Camera的取景器PreviewDisplayWindow是个SurfaceView,所在的Layer(HWC_OVERLAY)在UILayer底下,但是UIlayer会开个全透的区域露出这个HWC_OVERLAYLayer。

下面描述其合成过程。

3DGPU绘制:所有的HWC_FRAMEBUFFERlayers会首先经OpenGL3DGPU绘制到backFramebuffer上;

OV_PIPE0配置:然后在setupHardwareComposer=>HWComposer::prepare=>hwc_prepare=>VideoOverlay::prepare中使用HWC_OVERLAYLayer参数设置OV_PIPE0分配的类型为Video/Graphic的MDPpipe;

OV_PIPE0提交:DisplayHardware::flip=>HWComposer::commit=>hwc_set=>VideoOverlay::draw提交HWC_OVERLAYLayer的图像数据到OV_PIPE0的内核VGpipe的工作队列中(单容量队列即可);

OV_PIPE3配置:DisplayHardware::flip=>HWComposer::commit=>hwc_set=>eglSwapBuffers=>queueBuffer=>fb_post=>update_framebuffer中使用baselayerbackframebuffer参数配置OV_PIPE3即RGB1pipe(注意baselayer的特殊地位);

OV_PIPE3提交:当update_framebuffer真正提交图像数据到RGB1pipe时,对应stage0forbaselayer(内核驱动)会启动MDPLayerMixer0进行Layers合成;

DMA传输:然后fb_post通过FBIOPUT_VSCREENINFO或PAN_DISPLAY启动DMA_P送合成好的图像经MIPI-DSI输出显示。

BufferFlip:queueBuffer后,该framebuffer即为frontframebuffer,然后eglSwapBuffers会dequeueBuffer下一个Framebuffer做为backframebuffer。

此时HardwareComposer使用的是VideoOverlay@hwcomposer.msm8960.so对应的逻辑。VGpipe设置和提交的栈示例如下:

/system/lib/liboverlay.so(overlay::Overlay::commit(overlay::utils::eDest)+71)

/system/lib/liboverlay.soconfigPrimVid(hwc_context_t*ctx,hwc_layer_t*layer)

/system/lib/liboverlay.soVideoOverlay::configure(hwc_context_t*ctx,hwc_layer_t*,hwc_layer_t*)

/system/lib/hw/hwcomposer.msm8960.so(qhwc::VideoOverlay::prepare(hwc_context_t*,hwc_layer_list_t*)+xxx)

/system/lib/hw/hwcomposer.msm8960.so(qhwc::MDPComp::setup(hwc_context_t*,hwc_layer_list*)+163)foreachpipe.

/system/lib/hw/hwcomposer.msm8960.so(qhwc::MDPComp::configure(hwc_composer_device*,hwc_layer_list*)+59)

/system/lib/hw/hwcomposer.msm8960.sohwc_prepare(hwc_composer_device_t*dev,hwc_layer_list_t*list)

/system/lib/libsurfaceflinger.so(android::HWComposer::prepare()const+9)

/system/lib/libsurfaceflinger.so(android::SurfaceFlinger::setupHardwareComposer()+127)

/system/lib/libsurfaceflinger.so(android::SurfaceFlinger::handleRepaint()+147)

/system/lib/libsurfaceflinger.so(android::SurfaceFlinger::onMessageReceived(int)+91)

/system/lib/liboverlay.soOverlay::queueBuffer(intfd,uint32_toffset,utils::eDestdest)

/system/lib/hw/hwcomposer.msm8960.so(qhwc::VideoOverlay::draw(hwc_context_t*,hwc_layer_list*)+xx)foreachpipe.

/system/lib/hw/hwcomposer.msm8960.sostaticinthwc_set(hwc_composer_device_t*dev,hwc_display_tdpy,hwc_surface_tsur,hwc_layer_list_t*list)

/system/lib/libsurfaceflinger.so(android::HWComposer::commit()const+11)

/system/lib/libsurfaceflinger.so(android::DisplayHardware::flip(android::Regionconst&)const+59)

/system/lib/libsurfaceflinger.so(android::SurfaceFlinger::postFramebuffer()+61)

/system/lib/libsurfaceflinger.so(android::SurfaceFlinger::onMessageReceived(int)+103)

Baselayer对应的RGBpipe设置和提交的栈示例从略。Baselayer提交到RGB1时真正启动LayerMixer0的4-stagelayercomposition。

FBLayerHardwareComposition

在系统根目录文件/systembuild.prop中,

debug.sf.hw=1

debug.egl.hw=1

debug.composition.type=mdp

debug.enable.wl_log=1

debug.mdpcomp.maxlayer=3

debug.mdpcomp.logs=0

其中CompositionType有4种,debug.composition.type=dyn/c2d/mdp/cpu/gpu,dyn是根据c2d和mdp的硬件存在选择使用二者之一。

gpu/c2d/mdp可以释放cpu/gpu,减轻cpu/gpu的负担,但并不一定能使显示效果流畅。

782sMaxLayers=0;

783if(property_get("debug.mdpcomp.maxlayer",property,NULL)>0){

784if(atoi(property)!=0)

785sMaxLayers=atoi(property);

786}

当没有Camera/Video/HDMI/WFD等Overlay应用情景时,所有Layer都是HWC_FRAMEBUFFER的,MDP的LayerMixer0是闲置的,这时可以优化利用其来做FramebufferLayer合成。由于管道数目限制的原因,只能合成小于等于sMaxLayers个Layers。多于3个的时候是否可以MDP合成其中的3个?可能需要考虑Layerbuffer维度、格式、缩放、Z序等因素。当多于3个的时候,是遍历layer::draw使用GPU来绘制纹理到backframebuffer上的。

下面着重看少于等于3个Layer,MDP合成的情况。

首先,所有的Layer的compositiontype都是HWC_FRAMEBUFFER的;

然后SurfaceFlinger在setHardwareComposer时发现hwc_prepare没有别的优先的Overlay情景,最后的一个if分支就是使用MDP来做Layer合成;SurfaceFlinger会检查Layer的属性看是否满足使用MDP的条件,然后设置满足条件的Layer的属性[compositionType,flags]=(HWC_OVERLAY,MDPCOMP);这样SurfaceFlinger::composeSurfaces时,就不再通过layer::draw使用GPU来绘制;

PIPE配置:SurfaceFlinger为HWC创建工作集,为每个Layer分配并使用MDPComp::prepare配置每个pipe,如果有缩放需求,则会分配VGpipe,因为RGBpipe不支持缩放;有一个Layer则Overlay状态为BYPASS_1_LAYER,表示有1个LayerBypass,不需要OpenGL绘制,同理2个Layer为BYPASS_2_LAYER,3个为BYPASS_3_LAYER;

PIPE提交:在DisplayHardware::flip中提交每个Layer的数据到管道,即MDPComp::draw()=>Overlay::queueBuffer()。注意Layer图像数据是在PMEM/ASHMEM内存中而不是Framebuffer内存中,但仍物理连续或IOMMU映射连续,LayerMixerdraw可访问。MDPComp::draw完即提交数据到对应管道的单容量队列后,清layer->flags&=~HWC_MDPCOMP标志;

在eglSwapBuffers时,真正做back&frontBuffer的切换(准确地说双缓冲是切换,三缓冲是轮用)

OV_PIPE3配置:DisplayHardware::flip=>HWComposer::commit=>hwc_set=>eglSwapBuffers=>queueBuffer=>fb_post=>update_framebuffer中使用baselayerbackframebuffer参数配置OV_PIPE3即RGB1pipe;

OV_PIPE3提交:当update_framebuffer真正提交图像数据到RGB1pipe时,对应stage0forbaselayer(内核驱动)会启动MDPLayerMixer0进行Layers合成;此时baselayer是个最顶层的全透明Layer,不妨碍底层Layer的显示;

HWC功能时,是MDPComp@hwcomposer.msm8960.so对应的逻辑,一个Layer对应的pipe设置和提交的栈示例如下:

#00pc0000b0d0/system/lib/liboverlay.so(overlay::Overlay::commit(overlay::utils::eDest)+71)

#01pc00005d16/system/lib/hw/hwcomposer.msm8960.so(qhwc::MDPComp::prepare(hwc_context_t*,hwc_layer*,qhwc::MDPComp::mdp_pipe_info&)+577)

#02pc00006190/system/lib/hw/hwcomposer.msm8960.so(qhwc::MDPComp::setup(hwc_context_t*,hwc_layer_list*)+163)foreachpipe.

#03pc0000652c/system/lib/hw/hwcomposer.msm8960.so(qhwc::MDPComp::configure(hwc_composer_device*,hwc_layer_list*)+59)

#04pc000037ea/system/lib/hw/hwcomposer.msm8960.sohwc_prepare(hwc_composer_device_t*dev,hwc_layer_list_t*list)

#05pc0001ebba/system/lib/libsurfaceflinger.so(android::HWComposer::prepare()const+9)

#06pc00020df4/system/lib/libsurfaceflinger.so(android::SurfaceFlinger::setupHardwareComposer()+127)

#07pc000212f4/system/lib/libsurfaceflinger.so(android::SurfaceFlinger::handleRepaint()+147)

#08pc000222e8/system/lib/libsurfaceflinger.so(android::SurfaceFlinger::onMessageReceived(int)+91)

#00pc00006592/system/lib/hw/hwcomposer.msm8960.so(qhwc::MDPComp::draw(hwc_context_t*,hwc_layer_list*)+85)foreachpipe.

#01pc00003ae4/system/lib/hw/hwcomposer.msm8960.sostaticinthwc_set(hwc_composer_device_t*dev,hwc_display_tdpy,hwc_surface_tsur,hwc_layer_list_t*list)

#02pc0001ed56/system/lib/libsurfaceflinger.so(android::HWComposer::commit()const+11)

#03pc0001e57c/system/lib/libsurfaceflinger.so(android::DisplayHardware::flip(android::Regionconst&)const+59)

#04pc000209c6/system/lib/libsurfaceflinger.so(android::SurfaceFlinger::postFramebuffer()+61)

#05pc00022474/system/lib/libsurfaceflinger.so(android::SurfaceFlinger::onMessageReceived(int)+103)

Baselayer对应的RGBpipe设置和提交的栈示例从略。重复地,Baselayer提交到RGB1时真正启动LayerMixer0的4-stagelayercomposition。

HDMI/WFD

有机会待续。

LayerMixer0合成后图像经DMA_P输出时,在BLT模式和DIRECTOUT模式下LayerMixer0与DMA之间没有buffer;即使FB模式,也是一个Overlaybuffer而未必是做为Framebuffer内存,当然这个Overlaybuffer做为DMA_P输入传输给MIPI-DSI/LCDC。

FrameBufferdriverincludingMDPOverlay

Framebuffer设备的sysfs

330staticintmsm_fb_create_sysfs(structplatform_device*pdev)

331{

332intrc;

333structmsm_fb_data_type*mfd=platform_get_drvdata(pdev);

334

335rc=sysfs_create_group(&mfd->fbi->dev->kobj,&msm_fb_attr_group);

336if(rc)

337MSM_FB_ERR("%s:sysfsgroupcreationfailed,rc=%d\n",__func__,

338rc);

339returnrc;

340}

root@android:/sys/class/graphics/fb0#ls-al

-rw-r--r--rootroot40961970-06-2709:37bits_per_pixel

-rw-r--r--rootroot40961970-06-2709:37blank

-rw-r--r--rootroot40961970-06-2709:37console

-rw-r--r--rootroot40961970-06-2709:37cursor

-r--r--r--rootroot40961970-06-2709:37dev

-rw-r--r--rootroot40961970-06-2709:37mode

-rw-r--r--rootroot40961970-06-2709:37modes

-r--r--r--rootroot40961970-06-2709:37msm_fb_type

-r--r--r--rootroot40961970-06-2709:37name

-rw-r--r--rootroot40961970-06-2709:37pan

drwxr-xr-xrootroot1970-06-2708:28power

-rw-r--r--rootroot40961970-06-2709:37rotate

-rw-r--r--rootroot40961970-06-2709:37state

-r--r--r--rootroot40961970-06-2709:37stride

lrwxrwxrwxrootroot1970-06-2709:37subsystem->../../../../class/graphics

-rw-r--r--rootroot40961970-06-2708:28uevent

-rw-r--r--rootroot40961970-06-2709:37virtual_size

-r--r--r--rootroot40961970-06-2708:28vsync_event

root@android:/sys/class/graphics/fb0#catmsm_fb_type

mipidsicmdpanel

root@android:/sys/class/graphics/fb0#catbits_per_pixel

32

130|root@android:/sys/class/graphics/fb0#catdev

29:0

root@android:/sys/class/graphics/fb0#catmodes

U:480x854p-0

root@android:/sys/class/graphics/fb0#catname

msmfb42_90501

root@android:/sys/class/graphics/fb0#catstride

1920

root@android:/sys/class/graphics/fb0#catvirtual_size

480,2566

cont_splash_donefield

Addsupportfor"ContinuousSplashScreen"feature.

Theimagedisplayedonthescreenbytheandroidbootloaderdrivershouldcontinuetilltheandroidanimationshowsup.

DelaythedisplayinitializationforMDP,displaydependentclocksandpanelpoweronfunctions.

bootloader显示的image在linux内核启动过程中保持显示在屏幕上,知道开机动画显示,即linux内核启动过程中不要出现黑屏。

Earlysuspend&Earlyresume

Earlysuspend是有wakelock还占有,系统还不能整体suspend,但是可以关闭屏幕、背光、输入等;在Earlysuspended状态时,重新打开屏幕、背光和输入,是为对应的earlyresume。

1551mfd->early_suspend.suspend=msmfb_early_suspend;

1552mfd->early_suspend.resume=msmfb_early_resume;

1553mfd->early_suspend.level=EARLY_SUSPEND_LEVEL_DISABLE_FB-2;

1554register_early_suspend(&mfd->early_suspend);

数据结构定义如下:

23/*Theearly_suspendstructuredefinessuspendandresumehookstobecalled

24*whentheuservisiblesleepstateofthesystemchanges,andalevelto

25*controltheorder.Theycanbeusedtoturnoffthescreenandinput

26*devicesthatarenotusedforwakeup.

27*Suspendhandlersarecalledinlowtohighlevelorder,resumehandlersare

28*calledintheoppositeorder.If,whencallingregister_early_suspend,

29*thesuspendhandlershavealreadybeencalledwithoutamatchingcalltothe

30*resumehandlers,thesuspendhandlerwillbecalleddirectlyfrom

31*register_early_suspend.Thisdirectcallcanviolatethenormallevelorder.

32*/

33enum{

34EARLY_SUSPEND_LEVEL_BLANK_SCREEN=50,

35EARLY_SUSPEND_LEVEL_STOP_DRAWING=100,

36EARLY_SUSPEND_LEVEL_DISABLE_FB=150,

37};

38structearly_suspend{

39#ifdefCONFIG_HAS_EARLYSUSPEND

40structlist_headlink;

41intlevel;

42void(*suspend)(structearly_suspend*h);

43void(*resume)(structearly_suspend*h);

44#endif

45};

backlight

msm_fb_set_backlight以后是使用led_trigger调用真正led_classdev"wled"的brightnes_set去设置背光。

用户态ioctl通过msm_fb_set_backlight调用到msm_fb_panel_data::set_backlight,

"lcd_backlight".brightness_set->msm_fb_panel_data::set_backlight->"bkl_trigger".led_trigger->"wled".brightness_set。然后找真正操作硬件IC部分。

驱动中设置背光则是绕过"lcd_backlight"设备直接通过backlight_worker工作执行到msm_fb_panel_data::set_backlight,然后->"bkl_trigger".led_trigger->"wled".brightness_set。

可以认为"lcd_backlight"是背光抽象设备,通过led_trigger的led组映射到不同的led_classdev设备

以三星DSICMD屏为例:

Inmipi_samsung.c

294staticvoidmipi_samsung_set_backlight(structmsm_fb_data_type*mfd)

295{

296if(!cci_fb_UpdateDone){

297printk("Taylor:NoBLbeforeLCMon\n");

298return;

299}

300

301pr_debug("Taylor:%s:SetBL:%d\n",__func__,mfd->bl_level);

302if((mipi_samsung_pdata->enable_wled_bl_ctrl)

303&&(wled_trigger_initialized)){

304led_trigger_event(bkl_led_trigger,mfd->bl_level);

305return;

306}

307}

kernel/drivers/leds/leds-pm8xxx.c

#definePM8XXX_LEDS_DEV_NAME"pm8xxx-led"

2283staticstructplatform_driverpm8xxx_led_driver={

2284.probe=pm8xxx_led_probe,

2285.remove=__devexit_p(pm8xxx_led_remove),

2286.driver={

2287.name=PM8XXX_LEDS_DEV_NAME,

2288.owner=THIS_MODULE,

2289},

2290};

pm8xxx_led_probe会对pm8038_led_info数组中的每个led使用设置led_classdev字段,并且初始化workitem,然后使用led_classdev_register向系统注册每个led设备。

2197INIT_WORK(&led_dat->work,pm8xxx_led_work);

2198INIT_WORK(&led_dat->modework,pm8xxx_mode_work);

2199INIT_WORK(&led_dat->testwork,pm8xxx_test_work);

每个led的brightness_set字段设置为pm8xxx_led_set。

1790staticvoidpm8xxx_led_set(structled_classdev*led_cdev,

1791enumled_brightnessvalue)

1792{

1793structpm8xxx_led_data*led;

1794

1795led=container_of(led_cdev,structpm8xxx_led_data,cdev);

1796

1797if(valueled->cdev.max_brightness){

1798dev_err(led->cdev.dev,"Invalidbrightnessvalueexceeds");

1799return;

1800}

1801

1802led->cdev.brightness=value;

1803schedule_work(&led->work);

1804}

1730staticvoidpm8xxx_led_work(structwork_struct*work)

1731{

1732intrc;

1733

1734structpm8xxx_led_data*led=container_of(work,

1735structpm8xxx_led_data,work);

1736

1737if(led->pwm_dev==NULL){

1738__pm8xxx_led_work(led,led->cdev.brightness);

1739}else{

1740rc=pm8xxx_led_pwm_work(led);

1741if(rc)

1742pr_err("couldnotconfigurePWMmodeforLED:%d\n",

1743led->id);

1744}

1745}

对PM8XXX_ID_WLED,是使用__pm8xxx_led_work

1692staticvoid__pm8xxx_led_work(structpm8xxx_led_data*led,

1693enumled_brightnesslevel)

1694{

1695intrc;

1696

1697mutex_lock(&led->lock);

1698

1699switch(led->id){

1700casePM8XXX_ID_LED_KB_LIGHT:

1701led_kp_set(led,level);

1702break;

1703casePM8XXX_ID_LED_0:

1704casePM8XXX_ID_LED_1:

1705casePM8XXX_ID_LED_2:

1706led_lc_set(led,level);

1707break;

1708casePM8XXX_ID_FLASH_LED_0:

1709casePM8XXX_ID_FLASH_LED_1:

1710led_flash_set(led,level);

1711break;

1712casePM8XXX_ID_WLED:

1713rc=led_wled_set(led,level);

1714if(rc<0)

1715pr_err("wledbrightnesssetfailed%d\n",rc);

1716break;

1717casePM8XXX_ID_RGB_LED_RED:

1718casePM8XXX_ID_RGB_LED_GREEN:

1719casePM8XXX_ID_RGB_LED_BLUE:

1720led_rgb_set(led,level);

1721break;

1722default:

1723dev_err(led->cdev.dev,"unknownledid%d",led->id);

1724break;

1725}

1726

1727mutex_unlock(&led->lock);

1728}

led_wled_set写电源管理芯片pm8xxx的控制寄存器,控制wled。

Framebufferfb_info::node

registered_fb它是一个数组,它的类型就是structfb_info,它用于保存我们调用register_framebuffer传进来的structfb_info。

num_registered_fb代表注册帧缓冲设备的个数。

1522for(i=0;inode=i;

相当于找到一个空的次设备号。

Framebuffer像素格式

主屏RGBA8888

Inmipi_dsi_probe()

481/*

482*get/setpanelspecificfbinfo

483*/

484mfd->panel_info=pdata->panel_info;

485pinfo=&mfd->panel_info;

486

487if(mfd->panel_info.type==MIPI_VIDEO_PANEL)

488mfd->dest=DISPLAY_LCDC;

489else

490mfd->dest=DISPLAY_LCD;

491

492if(mdp_rev==MDP_REV_303&&

493mipi_dsi_pdata->get_lane_config){

494if(mipi_dsi_pdata->get_lane_config()!=2){

495pr_info("ChangingtoDSISingleModeConfiguration\n");

496#ifdefCONFIG_FB_MSM_MDP303

497update_lane_config(pinfo);

498#endif

499}

500}

501

502if(mfd->index==0)

503mfd->fb_imgType=MSMFB_DEFAULT_TYPE;//configedasRGBA8888forfb0

504else

505mfd->fb_imgType=MDP_RGB_565;

msmfb_update_notify/msmfb_no_update_notify

用于CABL功能时,统计直方图使用;

sw_refresher

使用定时器去触发mdp_refresh_screen,添加work进行dma_fnc调用。

针对某些接口的VIDEO模式屏且控制器没有hw_refresh支持时,可以使用sw_refresher。

FB_ACTIVATE_VBL标志涵义

fb_var_screeninfo结构体的成员变量activate的值设置FB_ACTIVATE_VBL,表示要等到下一个垂直同步事件出现时,再将当前要渲染的图形缓冲区的内容绘制出来。这样做的目的是避免出现屏幕闪烁,即避免前后两个图形缓冲区的内容各有一部分同时出现屏幕中。

Framebuffer初始化

设备资源申请是在MACHINE_DESC中实现的。示例如下:

3463MACHINE_START(MSM8930_CDP,"QCTMSM8930CDP")

3464.map_io=msm8930_map_io,

3465.reserve=msm8930_reserve,

3466.init_irq=msm8930_init_irq,

3467.handle_irq=gic_handle_irq,

3468.timer=&msm_timer,

3469.init_machine=msm8930_cdp_init,

3470.init_early=msm8930_allocate_memory_regions,

3471.init_very_early=msm8930_early_memory,

3472.restart=msm_restart,

3473MACHINE_END

函数msm8930_cdp_init做Machine级别的设备的注册,部分代码如下:

staticvoid__initmsm8930_cdp_init(void)@kernel/arch/arm/mach-msm/board-8930.c

platform_add_devices(common_devices,ARRAY_SIZE(common_devices));

msm8930_init_gpu();

msm8930_init_mmc();

msm8930_init_cam();

msm8930_init_fb();

“msm_fb”msmframebuffer设备,注意不是linuxframebuffer设备,但是有对应关系;

“wfd”wifi显示设备;

“mipi_dsi_cmd_samsung_fwvga”mipi-dsi接口cmd模式LCD屏设备;

“hdmi_msm”HDMI显示器设备;

“mdp”mobiledisplaystation显示引擎设备;

“mipi-dsi”MIPI-DSI显示器驱动设备(id例外用了1,可能有的平台上有两个MIPI-DSI,另一个id为0);

1168void__initmsm8930_init_fb(void)@kernel/arch/arm/mach-msm/board-8930-display.c

1169{

1170platform_device_register(&msm_fb_device);

1171

1172#ifdefCONFIG_FB_MSM_WRITEBACK_MSM_PANEL

1173platform_device_register(&wfd_panel_device);

1174platform_device_register(&wfd_device);

1175#endif

1176

1178#ifdefCONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT

1179platform_device_register(&mipi_dsi_novatek_panel_device);

1180#endif

1181

1184#ifdefCONFIG_FB_MSM_MIPI_SA77_CMD_FWVGA_PANEL

1185platform_device_register(&mipi_dsi_cmd_chimei_fwvga_panel_device);

1186platform_device_register(&mipi_dsi_cmd_samsung_fwvga_panel_device);

1187#endif

1189

1190#ifdefCONFIG_FB_MSM_HDMI_MSM_PANEL

1191platform_device_register(&hdmi_msm_device);

1192#endif

1193

1194platform_device_register(&mipi_dsi_toshiba_panel_device);

1195

1196msm_fb_register_device("mdp",&mdp_pdata);

1197msm_fb_register_device("mipi_dsi",&mipi_dsi_pdata);

1198#ifdefCONFIG_MSM_BUS_SCALING

1199#ifdefCONFIG_FB_MSM_DTV

1200msm_fb_register_device("dtv",&dtv_pdata);

1201#endif

1202#endif

1203}

因为注册这些设备的意图主要是资源申请和初步初始化设备,所以各设备注册顺序并无关紧要。其初始化顺序还与后来的驱动实际注册顺序有关。

首先注册paltform_devicemsm_fb_device,设备定义如下

71staticstructresourcemsm_fb_resources[]={

72{

73.flags=IORESOURCE_DMA,

74}

75};

135staticstructmsm_fb_platform_datamsm_fb_pdata={

136.detect_client=msm_fb_detect_panel,

137};

138

139staticstructplatform_devicemsm_fb_device={

140.name="msm_fb",

141.id=0,

142.num_resources=ARRAY_SIZE(msm_fb_resources),

143.resource=msm_fb_resources,

144.dev.platform_data=&msm_fb_pdata,

145};

然后注册panel设备,设备定义如下

845staticstructmipi_dsi_panel_platform_datasamsung_pdata={

846.enable_wled_bl_ctrl=0x1,

847};

848

849staticstructplatform_devicemipi_dsi_cmd_samsung_fwvga_panel_device={

850.name="dsi_cmd_samsung_fwvga",

851.id=0,

852.dev={

853.platform_data=&samsung_pdata,

854}

855};

然后关键的注册mdp和mipi-dsicontroller,设备定义如下

1749void__initmsm_fb_register_device(char*name,void*data)

1750{

1751if(!strncmp(name,"mdp",3))

1752msm_register_device(&msm_mdp_device,data);

1753elseif(!strncmp(name,"lcdc",4))

1754msm_register_device(&msm_lcdc_device,data);

1755elseif(!strncmp(name,"mipi_dsi",8))

1756msm_register_device(&msm_mipi_dsi_device,data);

1757#ifdefCONFIG_FB_MSM_TVOUT

1758elseif(!strncmp(name,"tvenc",5))

1759msm_register_device(&msm_tvenc_device,data);

1760elseif(!strncmp(name,"tvout_device",12))

1761msm_register_device(&msm_tvout_device,data);

1762#endif

1763#ifdefCONFIG_MSM_BUS_SCALING

1764elseif(!strncmp(name,"dtv",3))

1765msm_register_device(&msm_dtv_device,data);

1766#endif

1767else

1768printk(KERN_ERR"%s:unknowndevice!%s\n",__func__,name);

1769}

mdp和mipi-dsi设备及寄存器映射和中断需求如下

1484#defineMIPI_DSI_HW_BASE0x04700000

1485#defineROTATOR_HW_BASE0x04E00000

1486#defineTVENC_HW_BASE0x04F00000

1487#defineMDP_HW_BASE0x05100000

1488

1489staticstructresourcemsm_mipi_dsi_resources[]={

1490{

1491.name="mipi_dsi",

1492.start=MIPI_DSI_HW_BASE,

1493.end=MIPI_DSI_HW_BASE+0x000F0000-1,

1494.flags=IORESOURCE_MEM,

1495},

1496{

1497.start=DSI_IRQ,

1498.end=DSI_IRQ,

1499.flags=IORESOURCE_IRQ,

1500},

1501};

1502

1503staticstructplatform_devicemsm_mipi_dsi_device={

1504.name="mipi_dsi",

1505.id=1,

1506.num_resources=ARRAY_SIZE(msm_mipi_dsi_resources),

1507.resource=msm_mipi_dsi_resources,

1508};

1509

1510staticstructresourcemsm_mdp_resources[]={

1511{

1512.name="mdp",

1513.start=MDP_HW_BASE,

1514.end=MDP_HW_BASE+0x000F0000-1,

1515.flags=IORESOURCE_MEM,

1516},

1517{

1518.start=INT_MDP,

1519.end=INT_MDP,

1520.flags=IORESOURCE_IRQ,

1521},

1522};

1523

1524staticstructplatform_devicemsm_mdp_device={

1525.name="mdp",

1526.id=0,

1527.num_resources=ARRAY_SIZE(msm_mdp_resources),

1528.resource=msm_mdp_resources,

1529};

以上设备注册时,其驱动并未加载,因为machine_desc.init_machine设备注册的arch_initcall是在.initcall3.init中,而module_initdriver注册是在.initcall6.init中。等其驱动加载时,在对应的probe函数中,判断设备id,初步初始化设备,保存其资源分配。

此时,以下id为0的设备panel,dtv,hdmi,mdp,msm_fb,id为1的mipi-dsi设备已经注册进系统了。

dsi_cmd_chimei_fwvga.0

dsi_cmd_samsung_fwvga.0

dsi_cmd_samsung_fwvga.1281

dtv.0

dtv.458753

hdmi_msm.0

hdmi_msm.1

mdp.0

mdp.458753

mdp.591105

mdp.655361

mipi_dsi.1

mipi_dsi.591105

mipi_toshiba.0

msm_fb.0

msm_fb.458753

msm_fb.591105

msm_fb.655361

下面描述各驱动注册,各驱动都是module_init的。

msm_fb驱动注册如下

module_init(msm_fb_init);

3898int__initmsm_fb_init(void)@kernel/drivers/video/msm/msm_fb.c

3899{

3900intrc=-ENODEV;

3901

3902if(msm_fb_register_driver())

3903returnrc;

….

3705staticintmsm_fb_register_driver(void)

3706{

3707returnplatform_driver_register(&msm_fb_driver);

3708}

msm_fb_driver驱动定义如下

734staticstructplatform_drivermsm_fb_driver={

735.probe=msm_fb_probe,

736.remove=msm_fb_remove,

737#ifndefCONFIG_HAS_EARLYSUSPEND

738.suspend=msm_fb_suspend,

739.resume=msm_fb_resume,

740#endif

741.shutdown=NULL,

742.driver={

743/*Drivernamemustmatchthedevicenameaddedinplatform.c.*/

744.name="msm_fb",

745.pm=&msm_fb_dev_pm_ops,

746},

747};

platform_device“msm_fb”的resource[0]是一块DMA内存,是framebuffer内存,但是在资源定义中并没有设置size,而在msm_fb_probe中从其使用可以看到该DMA内存已经分配。其size是在machine的init_early中从bootmem中分配的,比machine级别设备注册要早!

.init_early=msm8930_allocate_memory_regions,

msm8930_allocate_memory_regions(void)@kernel/arch/arm/mach-msm/board-8930.c

1006staticvoid__initmsm8930_allocate_memory_regions(void)

1007{

1008msm8930_allocate_fb_region();

1009}

msm8930_allocate_fb_region()@kernel/arch/arm/mach-msm/board-8930-display.c

1205void__initmsm8930_allocate_fb_region(void)

1206{

1207void*addr;

1208unsignedlongsize;

1209

1210size=MSM_FB_SIZE;

1211addr=alloc_bootmem_align(size,0x1000);

1212msm_fb_resources[0].start=__pa(addr);

1213msm_fb_resources[0].end=msm_fb_resources[0].start+size-1;

1214pr_info("allocating%lubytesat%p(%lxphysical)forfb\n",size,addr,__pa(addr));

1216}

MSM_FB_SIZE宏定义如下,TRIPLE_BUFFER已是主流

32#ifdefCONFIG_FB_MSM_TRIPLE_BUFFER

33#defineMSM_FB_PRIM_BUF_SIZE\

34(roundup((1920*1088*4),4096)*3)/*4bppx3pages*/

35#else

36#defineMSM_FB_PRIM_BUF_SIZE\

37(roundup((1920*1088*4),4096)*2)/*4bppx2pages*/

38#endif

39/*Note:mustbemultipleof4096*/

40#defineMSM_FB_SIZEroundup(MSM_FB_PRIM_BUF_SIZE,4096)

当”msm_fb”注册时,msm_fb_probe在设备和驱动match后被调用。对于msm_fb_deviceid=0,只做fbram保存和IONclient创建;这时probe到的设备对应/sys/bus/platform/devices/msm_fb.0。

msm_ion_client_create(-1,pdev->name);

”mipi-dsi”driver定义和注册如下(inkernel/drivers/video/msm/mipi_dsi.c)

55staticstructplatform_drivermipi_dsi_driver={

56.probe=mipi_dsi_probe,

57.remove=mipi_dsi_remove,

58.shutdown=NULL,

59.driver={

60.name="mipi_dsi",

61},

62};

603staticintmipi_dsi_register_driver(void)

604{

605returnplatform_driver_register(&mipi_dsi_driver);

606}

608staticint__initmipi_dsi_driver_init(void)

609{

610intret;

611

612mipi_dsi_init();

613

614ret=mipi_dsi_register_driver();

616device_initialize(&dsi_dev);

617

618if(ret){

619pr_err("mipi_dsi_register_driver()failed!\n");

620returnret;

621}

623returnret;

624}

625

626module_init(mipi_dsi_driver_init);

“mdp”driver定义和注册如下(inkernel/drivers/video/msm/mdp.c)

2094staticstructplatform_drivermdp_driver={

2095.probe=mdp_probe,

2096.remove=mdp_remove,

2097#ifndefCONFIG_HAS_EARLYSUSPEND

2098.suspend=mdp_suspend,

2099.resume=NULL,

2100#endif

2101.shutdown=NULL,

2102.driver={

2103/*

2104*Drivernamemustmatchthedevicenameaddedin

2105*platform.c.

2106*/

2107.name="mdp",

2108.pm=&mdp_dev_pm_ops,

2109},

2110};

3001staticintmdp_register_driver(void)

3002{

3003#ifdefCONFIG_HAS_EARLYSUSPEND

3004early_suspend.level=EARLY_SUSPEND_LEVEL_DISABLE_FB-1;

3005early_suspend.suspend=mdp_early_suspend;

3006early_suspend.resume=mdp_early_resume;

3007register_early_suspend(&early_suspend);

3008#endif

3009

3010returnplatform_driver_register(&mdp_driver);

3011}

3012

3013staticint__initmdp_driver_init(void)

3014{

3015intret;

3016

3017mdp_drv_init();

3018

3019ret=mdp_register_driver();

3020if(ret){

3021printk(KERN_ERR"mdp_register_driver()failed!\n");

3022returnret;

3023}

3024

3025#ifdefined(CONFIG_DEBUG_FS)

3026mdp_debugfs_init();

3027#endif

3028

3029return0;

3030

3031}

3032

3033module_init(mdp_driver_init);

当真正从屏驱动中添加一块显示设备时,为了让上级设备(“mipi-dsi”“mdp”“msm_fb”“fb”)使用下级设备,高通实现为下级设备创建了每个上级设备的实例,通过从下到上的设备probe链一级一级向上注册。这时保证从下到上的设备注册顺序就是至关重要的了,probe链做注册来保证这一点。

下面描述由屏驱动添加屏到注册linuxframebuffer设备的流程。

在各自的屏设备注册文件中,会去探测屏,这种探测不是做真正扫描,仅仅是使用设备名字验证一下,以SAMSUNGMIPIDSICMD屏为例,驱动会使用相应规则ID注册一块屏。

staticint__initmipi_cmd_samsung_fwvga_pt_init(void)@kernel/drivers/video/msm/mipi_samsung_cmd_fwvga_pt.c

37intret;

38

39if(msm_fb_detect_client("mipi_cmd_samsung_fwvga"))

40return0;

……

88ret=mipi_samsung_device_register(&pinfo,MIPI_DSI_PRIM,MIPI_DSI_PANEL_QHD_PT);

90if(ret)

91pr_err("%s:failedtoregisterdevice!\n",__func__);

92

93returnret;

94}

95

96module_init(mipi_cmd_samsung_fwvga_pt_init);

探测函数intmsm_fb_detect_client(constchar*name)首先使用主屏和外屏名字匹配,匹配不成则使用msm_fd_device.dev.platform.detect_client=msm_fb_detect_panel去匹配。上面欲同时注册SAMSUNG和CHIMEI两块屏设备,当然不能同时detect_panel成功,使用GPIO管脚配置做了二次匹配。实际不同批次的手机可能用到两块屏中的一种。

然后mipi_samsung_device_register()(对CHIMEI则是mipi_chimei_device_register)注册屏驱动和屏设备。驱动定义和注册具体如下,

358staticstructplatform_driverthis_driver={

359.probe=mipi_samsung_lcd_probe,

360.driver={

361.name="dsi_cmd_samsung_fwvga",

362},

363};

364

365staticstructmsm_fb_panel_datasamsung_panel_data={

366.on=mipi_samsung_lcd_on,

367.off=mipi_samsung_lcd_off,

368.set_backlight=mipi_samsung_set_backlight,

369};

373intmipi_samsung_device_register(structmsm_panel_info*pinfo,u32channel,u32panel)

375{

376structplatform_device*pdev=NULL;

377intret;

378

379if((channel>=3)||ch_used[channel])

380return-ENODEV;

381

382ch_used[channel]=TRUE;

383

384ret=mipi_samsung_lcd_init();

385if(ret){

386pr_err("mipi_samsung_lcd_init()failedwithret%u\n",ret);

387returnret;

388}

389

390pdev=platform_device_alloc("dsi_cmd_samsung_fwvga",(panel<<8)|channel);

391if(!pdev)

392return-ENOMEM;

393

394samsung_panel_data.panel_info=*pinfo;

395

396ret=platform_device_add_data(pdev,&samsung_panel_data,

397sizeof(samsung_panel_data));

398if(ret){

399printk(KERN_ERR

400"%s:platform_device_add_datafailed!\n",__func__);

401gotoerr_device_put;

402}

403

404ret=platform_device_add(pdev);

405if(ret){

406printk(KERN_ERR

407"%s:platform_device_registerfailed!\n",__func__);

408gotoerr_device_put;

409}

410

411return0;

412

413err_device_put:

414platform_device_put(pdev);

415returnret;

416}

417

418staticintmipi_samsung_lcd_init(void)

419{

420

421led_trigger_register_simple("bkl_trigger",&bkl_led_trigger);

422pr_info("%s:SUCCESS(WLEDTRIGGER)\n",__func__);

423wled_trigger_initialized=1;

424

425mipi_dsi_buf_alloc(&samsung_tx_buf,DSI_BUF_SIZE);

426mipi_dsi_buf_alloc(&samsung_rx_buf,DSI_BUF_SIZE);

427

428returnplatform_driver_register(&this_driver);

429}

在mipi_samsung_lcd_init()中注册platform_driver屏驱动,然后分配注册platform_device屏设备;platform_data设置为msm_fb_panel_data向上传递参数并用于上级设备调用控制屏开关和背光。

屏设备注册后,platform_device和platform_drivermatch,驱动的probe函数被调用,把参数一级一级向上带,用于设置上级设备参数,向上一级一级注册设备(“mipi-dsi”,“mdp”,“msm_fb”,“framebuffer”)。

一个类似的调用栈如下

------------[cuthere]------------

WARNING:at/home/CORPUSERS/xp010548/myworkdroid/7x25a/LINUX/kernel/drivers/video/msm/msm_fb.c:1221msm_fb_probe+0xf4/0xcbc()

msm_fb_probe

Moduleslinkedin:

[](unwind_backtrace+0x0/0x12c)from[](warn_slowpath_common+0x4c/0x64)

[](warn_slowpath_common+0x4c/0x64)from[](warn_slowpath_fmt+0x2c/0x3c)

[](warn_slowpath_fmt+0x2c/0x3c)from[](msm_fb_probe+0xf4/0xcbc)

[](msm_fb_probe+0xf4/0xcbc)from[](platform_drv_probe+0x18/0x1c)

[](platform_drv_probe+0x18/0x1c)from[](driver_probe_device+0x144/0x264)

[](driver_probe_device+0x144/0x264)from[](bus_for_each_drv+0x48/0x84)

[](bus_for_each_drv+0x48/0x84)from[](device_attach+0x74/0xa0)

[](device_attach+0x74/0xa0)from[](bus_probe_device+0x24/0x40)

[](bus_probe_device+0x24/0x40)from[](device_add+0x3f0/0x570)

[](device_add+0x3f0/0x570)from[](platform_device_add+0x13c/0x1a0)

[](platform_device_add+0x13c/0x1a0)from[](mdp_probe+0x828/0x940)

[](mdp_probe+0x828/0x940)from[](platform_drv_probe+0x18/0x1c)

[](platform_device_add+0x13c/0x1a0)from[](mipi_dsi_probe+0x514/0x5d0)

[](mipi_dsi_probe+0x514/0x5d0)from[](platform_drv_probe+0x18/0x1c)

[](platform_device_add+0x13c/0x1a0)from[](msm_fb_add_device+0x150/0x1b4)

[](msm_fb_add_device+0x150/0x1b4)from[](mipi_himax_lcd_probe+0x38/0x108)

[](mipi_himax_lcd_probe+0x38/0x108)from[](platform_drv_probe+0x18/0x1c)

[](platform_device_add+0x13c/0x1a0)from[](mipi_himax_device_register+0x7c/0xc0)

[](mipi_himax_device_register+0x7c/0xc0)from[](mipi_cmd_himax_hvga_pt_init+0x148/0x180)

[](mipi_cmd_himax_hvga_pt_init+0x148/0x180)from[](do_one_initcall+0x94/0x164)

[](do_one_initcall+0x94/0x164)from[](kernel_init+0x98/0x144)

[](kernel_init+0x98/0x144)from[](kernel_thread_exit+0x0/0x8)

---[endtrace65f8ea860415c051]---

下面考察设备probe链。

311staticint__devinitmipi_samsung_lcd_probe(structplatform_device*pdev)

312{

…..

338current_pdev=msm_fb_add_device(pdev);

339

340if(current_pdev){

341mfd=platform_get_drvdata(current_pdev);

342if(!mfd)

343return-ENODEV;

344if(mfd->key!=MFD_KEY)

345return-EINVAL;

346

347mipi=&mfd->panel_info.mipi;

348

349if(phy_settings!=NULL)

350mipi->dsi_phy_db=phy_settings;

351

352if(dlane_swap)

353mipi->dlane_swap=dlane_swap;

354}

355return0;

msm_fb_add_device做如下事情:

调用structplatform_device*msm_fb_device_alloc(structmsm_fb_panel_data*pdata,u32type,u32id)函数分配platform_device“mipi-dsi.type_devid”并设置其platform_data为msm_fb_panel_data。

以额外msm_fb_data_type结构分配framebuffer。

注册”mipi-dsi”设备platform_device_add(this_dev)。

“mipi-dsi”设备和驱动进行attach,match后,“mipi-dsi”driver的probe被调用到。

staticintmipi_dsi_probe(structplatform_device*pdev)@kernel/drivers/video/msm/mipi_dsi.c做如下事情(这次的设备ID是真正实例的了,不是1了):

分配”mdp”设备实例并设置其数据,设置”mipi-dsi”设备操作函数,用于向下操作一级一级设备;

继续设置linuxframebuffer的参数;

根据屏分辨率设置mipi-dsi工作频率;

注册该”mdp”设备实例;

“mdp”设备和驱动进行attach,match后,“mdp”driver的probe被调用到。

staticintmdp_probe(structplatform_device*pdev)@kernel/drivers/video/msm/mdp.c

分配”msm_fb”实例并设置其数据;

配置”mdp”工作模式;

注册”msm_fb”实例;

“msm_fb”设备和驱动进行attach,match后,“msm_fb”driver的probe被调用到。

staticintmsm_fb_probe(structplatform_device*pdev)@kernel/drivers/video/msm/msm_fb.c

调用msm_fb_register设置linuxframebuffer并register_framebuffer。

dsi_cmd_samsung_fwvga.1281->mipi_dsi.591105->mdp.591105->msm_fb.591105->fbx

dtv.458753->mdp.458753->msm_fb.458753->fbx

hdmi_msm.1->mdp.655361->msm_fb.655361->fbx(hdmi)

MDP图像合成和显示过程—Framebuffer驱动

Overlay设置和提交过程

msmfb_overlay_set(structfb_info*info,void__user*p)

èmdp4_overlay_set(structfb_info*info,structmdp_overlay*req)

申请pipe,并设置pipe的clock和bandwidth。

intmsmfb_overlay_play(structfb_info*info,unsignedlong*argp)

èintmdp4_overlay_play(structfb_info*info,structmsmfb_overlay_data*req)

计算pipe输入缓冲地址、参数配置等;将request暂存,如对DSICMD屏,是使用voidmdp4_dsi_cmd_pipe_queue(intcndx,structmdp4_overlay_pipe*pipe),而DSIVIDEO屏是使用voidmdp4_dsi_video_pipe_queue(intcndx,structmdp4_overlay_pipe*pipe)。

base_pipe设置和提交,LayerMix,DMA

当送FBIOPUT_VSCREENINFO或FBIOPAN_DISPLAY命令给FB设备时,最终会调用msm_fb_pan_display(structfb_var_screeninfo*var,structfb_info*info)

msm_fb_pan_display

èmdp_set_dma_pan_info(info,dirtyPtr,(var->activate==FB_ACTIVATE_VBL));//设置DMA区域

èmdp_dma_pan_update(info);//启动DMA,包括LayerMixer合成和DMA输出

voidmdp_dma_pan_update(structfb_info*info)

èmfd->dma_fnc(mfd);

对MIPIDSICMD屏,该DMA函数是mdp4_dsi_cmd_overlay。

voidmdp4_dsi_cmd_overlay(structmsm_fb_data_type*mfd)

pipe=vctrl->base_pipe;

if(pipe->mixer_stage==MDP4_MIXER_STAGE_BASE){//itiscertainforbase_pipe

//itissetupinmdp4_overlay_update_dsi_cmdwhendsicmdpanelison.

mdp4_mipi_vsync_enable(mfd,pipe,0);

mdp4_overlay_setup_pipe_addr(mfd,pipe);

mdp4_dsi_cmd_pipe_queue(0,pipe);//armthebasepipewithframebuffer

mdp4_overlay_mdp_perf_upd(mfd,1);//levelupthemdpperformance

mutex_lock(&mfd->dma->ov_mutex);

mdp4_dsi_cmd_pipe_commit(cndx,0);//firetheoverlaylayermixeranddma

mdp4_overlay_mdp_perf_upd(mfd,0);

mutex_unlock(&mfd->dma->ov_mutex);

intmdp4_dsi_cmd_pipe_commit(intcndx,intwait)

inti,undx;

intmixer=0;

structvsycn_ctrl*vctrl;

structvsync_update*vp;

structmdp4_overlay_pipe*pipe;

structmdp4_overlay_pipe*real_pipe;

unsignedlongflags;

intneed_dmap_wait=0;

intneed_ov_wait=0;

intcnt=0;

/**

*staticstructvsycn_ctrl{…}vsync_ctrl_db[MAX_CONTROLLER]

*在不同的显示器文件中有不同的定义,注意其是static的。

*不同显示应用时关联不同的LayerMixer,所以同一种应用中,

*使用具有同一mixer_num的pipe,mixer_num由应用分配的主pipe即该应用对应的

*vsync_ctrl_db的base_pipe指定。所有pipe的LayerMixer0是驱动中固定指定好的。

vctrl=&vsync_ctrl_db[0];

mutex_lock(&vctrl->update_lock);

undx=vctrl->update_ndx;

vp=&vctrl->vlist[undx];

mixer=pipe->mixer_num;//theLayermixerused,LayerMixer0here.

if(vp->update_cnt==0){

mutex_unlock(&vctrl->update_lock);

returncnt;

//vctrl->update_ndx++;

//vctrl->update_ndx&=0x01;

vp->update_cnt=0;/*reset*/

if(vctrl->blt_free){

vctrl->blt_free--;

if(vctrl->blt_free==0)

mdp4_free_writeback_buf(vctrl->mfd,mixer);

/*freepreviouscommittediommubacktopool*/

mdp4_overlay_iommu_unmap_freelist(mixer);

spin_lock_irqsave(&vctrl->spin_lock,flags);

if(pipe->ov_blt_addr){

/*Blt*/

if(vctrl->blt_wait)

need_dmap_wait=1;

if(vctrl->ov_koff!=vctrl->ov_done){

INIT_COMPLETION(vctrl->ov_comp);

need_ov_wait=1;

}else{

/*directout*/

if(vctrl->dmap_koff!=vctrl->dmap_done){

INIT_COMPLETION(vctrl->dmap_comp);

pr_debug("%s:wait,ok=%dod=%ddk=%ddd=%dcpu=%d\n",

__func__,vctrl->ov_koff,vctrl->ov_done,

vctrl->dmap_koff,vctrl->dmap_done,smp_processor_id());

spin_unlock_irqrestore(&vctrl->spin_lock,flags);

/*setupcompletionfordmapwaitinDIRECTOUTmodeoroverlaywaitinBLTmode*/

if(need_dmap_wait){

pr_debug("%s:wait4dmap\n",__func__);

mdp4_dsi_cmd_wait4dmap(0);

if(need_ov_wait){

pr_debug("%s:wait4ov\n",__func__);

mdp4_dsi_cmd_wait4ov(0);

if(vctrl->blt_end){

vctrl->blt_end=0;

pipe->ov_blt_addr=0;

pipe->dma_blt_addr=0;

pr_info("%s:resetov_blt_addranddma_blt_addr\n",__func__);

/*ifblthassomechange,reconfigoverlayproccessoranddmap*/

if(vctrl->blt_change){

mdp4_overlayproc_cfg(pipe);

mdp4_overlay_dmap_xy(pipe);

vctrl->blt_change=0;

pipe=vp->plist;

*AllpipesusedhereistargetedtoLayerMixer0.

*ThesepipesareallocatedwithMIXER0indeed,

*andqueuedinvctrl->vlist[undx].>plist[pipe->pipe_ndx-1]again.

*ObviouswithtargettoMIXER0.

for(i=0;i

if(pipe->pipe_used){

/*pipesinvp->plistmaybepointtosamephysicalpipe*/

cnt++;

real_pipe=mdp4_overlay_ndx2pipe(pipe->pipe_ndx);

if(real_pipe&&real_pipe->pipe_used){

/*pipenotunset*/

mdp4_overlay_vsync_commit(pipe);

/*freepreviousiommutofreelist

*whichwillbefreedatnext

*pipe_commit

mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx,0);

pipe->pipe_used=0;/*clear*/

/*txdcscommandifhadany*/

mipi_dsi_cmdlist_commit(1);

/*mixerisMIXER0here.Setupmixer’seachstagewithpipe*/

mdp4_mixer_stage_commit(mixer);

mdp4_dsi_cmd_blt_ov_update(pipe);

pipe->ov_cnt++;

vctrl->ov_koff++;

vsync_irq_enable(INTR_OVERLAY0_DONE,MDP_OVERLAY0_TERM);

vsync_irq_enable(INTR_DMA_P_DONE,MDP_DMAP_TERM);

vctrl->dmap_koff++;

pr_debug("%s:kickoff,pid=%d\n",__func__,current->pid);

/*kickoffoverlayengine*/

mdp4_stat.kickoff_ov0++;

outpdw(MDP_BASE+0x0004,0);

mb();

mdp4_stat.overlay_commit[pipe->mixer_num]++;

/*waitforvsync*/

if(wait){

longlongtick;

mdp4_dsi_cmd_wait4vsync(cndx,&tick);

voidmdp4_overlay_vsync_commit(structmdp4_overlay_pipe*pipe)

if(pipe->pipe_type==OVERLAY_TYPE_VIDEO)

mdp4_overlay_vg_setup(pipe);/*video/graphicpipe*/

else

mdp4_overlay_rgb_setup(pipe);/*rgbpipe*/

pr_debug("%s:pipe=%xndx=%dnum=%dused=%d\n",__func__,

(int)pipe,pipe->pipe_ndx,pipe->pipe_num,pipe->pipe_used);

/*figureouttheflushvaluetofillregister*/

mdp4_overlay_reg_flush(pipe,1);

/*stagesetupbutnotcommit*/

mdp4_mixer_stage_up(pipe,0);

voidmdp4_mixer_stage_commit(intmixer)

inti,num;

u32data,stage;

intoff;

data=0;

for(i=MDP4_MIXER_STAGE_BASE;i

pipe=ctrl->stage[mixer][i];

if(pipe==NULL)

continue;

pr_debug("%s:mixer=%dndx=%dstage=%d\n",__func__,

mixer,pipe->pipe_ndx,i);

stage=pipe->mixer_stage;

if(mixer>=MDP4_MIXER1)

stage+=8;

stage<<=(4*pipe->pipe_num);

data|=stage;

*stage_commitmaybecalledfromoverlay_unset

*forcommandpanel,mdpclocksmaybeoffatthistime.

*somdpclockenabledisnecessary

mdp_pipe_ctrl(MDP_CMD_BLOCK,MDP_BLOCK_POWER_ON,FALSE);

mdp_clk_ctrl(1);

mdp4_mixer_blend_setup(mixer);

off=0;

if(data!=ctrl->mixer_cfg[mixer]){

ctrl->mixer_cfg[mixer]=data;

if(mixer>=MDP4_MIXER2){

/*MDP_LAYERMIXER2_IN_CFG*/

off=0x100f0;

/*mixer0or1*/

num=mixer+1;

num&=0x01;

data|=ctrl->mixer_cfg[num];

off=0x10100;

pr_debug("%s:mixer=%ddata=%xflush=%xpid=%d\n",__func__,

mixer,data,ctrl->flush[mixer],current->pid);

local_irq_save(flags);

if(off)

outpdw(MDP_BASE+off,data);

if(ctrl->flush[mixer]){

outpdw(MDP_BASE+0x18000,ctrl->flush[mixer]);

ctrl->flush[mixer]=0;

local_irq_restore(flags);

mdp_pipe_ctrl(MDP_CMD_BLOCK,MDP_BLOCK_POWER_OFF,FALSE);

THE END
1.FinetunedLanguageModelsAreZeroShotLearners预训练设置:pretrained on a collection of web documents (including those with computer code), dialog data, and Wikipedia, tokenized into 2.49T BPE tokens with a 32k vocabulary using the SentencePiece library. Around 10% of the pretraining data was non-English. https://zhuanlan.zhihu.com/p/618629129
2.T*姓名: *工号: *电话号码: *部门: 无锡工厂 T-Learning 平台是在哪里搭建的 SAP ORACLE TEAMS APP 无锡工厂 T-Learning 平台请来的首位培训师是? 张老师 李老师 徐老师 冯老师 2020世界经历了哪些黑天鹅事件?【多选题】 新冠疫情 澳I洲山火 非洲蝗灾 https://www.wjx.cn/vj/mPgticX.aspx
3.2024中国企业服务最新的软件行业名单库(0514)人称T客E-learning学习平台 网易云课堂、云学堂、即刻学堂、网龙多学、魔学院、新风向、知学云、时代光华、云朵课堂、睿泰集团、企学宝、学友科技 电商系统 微盟软件、同徽软件、万里牛、聚水潭、管易云 采购系统 云徙科技、用友软件、商越、同徽软件、企企通 B2B供应链系统 https://www.shangyexinzhi.com/article/19528843.html
4.石嘴山市第七小学英语课程社区T: Today we will learn M7U1 I don’t believe it! 2. Text learning (1)T: Today is Daming’s birthday,his grandma gives him a present. Do you want to know something about the present? Ss:… Look, lisren and answer ◇ What is the present?---A DVD. (领学DVD) ◇https://kcsq.nxeduyun.com/index.php?r=teach/achievement/info&sid=810362&id=52977
5.CAR-- CD-19 和 BCMA靶点的新一代CAR-T疗法 在血液系统恶性肿瘤和多发性骨髓瘤中的临床研究及开发进展 新药研发合作及人工智能创新: -- 促进来源于中国的高质量研发创新, 开放式创新模式,加速新药研发策略及新药研发外部合作新模式 -- 人工智能,深度学习与机器学习平台结合新药研发,临床开发及医疗产业的应用与合作创https://www.bio-equip.com/news.asp?ID=453079318
6.东北大学主页平台宋克臣中文主页MultiRGB-T Saliency Detection via Low-rank Tensor Learning and Unified Collaborative Ranking [J]. IEEE Signal Processing Letters, 2020, 27,1585-1589. (paper) (code and datasets) IFFNet A novel information flow fusion network (IFFNet) method is proposed for the RGB-T cross-modal images. The http://faculty.neu.edu.cn/songkechen/zh_CN/zdylm/395574/list/index.htm
7.AWS云服务亲自聆听 AWS 专家和思想领袖对生成式人工智能数据、云运维、网络和内容分发、Amazon Q 企业版以及迁移和现代化等重要主题的深入探讨。 了解所有会议 re:Invent 博客 查看re:Invent 2024 的更多热门公告 机器学习 隆重推出 Amazon SageMaker HyperPod 配方 http://aws.amazon.com/
8.日本最高设计奖GOODDESIGNAWARD2021百件获奖作品精彩出炉70、教育设施 梅光学院大学The Learning Station CROSSLIGHT 获奖公司/组织:Baiko Gakuin 77、冰淇淋 yuttari vanilla 获奖公司/组织:LOTTE.CO.,LTD 颠覆常识的不易融化的冰淇淋。在供应或享用作为旅游产业、公共教育、食品及农业文化的重要平台,创建了一个慢食社区,推广传统饮食文化,支持当地产品https://www.digitaling.com/articles/621312.html
9.elearning平台系统专家新为软件新为是e-learning平台解决方案领导品牌,15年专注,6000多家企事业单位和140多所高校成功案例,是国内专业的学习平台、考试系统和移动学习平台提供商,提供在线学习系统,在线考试系统、企业网络学院、移动学习平台、培训会议系统和培训管理系统等e-learning产品。http://www.newv.com.cn/
10.温县智慧教育云平台Boy: You did really well on the last English test, didn’t you, Meiping? Meiping: Yeah, I did OK. Boy: Well, how did you study for it? Meiping: By making words cards. Boy: Maybe I’ll try that. So, how do you study for a test, Peter? https://www.wxeduyun.cn/index.php?r=space/person/blog/view&sid=561ebac570bc48f9b257562b1d45ae3f&id=3144
11.deeplearning.ainotebook + data + solutions and video link it's impossible to upload 3 big files, yolo.h5, ResNet50.h5, imagenet-vgg-verydeep-19.mat, download from link: https://pan.baidu.com/s/1ggaDEV5 password:o7rb Repo forked form github: https://github.com/robbertliu/deeplearning.ai-andrewhttps://gitee.com/Mei_HW/deeplearning.ai-andrew-ng
12.www.edx.org/Submit your search query SearchSubmit your search query Take online courses from260+ world-class universities and companies You set the goal. We'll mark the path. Career progress isn't always linear. So when your industry evolves or your plans change, edX is the education destination that workhttps://www.edx.org/
13.learningthingsIn addition, we alwayskeeplearningnewthingsandwe have acquired rich experience in development of mobile OA (Office Automation), AR (Augmented Reality), somatic game (motion[] hydom.cn hydom.cn 近年来我们在棋牌游戏、3D益智游戏、AR现实增强、体感平台、移动办公系统方面积累了丰富而成熟的经验, 深http://cn.linguee.com/%E8%8B%B1%E8%AF%AD-%E4%B8%AD%E6%96%87/%E7%BF%BB%E8%AD%AF/learning+things.html
14.核心用户不是K12群体,而是面向成年人的非学历职业技能培训平台tj-learning学习服务X tj-promotion促销服务X tj-media媒资服务√ tj-data数据服务O tj-remark评价服务X **3.1.**企业开发模式 Git私服git.tianji.comtjxt/12332110880 Jenkins持续集成jenkins.tianji.comroot/12318080 RabbitMQmq.tianji.comtjxt/12332115672 https://github.com/small-xiexu/tjxt/
15.腾讯云面向高校专业建设提供的一站式教学练训平台 集成腾讯热门产品技术 采取微服务架构,具备高灵活性和扩展性,底层集成云服务器、人工智能API、TI-ONE、微搭、腾讯云BI、raydata等10余款热门产品和技术,持续提升学员创新实践能力。 基于企业案例实训课程 拥有丰富的实训项目案例,案例来自智慧工业、教育、医疗、金融、智能制造等https://tlearning.cloud.tencent.com/
16.企业培训系统elearning企业在线学习平台企业培训方案问鼎云学习,企业在线学习一站式服务商,提供企业在线培训平台系统搭建、e-learning平台建设、企业内训系统开发,企业在线学习平台运营等服务,支持企业培训管理、员工在线学习、企业培训考试,组织知识共享,助力企业打造专属线上培训学习平台系统。https://www.wdxuexi.com/
17.TUV南德知识服务线上课程TüV南德知识服务线上学习平台是现代和面向未来的先进培训方式。传统培训内容与新技术的结合,能帮助您提高专业培训和职业继续教育中的学习灵活性和效率。数字化的学习方式能使课中准备及培训后的跟进更加容易,从而助您实现更持久、有效的学习成果。 线上课程 https://www.tuvsud.cn/zh-cn/services/training/cn/e-learning
18.基于一致性的半监督语义分割方法:刷新多项SOTA,还有更好泛化性2). Ablation Learnings. 我们使用 VOC 数据集中 1/8 的 ratio 来进行消融实验。原本的 MT 我们依照之前的工作使用了 MSE 的 loss 方式。可以看到, conf-CE 带来了接近 3 个点的巨大提升。在这之后, T-VAT (teacher-based virtual adversarial training)使 student 模型的一致性学习更有效率, 它对两个架构https://www.thepaper.cn/newsDetail_forward_19379122
19.gzoftju/gzoft202308011423431:精选了千余项目,包括机器学习对未标注样本进行低熵预测,并与标注样本混合进行TMix。MixText可以挖掘句子之间的隐式关系,并在学习标注样本的同时利用无标注样本的信息。超越预训练模型和其他半监督方法 beyondguo/label_confusion_learning 利用标签之间的混淆关系,提升文本分类效果。利用标签信息时能够充分考虑标签之间的重叠或者依赖关系。 AIRobotZhanghttps://openi.pcl.ac.cn/gzoftju/gzoft202308011423431
20.CyberC2024(IEEETCCC)征稿:第16届网络分布式计算与知识发现国际会议主办方:IEEE计算机协会计算机通信技术委员会(TCCC) (https://cs-tccc.org/) 网站:www.Cyberc.org 出版方:IEEE (EI & Explore) 截止日期:2024年6月1日 提交途径: EDAS平台在线提交:https://edas.info/N32344 发送至邮箱:Papers@cyberc.org并在题目中标明"CyberC 2024 Submission". https://www.eet-china.com/mp/a314105.html
21.解析DeepMind采用双Q学习(DoubleQ注意到在 argmax 中行动的选择仍旧取决于在线的权重θ_t。这表示,如同 Q-学习中那样,我们仍然会根据当前值来估计贪心策略的值。然而,我们使用了第二个权重集合θ'_t来公平地衡量这个策略的值。第二个权重的集合可以对称式地通过交换θ和θ'的更新。https://www.jianshu.com/p/193ca0106aa5
22.imagebasedonchannelattentionandtransferlearning式中:β1=0.9,β2=0.999,gt=▽θft(θ)代表随机目标函数ft(θ)关于θ的梯度,t是当前时间,t-1代表前一时刻。m^t和n^t分别是对mt、nt的修正,m^t=mt1?β1t,n^t=nt1?β2t,用来实现无偏估计; β1和β2为矩估计指数的衰减速率,θ是模型参数,η是步长,权值θt的更新公式为 θt+1=θt?ηnhttps://www.oejournal.org/article/doi/10.12086/oee.2021.200045
23.T/AHAI012T/AHAI 012-2024发布 2024年发布单位 中国团体标准当前最新T/AHAI 012-2024 适用范围 本文件界定了面向终身学习的个性化学习与测评系统的术语和定义、缩略语,并规定了系统框架、设计要求和非功能性要求。本文件适用于面向终身学习的个性化学习与测评系统(以下简称“系统”)的设计。Thttps://www.antpedia.com/standard/1991201760.html