FrameInfoVisualizer.cpp revision 41300274cf8efde2ca95d3c767b214d1edb97f8d
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "FrameInfoVisualizer.h"
17
18#include "OpenGLRenderer.h"
19
20#include <cutils/compiler.h>
21#include <array>
22
23#define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == ProfileType::None)) return
24#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return
25
26#define PROFILE_DRAW_WIDTH 3
27#define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
28#define PROFILE_DRAW_DP_PER_MS 7
29
30// Must be NUM_ELEMENTS in size
31static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d;
32static const SkColor THRESHOLD_COLOR = 0xff5faa4d;
33static const SkColor BAR_FAST_ALPHA = 0x8F000000;
34static const SkColor BAR_JANKY_ALPHA = 0xDF000000;
35
36// We could get this from TimeLord and use the actual frame interval, but
37// this is good enough
38#define FRAME_THRESHOLD 16
39#define FRAME_THRESHOLD_NS 16000000
40
41namespace android {
42namespace uirenderer {
43
44struct BarSegment {
45    FrameInfoIndex start;
46    FrameInfoIndex end;
47    SkColor color;
48};
49
50static const std::array<BarSegment,7> Bar {{
51    { FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, 0x00796B },
52    { FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, 0x388E3C },
53    { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, 0x689F38},
54    { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, 0x2196F3},
55    { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, 0x4FC3F7},
56    { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, 0xF44336},
57    { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, 0xFF9800},
58}};
59
60static int dpToPx(int dp, float density) {
61    return (int) (dp * density + 0.5f);
62}
63
64FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source)
65        : mFrameSource(source) {
66    setDensity(1);
67}
68
69FrameInfoVisualizer::~FrameInfoVisualizer() {
70    destroyData();
71}
72
73void FrameInfoVisualizer::setDensity(float density) {
74    if (CC_UNLIKELY(mDensity != density)) {
75        mDensity = density;
76        mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
77        mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
78    }
79}
80
81void FrameInfoVisualizer::unionDirty(SkRect* dirty) {
82    RETURN_IF_DISABLED();
83    // Not worth worrying about minimizing the dirty region for debugging, so just
84    // dirty the entire viewport.
85    if (dirty) {
86        mDirtyRegion = *dirty;
87        dirty->setEmpty();
88    }
89}
90
91void FrameInfoVisualizer::draw(OpenGLRenderer* canvas) {
92    RETURN_IF_DISABLED();
93
94    if (mShowDirtyRegions) {
95        mFlashToggle = !mFlashToggle;
96        if (mFlashToggle) {
97            SkPaint paint;
98            paint.setColor(0x7fff0000);
99            canvas->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
100                    mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint);
101        }
102    }
103
104    if (mType == ProfileType::Bars) {
105        // Patch up the current frame to pretend we ended here. CanvasContext
106        // will overwrite these values with the real ones after we return.
107        // This is a bit nicer looking than the vague green bar, as we have
108        // valid data for almost all the stages and a very good idea of what
109        // the issue stage will look like, too
110        FrameInfo& info = mFrameSource.back();
111        info.markSwapBuffers();
112        info.markFrameCompleted();
113
114        initializeRects(canvas->getViewportHeight(), canvas->getViewportWidth());
115        drawGraph(canvas);
116        drawThreshold(canvas);
117    }
118}
119
120void FrameInfoVisualizer::createData() {
121    if (mFastRects.get()) return;
122
123    mFastRects.reset(new float[mFrameSource.capacity() * 4]);
124    mJankyRects.reset(new float[mFrameSource.capacity() * 4]);
125}
126
127void FrameInfoVisualizer::destroyData() {
128    mFastRects.reset(nullptr);
129    mJankyRects.reset(nullptr);
130}
131
132void FrameInfoVisualizer::initializeRects(const int baseline, const int width) {
133    // Target the 95% mark for the current frame
134    float right = width * .95;
135    float baseLineWidth = right / mFrameSource.capacity();
136    mNumFastRects = 0;
137    mNumJankyRects = 0;
138    int fast_i = 0, janky_i = 0;
139    // Set the bottom of all the shapes to the baseline
140    for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) {
141        if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
142            continue;
143        }
144        float lineWidth = baseLineWidth;
145        float* rect;
146        int ri;
147        // Rects are LTRB
148        if (mFrameSource[fi].totalDuration() <= FRAME_THRESHOLD_NS) {
149            rect = mFastRects.get();
150            ri = fast_i;
151            fast_i += 4;
152            mNumFastRects++;
153        } else {
154            rect = mJankyRects.get();
155            ri = janky_i;
156            janky_i += 4;
157            mNumJankyRects++;
158            lineWidth *= 2;
159        }
160
161        rect[ri + 0] = right - lineWidth;
162        rect[ri + 1] = baseline;
163        rect[ri + 2] = right;
164        rect[ri + 3] = baseline;
165        right -= lineWidth;
166    }
167}
168
169void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) {
170    int fast_i = (mNumFastRects - 1) * 4;
171    int janky_i = (mNumJankyRects - 1) * 4;;
172    for (size_t fi = 0; fi < mFrameSource.size(); fi++) {
173        if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
174            continue;
175        }
176
177        float* rect;
178        int ri;
179        // Rects are LTRB
180        if (mFrameSource[fi].totalDuration() <= FRAME_THRESHOLD_NS) {
181            rect = mFastRects.get();
182            ri = fast_i;
183            fast_i -= 4;
184        } else {
185            rect = mJankyRects.get();
186            ri = janky_i;
187            janky_i -= 4;
188        }
189
190        // Set the bottom to the old top (build upwards)
191        rect[ri + 3] = rect[ri + 1];
192        // Move the top up by the duration
193        rect[ri + 1] -= mVerticalUnit * duration(fi, start, end);
194    }
195}
196
197void FrameInfoVisualizer::drawGraph(OpenGLRenderer* canvas) {
198    SkPaint paint;
199    for (size_t i = 0; i < Bar.size(); i++) {
200        nextBarSegment(Bar[i].start, Bar[i].end);
201        paint.setColor(Bar[i].color | BAR_FAST_ALPHA);
202        canvas->drawRects(mFastRects.get(), mNumFastRects * 4, &paint);
203        paint.setColor(Bar[i].color | BAR_JANKY_ALPHA);
204        canvas->drawRects(mJankyRects.get(), mNumJankyRects * 4, &paint);
205    }
206}
207
208void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) {
209    SkPaint paint;
210    paint.setColor(THRESHOLD_COLOR);
211    paint.setStrokeWidth(mThresholdStroke);
212
213    float pts[4];
214    pts[0] = 0.0f;
215    pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
216    pts[2] = canvas->getViewportWidth();
217    canvas->drawLines(pts, 4, &paint);
218}
219
220bool FrameInfoVisualizer::consumeProperties() {
221    bool changed = false;
222    ProfileType newType = Properties::getProfileType();
223    if (newType != mType) {
224        mType = newType;
225        if (mType == ProfileType::None) {
226            destroyData();
227        } else {
228            createData();
229        }
230        changed = true;
231    }
232
233    bool showDirty = Properties::showDirtyRegions;
234    if (showDirty != mShowDirtyRegions) {
235        mShowDirtyRegions = showDirty;
236        changed = true;
237    }
238    return changed;
239}
240
241void FrameInfoVisualizer::dumpData(int fd) {
242    RETURN_IF_PROFILING_DISABLED();
243
244    // This method logs the last N frames (where N is <= mDataSize) since the
245    // last call to dumpData(). In other words if there's a dumpData(), draw frame,
246    // dumpData(), the last dumpData() should only log 1 frame.
247
248    FILE *file = fdopen(fd, "a");
249    fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
250
251    for (size_t i = 0; i < mFrameSource.size(); i++) {
252        if (mFrameSource[i][FrameInfoIndex::IntendedVsync] <= mLastFrameLogged) {
253            continue;
254        }
255        mLastFrameLogged = mFrameSource[i][FrameInfoIndex::IntendedVsync];
256        fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
257                duration(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart),
258                duration(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart),
259                duration(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
260                duration(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
261    }
262
263    fflush(file);
264}
265
266} /* namespace uirenderer */
267} /* namespace android */
268