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

Commit 75e7d7eb authored by John Reck's avatar John Reck Committed by Android (Google) Code Review
Browse files

Merge "Unify DrawProfiler/JankStats" into mnc-dev

parents 7b0e380b 4c9e59d0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -40,11 +40,11 @@ LOCAL_SRC_FILES := \
    DisplayList.cpp \
    DisplayListCanvas.cpp \
    Dither.cpp \
    DrawProfiler.cpp \
    Extensions.cpp \
    FboCache.cpp \
    FontRenderer.cpp \
    FrameInfo.cpp \
    FrameInfoVisualizer.cpp \
    GammaFontRenderer.cpp \
    GlopBuilder.cpp \
    GradientCache.cpp \
+35 −76
Original line number Diff line number Diff line
@@ -13,19 +13,15 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "DrawProfiler.h"

#include <cutils/compiler.h>
#include "FrameInfoVisualizer.h"

#include "OpenGLRenderer.h"

#define DEFAULT_MAX_FRAMES 128
#include <cutils/compiler.h>

#define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == ProfileType::None)) return
#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return

#define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f)

#define PROFILE_DRAW_WIDTH 3
#define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
#define PROFILE_DRAW_DP_PER_MS 7
@@ -55,15 +51,16 @@ static int dpToPx(int dp, float density) {
    return (int) (dp * density + 0.5f);
}

DrawProfiler::DrawProfiler() {
FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source)
        : mFrameSource(source) {
    setDensity(1);
}

DrawProfiler::~DrawProfiler() {
FrameInfoVisualizer::~FrameInfoVisualizer() {
    destroyData();
}

void DrawProfiler::setDensity(float density) {
void FrameInfoVisualizer::setDensity(float density) {
    if (CC_UNLIKELY(mDensity != density)) {
        mDensity = density;
        mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
@@ -72,35 +69,7 @@ void DrawProfiler::setDensity(float density) {
    }
}

void DrawProfiler::startFrame(nsecs_t recordDurationNanos) {
    RETURN_IF_PROFILING_DISABLED();
    mData[mCurrentFrame].record = NANOS_TO_MILLIS_FLOAT(recordDurationNanos);
    mPreviousTime = systemTime(CLOCK_MONOTONIC);
}

void DrawProfiler::markPlaybackStart() {
    RETURN_IF_PROFILING_DISABLED();
    nsecs_t now = systemTime(CLOCK_MONOTONIC);
    mData[mCurrentFrame].prepare = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
    mPreviousTime = now;
}

void DrawProfiler::markPlaybackEnd() {
    RETURN_IF_PROFILING_DISABLED();
    nsecs_t now = systemTime(CLOCK_MONOTONIC);
    mData[mCurrentFrame].playback = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
    mPreviousTime = now;
}

void DrawProfiler::finishFrame() {
    RETURN_IF_PROFILING_DISABLED();
    nsecs_t now = systemTime(CLOCK_MONOTONIC);
    mData[mCurrentFrame].swapBuffers = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
    mPreviousTime = now;
    mCurrentFrame = (mCurrentFrame + 1) % mDataSize;
}

void DrawProfiler::unionDirty(SkRect* dirty) {
void FrameInfoVisualizer::unionDirty(SkRect* dirty) {
    RETURN_IF_DISABLED();
    // Not worth worrying about minimizing the dirty region for debugging, so just
    // dirty the entire viewport.
@@ -110,7 +79,7 @@ void DrawProfiler::unionDirty(SkRect* dirty) {
    }
}

void DrawProfiler::draw(OpenGLRenderer* canvas) {
void FrameInfoVisualizer::draw(OpenGLRenderer* canvas) {
    RETURN_IF_DISABLED();

    if (mShowDirtyRegions) {
@@ -131,27 +100,21 @@ void DrawProfiler::draw(OpenGLRenderer* canvas) {
    }
}

void DrawProfiler::createData() {
    if (mData) return;
void FrameInfoVisualizer::createData() {
    if (mRects.get()) return;

    mDataSize = property_get_int32(PROPERTY_PROFILE_MAXFRAMES, DEFAULT_MAX_FRAMES);
    if (mDataSize <= 0) mDataSize = 1;
    if (mDataSize > 4096) mDataSize = 4096; // Reasonable maximum
    mData = (FrameTimingData*) calloc(mDataSize, sizeof(FrameTimingData));
    mRects = new float*[NUM_ELEMENTS];
    mRects.reset(new float*[mFrameSource.capacity()]);
    for (int i = 0; i < NUM_ELEMENTS; i++) {
        // 4 floats per rect
        mRects[i] = (float*) calloc(mDataSize, 4 * sizeof(float));
        mRects.get()[i] = (float*) calloc(mFrameSource.capacity(), 4 * sizeof(float));
    }
    mCurrentFrame = 0;
}

void DrawProfiler::destroyData() {
    delete mData;
    mData = nullptr;
void FrameInfoVisualizer::destroyData() {
    mRects.reset(nullptr);
}

void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) {
void FrameInfoVisualizer::addRect(Rect& r, float data, float* shapeOutput) {
    r.top = r.bottom - (data * mVerticalUnit);
    shapeOutput[0] = r.left;
    shapeOutput[1] = r.top;
@@ -160,40 +123,40 @@ void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) {
    r.bottom = r.top;
}

void DrawProfiler::prepareShapes(const int baseline) {
void FrameInfoVisualizer::prepareShapes(const int baseline) {
    Rect r;
    r.right = mHorizontalUnit;
    for (int i = 0; i < mDataSize; i++) {
    for (size_t i = 0; i < mFrameSource.size(); i++) {
        const int shapeIndex = i * 4;
        r.bottom = baseline;
        addRect(r, mData[i].record, mRects[RECORD_INDEX] + shapeIndex);
        addRect(r, mData[i].prepare, mRects[PREPARE_INDEX] + shapeIndex);
        addRect(r, mData[i].playback, mRects[PLAYBACK_INDEX] + shapeIndex);
        addRect(r, mData[i].swapBuffers, mRects[SWAPBUFFERS_INDEX] + shapeIndex);
        addRect(r, recordDuration(i), mRects.get()[RECORD_INDEX] + shapeIndex);
        addRect(r, prepareDuration(i), mRects.get()[PREPARE_INDEX] + shapeIndex);
        addRect(r, issueDrawDuration(i), mRects.get()[PLAYBACK_INDEX] + shapeIndex);
        addRect(r, swapBuffersDuration(i), mRects.get()[SWAPBUFFERS_INDEX] + shapeIndex);
        r.translate(mHorizontalUnit, 0);
    }
}

void DrawProfiler::drawGraph(OpenGLRenderer* canvas) {
void FrameInfoVisualizer::drawGraph(OpenGLRenderer* canvas) {
    SkPaint paint;
    for (int i = 0; i < NUM_ELEMENTS; i++) {
        paint.setColor(ELEMENT_COLORS[i]);
        canvas->drawRects(mRects[i], mDataSize * 4, &paint);
        canvas->drawRects(mRects.get()[i], mFrameSource.capacity() * 4, &paint);
    }
}

void DrawProfiler::drawCurrentFrame(OpenGLRenderer* canvas) {
void FrameInfoVisualizer::drawCurrentFrame(OpenGLRenderer* canvas) {
    // This draws a solid rect over the entirety of the current frame's shape
    // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1]
    // which will therefore fully overlap the previously drawn rects
    SkPaint paint;
    paint.setColor(CURRENT_FRAME_COLOR);
    const int i = mCurrentFrame * 4;
    canvas->drawRect(mRects[0][i], mRects[NUM_ELEMENTS-1][i+1], mRects[0][i+2],
            mRects[0][i+3], &paint);
    const int i = (mFrameSource.size() - 1) * 4;
    canvas->drawRect(mRects.get()[0][i], mRects.get()[NUM_ELEMENTS-1][i+1],
            mRects.get()[0][i+2], mRects.get()[0][i+3], &paint);
}

void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) {
void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) {
    SkPaint paint;
    paint.setColor(THRESHOLD_COLOR);
    paint.setStrokeWidth(mThresholdStroke);
@@ -205,7 +168,7 @@ void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) {
    canvas->drawLines(pts, 4, &paint);
}

bool DrawProfiler::consumeProperties() {
bool FrameInfoVisualizer::consumeProperties() {
    bool changed = false;
    ProfileType newType = Properties::getProfileType();
    if (newType != mType) {
@@ -226,29 +189,25 @@ bool DrawProfiler::consumeProperties() {
    return changed;
}

void DrawProfiler::dumpData(int fd) {
void FrameInfoVisualizer::dumpData(int fd) {
    RETURN_IF_PROFILING_DISABLED();

    // This method logs the last N frames (where N is <= mDataSize) since the
    // last call to dumpData(). In other words if there's a dumpData(), draw frame,
    // dumpData(), the last dumpData() should only log 1 frame.

    const FrameTimingData emptyData = {0, 0, 0, 0};

    FILE *file = fdopen(fd, "a");
    fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");

    for (int frameOffset = 1; frameOffset <= mDataSize; frameOffset++) {
        int i = (mCurrentFrame + frameOffset) % mDataSize;
        if (!memcmp(mData + i, &emptyData, sizeof(FrameTimingData))) {
    for (size_t i = 0; i < mFrameSource.size(); i++) {
        if (mFrameSource[i][FrameInfoIndex::kIntendedVsync] <= mLastFrameLogged) {
            continue;
        }
        mLastFrameLogged = mFrameSource[i][FrameInfoIndex::kIntendedVsync];
        fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
                mData[i].record, mData[i].prepare, mData[i].playback, mData[i].swapBuffers);
                recordDuration(i), prepareDuration(i),
                issueDrawDuration(i), swapBuffersDuration(i));
    }
    // reset the buffer
    memset(mData, 0, sizeof(FrameTimingData) * mDataSize);
    mCurrentFrame = 0;

    fflush(file);
}
+46 −21
Original line number Diff line number Diff line
@@ -16,42 +16,41 @@
#ifndef DRAWPROFILER_H
#define DRAWPROFILER_H

#include "FrameInfo.h"
#include "Properties.h"
#include "Rect.h"
#include "utils/RingBuffer.h"

#include <utils/Timers.h>

#include <memory>

namespace android {
namespace uirenderer {

class OpenGLRenderer;

class DrawProfiler {
// TODO: This is a bit awkward as it needs to match the thing in CanvasContext
// A better abstraction here would be nice but iterators are painful
// and RingBuffer having the size baked into the template is also painful
// But making DrawProfiler also be templated is ALSO painful
// At least this is a compile failure if this doesn't match, so there's that.
typedef RingBuffer<FrameInfo, 120> FrameInfoSource;

class FrameInfoVisualizer {
public:
    DrawProfiler();
    ~DrawProfiler();
    FrameInfoVisualizer(FrameInfoSource& source);
    ~FrameInfoVisualizer();

    bool consumeProperties();
    void setDensity(float density);

    void startFrame(nsecs_t recordDurationNanos = 0);
    void markPlaybackStart();
    void markPlaybackEnd();
    void finishFrame();

    void unionDirty(SkRect* dirty);
    void draw(OpenGLRenderer* canvas);

    void dumpData(int fd);

private:
    typedef struct {
        float record;
        float prepare;
        float playback;
        float swapBuffers;
    } FrameTimingData;

    void createData();
    void destroyData();

@@ -61,14 +60,39 @@ private:
    void drawCurrentFrame(OpenGLRenderer* canvas);
    void drawThreshold(OpenGLRenderer* canvas);

    static inline float duration(nsecs_t start, nsecs_t end) {
        float duration = ((end - start) * 0.000001f);
        return duration > 0.0f ? duration : 0.0f;
    }

    inline float recordDuration(size_t index) {
        return duration(
                mFrameSource[index][FrameInfoIndex::kIntendedVsync],
                mFrameSource[index][FrameInfoIndex::kSyncStart]);
    }

    inline float prepareDuration(size_t index) {
        return duration(
                mFrameSource[index][FrameInfoIndex::kSyncStart],
                mFrameSource[index][FrameInfoIndex::kIssueDrawCommandsStart]);
    }

    inline float issueDrawDuration(size_t index) {
        return duration(
                mFrameSource[index][FrameInfoIndex::kIssueDrawCommandsStart],
                mFrameSource[index][FrameInfoIndex::kSwapBuffers]);
    }

    inline float swapBuffersDuration(size_t index) {
        return duration(
                mFrameSource[index][FrameInfoIndex::kSwapBuffers],
                mFrameSource[index][FrameInfoIndex::kFrameCompleted]);
    }

    ProfileType mType = ProfileType::None;
    float mDensity = 0;

    FrameTimingData* mData = nullptr;
    int mDataSize = 0;

    int mCurrentFrame = -1;
    nsecs_t mPreviousTime = 0;
    FrameInfoSource& mFrameSource;

    int mVerticalUnit = 0;
    int mHorizontalUnit = 0;
@@ -81,11 +105,12 @@ private:
     * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record
     * information.
     */
    float** mRects = nullptr;
    std::unique_ptr<float*> mRects;

    bool mShowDirtyRegions = false;
    SkRect mDirtyRegion;
    bool mFlashToggle = false;
    nsecs_t mLastFrameLogged = 0;
};

} /* namespace uirenderer */
+0 −14
Original line number Diff line number Diff line
@@ -108,20 +108,6 @@ enum DebugLevel {
#define PROPERTY_PROFILE "debug.hwui.profile"
#define PROPERTY_PROFILE_VISUALIZE_BARS "visual_bars"

/**
 * System property used to specify the number of frames to be used
 * when doing hardware rendering profiling.
 * The default value of this property is #PROFILE_MAX_FRAMES.
 *
 * 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:
 * "60", to set the limit of frames to 60
 */
#define PROPERTY_PROFILE_MAXFRAMES "debug.hwui.profile.maxframes"

/**
 * Used to enable/disable non-rectangular clipping debugging.
 *
+2 −6
Original line number Diff line number Diff line
@@ -46,7 +46,8 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
        , mOpaque(!translucent)
        , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
        , mRootRenderNode(rootRenderNode)
        , mJankTracker(thread.timeLord().frameIntervalNanos()) {
        , mJankTracker(thread.timeLord().frameIntervalNanos())
        , mProfiler(mFrames) {
    mRenderThread.renderState().registerCanvasContext(this);
    mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
}
@@ -218,7 +219,6 @@ void CanvasContext::draw() {
        return;
    }

    profiler().markPlaybackStart();
    mCurrentFrameInfo->markIssueDrawCommandsStart();

    EGLint width, height;
@@ -251,8 +251,6 @@ void CanvasContext::draw() {

    bool drew = mCanvas->finish();

    profiler().markPlaybackEnd();

    // Even if we decided to cancel the frame, from the perspective of jank
    // metrics the frame was swapped at this point
    mCurrentFrameInfo->markSwapBuffers();
@@ -267,7 +265,6 @@ void CanvasContext::draw() {
    mCurrentFrameInfo->markFrameCompleted();
    mJankTracker.addFrame(*mCurrentFrameInfo);
    mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
    profiler().finishFrame();
}

// Called by choreographer to do an RT-driven animation
@@ -278,7 +275,6 @@ void CanvasContext::doFrame() {

    ATRACE_CALL();

    profiler().startFrame();
    int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
    UiFrameInfoBuilder(frameInfo)
        .addFlag(FrameInfoFlags::kRTAnimation)
Loading