1. 程式人生 > >[置頂] Android L SurfaceFlinger dump資訊全解(1)

[置頂] Android L SurfaceFlinger dump資訊全解(1)

SurfaceFlinger的dump資訊詳解

對於很多Android的顯示問題,我們需要使用adb shell dumpsys SurfaceFlinger命令來獲取SurfaceFlinger的dump資訊,這對於我們分析問題有很大的幫助,因此我們這裡來詳細講解下SurfaceFlinger的dump.

SurfaceFlinger的dump資訊主要通過dumpAllLocked 函式來獲取,因此我們這裡就以android 5.0在主螢幕上的一份dump來詳細說明下dump的作用.

1 特殊巨集的開啟

一般dump的第一行都是這樣的:

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

這說明其實沒有開啟任何特殊的巨集,實際上,如果一下特殊巨集開啟,第一行log會打印出來:

  1. FRAMEBUFFER_FORCE_FORMAT,HAS_CONTEXT_PRIORITY,NEVER_DEFAULT_TO_ASYNC_MODE,TARGET_DISABLE_TRIPLE_BUFFERING,DONT_USE_FENCE_SYNC  

一般情況下,這些巨集一個都不會開啟.

2 Sync機制

第二行一般是這樣的:

  1. Sync configuration: [using: EGL_ANDROID_native_fence_sync EGL_KHR_wait_sync]  

這行其實列印了目前使用的sync機制,這個值源於這段邏輯:

  1. 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相關的引數:

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


這些引數我們還是比較熟悉的,有意思的是列印這些引數時候使用的語法:

  1. 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位整數的做法:

  1. printf("%" PRId64 "\n", value);// 相當於64位的:printf("%" "ld" "\n", value);// 或32位的:printf("%" "lld" "\n", value);


4 layer的dump

接下來就是很長的一段layer的dump,一般以這樣一句話開始:



count的值來源於layersSortedByZ中layer的數量.
接下來就進入各個layer的dump,我們參考程式碼並以launcher所在的layer為例來解釋下各行的意義:

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

0xb3f92000指向當前layer物件的值,括號中是當前layer的名稱,id是建立layer時產生的序列號.

4.1 區域資訊

  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 基本資訊

  1. 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源自這段程式碼:

  1. 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或出來的結果,影響它值的包括:
    1. 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資訊

  1. 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的過程,值也是建立時指定的.值的定義如下:

  1. 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建立時會被轉換:

  1. 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也就是這幾種.

  1. 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函式.
先來看下程式碼:

  1. 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);  


它會對應打印出來這一段資訊:

  1. mTexName=38 mCurrentTexture=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. // generate unique (shared) texture names   c->surfaceManager->getToken(n, textures);

還是繼續回來看SurfaceFlingerConsumer的建立:

  1. SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer,            uint32_t tex)        : GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, falsefalse),          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中的定義:

  1. /* 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做為消費者.

  1. 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函式.

打印出來的資訊一般是這樣的:

  1. -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,對應這段程式碼會把這個佇列打印出來:

  1. 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資訊是這樣的:

  1. 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是縮放模式,一般取值如下:

    1. 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的一些基本資訊:

  1. 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());  


一般會列印如下:

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

4.5.2.1 允許同時acquire的buffer的數量

mMaxAcquiredBufferCount是允許同時acquire的buffer的數量,解釋如下:

  1. // 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

  1. // 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的資訊,一般列印如下:

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


是由下面的程式碼打印出來的.

  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加入,比如下面這個:

  1. 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則是一個列舉值:

  1. 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算出來的.
    基本是下面這些值與或非出來的:
    1. enum type_mask {                IDENTITY            = 0,                TRANSLATE           = 0x1,                ROTATE              = 0x2,                SCALE               = 0x4,                UNKNOWN             = 0x8            };  

5.5 powerMode

powerMode表示了螢幕當前的狀態,它有以下取值:

  1. 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.

相關推薦

[] Android L SurfaceFlinger dump資訊(1)

SurfaceFlinger的dump資訊詳解 對於很多Android的顯示問題,我們需要使用adb shell dumpsys SurfaceFlinger命令來獲取SurfaceFlinger的dump資訊,這對於我們分析問題有很大的幫助,因此我們這裡來詳細講解下SurfaceFlinger的dump.

[] Android之基礎建設之IWindow和IWindowSession

private final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final IInputMethodClient mClient; final IInputContext mInputCo

[] Android APK反編譯就這麼簡單 詳(附圖)

在學習Android開發的過程你,你往往會去借鑑別人的應用是怎麼開發的,那些漂亮的動畫和精緻的佈局可能會讓你愛不釋手,作為一個開發者,你可能會很想知道這些效果介面是怎麼去實現的,這時,你便可以對改應用的APK進行反編譯檢視。下面是我參考了一些文章後簡單的教程詳解。 (注

Android 獲取手機儲存資訊(記憶體,外存等)

ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); //系統記憶體資訊 ActivityManager.MemoryInfo memInfo = new ActivityManager

Android-TabHost選項卡-疑難

前言: 雖然現在谷歌已經不推薦使用TabHost,但是初學者還是很有必要接觸下這一成金的經典的,本文將介紹纖細介紹這一空間的使用,以及大家可能遇到的問題。注:文末給出完整實現程式碼   三個問題: 1. 無法顯示TabHost 2. 新增圖片 + 文字 無法同時

Android 獲取手機儲存資訊(記憶體,外存等)

        android  獲取手機儲存資訊詳解(記憶體,外存等)         android不像ios,android可以外接Sd卡,並且也會有內接的儲存卡,此次專門研究下如何獲取android的儲存資訊。 一、RAM記憶體         RAM,也就是我們常說的手機記憶體。最早的記憶體大小

Android Studio第三十九期 - popWindows

android windows systems power always /** * Window type: system window, such as low power alert. These windows * are always on top of application

androidGridView高度自適應,實現屏鋪滿效果

== post http istview div GridView dap item 拉伸 使GridView每個item的高度自適應拉伸,達到整個GridView剛好鋪滿全屏的效果。 public static void setGridViewMatchParent(G

Android仿ios微信左劃條目刪除、的實現,程式碼簡潔,更容易理解使用

<span style="font-family:Arial, Helvetica, sans-serif;"><span style="background-color: rgb(255, 255, 255);">歡迎大家</span></span

Android自定義ScrollView下拉圖片變大且帶有一鍵功能

描述:下拉ScrollView時頂部的圖片隨之變大,回拉縮小,且帶有一鍵置頂的功能   1.自定義類: import android.animation.ObjectAnimator; import android.animation.ValueAnimator; imp

Android實現介面滾動時頂部部分內容

先看效果。 實現與分析 很顯然,這樣的效果用到了Android Material Design裡的控制元件,分別是CoordinatorLayout和AppBarLayout。其中,AppBarLayout控制元件便具備頂部固定的功能,但它需要被Coord

Android仿QQ和iOS的ListView左滑出現刪除和等操作,可自定義選單

一:介紹 大家在專案中可能會有對ListView向左滑動的時候出現刪除,置頂等等操作的需求,例如QQ聊天列表左滑,iOS中左滑刪除等等,下面就介紹一下如何實現這種效果 二:先給大家展示效果圖,先睹為快 三:實現步驟 1.這種效果的ListView是自定義的控制元件,開源庫的下載地址是

Android Popupwindow中ScrollView中軟鍵盤無法EditText

問題描述:Popupwindow中ScrollView中存在很多EditText,軟鍵盤無法把EditText頂上去 解決辦法:1、去掉ScollView的滾動條——在xml中新增屬性android:scrollbars="none" 2、設定軟鍵盤彈出方式為:setInputMeth

Android 滑動懸浮效果的新實現

最近專案要實現如圖的效果,就是滑動的時候課程介紹那一欄到頂懸浮,而裡面的可滑動控制元件繼續滑動, 百度了好多,網上都是利用重寫scrollview滑動的監聽,來隱藏和顯示某一個控制元件來實現的,通過

Android實現滑動懸浮效果

網上都是利用重寫scrollview滑動的監聽,來隱藏和顯示某一個控制元件來實現的,通過這個方法實現的滑動不是很流暢,於是我決定用最新的material design來實現這個效果。廢話不多說,直接上程式碼: <LinearLayout

Android懸浮窗的實現--可以,可以設定優先順序的view

懸浮窗,顧名思義,顯示在window介面之上的一種檢視。如今,有這麼個需求:設計一個能夠在任意介面上顯示的60s倒計時彈窗,60s之後執行其他操作。注意哦,這裡的任意不僅限於當前的應用,而是所有的介面。效果如下圖: 這裡,我們用WindowManager + view來

Android dump 資訊

在除錯 Android framework 程式碼的時候,需要查詢寫log 資訊,其實有部分資訊已經在 Android dump函式中含有,我們僅僅需要通過dump 便可以抓取資訊,不需要自己在設定log,android原始碼中很多service提供了dump函式 抓取方式 #adb

糾正:Android RecyclerView滾動到指定位置並(滾動方法、移動、定位滑動到指定位置item)

最近博主發現讓RecyclerView滑動到某一位置並置頂的部落格一大堆,抄的是完全一模一樣。此外,雖然這些部落格“解決”了這些問題,但這種解決方案過於淺顯、粗暴,甚至都違背了開發思想。遂在此糾正這種錯誤。 RecyclerView提供了幾種移動的方法 scrollToP

Android RecyclerView滾動定位到item,並使其

/**準確定位到指定位置,並且將指定位置的item置頂, 若直接呼叫scrollToPosition(...)方法,則不會置頂。**/

Android listView item側滑實現刪除和功能

         第一次寫部落格,先說下大概思路吧~         要顯示item側滑顯示刪除,置頂。首先要隱藏一部分item的佈局(自定義隱藏佈局寬度,在adapter裡設定LayoutParams)。然後重寫listview的onInterceptTouchEvent()和onTouchEvent(