自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. 一款js、css压缩工具yuicompressor

    //压缩JS java -jar yuicompressor-.jar --type js --charset utf- -v src.js > packed.js //压缩CSS java - ...

  2. Chrome浏览器允许跨域请求配置

    最近有个做数据标注的任务,但是标注平台是别人公司的,他们又不愿意对平台进行升级改造: 其实要改的地方也很简单,就是对页面做一些处理,做一些脚本控制. 没办法,做了个 iframe 给她嵌入到我们自己的 ...

  3. SQLite 入门教程(四)增删改查,有讲究

    增删改查操作,其中增删改操作被称为数据操作语言 DML,相对来说简单一点. 查操作相对来说复杂一点,涉及到很多子句,所以这篇先讲增删改操作,以例子为主,后面再讲查操作. 一.插入数据 INSERT I ...

  4. 关于ios8斯坦福公开课第二课

    在这个课程中,我们遇到了这样的代码 @IBAction func oprate(sender: UIButton) { let opration = sender.currentTitle! if u ...

  5. 十一、C# 泛型

    为了促进代码重用,尤其是算法的重用,C#支持一个名为泛型的特性. 泛型与模块类相似. 泛型使算法和模式只需要实现一交.而不必为每个类型都实现一次.在实例化的时候,传入相应的数据类型便可. 注:可空值类 ...

  6. 运用BeanUtils构建通用的查询 更新方法(个人拙作,不喜勿喷)

    ------------------------------------更新方法----------------------------------- public void update(Strin ...

  7. Zend Server安装后首次运行就出现Internal Server Error的解决(转)

    新近学习php,结果装了Zend Server上来就报错,网上找到了解决方法,照着做果然可行,转之. 刚才安装了Zend Server,安装后首次运行就爆出了一个Internal Server Err ...

  8. Java学习----对象间的继承

    继承:子类可以使用父类非私有的成员变量和方法 public class Father { public String name; public String bloodType; private in ...

  9. 【JQuery学习历程】2.JQuery选择器

    基本选择器 选择器 描述 返回 示例 #id 根据给定的id匹配元素 单个元素 $("#myId") .class 根据给定的class类匹配元素 集合元素 $(".my ...

  10. phpcms栏目调用

    {loop subcat(0,0,0,$siteid) $r} {php $num++} <h3><a href="{$r[url]}">{$r[catna ...