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的第一行都是這樣的:
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中的定義:
/* 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.