自Android 4.1引入了“Profile GPU rendering”这个开发工具以帮助分析应用程序性能并并精确定位渲染问题,Android 4.3增加了可视效果:On screen as bars/lines。模拟器中此功能生效要勾选AVD的:Emulation Options:User Host GPU。

这个选项的开关实现位于settings中DevelopmentSettings.java中的函数writeTrackFrameTimeOptions:

Codeprivate void writeTrackFrameTimeOptions(Object newValue) {
SystemProperties.set(HardwareRenderer.PROFILE_PROPERTY,
newValue == null ? "" : newValue.toString());
pokeSystemProperties();
updateTrackFrameTimeOptions();
} void pokeSystemProperties() {
if (!mDontPokeProperties) {
//noinspection unchecked
(new SystemPropPoker()).execute();
}
} static class SystemPropPoker extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
String[] services;
try {
services = ServiceManager.listServices();
} catch (RemoteException e) {
return null;
}
for (String service : services) {
IBinder obj = ServiceManager.checkService(service);
if (obj != null) {
Parcel data = Parcel.obtain();
try {
obj.transact(IBinder.SYSPROPS_TRANSACTION, data, null, 0);
} catch (RemoteException e) {
} catch (Exception e) {
Log.i(TAG, "Somone wrote a bad service '" + service
+ "' that doesn't like to be poked: " + e);
}
data.recycle();
}
}
return null;
}
}

其中HardwareRenderer.PROFILE_PROPERTY定义于HardwareRenderer.java

Code    /**
* System property used to enable or disable hardware rendering profiling.
* The default value of this property is assumed to be false.
*
* When profiling is enabled, the adb shell dumpsys gfxinfo command will
* output extra information about the time taken to execute by the last
* frames.
*
* Possible values:
* "true", to enable profiling
* "visual_bars", to enable profiling and visualize the results on screen
* "visual_lines", to enable profiling and visualize the results on screen
* "false", to disable profiling
*
* @see #PROFILE_PROPERTY_VISUALIZE_BARS
* @see #PROFILE_PROPERTY_VISUALIZE_LINES
*
* @hide
*/
public static final String PROFILE_PROPERTY = "debug.hwui.profile";

抽象类GlRenderer中重写函数dumpGfxInfo:

Code@Override
void dumpGfxInfo(PrintWriter pw) {
if (mProfileEnabled) {
pw.printf("\n\tDraw\tProcess\tExecute\n"); mProfileLock.lock();
try {
for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
if (mProfileData[i] < 0) {
break;
}
pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
mProfileData[i + 2]);④
mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
}
mProfileCurrentFrame = mProfileData.length;
} finally {
mProfileLock.unlock();
}
}
}

在“Profile GPU rendering”中勾选“In adb shell dumpsys gfxinfo”或者“adb shell setprop debug.hwui.profile true”之后,运行adb shell dumpsys gfxinfo package_name输出(ms):

com.android.deskclock/com.android.deskclock.DeskClock/android.view.ViewRootImpl@41770410
Draw    Process    Execute
31.73    10.66    11.73
29.34    6.74    8.19
74.70    31.81    26.01
5.96    4.51    5.67
59.04    3.42    6.61
19.34    5.00    7.14
33.70    18.27    138.73
2.97    14.80    14.26
4.28    3.27    15.31
2.25    3.83    4.03
3.18    1.87    3.89
3.09    1.92    5.38
14.55    3.01    16.52
2.66    3.74    3.83
5.69    1.97    72.48
4.27    1.93    12.90
0.73    4.84    4.49
36.66    6.62    7.53
12.80    3.22    17.49
6.31    4.86    4.73
4.55    3.42    4.46
1.95    1.91    13.81
15.05    23.02    5.10
21.86    1.86    4.61
4.87    8.40    5.24
4.49    6.78    4.21
7.15    30.36    3.70
12.38    24.17    4.89
2.77    5.27    22.00
3.26    5.66    4.02
1.74    3.98    22.07
2.23    3.30    4.34
0.86    2.89    13.56
1.00    13.85    13.94
0.84    1.34    13.86
3.45    2.42    3.81
0.69    1.48    14.75
0.64    1.32    13.88
0.46    1.41    14.48

其中:

Draw--Build display lists

Process—Process(Draw) display lists

Excute--Swap GL buffers

关于此数据分析详见Android Performance Case StudyAndroid application (performance and more) analysis tools - Tutorial。

重写了函数draw:

Code@Override
void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
Rect dirty) {
if (canDraw()) {
if (!hasDirtyRegions()) {
dirty = null;
}
attachInfo.mIgnoreDirtyState = true;
attachInfo.mDrawingTime = SystemClock.uptimeMillis(); view.mPrivateFlags |= View.PFLAG_DRAWN; final int surfaceState = checkCurrent();
if (surfaceState != SURFACE_STATE_ERROR) {
HardwareCanvas canvas = mCanvas;
attachInfo.mHardwareCanvas = canvas; if (mProfileEnabled) {
mProfileLock.lock();
} dirty = beginFrame(canvas, dirty, surfaceState); DisplayList displayList = buildDisplayList(view, canvas); int saveCount = 0;
int status = DisplayList.STATUS_DONE; try {
status = prepareFrame(dirty);
saveCount = canvas.save();
callbacks.onHardwarePreDraw(canvas); if (displayList != null) {
status |= drawDisplayList(attachInfo, canvas, displayList, status);
} else {
// Shouldn't reach here
view.draw(canvas);
}
} catch (Exception e) {
Log.e(LOG_TAG, "An error has occurred while drawing:", e);
} finally {
callbacks.onHardwarePostDraw(canvas);
canvas.restoreToCount(saveCount);
view.mRecreateDisplayList = false; mFrameCount++; debugDirtyRegions(dirty, canvas);
drawProfileData(attachInfo);
} onPostDraw(); swapBuffers(status); if (mProfileEnabled) {
mProfileLock.unlock();
} attachInfo.mIgnoreDirtyState = false;
}
}
}

1.buildDisplayList

Codeprivate long startBuildDisplayListProfiling() {
if (mProfileEnabled) {
mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT;
if (mProfileCurrentFrame >= mProfileData.length) {
mProfileCurrentFrame = 0;
} return System.nanoTime();
}
return 0;
} private void endBuildDisplayListProfiling(long getDisplayListStartTime) {
if (mProfileEnabled) {
long now = System.nanoTime();
float total = (now - getDisplayListStartTime) * 0.000001f;
//noinspection PointlessArithmeticExpression
mProfileData[mProfileCurrentFrame] = total;①
}
} private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {
view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
== View.PFLAG_INVALIDATED;
view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; long buildDisplayListStartTime = startBuildDisplayListProfiling();
canvas.clearLayerUpdates(); Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
DisplayList displayList = view.getDisplayList();
Trace.traceEnd(Trace.TRACE_TAG_VIEW); endBuildDisplayListProfiling(buildDisplayListStartTime); return displayList;
}

view.getDisplayList()来自View.java

/**
* <p>Returns a display list that can be used to draw this view again
* without executing its draw method.</p>
*
* @return A DisplayList ready to replay, or null if caching is not enabled.
*
* @hide
*/
public DisplayList getDisplayList() {
mDisplayList = getDisplayList(mDisplayList, false);
return mDisplayList;
}
/**
* Returns a DisplayList. If the incoming displayList is null, one will be created.
* Otherwise, the same display list will be returned (after having been rendered into
* along the way, depending on the invalidation state of the view).
*
* @param displayList The previous version of this displayList, could be null.
* @param isLayer Whether the requester of the display list is a layer. If so,
* the view will avoid creating a layer inside the resulting display list.
* @return A new or reused DisplayList object.
*/
private DisplayList getDisplayList(DisplayList displayList, boolean isLayer) {
if (!canHaveDisplayList()) {
return null;
} if (((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 ||
displayList == null || !displayList.isValid() ||
(!isLayer && mRecreateDisplayList))) {
// Don't need to recreate the display list, just need to tell our
// children to restore/recreate theirs
if (displayList != null && displayList.isValid() &&
!isLayer && !mRecreateDisplayList) {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchGetDisplayList(); return displayList;
} if (!isLayer) {
// If we got here, we're recreating it. Mark it as such to ensure that
// we copy in child display lists into ours in drawChild()
mRecreateDisplayList = true;
}
if (displayList == null) {
final String name = getClass().getSimpleName();
displayList = mAttachInfo.mHardwareRenderer.createDisplayList(name);
// If we're creating a new display list, make sure our parent gets invalidated
// since they will need to recreate their display list to account for this
// new child display list.
invalidateParentCaches();
} boolean caching = false;
int width = mRight - mLeft;
int height = mBottom - mTop;
int layerType = getLayerType(); final HardwareCanvas canvas = displayList.start(width, height); try {
if (!isLayer && layerType != LAYER_TYPE_NONE) {
if (layerType == LAYER_TYPE_HARDWARE) {
final HardwareLayer layer = getHardwareLayer();
if (layer != null && layer.isValid()) {
canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
} else {
canvas.saveLayer(0, 0, mRight - mLeft, mBottom - mTop, mLayerPaint,
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
Canvas.CLIP_TO_LAYER_SAVE_FLAG);
}
caching = true;
} else {
buildDrawingCache(true);
Bitmap cache = getDrawingCache(true);
if (cache != null) {
canvas.drawBitmap(cache, 0, 0, mLayerPaint);
caching = true;
}
}
} else { computeScroll(); canvas.translate(-mScrollX, -mScrollY);
if (!isLayer) {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
} // Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
} else {
draw(canvas);
}
}
} finally {
displayList.end();
displayList.setCaching(caching);
if (isLayer) {
displayList.setLeftTopRightBottom(0, 0, width, height);
} else {
setDisplayListProperties(displayList);
}
}
} else if (!isLayer) {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
} return displayList;
}

2.drawDisplayList

Codeprivate int drawDisplayList(View.AttachInfo attachInfo, HardwareCanvas canvas,
DisplayList displayList, int status) { long drawDisplayListStartTime = 0;
if (mProfileEnabled) {
drawDisplayListStartTime = System.nanoTime();
} Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList");
try {
status |= canvas.drawDisplayList(displayList, mRedrawClip,
DisplayList.FLAG_CLIP_CHILDREN);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
} if (mProfileEnabled) {
long now = System.nanoTime();
float total = (now - drawDisplayListStartTime) * 0.000001f;
mProfileData[mProfileCurrentFrame + 1] = total;②
} handleFunctorStatus(attachInfo, status);
return status;
}

canvas.drawDisplayList来自HardwareCanvas.java,这是一个抽象接口:

Code/**
* Draws the specified display list onto this canvas. The display list can only
* be drawn if {@link android.view.DisplayList#isValid()} returns true.
*
* @param displayList The display list to replay.
*/
public void drawDisplayList(DisplayList displayList) {
drawDisplayList(displayList, null, DisplayList.FLAG_CLIP_CHILDREN);
} /**
* Draws the specified display list onto this canvas.
*
* @param displayList The display list to replay.
* @param dirty The dirty region to redraw in the next pass, matters only
* if this method returns {@link DisplayList#STATUS_DRAW}, can be null.
* @param flags Optional flags about drawing, see {@link DisplayList} for
* the possible flags.
*
* @return One of {@link DisplayList#STATUS_DONE}, {@link DisplayList#STATUS_DRAW}, or
* {@link DisplayList#STATUS_INVOKE}, or'd with {@link DisplayList#STATUS_DREW}
* if anything was drawn.
*
* @hide
*/
public abstract int drawDisplayList(DisplayList displayList, Rect dirty, int flags);

3.swapBuffers

Codeprivate void swapBuffers(int status) {
if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) {
long eglSwapBuffersStartTime = 0;
if (mProfileEnabled) {
eglSwapBuffersStartTime = System.nanoTime();
} sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); if (mProfileEnabled) {
long now = System.nanoTime();
float total = (now - eglSwapBuffersStartTime) * 0.000001f;
mProfileData[mProfileCurrentFrame + 2] = total;③
} checkEglErrors();
}
}

EGL10.java中的eglSwapBuffers->EGLImpl.java->com_google_android_gles_jni_EGLImpl.cpp

com_google_android_gles_jni_EGLImpl.cpp中:

Codestatic jboolean jni_eglSwapBuffers(JNIEnv *_env, jobject _this, jobject display, jobject surface) {
if (display == NULL || surface == NULL) {
jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
EGLSurface sur = getSurface(_env, surface);
return eglSwapBuffers(dpy, sur);
}

Profile GPU rendering的更多相关文章

  1. Android性能分析工具Profile GPU rendering详细介绍

    如何在一个应用中追踪和定位性能问题,甚至在没有它的源代码的情况下?? “Profile GPU rendering”(GPU渲染分析),一款Android4.1所引入的工具.你可以在“设置”应用的“开 ...

  2. Android 性能优化(21)*性能工具之「GPU呈现模式分析」Profiling GPU Rendering Walkthrough:分析View显示是否超标

    Profiling GPU Rendering Walkthrough 1.In this document Prerequisites Profile GPU Rendering $adb shel ...

  3. 【腾讯优测干货分享】安卓专项测试之GPU测试探索

    本文来自于Dev Club 开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57c7ffdc0569a1191bce8a63 作者:章未哲——腾讯SNG质 ...

  4. Android开发者选项——Gpu呈现模式分析

    对于Android用户来说,无论你用的什么品牌的手机,在开发者选项中都能发现“玄学曲线”的开关,之所以称其为玄学曲线,还是因为它被很多网友用于测试一个说不清道不明的东西——流畅度.到底多流畅才叫流畅, ...

  5. Android 性能优化(3)性能工具之「调试 GPU 过度绘制」Debug GPU Overdraw Walkthrough-查看哪些view过度绘制了

    Debug GPU Overdraw Walkthrough 1.In this document Prerequisites Visualizing Overdraw You should also ...

  6. Android 性能测试

    写在前面: 测试一道,博主接触的也是皮毛而已,没有接触过rom的测试,下边所说的都是博主接触过的app的性能测试.我只谈方法,少提概念.各位大神不喜勿喷. 概述 除启动时间外,我们应该做的测试,可能需 ...

  7. Android性能优化典范

    来源:http://hukai.me/android-performance-patterns/#jtss-tsina 0)Render Performance 大多数用户感知到的卡顿等性能问题的最主 ...

  8. Android性能优化典范第一季

    2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关 ...

  9. 【Bugly干货】Android性能优化典范之多线程篇

    本文涉及的内容有:多线程并发的性能问题,介绍了 AsyncTask,HandlerThread,IntentService 与 ThreadPool 分别适合的使用场景以及各自的使用注意事项,这是一篇 ...

随机推荐

  1. 什么是JPA

    起源 JPA由EJB 3.0软件专家组开发,作为JSR-220实现的一部分.但它不囿于EJB 3.0,你可以在Web应用.甚至桌面应用中使用.JPA的宗旨是为POJO提供持久化标准规范,由此可见,经过 ...

  2. one way WebService

    WSDL支持4种消息交换方式:   1)单向(One-way):服务端接收消息:   2)请求响应(Request-response):服务端点接收请求消息,然后发送响应消息:   3)要求应答(So ...

  3. .net中使用JQuery Ajax判断用户名是否存在的方法

    //第一步:新建一个(*.aspx|*.html)Index.aspx页面 添加jquery 1 <html xmlns="http://www.w3.org/1999/xhtml&q ...

  4. Sqoop import加载HBase案例详解

    简单写一下如何将订单表sqoop到hbase表中的步骤. 下表: 1.通过hbase shell 打开hbase. 2.创建一个hbase表 create 'so','o' 3.将so表的数据导入到h ...

  5. centos7/RHEL7安装LibreOffice

    1.下载 wget http://download.documentfoundation.org/libreoffice/testing/4.4.0/rpm/x86_64/LibreOfficeDev ...

  6. Oracle 面试宝典 - General Questions

    转自 http://www.orafaq.com/wiki/Interview_Questions Tell us about yourself/ your background. What are ...

  7. jQuery常用技巧大放送

    1.关于页面元素的引用 通过jquery的$()引用元素包括通过id.class.元素名以及元素的层级关系及dom或者xpath条件等方法,且返回的对象为jquery对象(集合对象),不能直接调用do ...

  8. ECSHOP在商品详细页面上获取该商品的顶级分类id和名称

    在 goods.php 文件, 找到 $smarty->assign('goods', $goods); 在它上面增加下面代码: 方法一: $cat_arr = get_parent_cats( ...

  9. apache 服务器端口被IIS服务器占用

    今天遇到一个问题 同事机子上安装了wamp运行环境,所有服务也打开了,把dede系统放到了www目录下面,但是打开localhost网址,一直是跳转到一个IIS的web服务器主页 想不到是什么问题,又 ...

  10. discuz二次开发技巧

    discuz二次开发技巧 二次开发大多时候知识设置和处理,如果能够获知模板文件获得的变量数组将大大提高我们的开发效率 获取页面已经定义的变量 <--{eval printf_r(get_defi ...