Android客户端性能工具2:FrameInfoVisualizer(gfxinfo和开发者选项gpu信息)分析

该类的作用是提供一些gfxinfo的信息还开发者选项中的gpu呈现模式分析和显示gpu视图更新选项

该类提供的接口只有下面五个

typedef RingBuffer<FrameInfo, 120> FrameInfoSource;
class FrameInfoVisualizer {
public:
    FrameInfoVisualizer(FrameInfoSource& source);
    ~FrameInfoVisualizer();

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

    void unionDirty(SkRect* dirty);
    void draw(ContentRenderer* renderer);

    void dumpData(int fd);

该类在CanvasContext创建的时候构造

RingBuffer<FrameInfo, 120> mFrames
CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
        RenderNode* rootRenderNode, IContextFactory* contextFactory)
        : mRenderThread(thread)
        , mEglManager(thread.eglManager())
        , mOpaque(!translucent)
        , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
        , mJankTracker(thread.mainDisplayInfo())
        , mProfiler(mFrames)
        , mContentDrawBounds(0, 0, 0, 0) {
    mRenderNodes.emplace_back(rootRenderNode);
    mRenderThread.renderState().registerCanvasContext(this);
    mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
}

mFrames数据源为能记录120帧的数组.

#define PROFILE_DRAW_WIDTH 3
#define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
#define PROFILE_DRAW_DP_PER_MS 7
void FrameInfoVisualizer::setDensity(float density) {
    if (CC_UNLIKELY(mDensity != density)) {
        mDensity = density;
        mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
        mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
    }
}

既然是用于gfxinfo就从dumpData入手吧

CREATE_BRIDGE4(dumpProfileInfo, CanvasContext* context, RenderThread* thread,
        int fd, int dumpFlags) {
    args->context->profiler().dumpData(args->fd);
    if (args->dumpFlags & DumpFlags::FrameStats) {
        args->context->dumpFrames(args->fd);
    }
    if (args->dumpFlags & DumpFlags::Reset) {
        args->context->resetFrameStats();
    }
    if (args->dumpFlags & DumpFlags::JankStats) {
        args->thread->jankTracker().dump(args->fd);
    }
    return nullptr;
}

args->context->profiler().dumpData(args->fd);用于本次要分析的函数,后边分别是用于reset frameinfo和dump jankTracker数据 上一篇文章有分析

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.

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

    for (size_t i = 0; i < mFrameSource.size(); i++) {
        if (mFrameSource[i][FrameInfoIndex::IntendedVsync] <= mLastFrameLogged) {
            continue;
        }
        mLastFrameLogged = mFrameSource[i][FrameInfoIndex::IntendedVsync];
        fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
                durationMS(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart),
                durationMS(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart),
                durationMS(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
                durationMS(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
    }

    fflush(file);
}

非常简单,只是输出了自上次打印后最近的一些帧的信息,其中包括四部分
1main thread 收到的vsync时间到处开始处理vsync时间
2 render thread收到同步的时间
3 render thread绘制的时间
4 swapbuffer的时间

另外FrameInfoVisualizer还有一个作用就是在调试模式的时候绘制出绘制的耗时,在开发者选项中打开

#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return
void FrameInfoVisualizer::unionDirty(SkRect* dirty) {
    RETURN_IF_DISABLED();
    // Not worth worrying about minimizing the dirty region for debugging, so just
    // dirty the entire viewport.
    if (dirty) {
        mDirtyRegion = *dirty;
        dirty->setEmpty();
    }
}

如上在gpu呈现模式上设置在屏幕上显示条形图后就会在屏幕上绘制呈现数据,另外还会加载dirtyRegion(变成整个window),这个主要是用于发现问题,so just dirty the entire viewport.
然后在绘制的时候就会多绘制这一部分(debug.hwui.profile=visual_bars),主要是通过void FrameInfoVisualizer::draw(ContentRenderer* renderer)方法完成的(这么做会不会影响后draw时常有待考察)

#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return
void FrameInfoVisualizer::draw(ContentRenderer* renderer) {
    RETURN_IF_DISABLED();

    if (mShowDirtyRegions) {
        mFlashToggle = !mFlashToggle;
        if (mFlashToggle) {
            SkPaint paint;
            paint.setColor(0x7fff0000);
            renderer->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
                    mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint);
        }
    }

    if (mType == ProfileType::Bars) {
        // Patch up the current frame to pretend we ended here. CanvasContext
        // will overwrite these values with the real ones after we return.
        // This is a bit nicer looking than the vague green bar, as we have
        // valid data for almost all the stages and a very good idea of what
        // the issue stage will look like, too
        FrameInfo& info = mFrameSource.back();
        info.markSwapBuffers();
        info.markFrameCompleted();

        initializeRects(renderer->getViewportHeight(), renderer->getViewportWidth());
        drawGraph(renderer);
        drawThreshold(renderer);
    }
}

没有开调试模式直接返回,另外还有mShowDirtyRegions表示调试模式的显示gpu视图更新有没有打开(debug.hwui.show_dirty_regions=true),也就是要重绘的区域,所以做好不要把这个变量和visual_bars同时打开.
如果显示gpu视图更新区域打开就把该区域绘制成0x7fff0000的颜色
这里写图片描述

后面就是处理visual_bars的逻辑
注意这里还没有进行swapbuffer,只是简单的生成了swapbuffers和drawcompeted的时间,所以这两个值也是没有参考意义的
initializeRects用于计算线宽的函数

void FrameInfoVisualizer::initializeRects(const int baseline, const int width) {
    // Target the 95% mark for the current frame
    float right = width * .95;
    float baseLineWidth = right / mFrameSource.capacity();
    mNumFastRects = 0;
    mNumJankyRects = 0;
    int fast_i = 0, janky_i = 0;
    // Set the bottom of all the shapes to the baseline
    for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) {
        if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
            continue;
        }
        float lineWidth = baseLineWidth;
        float* rect;
        int ri;
        // Rects are LTRB
        if (mFrameSource[fi].totalDuration() <= FRAME_THRESHOLD_NS) {
            rect = mFastRects.get();
            ri = fast_i;
            fast_i += 4;
            mNumFastRects++;
        } else {
            rect = mJankyRects.get();
            ri = janky_i;
            janky_i += 4;
            mNumJankyRects++;
            lineWidth *= 2;
        }

        rect[ri + 0] = right - lineWidth;
        rect[ri + 1] = baseline;
        rect[ri + 2] = right;
        rect[ri + 3] = baseline;
        right -= lineWidth;
    }
}

这里就是计算表示每一帧矩形的x坐标位置,从又向作计算位置,一个矩形用四个float数值表示.其中left和right被计算出来,top和bottom都是baseline.

static const std::array<BarSegment,7> Bar {{
    { FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, Color::Teal_700 },
    { FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, Color::Green_700 },
    { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, Color::LightGreen_700 },
    { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, Color::Blue_500 },
    { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, Color::LightBlue_300 },
    { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, Color::Red_500},
    { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, Color::Orange_500},
}};
void FrameInfoVisualizer::drawGraph(ContentRenderer* renderer) {
    SkPaint paint;
    for (size_t i = 0; i < Bar.size(); i++) {
        nextBarSegment(Bar[i].start, Bar[i].end);
        paint.setColor(Bar[i].color & BAR_FAST_MASK);
        renderer->drawRects(mFastRects.get(), mNumFastRects * 4, &paint);
        paint.setColor(Bar[i].color & BAR_JANKY_MASK);
        renderer->drawRects(mJankyRects.get(), mNumJankyRects * 4, &paint);
    }
}

drawGraph用于真正绘制表示每一帧的矩形,每一段代表的数据指标和颜色由Bar的数据结构表示.这里做以说明,有7列数据,从下向上拼接矩形.这里说明含义和颜色
Fast代表低于16ms的一帧,Janky代表高于16ms的一帧
1 main thrad vsync的时间到处理事件花费的时间
Fast 这里写图片描述  Jank: 这里写图片描述

2 main thread处理事件花费的时间

Fast:这里写图片描述 Jank:这里写图片描述

3 main thread处理视图变化花费的时间
Fast:这里写图片描述    Jank:这里写图片描述

4 main thread渲染花费的时间
Fast:这里写图片描述  Jank:这里写图片描述

5render thread 同步main thread花费的时间
Fast:这里写图片描述   Jank:这里写图片描述

6render thread绘制花费的时间
Fast:这里写图片描述 Jank:这里写图片描述

7 swapbuffer花费的时间(虽然这个数据无效也解释下)
Fast: 这里写图片描述 Jank: 这里写图片描述

下一篇将分析GpuMemoryTracker

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值