对于很多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 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. 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());
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的状态,涉及不同模块不同功能,这里不再展开.
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代表未知格式. 4.3.2 activeBuffer
4.3.3 queued-frames 新的帧的数量
queued-frames的含义是是否有新的帧,如果当前没有新的帧,这个值是0. 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的值来源于前面创建的材质名称. 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函数.
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.表示有新的一帧.
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
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含义可以参考前面的解释. 4.5.2.5 mTransformHint
同样用于旋转. 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. 至此,layer的dump已经说明完毕,我们继续分析Displays的dump. 5 display信息的dump
首先会打印当前display的数量,数量基于mDisplays的大小,这个容器在SurfaceFlinger初始化时会生成数据,后面根据收到不同的消息在handleTransactionLocked函数中也会调整.
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. 5.4 屏幕方向
5.4 其他一些参数
7 EventThread的dumpEventThread的dump信息: VSYNC state: disabled soft-vsync: disabled numListeners=33, events-delivered: 18546 8 HWC的dumpHWC的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选项决定. 上述四个选项有任意一个打开,就composer的状态就会使disable. 接下来进入HWComposer的dump函数:
接下来就开始打印各个layer的信息,这个也是我们最常见到的layer信息.
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
逐次来看下:
|

shopex酷炫运动商城模板
人气:716
代运/转运网站系统
人气:2268
ecshop也买茶模板|ecshop茶叶
人气:979
麦考林ecshop模板 M18模板
人气:796
免费全网唯一ecshop后台模
人气:5935
ecshop仿emoi家居用品商城
人气:979