自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. 处理ASP.NET 请求(IIS)(2)

    ASP.NET与IIS是紧密联系的,由于IIS6.0与IIS7.0的工作方式的不同,导致ASP.NET的工作原理也发生了相应的变化. IIS6(IIS7的经典模式)与IIS7的集成模式的不同 IIS6 ...

  2. (tomcat访问不了的两种解决方法)Bad Request(Invalid Hostname)

    显示这个页面的时候一般有几中解决方法: 第一种就是如下图所示的方法: 具体步骤是: 1.也就是左下角win的“运行”中输入cmd进入doc窗口中 2.输入代码:netstat -ano 3.找到占用8 ...

  3. c读mysql产生乱码问题

    在编写接口API时,发现中文字utf8输入的在linux下采用c读取显示为”??”问号,这是由于编码造成的. 很简单的两个地方做修改就搞定. 1.先找到mysql的my.cnf配置文件/etc/my. ...

  4. ViewPager + Fragment 实现类微信界面

    在如今的互联网时代,微信已是一个超级App.这篇通过ViewPager + Fragment实现一个类似于微信的界面,之前有用FragmentTabHost实现过类似界面,ViewPager的实现方式 ...

  5. tomcat 正常启动,无法访问。且项目启动无问题。。。的解决办法。。

    Eclipes解决方法: 1.右击项目,选择propreties选项 2.在弹出的首选项窗口的左侧选择“Web Project Settings” 3.修改context root:输入框,修改成自己 ...

  6. power desinger 学习笔记<八>

    转-PowerDesigner 把Comment复制到name中和把name复制到Comment 在使用PowerDesigner对数据库进行概念模型和物理模型设计时,一般在NAME或Comment中 ...

  7. Notification和KVO有什么不同

    Notification是推送通知,我们可以建立一个通知中心,存放创建多个通知,在不同的地方在需要的时候push调用和KVO不同的是,KVO是键值观察,只能观察一个值,这就是区别

  8. Ubuntu14.04安装AMD显卡驱动双屏显示器完全解决方案

    网上有很多方法,但是针对AMD显卡的方案不多,所以笔者今天想写一篇关于AMD显卡的教程. 首先,进入这个网址下载一些东东:http://support.amd.com/zh-cn/download/d ...

  9. Java多线程:常用的实现多线程的两种方式

    之所以说是常用的,是因为通过还可以通过java.util.concurrent包中的线程池来实现多线程.关于线程池的内容,我们以后会详细介绍;现在,先对的Thread和Runnable进行了解.本章内 ...

  10. Git配置安装使用教程操作github上传克隆数据

    Git是何方神圣? Git是用C语言开发的分布版本控制系统.版本控制系统可以保留一个文件集合的历史记录,并能回滚文件集合到另一个状态(历史记录状态).另一个状态可以是不同的文件,也可以是不同的文件内容 ...