AndroidLSurfaceFlingerdump信息全解(1)
2015年01月22日⁄
Android⁄共19446字⁄字号小中大⁄
暂无评论
SurfaceFlinger的dump信息详解
对于很多Android的显示问题,我们需要使用adbshelldumpsysSurfaceFlinger命令来获取SurfaceFlinger的dump信息,这对于我们分析问题有很大的帮助,因此我们这里来详细讲解下SurfaceFlinger的dump.
SurfaceFlinger的dump信息主要通过dumpAllLocked函数来获取,因此我们这里就以android5.0在主屏幕上的一份dump来详细说明下dump的作用.
1特殊宏的打开
一般dump的第一行都是这样的:
1 | Buildconfiguration:[sf][libui][libgui] |
这说明其实没有打开任何特殊的宏,实际上,如果一下特殊宏打开,第一行log会打印出来:
1 | FRAMEBUFFER_FORCE_FORMAT,HAS_CONTEXT_PRIORITY,NEVER_DEFAULT_TO_ASYNC_MODE,TARGET_DISABLE_TRIPLE_BUFFERING,DONT_USE_FENCE_SYNC |
一般情况下,这些宏一个都不会打开.
2Sync机制
第二行一般是这样的:
1 | Syncconfiguration:[ using :EGL_ANDROID_native_fence_syncEGL_KHR_wait_sync] |
这行其实打印了目前使用的sync机制,这个值源于这段逻辑:
1 | if
(useNativeFenceSync()){ |
2 | mString.append( "EGL_ANDROID_native_fence_sync" ); |
5 | mString.append( "EGL_KHR_fence_sync" ); |
8 | mString.append( "EGL_KHR_wait_sync" ); |
注意,一二是互斥的,三可以与一二共存.
3DispSync参数
第三行是打印的是Vsync相关的参数:
1 | DispSyncconfiguration:appphase0ns,sfphase0ns,presentoffset0ns(refresh16666667ns) |
这些参数我们还是比较熟悉的,有意思的是打印这些参数时候使用的语法:
1 | result.appendFormat( "appphase%"
PRId64 "ns,sfphase%"
PRId64 "ns," |
2 | "presentoffset%dns(refresh%"
PRId64 "ns)" , |
3 | vsyncPhaseOffsetNs,sfVsyncPhaseOffsetNs,PRESENT_TIME_OFFSET_FROM_VSYNC_NS, |
4 | mHwc->getRefreshPeriod(HWC_DISPLAY_PRIMARY)); |
PRId64的用法很独特,这是一种跨平台的打印64位整数的做法:
1 | printf ( "%"
PRId64 "\n" ,value); |
3 | printf ( "%"
"ld" "\n" ,value); |
5 | printf ( "%"
"lld" "\n" ,value); |
4layer的dump
接下来就是很长的一段layer的dump,一般以这样一句话开始:
count的值来源于layersSortedByZ中layer的数量.
接下来就进入各个layer的dump,我们参考代码并以launcher所在的layer为例来解释下各行的意义:
1 | +Layer0xb3f92000(com.sec.android.app.launcher/com.android.launcher2.Launcher)id=87 |
0xb3f92000指向当前layer对象的值,括号中是当前layer的名称,id是创建layer时产生的序列号.
4.1区域信息
1 | RegiontransparentRegion( this =0xb3f92164,count=1) |
3 | RegionvisibleRegion( this =0xb3f92008,count=1) |
接下来的两段是两个Region的dump,每个region可能包含多个区域,所以这里count也可能不等于1.
前两行的值来源于activeTransparentRegion,表示的是这个layer里面透明区域的大小.
后两行值来源于visibleRegion,表示可见区域的大小.
4.2基本信息
1 | layerStack=0,z=21010,pos=(0,0),size=(1440,2560),crop=(0,0,1440,2560),isOpaque=0,
|
2 | invalidate=0,alpha=0xff,flags=0x00000000,tr=[1.00,0.00][0.00,1.00] |
上面这段dump源自这段代码:
02 | "layerStack=%4d,z=%9d,pos=(%g,%g),size=(%4d,%4d),crop=(%4d,%4d,%4d,%4d)," |
03 | "isOpaque=%1d,invalidate=%1d," |
04 | "alpha=0x%02x,flags=0x%08x,tr=[%.2f,%.2f][%.2f,%.2f]\n" |
06 | s.layerStack,s.z,s.transform.tx(),s.transform.ty(),s.active.w,s.active.h, |
07 | s.active.crop.left,s.active.crop.top, |
08 | s.active.crop.right,s.active.crop.bottom, |
09 | isOpaque(s),contentDirty, |
11 | s.transform[0][0],s.transform[0][1], |
12 | s.transform[1][0],s.transform[1][1], |
layerStack表示这个layer是保存在哪个layerstack中(不同的display是有不同的layerstack的,这点可以通过一个连接HDMI时的layerstack很容易确认).
z表示Z轴坐标,z值越大,layer越靠上.
pos的值是layer左上角的位置,这个值比较特殊的是ImageWallpaper这个layer的pos值,因为ImageWallpaper的大小大于屏幕大小,所以ImageWallpaper的pos值在屏幕的外面(note4是pos=(-560,0)).
size自然是layer的大小
crop代表裁剪区域,这点依然是对于壁纸很明显,因为壁纸layer大小大于屏幕,必须涉及到需要裁剪一部分显示在屏幕上,因此它的裁剪区域是crop=(560,0,2000,2560).
isOpaque代表是否是不透明的,只有完全不透明的layer这个值才是1,比如壁纸,像状态栏和launcher他们都是0,代表不是完全不透明的
invalidate表示这个layer的数据是失效的,这个值绝大多数情况下都是0.因为我们看到的一般都是绘制好的有效的数据.一种情况下这值特别频繁的多见为1,就是刚刚锁屏(解锁)时.因为突然锁屏,会导致绘制的内容和要显示的内容完全不同,导致layer的各种数据要重新计算,所以将layer置为失效.
alpha表示了这张layer的透明度,这个值跟isOpaque是有区别的.isOpaque表示了这个layer可以是透明的,也就是没有显示数据的地方,可以透明;而alpha表示透明度,也即是有数据的地方也可以因为透明度而收到影响产生透明的效果.
flag值含义丰富,它是众多flag或出来的结果,影响它值的包括:
02 | eLayerHidden=0x01,
//SURFACE_HIDDENinSurfaceControl.java |
03 | eLayerOpaque=0x02,
//SURFACE_OPAQUE |
04 | eLayerTransparent=0x200,
//SURFACE_TRANSPARENT |
08 | ePositionChanged=0x00000001, |
09 | eLayerChanged=0x00000002, |
10 | eSizeChanged=0x00000004, |
11 | eAlphaChanged=0x00000008, |
12 | eMatrixChanged=0x00000010, |
13 | eTransparentRegionChanged=0x00000020, |
14 | eVisibilityChanged=0x00000040, |
15 | eLayerStackChanged=0x00000080, |
16 | eCropChanged=0x00000100, |
17 | /*SRIB:SmgSurfaceAnimator:Statethatwillindicateanimationchange*/ |
18 | e3DAnimationChanged=0x00001000, |
19 | /*SRIB:SmgSurfaceAnimator:ChangeEnd*/ |
20 | eOpacityChanged=0x00000200, |
22 | eTranslucentRegionChanged=0x00000400, |
24 | eTransparencyChanged=0x80000000, |
26 | enum { //(keepinsyncwithSurface.java) |
28 | eDestroyBackbuffer=0x00000020, |
30 | eNonPremultiplied=0x00000100, |
32 | eProtectedByApp=0x00000800, |
33 | eProtectedByDRM=0x00001000, |
34 | eCursorWindow=0x00002000, |
35 | /*SISOChangesforInternal_Only-Start*/ |
36 | eFXInternalDisplay=0x80000000, |
37 | /*SISOChangesforInternal_Only-End*/ |
38 | eFXSurfaceNormal=0x00000000, |
39 | eFXSurfaceDim=0x00020000, |
40 | eFXSurfaceMask=0x000F0000, |
41 | //beginofappfw:fixedorientationwindow |
42 | eFixedOrientation=0x40000000, |
44 | //beginofMDMremotecontrol |
45 | eNoRemoteControl=0x08000000, |
46 | //endofMDMremotecontrol |
所有的这些值都可能影响layer的状态,涉及不同模块不同功能,这里不再展开.
接下来的一组tr数据代表屏幕的旋转和缩放程度.大多数的layer实际上是不需要旋转和缩放的,因为他们定义的大小就是跟屏幕一致的,所以他们的这组数据是[1.00,0.00][0.00,1.00],实际上如果你使用这组数据来做矩阵变换的话,矩阵是不会发生变化的.
需要旋转的比较典型的场景是照相机.横着拿相机时它的layer的变换矩阵是[-1.00,0.00][-0.00,-1.00],也就是旋转180°.
这个值的来源是上层调用setMatrix函数设置的.
client含义比较简单,值的来源是创建layer时,对应的SurfaceSession中mNativeClient.这东西也是跟SurfaceSession一一对应的,也就是跟SurfaceFlinger连接时一一对应的.从这个值我们可以判断,client值相同的layer,必然来自同一个进程(因为他们是由同一个连接创建出来的).
4.3buffer信息
1 | format=1,activeBuffer=[1440x2560:1664,1],queued-frames=0,mRefreshPending=0 |
2 | mTexName=38mCurrentTexture=2 |
3 | mCurrentCrop=[0,0,0,0]mCurrentTransform=0 |
5 | -BufferQueuemMaxAcquiredBufferCount=1,mDequeueBufferCannotBlock=0, |
6 | default -size=[1440x2560], default -format=1,transform-hint=00,FIFO(0)={} |
7 | [00:0xb110e100]state=FREE,0xb3eb1ec0[1440x2560:1664,1] |
8 | [01:0xb3ec7000]state=FREE,0xb620d060[1440x2560:1664,1] |
9 | >[02:0xb110e200]state=ACQUIRED,0xb1111100[1440x2560:1664,1] |
4.3.1数据格式
首先是数据的format,值的来源是layer创建时赋予的,当然我们如果追溯的话,可以追溯到WindowManagerService创建SurfaceControl的过程,值也是创建时指定的.值的定义如下:
03 | //theseconstantsneedtomatchthose |
04 | //ingraphics/PixelFormat.java&pixelflinger/format.h |
06 | PIXEL_FORMAT_UNKNOWN=0, |
08 | //logicalpixelformatsusedbytheSurfaceFlinger----------------------- |
09 | PIXEL_FORMAT_CUSTOM=-4, |
10 | //Custompixel-formatdescribedbyaPixelFormatInfostructure |
11 | PIXEL_FORMAT_TRANSLUCENT=-3, |
12 | //Systemchoosesaformatthatsupportstranslucency(manyalphabits) |
13 | PIXEL_FORMAT_TRANSPARENT=-2, |
14 | //Systemchoosesaformatthatsupportstransparency |
16 | PIXEL_FORMAT_OPAQUE=-1, |
17 | //Systemchoosesanopaqueformat(noalphabitsrequired) |
18 | //realpixelformatssupportedforrendering----------------------------- |
19 | PIXEL_FORMAT_RGBA_8888=HAL_PIXEL_FORMAT_RGBA_8888,
//4x8-bitRGBA |
20 | PIXEL_FORMAT_RGBX_8888=HAL_PIXEL_FORMAT_RGBX_8888,
//4x8-bitRGB0 |
21 | PIXEL_FORMAT_RGB_888=HAL_PIXEL_FORMAT_RGB_888,
//3x8-bitRGB |
22 | PIXEL_FORMAT_RGB_565=HAL_PIXEL_FORMAT_RGB_565,
//16-bitRGB |
23 | PIXEL_FORMAT_BGRA_8888=HAL_PIXEL_FORMAT_BGRA_8888,
//4x8-bitBGRA |
24 | PIXEL_FORMAT_RGBA_5551=6,
//16-bitARGB |
25 | PIXEL_FORMAT_RGBA_4444=7,
//16-bitARGB |
26 | PIXEL_FORMAT_sRGB_A_8888=HAL_PIXEL_FORMAT_sRGB_A_8888,
//4x8-bitsRGB+A |
27 | PIXEL_FORMAT_sRGB_X_8888=HAL_PIXEL_FORMAT_sRGB_X_8888,
//4x8-bitsRGB,noA |
其实只有下面的值是真实可用的,其余值在SurfaceFlinger创建时会被转换:
2 | case PIXEL_FORMAT_TRANSPARENT: |
3 | case PIXEL_FORMAT_TRANSLUCENT: |
4 | format=PIXEL_FORMAT_RGBA_8888; |
6 | case PIXEL_FORMAT_OPAQUE: |
7 | format=PIXEL_FORMAT_RGBX_8888; |
其实当前常见的format也就是这几种.
1 | HAL_PIXEL_FORMAT_RGBA_8888=1, |
2 | HAL_PIXEL_FORMAT_RGBX_8888=2, |
3 | HAL_PIXEL_FORMAT_RGB_888=3, |
4 | HAL_PIXEL_FORMAT_RGB_565=4, |
5 | HAL_PIXEL_FORMAT_BGRA_8888=5, |
0代表未知格式.
常见的layer中,dimlayer一般是0,大多数layer是1,壁纸是2,照相机的预览数据是4,视频播放也是4.
4.3.2activeBuffer
activeBuffer的前两项表示了当前正在显示的buffer的宽和高.
第三项表示Stride.这个值很有意思,我们发现他有时候是等于宽的,有时候是大于宽的,我们先来看下这个值的解释.
Thenumberofpixelsthatalineinthebuffertakesinmemory.Thismaybe>=width.
我们知道内存申请使用是需要成块对齐的,也就是说不是说使我们申请多大的内存,就会给我们多大的内存,因为涉及到对齐,所以很可能这个内存实际上是大于我们的需要的.(暂时没有仔细研究,有待确认)像有些marvell型号,内存是按照64位对齐的,那么我们申请一个100宽的buffer,系统就会给我们留出128的buffer大小供我们使用.
第四项并没有什么特殊,表示format,跟前面的format应该是一致的.
4.3.3queued-frames新的帧的数量
queued-frames的含义是是否有新的帧,如果当前没有新的帧,这个值是0.
一般在画面持续变化时(照相预览,视频播放,窗口滑动,游戏),这个值会是1.表示有新的一帧.
偶尔也可以见到这个值是2(这个值应该最大就是2,因为只有三个缓冲区).
4.3.4mRefreshPending刷新卡住了吗?
mRefreshPending几乎所有的常见情况下都是0,因为这个参数代表了一个layer执行了Invalidate却没有完成Refresh,除非发生错误这显然不可能.
4.4SurfaceFlingerConsumer的dump
接下来开始对消费者进行dump,SurfaceFlingerConsumer是GLConsumer子类,所以这里实际上是调用了GLConsumer的dumpLocked函数.
先来看下代码:
2 | "%smTexName=%dmCurrentTexture=%d\n" |
3 | "%smCurrentCrop=[%d,%d,%d,%d]mCurrentTransform=%#x\n" , |
4 | prefix,mTexName,mCurrentTexture,prefix,mCurrentCrop.left, |
5 | mCurrentCrop.top,mCurrentCrop.right,mCurrentCrop.bottom, |
它会对应打印出来这一段信息:
1 | mTexName=38mCurrentTexture=2 |
2 | mCurrentCrop=[0,0,0,0]mCurrentTransform=0 |
4.4.1材质名称
mTexName的值来源是在消费者被创建时,我们知道最常见的创建消费者的时候是Layer::onFirstRef时会调用:
1 | mSurfaceFlingerConsumer= new
SurfaceFlingerConsumer(consumer,mTextureName); |
创建一个消费者,有两个参数,其中mTextureName是我们目前关注的,如果追溯来源你会发现mTextureName的值来源于glGenTextures,这个函数的实现依赖平台,参考ligagl,它是这样的:
1 | //generateunique(shared)texturenames |
2 | c->surfaceManager->getToken(n,textures); |
还是继续回来看SurfaceFlingerConsumer的创建:
01 | SurfaceFlingerConsumer( const
sp<IGraphicBufferConsumer>&consumer, |
03 | :GLConsumer(consumer,tex,GLConsumer::TEXTURE_EXTERNAL,
false ,
false ), |
04 | mTransformToDisplayInverse( false ) |
06 | GLConsumer::GLConsumer( const
sp<IGraphicBufferConsumer>&bq,uint32_ttex, |
07 | uint32_ttexTarget,
bool useFenceSync, bool isControlledByApp): |
08 | ConsumerBase(bq,isControlledByApp), |
10 | mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), |
11 | mCurrentFence(Fence::NO_FENCE), |
13 | mCurrentFrameNumber(0), |
16 | mFilteringEnabled( true ), |
18 | mUseFenceSync(useFenceSync), |
20 | mEglDisplay(EGL_NO_DISPLAY), |
21 | mEglContext(EGL_NO_CONTEXT), |
22 | mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), |
我们现在可以看出来mTexName的值来源于前面创建的材质名称.
mCurrentTexture的初始值是INVALID_BUFFER_SLOT,也就是-1,后面会在updateAndReleaseLocked时被更改,值的来源是使用的BufferItem的mBuf值,也就是mSlot,这应该是使用的buffer数组的slot值,这个变量的合理取值只有0,1,2三个值(mSlotistheslotindexofthisbuffer,defaultINVALID_BUFFER_SLOT).
4.4.2mCurrentCrop裁剪区域
mCurrentCrop的值来源同样是updateAndReleaseLocked调用时被赋值,值的来源是BufferItem的mCrop值.这个值基本一直都是0,只有在视频播放和照相机时会被设置(值的来源有待更深入的研究,mCropisthecurrentcroprectangleforthisbufferslot).
4.4.3mCurrentTransform旋转相关
mCurrentTransform的值和前面我们说过的tr值很类似.(mTransformisthecurrenttransformflagsforthisbufferslot.refertoNATIVE_WINDOW_TRANSFORM_*in).
它也跟旋转有关,我们来看下window.h中的定义:
01 | /*parameterforNATIVE_WINDOW_SET_BUFFERS_TRANSFORM*/ |
03 | /*flipsourceimagehorizontally*/ |
04 | NATIVE_WINDOW_TRANSFORM_FLIP_H=HAL_TRANSFORM_FLIP_H, |
05 | /*flipsourceimagevertically*/ |
06 | NATIVE_WINDOW_TRANSFORM_FLIP_V=HAL_TRANSFORM_FLIP_V, |
07 | /*rotatesourceimage90degreesclock-wise,isappliedafterTRANSFORM_FLIP_{H|V}*/ |
08 | NATIVE_WINDOW_TRANSFORM_ROT_90=HAL_TRANSFORM_ROT_90, |
09 | /*rotatesourceimage180degrees*/ |
10 | NATIVE_WINDOW_TRANSFORM_ROT_180=HAL_TRANSFORM_ROT_180, |
11 | /*rotatesourceimage270degreesclock-wise*/ |
12 | NATIVE_WINDOW_TRANSFORM_ROT_270=HAL_TRANSFORM_ROT_270, |
13 | /*transformssourcebytheinversetransformofthescreenitisdisplayedonto.This |
14 | *transformisappliedlast*/ |
15 | NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY=0x08 |
19 | /*flipsourceimagehorizontally(aroundtheverticalaxis)*/ |
20 | HAL_TRANSFORM_FLIP_H=0x01, |
21 | /*flipsourceimagevertically(aroundthehorizontalaxis)*/ |
22 | HAL_TRANSFORM_FLIP_V=0x02, |
23 | /*rotatesourceimage90degreesclockwise*/ |
24 | HAL_TRANSFORM_ROT_90=0x04, |
25 | /*rotatesourceimage180degrees*/ |
26 | HAL_TRANSFORM_ROT_180=0x03, |
27 | /*rotatesourceimage270degreesclockwise*/ |
28 | HAL_TRANSFORM_ROT_270=0x07, |
29 | /*don'tuse.seesystem/window.h*/ |
30 | HAL_TRANSFORM_RESERVED=0x08, |
4.5ConsumerBase(消费者)的dump
子类GLConsumerdump完毕,调用了它的父类的dump函数,基本就是调用了IGraphicBufferConsumer的dump函数.
生产者消费者这套体系我们之前以前讲过,这里我们就不再详细展开,如果不清楚看下Layer::onFirstRef这个函数就明白了,消费者这个值来自于BufferQueue::createBufferQueue的创建,其中创建了新的BufferQueueConsumer做为消费者.
1 | void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>*outProducer,
|
2 | sp<IGraphicBufferConsumer>*outConsumer, |
3 | const
sp<IGraphicBufferAlloc>&allocator){ |
4 | sp<BufferQueueCore>core( new
BufferQueueCore(allocator)); |
5 | sp<IGraphicBufferProducer>producer( new
BufferQueueProducer(core)); |
6 | sp<IGraphicBufferConsumer>consumer( new
BufferQueueConsumer(core)); |
当然BufferQueueConsumer的dump函数啥也没写,就调用了BufferQueueCore的dump函数.
打印出来的信息一般是这样的:
1 | -BufferQueuemMaxAcquiredBufferCount=1,mDequeueBufferCannotBlock=0, |
2 | default -size=[1440x2560], default -format=1,transform-hint=00,FIFO(0)={} |
3 | [00:0xb110e100]state=FREE,0xb3eb1ec0[1440x2560:1664,1] |
4 | [01:0xb3ec7000]state=FREE,0xb620d060[1440x2560:1664,1] |
5 | >[02:0xb110e200]state=ACQUIRED,0xb1111100[1440x2560:1664,1] |
下面我们按照代码顺序详细解释一下:
4.5.1队列中的buffer
我们之前在解释queued-frames的含义时已经说过,在画面持续变化时(照相预览,视频播放,窗口滑动,游戏),queued-frames值会是1.表示有新的一帧.
对应的,有新的frames自然需要有在队列中等待的buffer,对应这段代码会把这个队列打印出来:
01 | Fifo::const_iteratorcurrent(mQueue.begin()); |
02 | while (current!=mQueue.end()){
|
03 | fifo.appendFormat( "%02d:%pcrop=[%d,%d,%d,%d]," |
04 | "xform=0x%02x,time=%#"
PRIx64 ",scale=%s\n" , |
05 | current->mSlot,current->mGraphicBuffer.get(), |
06 | current->mCrop.left,current->mCrop.top,current->mCrop.right, |
07 | current->mCrop.bottom,current->mTransform,current->mTimestamp, |
08 | BufferItem::scalingModeName(current->mScalingMode)); |
对应打印出来的dump信息是这样的:
1 | 02:0xb631e480crop=[0,0,0,0],xform=0x07,
time =0xc4d5da9b1e0,scale=FREEZE |
02是mSlot的值,crop是裁剪区域,xform是旋转,这三个我们上面已经讲过,这里不再展开.
time是这个buffer被queue的时间(mTimestampisthecurrenttimestampforthisbufferslot.ThisgetstosetbyqueueBuffereachtimethisslotisqueued.Thisvalueisguaranteedtobemonotonicallyincreasingforeachnewlyacquiredbuffer.).
scale是缩放模式,一般取值如下:
02 | /*thewindowcontentisnotupdated(frozen)untilabufferof |
03 | *thewindowsizeisreceived(enqueued) |
05 | NATIVE_WINDOW_SCALING_MODE_FREEZE=0, |
06 | /*thebufferisscaledinbothdimensionstomatchthewindowsize*/ |
07 | NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW=1, |
08 | /*thebufferisscaleduniformlysuchthatthesmallerdimension |
09 | *ofthebuffermatchesthewindowsize(croppingintheprocess) |
11 | NATIVE_WINDOW_SCALING_MODE_SCALE_CROP=2, |
12 | /*thewindowisclippedtothesizeofthebuffer'scroprectangle;pixels |
13 | *outsidethecroprectanglearetreatedasiftheyarecompletely |
16 | NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP=3, |
4.5.2BufferQueue的基本默认信息
接下来的一段代码会打印BufferQueue的一些基本信息:
1 | result.appendFormat( "%s-BufferQueuemMaxAcquiredBufferCount=%d," |
2 | "mDequeueBufferCannotBlock=%d,default-size=[%dx%d]," |
3 | "default-format=%d,transform-hint=%02x,FIFO(%zu)={%s}\n" , |
4 | prefix,mMaxAcquiredBufferCount,mDequeueBufferCannotBlock, |
5 | mDefaultWidth,mDefaultHeight,mDefaultBufferFormat,mTransformHint, |
6 | mQueue.size(),fifo.string()); |
一般会打印如下:
1 | -BufferQueuemMaxAcquiredBufferCount=1,mDequeueBufferCannotBlock=0,
default -size=[1920x1080], |
2 | default -format=4,transform-hint=04 |
4.5.2.1允许同时acquire的buffer的数量
mMaxAcquiredBufferCount是允许同时acquire的buffer的数量,解释如下:
1 | //mMaxAcquiredBufferCountisthenumberofbuffersthattheconsumermay |
2 | //acquireatonetime.Itdefaultsto1,andcanbechangedbytheconsumer |
3 | //viasetMaxAcquiredBufferCount,butthismayonlybedonewhileno |
4 | //producerisconnectedtotheBufferQueue.Thisvalueisusedtoderive |
5 | //thevaluereturnedfortheMIN_UNDEQUEUED_BUFFERSquerytotheproducer. |
基本这个值只能是1,不再深究.
4.5.2.2dequeueBuffer是否允许被block
1 | //mDequeueBufferCannotBlockindicateswhetherdequeueBufferisallowedto |
2 | //block.Thisflagissetduringconnectwhenboththeproducerand |
3 | //consumerarecontrolledbytheapplication. |
4 | bool mDequeueBufferCannotBlock; |
mDequeueBufferCannotBlock几乎总是为0,除非一个应用同时控制了生产者和消费者,这很罕见.
4.5.2.3bufferdefault-size默认buffer大小
这两个值的来源应该是BufferQueueConsumer::setDefaultBufferSize函数(不是特别确定,因为这段代码写的不好,严重破坏了封装性).
用处是这样的:mDefaultHeightholdsthedefaultheightofallocatedbuffers.ItisusedindequeueBufferifawidthandheightof0arespecified.
4.5.2.4mDefaultBufferFormat默认格式
mDefaultBufferFormat很简单,format含义可以参考前面的解释.
mDefaultBufferFormatcanbesetsoitwilloverridethebufferformatwhenitisn'tspecifiedindequeueBuffer.
4.5.2.5mTransformHint
同样用于旋转.
mTransformHintisthetransformprobablyappliedtobuffersofthiswindow.thisisonlyahint,actualtransformmaydiffer.
4.5.3各个Buffer的信息
接下来是打印BufferSlot中各个buffer的信息,一般打印如下:
1 | [00:0xb651d780]state=QUEUED,0xb6321240[1080x1920:1152,1] |
2 | [01:0xb1513200]state=FREE,0xb65189c0[1080x1920:1152,1] |
3 | >[02:0xb651d080]state=ACQUIRED,0xb6518330[1080x1920:1152,1] |
是由下面的代码打印出来的.
01 | for
( int s=0;s<maxBufferCount;++s){
|
02 | const
BufferSlot&slot(mSlots[s]); |
03 | const
sp<GraphicBuffer>&buffer(slot.mGraphicBuffer); |
04 | result.appendFormat( "%s%s[%02d:%p]state=%-8s" ,prefix, |
05 | (slot.mBufferState==BufferSlot::ACQUIRED)?
">" : "" , |
07 | BufferSlot::bufferStateName(slot.mBufferState)); |
09 | result.appendFormat( ",%p[%4ux%4u:%4u,%3X]" ,buffer->handle, |
10 | buffer->width,buffer->height,buffer->stride, |
ACQUIRED的buffer前面会打印>,表示这是当前在显示的buffer.
state表示buffer的状态,取值包括DEQUEUED,QUEUED,FREE,ACQUIRED.我们知道ACQUIRED是在显示的,DEQUEUED是在绘制的,QUEUED绘制完成还未显示的,free是未使用的.
后面的大小,stride,和format前面都讲过了,这里不再说明.
至此,layer的dump已经说明完毕,我们继续分析Displays的dump.
5display信息的dump
首先会打印当前display的数量,数量基于mDisplays的大小,这个容器在SurfaceFlinger初始化时会生成数据,后面根据收到不同的消息在handleTransactionLocked函数中也会调整.
正常情况下是1,也就是只有一个display(Built-inScreen),当设备连接了HDMI或者使用了屏幕共享等功能时,会有额外的display加入,比如下面这个:
02 | +DisplayDevice:HDMIScreen |
03 | type=1,hwcId=1,layerStack=6,(1920x1080),ANativeWindow=0xb4d94d08,orient=0(type=00000000),flips=1173,isSecure=1,
|
04 | secureVis=0,powerMode=2,activeConfig=0,numLayers=1 |
05 | v:[0,0,1920,1080],f:[0,0,1920,1080],s:[0,0,1920,1080],transform:[[1.000,0.000,-0.000][0.000,1.000,-0.000][0.000,0.000,1.000]] |
07 | -BufferQueuemMaxAcquiredBufferCount=2,mDequeueBufferCannotBlock=0,
default -size=[1920x1080], default -format=1,transform-hint=00,
|
09 | [00:0xb6418c80]state=FREE,0xb43ed880[1920x1080:1920,1] |
10 | [01:0xb43cb300]state=FREE,0xb640d970[1920x1080:1920,1] |
11 | >[02:0xb43cb280]state=ACQUIRED,0xb43ed830[1920x1080:1920,1] |
12 | +DisplayDevice:Built-inScreen |
13 | type=0,hwcId=0,layerStack=0,(1080x1920),ANativeWindow=0xb4d94608,orient=0(type=00000000),flips=3140,isSecure=1,
|
14 | secureVis=0,powerMode=2,activeConfig=0,numLayers=2 |
15 | v:[0,0,1080,1920],f:[0,0,1080,1920],s:[0,0,1080,1920],transform:[[1.000,0.000,-0.000][0.000,1.000,-0.000][0.000,0.000,1.000]] |
这个是连接了HDMI后的数据.
5.1设备名称
首先DisplayDevice是设备的名字,这个可以调用接口设置,但是比较常见的值一般有:Built-inScreen,HDMIScreen,VirtualScreen,wfdservice等等.
5.2设备类型
type则是一个枚举值:
03 | DISPLAY_PRIMARY=HWC_DISPLAY_PRIMARY, |
04 | DISPLAY_EXTERNAL=HWC_DISPLAY_EXTERNAL, |
05 | DISPLAY_VIRTUAL=HWC_DISPLAY_VIRTUAL, |
06 | NUM_BUILTIN_DISPLAY_TYPES=HWC_NUM_PHYSICAL_DISPLAY_TYPES, |
11 | HWC_DISPLAY_EXTERNAL=1,
//HDMI,DP,etc. |
12 | HWC_DISPLAY_VIRTUAL=2,
//wfdservice |
13 | HWC_NUM_PHYSICAL_DISPLAY_TYPES=2, |
14 | HWC_NUM_DISPLAY_TYPES=3, |
5.3layerStack
layerStack是存储layer的容器,我们知道每个display只会有一个layerstack来存储他要显示的layer,但是不同的display可以使用同一个layerStack,也可以使用不同的layerStack.
上面我们贴的这个就是两个display使用了不同的layerstack,因为他们显示的内容不一样(电视播放幻灯片).
后续我们可以研究下什么情况下会导致layerstack切换.
5.4屏幕方向
orient表示屏幕方向
后面括号里面的type,是和我们上面说的设备类型完全不同的东西,这个值是由Transform::type算出来的.
基本是下面这些值与或非出来的:
5.5powerMode
powerMode表示了屏幕当前的状态,它有以下取值:
02 | /*Thedisplayisturnedoff(blanked).*/ |
04 | /*Thedisplayisturnedonandconfiguredinalowpowerstate |
05 | *thatissuitableforpresentingambientinformationtotheuser, |
06 | *possiblywithlowerfidelitythannormalbutgreaterefficiency.*/ |
08 | /*Thedisplayisturnedonnormally.*/ |
09 | HWC_POWER_MODE_NORMAL=2, |
10 | /*ThedisplayisconfiguredasinHWC_POWER_MODE_DOZEbutmay |
11 | *stopapplyingframebufferupdatesfromthegraphicssubsystem. |
12 | *Thispowermodeiseffectivelyahintfromthedozedreamto |
13 | *tellthehardwarethatitisdonedrawingtothedisplayforthe |
14 | *timebeingandthatthedisplayshouldremainoninalowpower |
15 | *stateandcontinueshowingitscurrentcontentsindefinitely |
18 | *Thismodemayalsobeusedasasignaltoenablehardware-baseddoze |
19 | *functionality.Inthiscase,thedozedreamiseffectively |
20 | *indicatingthatthehardwareisfreetotakeoverthedisplay |
21 | *andmanageitautonomouslytoimplementlowpoweralways-ondisplay |
23 | HWC_POWER_MODE_DOZE_SUSPEND=3, |
常见的取值有0和2,代表屏幕熄灭和普通情况.
目前还没看到1和3的情况.
5.4其他一些参数
设备大小由eglQuerySurface得来,不展开.
ANativeWindow代表要渲染的本地窗口,这个不同的display之间应该肯定不同.
flips代表屏幕翻页的次数,其实也就是SurfaceFlinger调用doComposition的次数,也就是屏幕画面更新的次数
hwcId需要注意的是,如果一个设备不是HWC合成的,这个值会是负数.需要指出的是,这个值不受开关overlay的影响,也就是说如果这个设备是支持HWC的,应该就不会是负数.目前来看,只有开发者选项模拟二级显示出现的display这个会是负数.
mIsSecure是屏幕自身的属性,mSecureLayerVisible应该会跟播放DRM之类的场景相关
activeConfig目前看到的总是0,还不清楚作用
numLayers是这个display上可见的layer的数量
v,f,s分别代表三个大小:Viewport,Frame,Scissor.