SurfaceFlinger的dump信息详解


对于很多Android的显示问题,我们需要使用adb shell dumpsys SurfaceFlinger命令来获取SurfaceFlinger的dump信息,这对于我们分析问题有很大的帮助,因此我们这里来详细讲解下SurfaceFlinger的dump.

SurfaceFlinger的dump信息主要通过dumpAllLocked 函数来获取,因此我们这里就以android 5.0在主屏幕上的一份dump来详细说明下dump的作用.

1 特殊宏的打开

一般dump的第一行都是这样的:

Build configuration: [sf] [libui] [libgui]

这说明其实没有打开任何特殊的宏,实际上,如果一下特殊宏打开,第一行log会打印出来:

FRAMEBUFFER_FORCE_FORMAT,HAS_CONTEXT_PRIORITY,NEVER_DEFAULT_TO_ASYNC_MODE,TARGET_DISABLE_TRIPLE_BUFFERING,DONT_USE_FENCE_SYNC

一般情况下,这些宏一个都不会打开.

2 Sync机制

第二行一般是这样的:

Sync configuration: [using: EGL_ANDROID_native_fence_sync EGL_KHR_wait_sync]

这行其实打印了目前使用的sync机制,这个值源于这段逻辑:

         if (useNativeFenceSync()) {
              mString.append(" EGL_ANDROID_native_fence_sync");
          }
          if (useFenceSync()) {
              mString.append(" EGL_KHR_fence_sync");
          }
          if (useWaitSync()) {
              mString.append(" EGL_KHR_wait_sync");
     }

注意,一二是互斥的,三可以与一二共存.

3 DispSync参数

第三行是打印的是Vsync相关的参数:

DispSync configuration: app phase 0 ns, sf phase 0 ns, present offset 0 ns (refresh 16666667 ns)

这些参数我们还是比较熟悉的,有意思的是打印这些参数时候使用的语法:

    result.appendFormat("app phase %" PRId64 " ns, sf phase %" PRId64 " ns, "
            "present offset %d ns (refresh %" PRId64 " ns)",
        vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, PRESENT_TIME_OFFSET_FROM_VSYNC_NS,
        mHwc->getRefreshPeriod(HWC_DISPLAY_PRIMARY));

PRId64的用法很独特,这是一种跨平台的打印64位整数的做法:

printf("%" PRId64 "\n", value);
// 相当于64位的:
printf("%" "ld" "\n", value);
// 或32位的:
printf("%" "lld" "\n", value);

4 layer的dump

接下来就是很长的一段layer的dump,一般以这样一句话开始:

Visible layers (count = 9)

count的值来源于layersSortedByZ中layer的数量.
接下来就进入各个layer的dump,我们参考代码并以launcher所在的layer为例来解释下各行的意义:

+ Layer 0xb3f92000 (com.sec.android.app.launcher/com.android.launcher2.Launcher) id=87

0xb3f92000指向当前layer对象的值,括号中是当前layer的名称,id是创建layer时产生的序列号.

4.1 区域信息

  Region transparentRegion (this=0xb3f92164, count=1)
    [  0,   0,   0,   0]
  Region visibleRegion (this=0xb3f92008, count=1)
    [  0,   0, 1440, 2560]

接下来的两段是两个Region的dump,每个region可能包含多个区域,所以这里count也可能不等于1.
前两行的值来源于activeTransparentRegion,表示的是这个layer里面透明区域的大小.
后两行值来源于visibleRegion,表示可见区域的大小.

4.2 基本信息

layerStack=   0, z=    21010, pos=(0,0), size=(1440,2560), crop=(0, 0,1440,2560), isOpaque=0, 
invalidate=0, alpha=0xff, flags=0x00000000, tr=[1.00, 0.00][0.00, 1.00]
client=0xb11160c0

上面这段dump源自这段代码:

  result.appendFormat(            "      "
            "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), crop=(%4d,%4d,%4d,%4d), "
            "isOpaque=%1d, invalidate=%1d, "
            "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
            "      client=%p\n",
            s.layerStack, s.z, s.transform.tx(), s.transform.ty(), s.active.w, s.active.h,
            s.active.crop.left, s.active.crop.top,
            s.active.crop.right, s.active.crop.bottom,
            isOpaque(s), contentDirty,
            s.alpha, s.flags,
            s.transform[0][0], s.transform[0][1],
            s.transform[1][0], s.transform[1][1],
            client.get());
  • 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或出来的结果,影响它值的包括:

enum {
      eLayerHidden        = 0x01,     // SURFACE_HIDDEN in SurfaceControl.java
      eLayerOpaque        = 0x02,     // SURFACE_OPAQUE
      eLayerTransparent   = 0x200,     // SURFACE_TRANSPARENT
};
 
enum {
    ePositionChanged            = 0x00000001,
    eLayerChanged               = 0x00000002,
    eSizeChanged                = 0x00000004,
    eAlphaChanged               = 0x00000008,
    eMatrixChanged              = 0x00000010,
    eTransparentRegionChanged   = 0x00000020,
    eVisibilityChanged          = 0x00000040,
    eLayerStackChanged          = 0x00000080,
    eCropChanged                = 0x00000100,
/* SRIB : Smg Surface Animator : State that will indicate animation change */
        e3DAnimationChanged         = 0x00001000,
/* SRIB : Smg Surface Animator : Change End*/
        eOpacityChanged             = 0x00000200,
// { SRUK-SFBLUR
        eTranslucentRegionChanged   = 0x00000400,
// SRUK-SFBLUR }
        eTransparencyChanged        = 0x80000000,
};
enum { // (keep in sync with Surface.java)
    eHidden             = 0x00000004,
    eDestroyBackbuffer  = 0x00000020,
    eSecure             = 0x00000080,
    eNonPremultiplied   = 0x00000100,
    eOpaque             = 0x00000400,
    eProtectedByApp     = 0x00000800,
    eProtectedByDRM     = 0x00001000,
    eCursorWindow       = 0x00002000,
    /* SISO Changes for Internal_Only - Start */
    eFXInternalDisplay = 0x80000000,
    /* SISO Changes for Internal_Only - End */
    eFXSurfaceNormal    = 0x00000000,
    eFXSurfaceDim       = 0x00020000,
    eFXSurfaceMask      = 0x000F0000,
    // begin of app fw : fixed orientation window
    eFixedOrientation   = 0x40000000,
    // end of app fw
    // begin of MDM remote control
    eNoRemoteControl      = 0x08000000,
    // end of MDM remote control
};

所有的这些值都可能影响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.3 buffer信息

      format= 1, activeBuffer=[1440x2560:1664,  1], queued-frames=0, mRefreshPending=0
            mTexName=38 mCurrentTexture=2
            mCurrentCrop=[0,0,0,0] mCurrentTransform=0
            mAbandoned=0
            -BufferQueue mMaxAcquiredBufferCount=1, mDequeueBufferCannotBlock=0,
            default-size=[1440x2560], default-format=1, transform-hint=00, FIFO(0)={}
             [00:0xb110e100] state=FREE    , 0xb3eb1ec0 [1440x2560:1664,  1]
             [01:0xb3ec7000] state=FREE    , 0xb620d060 [1440x2560:1664,  1]
            >[02:0xb110e200] state=ACQUIRED, 0xb1111100 [1440x2560:1664,  1]

4.3.1 数据格式

首先是数据的format,值的来源是layer创建时赋予的,当然我们如果追溯的话,可以追溯到WindowManagerService创建SurfaceControl的过程,值也是创建时指定的.值的定义如下:

 enum {
    //
    // these constants need to match those
    // in graphics/PixelFormat.java & pixelflinger/format.h
    //
    PIXEL_FORMAT_UNKNOWN    =   0,
    PIXEL_FORMAT_NONE       =   0,
    // logical pixel formats used by the SurfaceFlinger -----------------------
    PIXEL_FORMAT_CUSTOM         = -4,
    // Custom pixel-format described by a PixelFormatInfo structure
    PIXEL_FORMAT_TRANSLUCENT    = -3,
    // System chooses a format that supports translucency (many alpha bits)
    PIXEL_FORMAT_TRANSPARENT    = -2,
    // System chooses a format that supports transparency
    // (at least 1 alpha bit)
    PIXEL_FORMAT_OPAQUE         = -1,
    // System chooses an opaque format (no alpha bits required)
    // real pixel formats supported for rendering -----------------------------
    PIXEL_FORMAT_RGBA_8888   = HAL_PIXEL_FORMAT_RGBA_8888,   // 4x8-bit RGBA
    PIXEL_FORMAT_RGBX_8888   = HAL_PIXEL_FORMAT_RGBX_8888,   // 4x8-bit RGB0
    PIXEL_FORMAT_RGB_888     = HAL_PIXEL_FORMAT_RGB_888,     // 3x8-bit RGB
    PIXEL_FORMAT_RGB_565     = HAL_PIXEL_FORMAT_RGB_565,     // 16-bit RGB
    PIXEL_FORMAT_BGRA_8888   = HAL_PIXEL_FORMAT_BGRA_8888,   // 4x8-bit BGRA
    PIXEL_FORMAT_RGBA_5551   = 6,                            // 16-bit ARGB
    PIXEL_FORMAT_RGBA_4444   = 7,                            // 16-bit ARGB
    PIXEL_FORMAT_sRGB_A_8888 = HAL_PIXEL_FORMAT_sRGB_A_8888, // 4x8-bit sRGB + A
    PIXEL_FORMAT_sRGB_X_8888 = HAL_PIXEL_FORMAT_sRGB_X_8888, // 4x8-bit sRGB, no A
};

其实只有下面的值是真实可用的,其余值在SurfaceFlinger创建时会被转换:

    switch (format) {
    case PIXEL_FORMAT_TRANSPARENT:
    case PIXEL_FORMAT_TRANSLUCENT:
        format = PIXEL_FORMAT_RGBA_8888;
        break;
    case PIXEL_FORMAT_OPAQUE:
        format = PIXEL_FORMAT_RGBX_8888;
        break;
    }

其实当前常见的format也就是这几种.

HAL_PIXEL_FORMAT_RGBA_8888          = 1,
HAL_PIXEL_FORMAT_RGBX_8888          = 2,
HAL_PIXEL_FORMAT_RGB_888            = 3,
HAL_PIXEL_FORMAT_RGB_565            = 4,
HAL_PIXEL_FORMAT_BGRA_8888          = 5,

0代表未知格式.
常见的layer中,dimlayer一般是0,大多数layer是1,壁纸是2,照相机的预览数据是4,视频播放也是4.

4.3.2 activeBuffer

  • activeBuffer的前两项表示了当前正在显示的buffer的宽和高.
  • 第三项表示Stride.这个值很有意思,我们发现他有时候是等于宽的,有时候是大于宽的,我们先来看下这个值的解释.
    The number of pixels that a line in the buffer takes in memory. This may be >= width.
    我们知道内存申请使用是需要成块对齐的,也就是说不是说使我们申请多大的内存,就会给我们多大的内存,因为涉及到对齐,所以很可能这个内存实际上是大于我们的需要的.(暂时没有仔细研究,有待确认)像有些marvell型号,内存是按照64位对齐的,那么我们申请一个100宽的buffer,系统就会给我们留出128的buffer大小供我们使用.
  • 第四项并没有什么特殊,表示format,跟前面的format应该是一致的.

4.3.3 queued-frames 新的帧的数量

queued-frames的含义是是否有新的帧,如果当前没有新的帧,这个值是0.
一般在画面持续变化时(照相预览,视频播放,窗口滑动,游戏),这个值会是1.表示有新的一帧.
偶尔也可以见到这个值是2(这个值应该最大就是2,因为只有三个缓冲区).

4.3.4 mRefreshPending刷新卡住了吗?

mRefreshPending几乎所有的常见情况下都是0,因为这个参数代表了一个layer执行了Invalidate却没有完成Refresh,除非发生错误这显然不可能.

4.4 SurfaceFlingerConsumer的dump

接下来开始对消费者进行dump,SurfaceFlingerConsumer是GLConsumer子类,所以这里实际上是调用了GLConsumer的dumpLocked函数.
先来看下代码:

    result.appendFormat(
       "%smTexName=%d mCurrentTexture=%d\n"
       "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
       prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
       mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
       mCurrentTransform);

它会对应打印出来这一段信息:

            mTexName=38 mCurrentTexture=2
            mCurrentCrop=[0,0,0,0] mCurrentTransform=0

4.4.1 材质名称

mTexName的值来源是在消费者被创建时,我们知道最常见的创建消费者的时候是Layer::onFirstRef时会调用:

mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName);

创建一个消费者,有两个参数,其中mTextureName是我们目前关注的,如果追溯来源你会发现mTextureName的值来源于glGenTextures,这个函数的实现依赖平台,参考ligagl,它是这样的:

   // generate unique (shared) texture names
   c->surfaceManager->getToken(n, textures);

还是继续回来看SurfaceFlingerConsumer的创建:

 SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer,
            uint32_t tex)
        : GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false, false),
          mTransformToDisplayInverse(false)

GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
        uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
    ConsumerBase(bq, isControlledByApp),
    mCurrentTransform(0),
    mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
    mCurrentFence(Fence::NO_FENCE),
    mCurrentTimestamp(0),
    mCurrentFrameNumber(0),
    mDefaultWidth(1),
    mDefaultHeight(1),
    mFilteringEnabled(true),
    mTexName(tex),
    mUseFenceSync(useFenceSync),
    mTexTarget(texTarget),
    mEglDisplay(EGL_NO_DISPLAY),
    mEglContext(EGL_NO_CONTEXT),
    mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
    mAttached(true)

我们现在可以看出来mTexName的值来源于前面创建的材质名称.
mCurrentTexture的初始值是INVALID_BUFFER_SLOT,也就是-1,后面会在updateAndReleaseLocked时被更改,值的来源是使用的BufferItem的mBuf值,也就是mSlot,这应该是使用的buffer数组的slot值,这个变量的合理取值只有0,1,2三个值(mSlot is the slot index of this buffer ,default INVALID_BUFFER_SLOT).

4.4.2 mCurrentCrop裁剪区域

mCurrentCrop的值来源同样是updateAndReleaseLocked调用时被赋值,值的来源是BufferItem的mCrop值.这个值基本一直都是0,只有在视频播放和照相机时会被设置(值的来源有待更深入的研究, mCrop is the current crop rectangle for this buffer slot).

4.4.3 mCurrentTransform 旋转相关

mCurrentTransform的值和前面我们说过的tr值很类似. (mTransform is the current transform flags for this buffer slot. refer to NATIVE_WINDOW_TRANSFORM_* in <window.h>).
它也跟旋转有关,我们来看下window.h中的定义:

/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */
enum {
    /* flip source image horizontally */
    NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
    /* flip source image vertically */
    NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
    /*rotate source image 90 degrees clock-wise, is applied after TRANSFORM_FLIP_{H|V} */
    NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
    /* rotate source image 180 degrees */
    NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
    /* rotate source image 270 degrees clock-wise */
    NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
    /* transforms source by the inverse transform of the screen it is displayed onto. This
     * transform is applied last */
    NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08
};

enum {
    /* flip source image horizontally (around the vertical axis) */
    HAL_TRANSFORM_FLIP_H    = 0x01,
    /* flip source image vertically (around the horizontal axis)*/
    HAL_TRANSFORM_FLIP_V    = 0x02,
    /* rotate source image 90 degrees clockwise */
    HAL_TRANSFORM_ROT_90    = 0x04,
    /* rotate source image 180 degrees */
    HAL_TRANSFORM_ROT_180   = 0x03,
    /* rotate source image 270 degrees clockwise */
    HAL_TRANSFORM_ROT_270   = 0x07,
    /* don't use. see system/window.h */
    HAL_TRANSFORM_RESERVED  = 0x08,
};

4.5 ConsumerBase(消费者)的dump

子类GLConsumer dump完毕,调用了它的父类的dump函数,基本就是调用了IGraphicBufferConsumer的dump函数.
生产者消费者这套体系我们之前以前讲过,这里我们就不再详细展开,如果不清楚看下Layer::onFirstRef这个函数就明白了,消费者这个值来自于BufferQueue::createBufferQueue的创建,其中创建了新的BufferQueueConsumer做为消费者.

void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
        sp<IGraphicBufferConsumer>* outConsumer,
        const sp<IGraphicBufferAlloc>& allocator) {
    sp<BufferQueueCore> core(new BufferQueueCore(allocator));
    sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core));
    sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
    *outProducer = producer;
    *outConsumer = consumer;

当然BufferQueueConsumer的dump函数啥也没写,就调用了BufferQueueCore的dump函数.

打印出来的信息一般是这样的:

            -BufferQueue mMaxAcquiredBufferCount=1, mDequeueBufferCannotBlock=0,
            default-size=[1440x2560], default-format=1, transform-hint=00, FIFO(0)={}
             [00:0xb110e100] state=FREE    , 0xb3eb1ec0 [1440x2560:1664,  1]
             [01:0xb3ec7000] state=FREE    , 0xb620d060 [1440x2560:1664,  1]
            >[02:0xb110e200] state=ACQUIRED, 0xb1111100 [1440x2560:1664,  1]

下面我们按照代码顺序详细解释一下:

4.5.1 队列中的buffer

我们之前在解释queued-frames的含义时已经说过,在画面持续变化时(照相预览,视频播放,窗口滑动,游戏),queued-frames值会是1.表示有新的一帧.
对应的,有新的frames自然需要有在队列中等待的buffer,对应这段代码会把这个队列打印出来:

    Fifo::const_iterator current(mQueue.begin());
    while (current != mQueue.end()) {
        fifo.appendFormat("%02d:%p crop=[%d,%d,%d,%d], "
                "xform=0x%02x, time=%#" PRIx64 ", scale=%s\n",
                current->mSlot, current->mGraphicBuffer.get(),
                current->mCrop.left, current->mCrop.top, current->mCrop.right,
                current->mCrop.bottom, current->mTransform, current->mTimestamp,
                BufferItem::scalingModeName(current->mScalingMode));
        ++current;
    }

对应打印出来的dump信息是这样的:

02:0xb631e480 crop=[0,0,0,0], xform=0x07, time=0xc4d5da9b1e0, scale=FREEZE
  • 02是mSlot的值,crop是裁剪区域,xform是旋转,这三个我们上面已经讲过,这里不再展开.

  • time是这个buffer被queue的时间(mTimestamp is the current timestamp for this buffer slot. This gets to set by queueBuffer each time this slot is queued. This value is guaranteed to be monotonically increasing for each newly acquired buffer.).

  • scale是缩放模式,一般取值如下:

enum {
    /* the window content is not updated (frozen) until a buffer of
     * the window size is received (enqueued)
     */
    NATIVE_WINDOW_SCALING_MODE_FREEZE           = 0,
    /* the buffer is scaled in both dimensions to match the window size */
    NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW  = 1,
    /* the buffer is scaled uniformly such that the smaller dimension
     * of the buffer matches the window size (cropping in the process)
     */
    NATIVE_WINDOW_SCALING_MODE_SCALE_CROP       = 2,
    /* the window is clipped to the size of the buffer's crop rectangle; pixels
     * outside the crop rectangle are treated as if they are completely
     * transparent.
     */
    NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP    = 3,
};

4.5.2 BufferQueue的基本默认信息

接下来的一段代码会打印BufferQueue的一些基本信息:

    result.appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, "
            "mDequeueBufferCannotBlock=%d, default-size=[%dx%d], "
            "default-format=%d, transform-hint=%02x, FIFO(%zu)={%s}\n",
            prefix, mMaxAcquiredBufferCount, mDequeueBufferCannotBlock,
            mDefaultWidth, mDefaultHeight, mDefaultBufferFormat, mTransformHint,
            mQueue.size(), fifo.string());

一般会打印如下:

-BufferQueue mMaxAcquiredBufferCount=1, mDequeueBufferCannotBlock=0, default-size=[1920x1080],
 default-format=4, transform-hint=04

4.5.2.1 允许同时acquire的buffer的数量

mMaxAcquiredBufferCount是允许同时acquire的buffer的数量,解释如下:

    // mMaxAcquiredBufferCount is the number of buffers that the consumer may
    // acquire at one time. It defaults to 1, and can be changed by the consumer
    // via setMaxAcquiredBufferCount, but this may only be done while no
    // producer is connected to the BufferQueue. This value is used to derive
    // the value returned for the MIN_UNDEQUEUED_BUFFERS query to the producer.

基本这个值只能是1,不再深究.

4.5.2.2 dequeueBuffer是否允许被block

    // mDequeueBufferCannotBlock indicates whether dequeueBuffer is allowed to
    // block. This flag is set during connect when both the producer and
    // consumer are controlled by the application.
    bool mDequeueBufferCannotBlock;

mDequeueBufferCannotBlock几乎总是为0,除非一个应用同时控制了生产者和消费者,这很罕见.

4.5.2.3 buffer default-size默认buffer大小

这两个值的来源应该是BufferQueueConsumer::setDefaultBufferSize函数(不是特别确定,因为这段代码写的不好,严重破坏了封装性).

用处是这样的: mDefaultHeight holds the default height of allocated buffers. It is used in dequeueBuffer if a width and height of 0 are specified.

4.5.2.4 mDefaultBufferFormat默认格式

mDefaultBufferFormat很简单,format含义可以参考前面的解释.
mDefaultBufferFormat can be set so it will override the buffer format when it isn't specified in dequeueBuffer.

4.5.2.5 mTransformHint

同样用于旋转.
mTransformHint is the transform probably applied to buffers of this window. this is only a hint, actual transform may differ.

4.5.3 各个Buffer的信息

接下来是打印BufferSlot中各个buffer的信息,一般打印如下:

             [00:0xb651d780] state=QUEUED  , 0xb6321240 [1080x1920:1152,  1]
             [01:0xb1513200] state=FREE    , 0xb65189c0 [1080x1920:1152,  1]
            >[02:0xb651d080] state=ACQUIRED, 0xb6518330 [1080x1920:1152,  1]

是由下面的代码打印出来的.

    for (int s = 0; s < maxBufferCount; ++s) {
        const BufferSlot& slot(mSlots[s]);
        const sp<GraphicBuffer>& buffer(slot.mGraphicBuffer);
        result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix,
                (slot.mBufferState == BufferSlot::ACQUIRED) ? ">" : " ",
                s, buffer.get(),
                BufferSlot::bufferStateName(slot.mBufferState));
        if (buffer != NULL) {
            result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle,
                    buffer->width, buffer->height, buffer->stride,
                    buffer->format);
        }
        result.append("\n");
    }
}

ACQUIRED的buffer前面会打印>,表示这是当前在显示的buffer.
state表示buffer的状态,取值包括DEQUEUED,QUEUED,FREE,ACQUIRED.我们知道ACQUIRED是在显示的,DEQUEUED是在绘制的,QUEUED绘制完成还未显示的,free是未使用的.
后面的大小,stride,和format前面都讲过了,这里不再说明.

至此,layer的dump已经说明完毕,我们继续分析Displays的dump.

5 display信息的dump

首先会打印当前display的数量,数量基于mDisplays的大小,这个容器在SurfaceFlinger初始化时会生成数据,后面根据收到不同的消息在handleTransactionLocked函数中也会调整.
正常情况下是1,也就是只有一个display(Built-in Screen),当设备连接了HDMI或者使用了屏幕共享等功能时,会有额外的display加入,比如下面这个:

Displays (2 entries)
+ DisplayDevice: HDMI Screen
   type=1, hwcId=1, layerStack=6, (1920x1080), ANativeWindow=0xb4d94d08, orient= 0 (type=00000000), flips=1173, isSecure=1, 
    secureVis=0, powerMode=2, activeConfig=0, numLayers=1
   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]]
mAbandoned=0
-BufferQueue mMaxAcquiredBufferCount=2, mDequeueBufferCannotBlock=0, default-size=[1920x1080], default-format=1, transform-hint=00, 
  FIFO(0)={}
 [00:0xb6418c80] state=FREE    , 0xb43ed880 [1920x1080:1920,  1]
 [01:0xb43cb300] state=FREE    , 0xb640d970 [1920x1080:1920,  1]
>[02:0xb43cb280] state=ACQUIRED, 0xb43ed830 [1920x1080:1920,  1]
+ DisplayDevice: Built-in Screen
   type=0, hwcId=0, layerStack=0, (1080x1920), ANativeWindow=0xb4d94608, orient= 0 (type=00000000), flips=3140, isSecure=1, 
    secureVis=0, powerMode=2, activeConfig=0, numLayers=2
   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-in Screen,HDMI Screen,Virtual Screen,wfdservice等等.

5.2 设备类型

type则是一个枚举值:

enum DisplayType {
        DISPLAY_ID_INVALID = -1,
        DISPLAY_PRIMARY     = HWC_DISPLAY_PRIMARY,
        DISPLAY_EXTERNAL    = HWC_DISPLAY_EXTERNAL,
        DISPLAY_VIRTUAL     = HWC_DISPLAY_VIRTUAL,
        NUM_BUILTIN_DISPLAY_TYPES = HWC_NUM_PHYSICAL_DISPLAY_TYPES,
    };
    
enum {
    HWC_DISPLAY_PRIMARY     = 0,
    HWC_DISPLAY_EXTERNAL    = 1,    // HDMI, DP, etc.
    HWC_DISPLAY_VIRTUAL     = 2,    // wfdservice
    HWC_NUM_PHYSICAL_DISPLAY_TYPES = 2,
    HWC_NUM_DISPLAY_TYPES          = 3,
};

5.3 layerStack

layerStack是存储layer的容器,我们知道每个display只会有一个layerstack来存储他要显示的layer,但是不同的display可以使用同一个layerStack,也可以使用不同的layerStack.
上面我们贴的这个就是两个display使用了不同的layerstack,因为他们显示的内容不一样(电视播放幻灯片).
后续我们可以研究下什么情况下会导致layerstack切换.

5.4 屏幕方向

  • orient表示屏幕方向
  • 后面括号里面的type,是和我们上面说的设备类型完全不同的东西,这个值是由Transform::type算出来的.
    基本是下面这些值与或非出来的:
            enum type_mask {
                IDENTITY            = 0,
                TRANSLATE           = 0x1,
                ROTATE              = 0x2,
                SCALE               = 0x4,
                UNKNOWN             = 0x8
            };

5.5 powerMode

powerMode表示了屏幕当前的状态,它有以下取值:

enum {
    /* The display is turned off (blanked). */
    HWC_POWER_MODE_OFF      = 0,
    /* The display is turned on and configured in a low power state
     * that is suitable for presenting ambient information to the user,
     * possibly with lower fidelity than normal but greater efficiency. */
    HWC_POWER_MODE_DOZE     = 1,
    /* The display is turned on normally. */
    HWC_POWER_MODE_NORMAL   = 2,
    /* The display is configured as in HWC_POWER_MODE_DOZE but may
     * stop applying frame buffer updates from the graphics subsystem.
     * This power mode is effectively a hint from the doze dream to
     * tell the hardware that it is done drawing to the display for the
     * time being and that the display should remain on in a low power
     * state and continue showing its current contents indefinitely
     * until the mode changes.
     *
     * This mode may also be used as a signal to enable hardware-based doze
     * functionality.  In this case, the doze dream is effectively
     * indicating that the hardware is free to take over the display
     * and manage it autonomously to implement low power always-on display
     * functionality. */
    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.

7 EventThread的dump

EventThread的dump信息:

VSYNC state: disabled
  soft-vsync: disabled
  numListeners=33,
  events-delivered: 18546

8 HWC的dump

HWC的dump从这句话开始:

h/w composer state:
  h/w composer present and enabled

其中present和enable与否由以下参数决定:

    result.appendFormat("  h/w composer %s and %s\n",
            hwc.initCheck()==NO_ERROR ? "present" : "not present",
                    (mDebugDisableHWC || mDebugRegion || mDaltonize
                            || mHasColorMatrix) ? "disabled" : "enabled");

只要init成功,就是present;而是否enable则由众多debug选项决定.
mDebugDisableHWC我们很熟悉,就是我们在开发者选项里勾选的禁用硬件叠加.
mDebugRegion是开发者选项里面的开发者选项中的显示屏幕更新,也可以通过属性debug.sf.showupdates来控制.
mDaltonize被1014号命令控制,暂时先不研究.
mHasColorMatrix被1015号命令控制.

上述四个选项有任意一个打开,就composer的状态就会使disable.

接下来进入HWComposer的dump函数:

  • 首先打印的是hwc的version,目前一般0103或者0104居多.
  • mDebugForceFakeVSync的值源自属性debug.sf.no_hw_vsync,打开之后会使用纯软件模拟VSync信号.
  • 接下来是打印各个display的一些基本信息:
  Display[0] configurations (* current):
    * 0: 1440x2560, xdpi=508.000000, ydpi=516.062988, secure=1 refresh=16666667

这些基本信息取自对应display设备的DisplayData的config信息.

接下来就开始打印各个layer的信息,这个也是我们最常见到的layer信息.
依然是分display打印的,分别打印每个display的可见layer,通过getVisibleLayersSortedByZ获取的.

    type   |  handle  | hint | flag | tr | blnd |  format     |     source crop(l,t,r,b)       |           frame        |      dirtyRect         |  name 

------------+----------+----------+----------+----+-------+----------+-----------------------------------+---------------------------+-------------------

       HWC | aed1c650 | 0002 | 0000 | 00 | 0100 | ? 00000011  |    0.0,    0.0, 1920.0, 1080.0 |    0,    0,  960,  540 | [    0,    0, 1920, 1080] | SurfaceView

       HWC | aed1c470 | 0000 | 0000 | 00 | 0105 | RGBA_8888   |    0.0,    0.0, 2560.0, 1440.0 |    0,    0,  960,  540 | [    0,    0, 2560, 1440] | SurfaceView

 FB TARGET | b3ec5240 | 0000 | 0000 | 00 | 0105 | RGBA_8888   |    0.0,    0.0,  960.0,  540.0 |    0,    0,  960,  540 | [    0,    0,    0,    0] | HWC_FRAMEBUFFER_TARGET

逐次来看下:

  • type表示合成类型,可取的值包括以下几种: "GLES","HWC","BKGND", "FB TARGET","SIDEBAND","HWC_CURSOR","FB_BLIT","UNKNOWN".(更详细的一些解释可以参见hwcomposer.h)
  • handle是个标识符,可以跟某个buffer的handle对应.(Handle of buffer to compose. This handle is guaranteed to have been allocated from gralloc using the GRALLOC_USAGE_HW_COMPOSER usage flag. If the layer's handle is unchanged across two consecutive prepare calls and the HWC_GEOMETRY_CHANGED flag is not set for the second call then the HWComposer implementation may assume that the contents of the buffer have not changed. )
  • hint is bit mask set by the HWC implementation during (prepare)().It is preserved between (prepare)() calls, unless the HWC_GEOMETRY_CHANGED flag is set, in which case it is reset to 0.
    下面是hint的可能取值.
/*
  * hwc_layer_t::hints values
  * Hints are set by the HAL and read by SurfaceFlinger
*/
enum {
    /*
     * HWC can set the HWC_HINT_TRIPLE_BUFFER hint to indicate to SurfaceFlinger
     * that it should triple buffer this layer. Typically HWC does this when
     * the layer will be unavailable for use for an extended period of time,
     * e.g. if the display will be fetching data directly from the layer and
     * the layer can not be modified until after the next set().
     */
    HWC_HINT_TRIPLE_BUFFER  = 0x00000001,

    /*
     * HWC sets HWC_HINT_CLEAR_FB to tell SurfaceFlinger that it should clear the
     * framebuffer with transparent pixels where this layer would be.
     * SurfaceFlinger will only honor this flag when the layer has no blending
     *
     */
    HWC_HINT_CLEAR_FB       = 0x00000002
};
  • flag的常见取值只有0和1,1代表这个layer由SF处理.如果我们关闭硬件叠加,可以看到layer的这个值都是1.都是这个值不能跟type画等号,在layer众多的时候,layer都是GLES类型,但是只有很少的layer的flag是1(原因有待研究,1的详细解释是这样的:HWC_SKIP_LAYER is set by SurfaceFlnger to indicate that the HAL shall not consider this layer for composition as it will be handled by SurfaceFlinger ).
  • tr是transform的缩写,但是注意这里的tr和前面单独layer dump时的tr或transform的值这不能划等号的,这里的tr的含义是transformation to apply to the buffer during composition.
  • blnd的含义如下:
enum {
    /* no blending */
    HWC_BLENDING_NONE     = 0x0100,
    /* ONE / ONE_MINUS_SRC_ALPHA */
    HWC_BLENDING_PREMULT  = 0x0105,
    /* SRC_ALPHA / ONE_MINUS_SRC_ALPHA */
    HWC_BLENDING_COVERAGE = 0x0405
};
  • format前面说过很多次了,这里不再说了.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 本篇文章是基于谷歌有关Graphic的一篇概览文章的翻译:http://source.android.com/de...
    lee_3do阅读 7,088评论 2 21
  • 教程一:视频截图(Tutorial 01: Making Screencaps) 首先我们需要了解视频文件的一些基...
    90后的思维阅读 4,648评论 0 3
  • “个人魅力”重要吗?或许你会说:“毋庸置疑!” 公司领导讲求个人魅力,才能创造不一样的公司文化,才能深入人心,才能...
    三月楚楚阅读 631评论 0 51
  • 技巧和窍门 在结束本章之前,我还想和大家分享一些 Git 使用的技巧和窍门。很多使用 Git 的开发者可能根本就没...
    很少更新了阅读 189评论 0 0