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