Add a new type of profiling graph: lines
Change-Id: I0e67d7227016fe37011e17d04220a200a6a5fba0
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 86d04b6..7ecdcbe 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -43,6 +43,7 @@
import java.io.File;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;
import static javax.microedition.khronos.egl.EGL10.*;
@@ -88,10 +89,12 @@
*
* Possible values:
* "true", to enable profiling
- * "visual", to enable profiling and visualize the results on screen
+ * "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
+ * @see #PROFILE_PROPERTY_VISUALIZE_BARS
+ * @see #PROFILE_PROPERTY_VISUALIZE_LINES
*
* @hide
*/
@@ -99,11 +102,19 @@
/**
* Value for {@link #PROFILE_PROPERTY}. When the property is set to this
- * value, profiling data will be visualized on screen.
+ * value, profiling data will be visualized on screen as a bar chart.
*
* @hide
*/
- public static final String PROFILE_PROPERTY_VISUALIZE = "visual";
+ public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars";
+
+ /**
+ * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
+ * value, profiling data will be visualized on screen as a line chart.
+ *
+ * @hide
+ */
+ public static final String PROFILE_PROPERTY_VISUALIZE_LINES = "visual_lines";
/**
* System property used to specify the number of frames to be used
@@ -619,6 +630,100 @@
mRequested = requested;
}
+ /**
+ * Describes a series of frames that should be drawn on screen as a graph.
+ * Each frame is composed of 1 or more elements.
+ */
+ abstract class GraphDataProvider {
+ /**
+ * Draws the graph as bars. Frame elements are stacked on top of
+ * each other.
+ */
+ public static final int GRAPH_TYPE_BARS = 0;
+ /**
+ * Draws the graph as lines. The number of series drawn corresponds
+ * to the number of elements.
+ */
+ public static final int GRAPH_TYPE_LINES = 1;
+
+ /**
+ * Returns the type of graph to render.
+ *
+ * @return {@link #GRAPH_TYPE_BARS} or {@link #GRAPH_TYPE_LINES}
+ */
+ abstract int getGraphType();
+
+ /**
+ * This method is invoked before the graph is drawn. This method
+ * can be used to compute sizes, etc.
+ *
+ * @param metrics The display metrics
+ */
+ abstract void prepare(DisplayMetrics metrics);
+
+ /**
+ * @return The size in pixels of a vertical unit.
+ */
+ abstract int getVerticalUnitSize();
+
+ /**
+ * @return The size in pixels of a horizontal unit.
+ */
+ abstract int getHorizontalUnitSize();
+
+ /**
+ * @return The size in pixels of the margin between horizontal units.
+ */
+ abstract int getHorizontaUnitMargin();
+
+ /**
+ * An optional threshold value.
+ *
+ * @return A value >= 0 to draw the threshold, a negative value
+ * to ignore it.
+ */
+ abstract float getThreshold();
+
+ /**
+ * The data to draw in the graph. The number of elements in the
+ * array must be at least {@link #getFrameCount()} * {@link #getElementCount()}.
+ * If a value is negative the following values will be ignored.
+ */
+ abstract float[] getData();
+
+ /**
+ * Returns the number of frames to render in the graph.
+ */
+ abstract int getFrameCount();
+
+ /**
+ * Returns the number of elements in each frame. This directly affects
+ * the number of series drawn in the graph.
+ */
+ abstract int getElementCount();
+
+ /**
+ * Returns the current frame, if any. If the returned value is negative
+ * the current frame is ignored.
+ */
+ abstract int getCurrentFrame();
+
+ /**
+ * Prepares the paint to draw the specified element (or series.)
+ */
+ abstract void setupGraphPaint(Paint paint, int elementIndex);
+
+ /**
+ * Prepares the paint to draw the threshold.
+ */
+ abstract void setupThresholdPaint(Paint paint);
+
+ /**
+ * Prepares the paint to draw the current frame indicator.
+ */
+ abstract void setupCurrentFramePaint(Paint paint);
+ }
+
@SuppressWarnings({"deprecation"})
static abstract class GlRenderer extends HardwareRenderer {
static final int SURFACE_STATE_ERROR = 0;
@@ -627,6 +732,14 @@
static final int FUNCTOR_PROCESS_DELAY = 4;
+ private static final int PROFILE_DRAW_MARGIN = 0;
+ private static final int PROFILE_DRAW_WIDTH = 3;
+ private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
+ private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
+ private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
+ private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
+ private static final int PROFILE_DRAW_DP_PER_MS = 7;
+
static EGL10 sEgl;
static EGLDisplay sEglDisplay;
static EGLConfig sEglConfig;
@@ -660,12 +773,13 @@
boolean mUpdateDirtyRegions;
boolean mProfileEnabled;
- boolean mProfileVisualizerEnabled;
+ int mProfileVisualizerType = -1;
float[] mProfileData;
ReentrantLock mProfileLock;
int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
- float[][] mProfileRects;
+ GraphDataProvider mDebugDataProvider;
+ float[][] mProfileShapes;
Paint mProfilePaint;
boolean mDebugDirtyRegions;
@@ -688,20 +802,32 @@
loadSystemProperties(null);
}
+ private static final String[] VISUALIZERS = {
+ PROFILE_PROPERTY_VISUALIZE_BARS,
+ PROFILE_PROPERTY_VISUALIZE_LINES
+ };
+
@Override
boolean loadSystemProperties(Surface surface) {
boolean value;
boolean changed = false;
String profiling = SystemProperties.get(PROFILE_PROPERTY);
- value = PROFILE_PROPERTY_VISUALIZE.equalsIgnoreCase(profiling);
+ int graphType = Arrays.binarySearch(VISUALIZERS, profiling);
+ value = graphType >= 0;
- if (value != mProfileVisualizerEnabled) {
+ if (graphType != mProfileVisualizerType) {
changed = true;
- mProfileVisualizerEnabled = value;
+ mProfileVisualizerType = graphType;
- mProfileRects = null;
+ mProfileShapes = null;
mProfilePaint = null;
+
+ if (value) {
+ mDebugDataProvider = new DrawPerformanceDataProvider(graphType);
+ } else {
+ mDebugDataProvider = null;
+ }
}
// If on-screen profiling is not enabled, we need to check whether
@@ -728,6 +854,7 @@
} else {
mProfileData = null;
mProfileLock = null;
+ mProfileVisualizerType = -1;
}
mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
@@ -1283,7 +1410,7 @@
}
}
- if (mProfileEnabled && mProfileVisualizerEnabled) dirty = null;
+ if (mDebugDataProvider != null) dirty = null;
return dirty;
}
@@ -1459,20 +1586,102 @@
}
return SURFACE_STATE_SUCCESS;
}
+
+ private static int dpToPx(int dp, float density) {
+ return (int) (dp * density + 0.5f);
+ }
+
+ class DrawPerformanceDataProvider extends GraphDataProvider {
+ private final int mGraphType;
+
+ private int mVerticalUnit;
+ private int mHorizontalUnit;
+ private int mHorizontalMargin;
+ private int mThresholdStroke;
+
+ DrawPerformanceDataProvider(int graphType) {
+ mGraphType = graphType;
+ }
+
+ @Override
+ void prepare(DisplayMetrics metrics) {
+ final float density = metrics.density;
+
+ mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
+ mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
+ mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density);
+ mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
+ }
+
+ @Override
+ int getGraphType() {
+ return mGraphType;
+ }
+
+ @Override
+ int getVerticalUnitSize() {
+ return mVerticalUnit;
+ }
+
+ @Override
+ int getHorizontalUnitSize() {
+ return mHorizontalUnit;
+ }
+
+ @Override
+ int getHorizontaUnitMargin() {
+ return mHorizontalMargin;
+ }
+
+ @Override
+ float[] getData() {
+ return mProfileData;
+ }
+
+ @Override
+ float getThreshold() {
+ return 16;
+ }
+
+ @Override
+ int getFrameCount() {
+ return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
+ }
+
+ @Override
+ int getElementCount() {
+ return PROFILE_FRAME_DATA_COUNT;
+ }
+
+ @Override
+ int getCurrentFrame() {
+ return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
+ }
+
+ @Override
+ void setupGraphPaint(Paint paint, int elementIndex) {
+ paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
+ if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
+ }
+
+ @Override
+ void setupThresholdPaint(Paint paint) {
+ paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
+ paint.setStrokeWidth(mThresholdStroke);
+ }
+
+ @Override
+ void setupCurrentFramePaint(Paint paint) {
+ paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
+ if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
+ }
+ }
}
/**
* Hardware renderer using OpenGL ES 2.0.
*/
static class Gl20Renderer extends GlRenderer {
- private static final int PROFILE_DRAW_MARGIN = 0;
- private static final int PROFILE_DRAW_WIDTH = 3;
- private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
- private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
- private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
- private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
- private static final int PROFILE_DRAW_DP_PER_MS = 7;
-
private GLES20Canvas mGlCanvas;
private DisplayMetrics mDisplayMetrics;
@@ -1581,101 +1790,149 @@
@Override
void drawProfileData(View.AttachInfo attachInfo) {
- if (mProfileEnabled && mProfileVisualizerEnabled) {
- initProfileDrawData(attachInfo);
+ if (mDebugDataProvider != null) {
+ final GraphDataProvider provider = mDebugDataProvider;
+ initProfileDrawData(attachInfo, provider);
- final int pxPerMs = (int) (PROFILE_DRAW_DP_PER_MS * mDisplayMetrics.density + 0.5f);
- final int margin = (int) (PROFILE_DRAW_MARGIN * mDisplayMetrics.density + 0.5f);
- final int width = (int) (PROFILE_DRAW_WIDTH * mDisplayMetrics.density + 0.5f);
+ final int height = provider.getVerticalUnitSize();
+ final int margin = provider.getHorizontaUnitMargin();
+ final int width = provider.getHorizontalUnitSize();
int x = 0;
int count = 0;
int current = 0;
- for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
- if (mProfileData[i] < 0.0f) break;
+ final float[] data = provider.getData();
+ final int elementCount = provider.getElementCount();
+ final int graphType = provider.getGraphType();
+
+ int totalCount = provider.getFrameCount() * elementCount;
+ if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) {
+ totalCount -= elementCount;
+ }
+
+ for (int i = 0; i < totalCount; i += elementCount) {
+ if (data[i] < 0.0f) break;
int index = count * 4;
- if (i == mProfileCurrentFrame) current = index;
+ if (i == provider.getCurrentFrame() * elementCount) current = index;
x += margin;
int x2 = x + width;
int y2 = mHeight;
- int y1 = (int) (y2 - mProfileData[i] * pxPerMs);
+ int y1 = (int) (y2 - data[i] * height);
- float[] r = mProfileRects[0];
- r[index] = x;
- r[index + 1] = y1;
- r[index + 2] = x2;
- r[index + 3] = y2;
+ switch (graphType) {
+ case GraphDataProvider.GRAPH_TYPE_BARS: {
+ for (int j = 0; j < elementCount; j++) {
+ //noinspection MismatchedReadAndWriteOfArray
+ final float[] r = mProfileShapes[j];
+ r[index] = x;
+ r[index + 1] = y1;
+ r[index + 2] = x2;
+ r[index + 3] = y2;
- y2 = y1;
- y1 = (int) (y2 - mProfileData[i + 1] * pxPerMs);
+ y2 = y1;
+ if (j < elementCount - 1) {
+ y1 = (int) (y2 - data[i + j + 1] * height);
+ }
+ }
+ } break;
+ case GraphDataProvider.GRAPH_TYPE_LINES: {
+ for (int j = 0; j < elementCount; j++) {
+ //noinspection MismatchedReadAndWriteOfArray
+ final float[] r = mProfileShapes[j];
+ r[index] = (x + x2) * 0.5f;
+ r[index + 1] = index == 0 ? y1 : r[index - 1];
+ r[index + 2] = r[index] + width;
+ r[index + 3] = y1;
- r = mProfileRects[1];
- r[index] = x;
- r[index + 1] = y1;
- r[index + 2] = x2;
- r[index + 3] = y2;
+ y2 = y1;
+ if (j < elementCount - 1) {
+ y1 = (int) (y2 - data[i + j + 1] * height);
+ }
+ }
+ } break;
+ }
- y2 = y1;
- y1 = (int) (y2 - mProfileData[i + 2] * pxPerMs);
-
- r = mProfileRects[2];
- r[index] = x;
- r[index + 1] = y1;
- r[index + 2] = x2;
- r[index + 3] = y2;
x += width;
-
count++;
}
+
x += margin;
- drawGraph(count);
- drawCurrentFrame(current);
- drawThreshold(x, pxPerMs);
+ drawGraph(graphType, count);
+ drawCurrentFrame(graphType, current);
+ drawThreshold(x, height);
}
}
- private void drawGraph(int count) {
- for (int i = 0; i < mProfileRects.length; i++) {
- mProfilePaint.setColor(PROFILE_DRAW_COLORS[i]);
- mGlCanvas.drawRects(mProfileRects[i], count, mProfilePaint);
- }
- }
-
- private void drawCurrentFrame(int index) {
- mProfilePaint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
- mGlCanvas.drawRect(mProfileRects[2][index], mProfileRects[2][index + 1],
- mProfileRects[2][index + 2], mProfileRects[0][index + 3], mProfilePaint);
- }
-
- private void drawThreshold(int x, int pxPerMs) {
- mProfilePaint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
- mProfilePaint.setStrokeWidth((int)
- (PROFILE_DRAW_THRESHOLD_STROKE_WIDTH * mDisplayMetrics.density + 0.5f));
- int y = mHeight - 16 * pxPerMs;
- mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
- mProfilePaint.setStrokeWidth(1.0f);
- }
-
- private void initProfileDrawData(View.AttachInfo attachInfo) {
- if (mProfileRects == null) {
- mProfileRects = new float[PROFILE_FRAME_DATA_COUNT][];
- for (int i = 0; i < mProfileRects.length; i++) {
- int count = mProfileData.length / PROFILE_FRAME_DATA_COUNT;
- mProfileRects[i] = new float[count * 4];
+ private void drawGraph(int graphType, int count) {
+ for (int i = 0; i < mProfileShapes.length; i++) {
+ mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
+ switch (graphType) {
+ case GraphDataProvider.GRAPH_TYPE_BARS:
+ mGlCanvas.drawRects(mProfileShapes[i], count, mProfilePaint);
+ break;
+ case GraphDataProvider.GRAPH_TYPE_LINES:
+ mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);
+ break;
}
+ }
+ }
+
+ private void drawCurrentFrame(int graphType, int index) {
+ if (index >= 0) {
+ mDebugDataProvider.setupCurrentFramePaint(mProfilePaint);
+ switch (graphType) {
+ case GraphDataProvider.GRAPH_TYPE_BARS:
+ mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1],
+ mProfileShapes[2][index + 2], mProfileShapes[0][index + 3],
+ mProfilePaint);
+ break;
+ case GraphDataProvider.GRAPH_TYPE_LINES:
+ mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1],
+ mProfileShapes[2][index], mHeight, mProfilePaint);
+ break;
+ }
+ }
+ }
+
+ private void drawThreshold(int x, int height) {
+ float threshold = mDebugDataProvider.getThreshold();
+ if (threshold > 0.0f) {
+ mDebugDataProvider.setupThresholdPaint(mProfilePaint);
+ int y = (int) (mHeight - threshold * height);
+ mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
+ }
+ }
+
+ private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) {
+ if (mProfileShapes == null) {
+ final int elementCount = provider.getElementCount();
+ final int frameCount = provider.getFrameCount();
+
+ mProfileShapes = new float[elementCount][];
+ for (int i = 0; i < elementCount; i++) {
+ mProfileShapes[i] = new float[frameCount * 4];
+ }
+
mProfilePaint = new Paint();
}
+ mProfilePaint.reset();
+ if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) {
+ mProfilePaint.setAntiAlias(true);
+ }
+
if (mDisplayMetrics == null) {
mDisplayMetrics = new DisplayMetrics();
}
+
attachInfo.mDisplay.getMetrics(mDisplayMetrics);
+ provider.prepare(mDisplayMetrics);
}
@Override