Merge "Add visual profiling feature"

This commit is contained in:
Romain Guy
2013-01-04 19:23:42 -08:00
committed by Android (Google) Code Review
7 changed files with 415 additions and 105 deletions

View File

@@ -1013,6 +1013,17 @@ class GLES20Canvas extends HardwareCanvas {
private static native void nDrawPath(int renderer, int path, int paint);
private static native void nDrawRects(int renderer, int region, int paint);
void drawRects(float[] rects, int count, Paint paint) {
int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
try {
nDrawRects(mRenderer, rects, count, paint.mNativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
private static native void nDrawRects(int renderer, float[] rects, int count, int paint);
@Override
public void drawPicture(Picture picture) {
if (picture.createdFromStream) {

View File

@@ -88,11 +88,26 @@ public abstract class HardwareRenderer {
* Possible values:
* "true", to enable profiling
* "false", to disable profiling
*
*
* @hide
*/
public static final String PROFILE_PROPERTY = "debug.hwui.profile";
/**
* System property used to enable or disable hardware rendering profiling
* visualization. The default value of this property is assumed to be false.
*
* This property is only taken into account when {@link #PROFILE_PROPERTY} is
* turned on.
*
* Possible values:
* "true", to enable on screen profiling
* "false", to disable on screen profiling
*
* @hide
*/
public static final String PROFILE_VISUALIZER_PROPERTY = "debug.hwui.profile_visualizer";
/**
* System property used to specify the number of frames to be used
* when doing hardware rendering profiling.
@@ -342,7 +357,7 @@ public abstract class HardwareRenderer {
* Notifies EGL that the frame is about to be rendered.
* @param size
*/
private static void beginFrame(int[] size) {
static void beginFrame(int[] size) {
nBeginFrame(size);
}
@@ -648,10 +663,14 @@ public abstract class HardwareRenderer {
boolean mUpdateDirtyRegions;
boolean mProfileEnabled;
boolean mProfileVisualizerEnabled;
float[] mProfileData;
ReentrantLock mProfileLock;
int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
float[][] mProfileRects;
Paint mProfilePaint;
boolean mDebugDirtyRegions;
boolean mShowOverdraw;
@@ -698,6 +717,18 @@ public abstract class HardwareRenderer {
mProfileData = null;
mProfileLock = null;
}
mProfileRects = null;
mProfilePaint = null;
}
value = SystemProperties.getBoolean(PROFILE_VISUALIZER_PROPERTY, false);
if (value != mProfileVisualizerEnabled) {
changed = true;
mProfileVisualizerEnabled = value;
mProfileRects = null;
mProfilePaint = null;
}
value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false);
@@ -1175,23 +1206,7 @@ public abstract class HardwareRenderer {
mProfileLock.lock();
}
// We had to change the current surface and/or context, redraw everything
if (surfaceState == SURFACE_STATE_UPDATED) {
dirty = null;
beginFrame(null);
} else {
int[] size = mSurfaceSize;
beginFrame(size);
if (size[1] != mHeight || size[0] != mWidth) {
mWidth = size[0];
mHeight = size[1];
canvas.setViewport(mWidth, mHeight);
dirty = null;
}
}
dirty = beginFrame(canvas, dirty, surfaceState);
int saveCount = 0;
int status = DisplayList.STATUS_DONE;
@@ -1201,63 +1216,19 @@ public abstract class HardwareRenderer {
== View.PFLAG_INVALIDATED;
view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
long getDisplayListStartTime = 0;
if (mProfileEnabled) {
mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT;
if (mProfileCurrentFrame >= mProfileData.length) {
mProfileCurrentFrame = 0;
}
getDisplayListStartTime = System.nanoTime();
}
long buildDisplayListStartTime = startBuildDisplayListProfiling();
canvas.clearLayerUpdates();
DisplayList displayList;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
try {
displayList = view.getDisplayList();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
DisplayList displayList = buildDisplayList(view);
status = prepareFrame(dirty);
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame");
try {
status = onPreDraw(dirty);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
saveCount = canvas.save();
callbacks.onHardwarePreDraw(canvas);
if (mProfileEnabled) {
long now = System.nanoTime();
float total = (now - getDisplayListStartTime) * 0.000001f;
//noinspection PointlessArithmeticExpression
mProfileData[mProfileCurrentFrame] = total;
}
endBuildDisplayListProfiling(buildDisplayListStartTime);
if (displayList != null) {
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);
status = drawDisplayList(attachInfo, canvas, displayList, status);
} else {
// Shouldn't reach here
view.draw(canvas);
@@ -1269,43 +1240,19 @@ public abstract class HardwareRenderer {
mFrameCount++;
if (mDebugDirtyRegions) {
if (mDebugPaint == null) {
mDebugPaint = new Paint();
mDebugPaint.setColor(0x7fff0000);
}
if (dirty != null && (mFrameCount & 1) == 0) {
canvas.drawRect(dirty, mDebugPaint);
}
}
debugDirtyRegions(dirty, canvas);
drawProfileData();
}
onPostDraw();
attachInfo.mIgnoreDirtyState = false;
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();
}
swapBuffers(status);
if (mProfileEnabled) {
mProfileLock.unlock();
}
attachInfo.mIgnoreDirtyState = false;
return dirty == null;
}
}
@@ -1313,6 +1260,133 @@ public abstract class HardwareRenderer {
return false;
}
abstract void drawProfileData();
private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) {
// We had to change the current surface and/or context, redraw everything
if (surfaceState == SURFACE_STATE_UPDATED) {
dirty = null;
beginFrame(null);
} else {
int[] size = mSurfaceSize;
beginFrame(size);
if (size[1] != mHeight || size[0] != mWidth) {
mWidth = size[0];
mHeight = size[1];
canvas.setViewport(mWidth, mHeight);
dirty = null;
}
}
if (mProfileEnabled && mProfileVisualizerEnabled) dirty = null;
return dirty;
}
private 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 static DisplayList buildDisplayList(View view) {
DisplayList displayList;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
try {
displayList = view.getDisplayList();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return displayList;
}
private int prepareFrame(Rect dirty) {
int status;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame");
try {
status = onPreDraw(dirty);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return status;
}
private 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;
}
private 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();
}
}
private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) {
if (mDebugDirtyRegions) {
if (mDebugPaint == null) {
mDebugPaint = new Paint();
mDebugPaint.setColor(0x7fff0000);
}
if (dirty != null && (mFrameCount & 1) == 0) {
canvas.drawRect(dirty, mDebugPaint);
}
}
}
private void handleFunctorStatus(View.AttachInfo attachInfo, int status) {
// If the draw flag is set, functors will be invoked while executing
// the tree of display lists
@@ -1389,6 +1463,15 @@ public abstract class HardwareRenderer {
* Hardware renderer using OpenGL ES 2.0.
*/
static class Gl20Renderer extends GlRenderer {
// TODO: Convert dimensions to dp instead of px
private static final int PROFILE_DRAW_MARGIN = 1;
private static final int PROFILE_DRAW_WIDTH = 3;
private static final int[] PROFILE_DRAW_COLORS = { 0xff3e66cc, 0xffdc3912, 0xffe69800 };
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_CURRENT_FRAME_COLOR = 0xff5faa4d;
private static final int PROFILE_DRAW_PX_PER_MS = 10;
private GLES20Canvas mGlCanvas;
private static EGLSurface sPbuffer;
@@ -1493,6 +1576,94 @@ public abstract class HardwareRenderer {
mGlCanvas.onPostDraw();
}
@Override
void drawProfileData() {
if (mProfileEnabled && mProfileVisualizerEnabled) {
initProfileDrawData();
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;
int index = count * 4;
if (i == mProfileCurrentFrame) current = index;
x += PROFILE_DRAW_MARGIN;
int x2 = x + PROFILE_DRAW_WIDTH;
int y2 = mHeight;
int y1 = (int) (y2 - mProfileData[i] * PROFILE_DRAW_PX_PER_MS);
float[] r = mProfileRects[0];
r[index] = x;
r[index + 1] = y1;
r[index + 2] = x2;
r[index + 3] = y2;
y2 = y1;
y1 = (int) (y2 - mProfileData[i + 1] * PROFILE_DRAW_PX_PER_MS);
r = mProfileRects[1];
r[index] = x;
r[index + 1] = y1;
r[index + 2] = x2;
r[index + 3] = y2;
y2 = y1;
y1 = (int) (y2 - mProfileData[i + 2] * PROFILE_DRAW_PX_PER_MS);
r = mProfileRects[2];
r[index] = x;
r[index + 1] = y1;
r[index + 2] = x2;
r[index + 3] = y2;
x += PROFILE_DRAW_WIDTH;
count++;
}
drawGraph(count);
drawCurrentFrame(current);
drawThreshold(x + PROFILE_DRAW_MARGIN);
}
}
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) {
mProfilePaint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
mProfilePaint.setStrokeWidth(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH);
int y = mHeight - 16 * 10;
mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
mProfilePaint.setStrokeWidth(1.0f);
}
private void initProfileDrawData() {
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];
}
mProfilePaint = new Paint();
}
}
@Override
void destroy(boolean full) {
try {

View File

@@ -449,16 +449,40 @@ static void android_view_GLES20Canvas_drawArc(JNIEnv* env, jobject clazz,
renderer->drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint);
}
static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject clazz,
static void android_view_GLES20Canvas_drawRegionAsRects(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, SkRegion* region, SkPaint* paint) {
SkRegion::Iterator it(*region);
while (!it.done()) {
const SkIRect& r = it.rect();
renderer->drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
it.next();
if (paint->getStyle() != SkPaint::kFill_Style ||
(paint->isAntiAlias() && !renderer->isCurrentTransformSimple())) {
SkRegion::Iterator it(*region);
while (!it.done()) {
const SkIRect& r = it.rect();
renderer->drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
it.next();
}
} else {
int count = 0;
Vector<float> rects;
SkRegion::Iterator it(*region);
while (!it.done()) {
const SkIRect& r = it.rect();
rects.push(r.fLeft);
rects.push(r.fTop);
rects.push(r.fRight);
rects.push(r.fBottom);
count++;
it.next();
}
renderer->drawRects(rects.array(), count, paint);
}
}
static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, jfloatArray rects, jint count, SkPaint* paint) {
jfloat* storage = env->GetFloatArrayElements(rects, NULL);
renderer->drawRects(storage, count, paint);
env->ReleaseFloatArrayElements(rects, storage, 0);
}
static void android_view_GLES20Canvas_drawPoints(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, jfloatArray points, jint offset, jint count, SkPaint* paint) {
jfloat* storage = env->GetFloatArrayElements(points, NULL);
@@ -958,7 +982,8 @@ static JNINativeMethod gMethods[] = {
{ "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor },
{ "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect },
{ "nDrawRects", "(III)V", (void*) android_view_GLES20Canvas_drawRects },
{ "nDrawRects", "(III)V", (void*) android_view_GLES20Canvas_drawRegionAsRects },
{ "nDrawRects", "(I[FII)V", (void*) android_view_GLES20Canvas_drawRects },
{ "nDrawRoundRect", "(IFFFFFFI)V", (void*) android_view_GLES20Canvas_drawRoundRect },
{ "nDrawCircle", "(IFFFI)V", (void*) android_view_GLES20Canvas_drawCircle },
{ "nDrawOval", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawOval },

View File

@@ -64,6 +64,7 @@ const char* DisplayList::OP_NAMES[] = {
"DrawTextOnPath",
"DrawPosText",
"DrawText",
"DrawRects",
"ResetShader",
"SetupShader",
"ResetColorFilter",
@@ -633,6 +634,13 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
text.text(), text.length(), count, paint);
}
break;
case DrawRects: {
int32_t count = 0;
float* rects = getFloats(count);
SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %d, %p", (char*) indent, OP_NAMES[op], count / 4, paint);
}
break;
case ResetShader: {
ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
}
@@ -1277,6 +1285,14 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag
x, y, positions, paint, length);
}
break;
case DrawRects: {
int32_t count = 0;
float* rects = getFloats(count);
SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s %d, %p", (char*) indent, OP_NAMES[op], count, paint);
drawGlStatus |= renderer.drawRects(rects, count / 4, paint);
}
break;
case ResetShader: {
DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
renderer.resetShader();
@@ -1814,6 +1830,15 @@ status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int cou
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawRects(const float* rects, int count, SkPaint* paint) {
if (count <= 0) return DrawGlInfo::kStatusDone;
addOp(DisplayList::DrawRects);
addFloats(rects, count * 4);
addPaint(paint);
return DrawGlInfo::kStatusDone;
}
void DisplayListRenderer::resetShader() {
addOp(DisplayList::ResetShader);
}

View File

@@ -106,6 +106,7 @@ public:
DrawTextOnPath,
DrawPosText,
DrawText,
DrawRects,
ResetShader,
SetupShader,
ResetColorFilter,
@@ -608,6 +609,7 @@ public:
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count,
float x, float y, const float* positions, SkPaint* paint, float length);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
virtual void resetShader();
virtual void setupShader(SkiaShader* shader);

View File

@@ -1254,7 +1254,8 @@ bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, fl
return !clip.intersects(transformed);
}
bool OpenGLRenderer::quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint) {
bool OpenGLRenderer::quickRejectPreStroke(float left, float top, float right, float bottom,
SkPaint* paint) {
if (paint->getStyle() != SkPaint::kFill_Style) {
float outset = paint->getStrokeWidth() * 0.5f;
return quickReject(left - outset, top - outset, right + outset, bottom + outset);
@@ -3045,6 +3046,76 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float
}
}
status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint) {
if (mSnapshot->isIgnored()) {
return DrawGlInfo::kStatusDone;
}
float left = FLT_MAX;
float top = FLT_MAX;
float right = FLT_MIN;
float bottom = FLT_MIN;
int vertexCount = 0;
Vertex mesh[count * 6];
Vertex* vertex = mesh;
for (int i = 0; i < count; i++) {
int index = i * 4;
float l = rects[index + 0];
float t = rects[index + 1];
float r = rects[index + 2];
float b = rects[index + 3];
if (!quickRejectNoScissor(left, top, right, bottom)) {
Vertex::set(vertex++, l, b);
Vertex::set(vertex++, l, t);
Vertex::set(vertex++, r, t);
Vertex::set(vertex++, l, b);
Vertex::set(vertex++, r, t);
Vertex::set(vertex++, r, b);
vertexCount += 6;
left = fminf(left, l);
top = fminf(top, t);
right = fmaxf(right, r);
bottom = fmaxf(bottom, b);
}
}
if (count == 0) return DrawGlInfo::kStatusDone;
int color = paint->getColor();
// If a shader is set, preserve only the alpha
if (mShader) {
color |= 0x00ffffff;
}
SkXfermode::Mode mode = getXfermode(paint->getXfermode());
setupDraw();
setupDrawNoTexture();
setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
setupDrawShader();
setupDrawColorFilter();
setupDrawBlending(mode);
setupDrawProgram();
setupDrawDirtyRegionsDisabled();
setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f);
setupDrawColorUniforms();
setupDrawShaderUniforms();
setupDrawColorFilterUniforms();
setupDrawVertices((GLvoid*) &mesh[0].position[0]);
if (hasLayer()) {
dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
}
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
return DrawGlInfo::kStatusDrew;
}
void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
int color, SkXfermode::Mode mode, bool ignoreTransform) {
// If a shader is set, preserve only the alpha

View File

@@ -201,6 +201,7 @@ public:
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
const float* positions, SkPaint* paint, float length = -1.0f);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
virtual void resetShader();
virtual void setupShader(SkiaShader* shader);
@@ -216,6 +217,10 @@ public:
SkPaint* filterPaint(SkPaint* paint);
ANDROID_API bool isCurrentTransformSimple() {
return mSnapshot->transform->isSimple();
}
/**
* Sets the alpha on the current snapshot. This alpha value will be modulated
* with other alpha values when drawing primitives.