Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 672433d9 authored by Romain Guy's avatar Romain Guy
Browse files

Add visual profiling feature

When profiling is enabled with debug.hwui.profile set to true,
setting debug.hwui.profile_visualizer to true will display the
profiling data directly on screen.

Change-Id: I3d5fe3f0347090815087b1cbfce66b8e76d9347b
parent 59131481
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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) {
+268 −97
Original line number Diff line number Diff line
@@ -93,6 +93,21 @@ public abstract class HardwareRenderer {
     */
    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,6 +1206,63 @@ public abstract class HardwareRenderer {
                        mProfileLock.lock();
                    }

                    dirty = beginFrame(canvas, dirty, surfaceState);

                    int saveCount = 0;
                    int status = DisplayList.STATUS_DONE;

                    try {
                        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
                                == View.PFLAG_INVALIDATED;
                        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;

                        long buildDisplayListStartTime = startBuildDisplayListProfiling();
                        canvas.clearLayerUpdates();

                        DisplayList displayList = buildDisplayList(view);
                        status = prepareFrame(dirty);

                        saveCount = canvas.save();
                        callbacks.onHardwarePreDraw(canvas);

                        endBuildDisplayListProfiling(buildDisplayListStartTime);

                        if (displayList != null) {
                            status = drawDisplayList(attachInfo, canvas, displayList, status);
                        } else {
                            // Shouldn't reach here
                            view.draw(canvas);
                        }
                    } finally {
                        callbacks.onHardwarePostDraw(canvas);
                        canvas.restoreToCount(saveCount);
                        view.mRecreateDisplayList = false;

                        mFrameCount++;

                        debugDirtyRegions(dirty, canvas);
                        drawProfileData();
                    }

                    onPostDraw();

                    swapBuffers(status);

                    if (mProfileEnabled) {
                        mProfileLock.unlock();
                    }

                    attachInfo.mIgnoreDirtyState = false;
                    return dirty == null;
                }
            }

            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;
@@ -1193,26 +1281,33 @@ public abstract class HardwareRenderer {
                }
            }

                    int saveCount = 0;
                    int status = DisplayList.STATUS_DONE;
            if (mProfileEnabled && mProfileVisualizerEnabled) dirty = null;

                    try {
                        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
                                == View.PFLAG_INVALIDATED;
                        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
            return dirty;
        }

                        long getDisplayListStartTime = 0;
        private long startBuildDisplayListProfiling() {
            if (mProfileEnabled) {
                mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT;
                if (mProfileCurrentFrame >= mProfileData.length) {
                    mProfileCurrentFrame = 0;
                }

                            getDisplayListStartTime = System.nanoTime();
                return System.nanoTime();
            }
            return 0;
        }

                        canvas.clearLayerUpdates();
        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 {
@@ -1220,24 +1315,23 @@ public abstract class HardwareRenderer {
            } 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);
            }
                        saveCount = canvas.save();
                        callbacks.onHardwarePreDraw(canvas);

                        if (mProfileEnabled) {
                            long now = System.nanoTime();
                            float total = (now - getDisplayListStartTime) * 0.000001f;
                            //noinspection PointlessArithmeticExpression
                            mProfileData[mProfileCurrentFrame] = total;
            return status;
        }

                        if (displayList != null) {
        private int drawDisplayList(View.AttachInfo attachInfo, HardwareCanvas canvas,
                DisplayList displayList, int status) {

            long drawDisplayListStartTime = 0;
            if (mProfileEnabled) {
                drawDisplayListStartTime = System.nanoTime();
@@ -1258,33 +1352,10 @@ public abstract class HardwareRenderer {
            }

            handleFunctorStatus(attachInfo, status);
                        } else {
                            // Shouldn't reach here
                            view.draw(canvas);
            return status;
        }
                    } finally {
                        callbacks.onHardwarePostDraw(canvas);
                        canvas.restoreToCount(saveCount);
                        view.mRecreateDisplayList = false;

                        mFrameCount++;

                        if (mDebugDirtyRegions) {
                            if (mDebugPaint == null) {
                                mDebugPaint = new Paint();
                                mDebugPaint.setColor(0x7fff0000);
                            }

                            if (dirty != null && (mFrameCount & 1) == 0) {
                                canvas.drawRect(dirty, mDebugPaint);
                            }
                        }
                    }

                    onPostDraw();

                    attachInfo.mIgnoreDirtyState = false;

        private void swapBuffers(int status) {
            if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) {
                long eglSwapBuffersStartTime = 0;
                if (mProfileEnabled) {
@@ -1301,16 +1372,19 @@ public abstract class HardwareRenderer {

                checkEglErrors();
            }
        }

                    if (mProfileEnabled) {
                        mProfileLock.unlock();
        private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) {
            if (mDebugDirtyRegions) {
                if (mDebugPaint == null) {
                    mDebugPaint = new Paint();
                    mDebugPaint.setColor(0x7fff0000);
                }

                    return dirty == null;
                if (dirty != null && (mFrameCount & 1) == 0) {
                    canvas.drawRect(dirty, mDebugPaint);
                }
            }

            return false;
        }

        private void handleFunctorStatus(View.AttachInfo attachInfo, int status) {
@@ -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 {
+32 −7
Original line number Diff line number Diff line
@@ -449,14 +449,38 @@ 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) {
    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,
@@ -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 },
+25 −0
Original line number Diff line number Diff line
@@ -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);
}
+2 −0
Original line number Diff line number Diff line
@@ -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);
Loading