FrameInfoVisualizer.cpp revision 1b54fb27ac48495ed0b33868fda5776fb49fe0f3
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_ALPHA = 0xCF000000;
34
35// We could get this from TimeLord and use the actual frame interval, but
36// this is good enough
37#define FRAME_THRESHOLD 16
38
39namespace android {
40namespace uirenderer {
41
42struct BarSegment {
43    FrameInfoIndex start;
44    FrameInfoIndex end;
45    SkColor color;
46};
47
48static const std::array<BarSegment,9> Bar {{
49    { FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync, 0x00695C },
50    { FrameInfoIndex::Vsync, FrameInfoIndex::HandleInputStart, 0x00796B },
51    { FrameInfoIndex::HandleInputStart, FrameInfoIndex::AnimationStart, 0x00897B },
52    { FrameInfoIndex::AnimationStart, FrameInfoIndex::PerformTraversalsStart, 0x009688 },
53    { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, 0x26A69A},
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        mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
78        mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
79    }
80}
81
82void FrameInfoVisualizer::unionDirty(SkRect* dirty) {
83    RETURN_IF_DISABLED();
84    // Not worth worrying about minimizing the dirty region for debugging, so just
85    // dirty the entire viewport.
86    if (dirty) {
87        mDirtyRegion = *dirty;
88        dirty->setEmpty();
89    }
90}
91
92void FrameInfoVisualizer::draw(OpenGLRenderer* canvas) {
93    RETURN_IF_DISABLED();
94
95    if (mShowDirtyRegions) {
96        mFlashToggle = !mFlashToggle;
97        if (mFlashToggle) {
98            SkPaint paint;
99            paint.setColor(0x7fff0000);
100            canvas->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
101                    mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint);
102        }
103    }
104
105    if (mType == ProfileType::Bars) {
106        initializeRects(canvas->getViewportHeight());
107        drawGraph(canvas);
108        drawCurrentFrame(canvas->getViewportHeight(), canvas);
109        drawThreshold(canvas);
110    }
111}
112
113void FrameInfoVisualizer::createData() {
114    if (mRects.get()) return;
115
116    mRects.reset(new float[mFrameSource.capacity() * 4]);
117}
118
119void FrameInfoVisualizer::destroyData() {
120    mRects.reset(nullptr);
121}
122
123void FrameInfoVisualizer::initializeRects(const int baseline) {
124    float left = 0;
125    // Set the bottom of all the shapes to the baseline
126    for (size_t i = 0; i < (mFrameSource.capacity() * 4); i += 4) {
127        // Rects are LTRB
128        mRects[i + 0] = left;
129        mRects[i + 1] = baseline;
130        left += mHorizontalUnit;
131        mRects[i + 2] = left;
132        mRects[i + 3] = baseline;
133    }
134}
135
136void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) {
137    for (size_t fi = 0, ri = 0; fi < mFrameSource.size(); fi++, ri += 4) {
138        // TODO: Skipped frames will leave little holes in the graph, but this
139        // is better than bogus and freaky lines, so...
140        if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
141            continue;
142        }
143
144        // Set the bottom to the old top (build upwards)
145        mRects[ri + 3] = mRects[ri + 1];
146        // Move the top up by the duration
147        mRects[ri + 1] -= mVerticalUnit * duration(fi, start, end);
148    }
149}
150
151void FrameInfoVisualizer::drawGraph(OpenGLRenderer* canvas) {
152    SkPaint paint;
153    for (size_t i = 0; i < Bar.size(); i++) {
154        paint.setColor(Bar[i].color | BAR_ALPHA);
155        nextBarSegment(Bar[i].start, Bar[i].end);
156        canvas->drawRects(mRects.get(), (mFrameSource.size() - 1) * 4, &paint);
157    }
158}
159
160void FrameInfoVisualizer::drawCurrentFrame(const int baseline, OpenGLRenderer* canvas) {
161    // This draws a solid rect over the entirety of the current frame's shape
162    // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1]
163    // which will therefore fully overlap the previously drawn rects
164    SkPaint paint;
165    paint.setColor(CURRENT_FRAME_COLOR);
166    size_t fi = mFrameSource.size() - 1;
167    size_t ri = fi * 4;
168    float top = baseline - (mVerticalUnit * duration(fi,
169            FrameInfoIndex::IntendedVsync, FrameInfoIndex::IssueDrawCommandsStart));
170    canvas->drawRect(mRects[ri], top, mRects[ri + 2], baseline, &paint);
171}
172
173void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) {
174    SkPaint paint;
175    paint.setColor(THRESHOLD_COLOR);
176    paint.setStrokeWidth(mThresholdStroke);
177
178    float pts[4];
179    pts[0] = 0.0f;
180    pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
181    pts[2] = canvas->getViewportWidth();
182    canvas->drawLines(pts, 4, &paint);
183}
184
185bool FrameInfoVisualizer::consumeProperties() {
186    bool changed = false;
187    ProfileType newType = Properties::getProfileType();
188    if (newType != mType) {
189        mType = newType;
190        if (mType == ProfileType::None) {
191            destroyData();
192        } else {
193            createData();
194        }
195        changed = true;
196    }
197
198    bool showDirty = Properties::showDirtyRegions;
199    if (showDirty != mShowDirtyRegions) {
200        mShowDirtyRegions = showDirty;
201        changed = true;
202    }
203    return changed;
204}
205
206void FrameInfoVisualizer::dumpData(int fd) {
207    RETURN_IF_PROFILING_DISABLED();
208
209    // This method logs the last N frames (where N is <= mDataSize) since the
210    // last call to dumpData(). In other words if there's a dumpData(), draw frame,
211    // dumpData(), the last dumpData() should only log 1 frame.
212
213    FILE *file = fdopen(fd, "a");
214    fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
215
216    for (size_t i = 0; i < mFrameSource.size(); i++) {
217        if (mFrameSource[i][FrameInfoIndex::IntendedVsync] <= mLastFrameLogged) {
218            continue;
219        }
220        mLastFrameLogged = mFrameSource[i][FrameInfoIndex::IntendedVsync];
221        fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
222                duration(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart),
223                duration(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart),
224                duration(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
225                duration(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
226    }
227
228    fflush(file);
229}
230
231} /* namespace uirenderer */
232} /* namespace android */
233