• 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 "BakedOpRenderer.h"
19 #include "IProfileRenderer.h"
20 #include "utils/Color.h"
21 
22 #include <cutils/compiler.h>
23 #include <array>
24 
25 #define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == ProfileType::None)) return
26 #define RETURN_IF_DISABLED() if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return
27 
28 #define PROFILE_DRAW_WIDTH 3
29 #define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
30 #define PROFILE_DRAW_DP_PER_MS 7
31 
32 namespace android {
33 namespace uirenderer {
34 
35 // Must be NUM_ELEMENTS in size
36 static const SkColor THRESHOLD_COLOR = Color::Green_500;
37 static const SkColor BAR_FAST_MASK = 0x8FFFFFFF;
38 static const SkColor BAR_JANKY_MASK = 0xDFFFFFFF;
39 
40 // We could get this from TimeLord and use the actual frame interval, but
41 // this is good enough
42 #define FRAME_THRESHOLD 16
43 #define FRAME_THRESHOLD_NS 16000000
44 
45 struct BarSegment {
46     FrameInfoIndex start;
47     FrameInfoIndex end;
48     SkColor color;
49 };
50 
51 static const std::array<BarSegment,7> Bar {{
52     { FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, Color::Teal_700 },
53     { FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, Color::Green_700 },
54     { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, Color::LightGreen_700 },
55     { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, Color::Blue_500 },
56     { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, Color::LightBlue_300 },
57     { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, Color::Red_500},
58     { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, Color::Orange_500},
59 }};
60 
dpToPx(int dp,float density)61 static int dpToPx(int dp, float density) {
62     return (int) (dp * density + 0.5f);
63 }
64 
FrameInfoVisualizer(FrameInfoSource & source)65 FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source)
66         : mFrameSource(source) {
67     setDensity(1);
68 }
69 
~FrameInfoVisualizer()70 FrameInfoVisualizer::~FrameInfoVisualizer() {
71     destroyData();
72 }
73 
setDensity(float density)74 void FrameInfoVisualizer::setDensity(float density) {
75     if (CC_UNLIKELY(mDensity != density)) {
76         mDensity = density;
77         mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
78         mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
79     }
80 }
81 
unionDirty(SkRect * dirty)82 void 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 
draw(IProfileRenderer & renderer)92 void FrameInfoVisualizer::draw(IProfileRenderer& renderer) {
93     RETURN_IF_DISABLED();
94 
95     if (mShowDirtyRegions) {
96         mFlashToggle = !mFlashToggle;
97         if (mFlashToggle) {
98             SkPaint paint;
99             paint.setColor(0x7fff0000);
100             renderer.drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
101                     mDirtyRegion.fRight, mDirtyRegion.fBottom, paint);
102         }
103     }
104 
105     if (mType == ProfileType::Bars) {
106         // Patch up the current frame to pretend we ended here. CanvasContext
107         // will overwrite these values with the real ones after we return.
108         // This is a bit nicer looking than the vague green bar, as we have
109         // valid data for almost all the stages and a very good idea of what
110         // the issue stage will look like, too
111         FrameInfo& info = mFrameSource.back();
112         info.markSwapBuffers();
113         info.markFrameCompleted();
114 
115         initializeRects(renderer.getViewportHeight(), renderer.getViewportWidth());
116         drawGraph(renderer);
117         drawThreshold(renderer);
118     }
119 }
120 
createData()121 void FrameInfoVisualizer::createData() {
122     if (mFastRects.get()) return;
123 
124     mFastRects.reset(new float[mFrameSource.capacity() * 4]);
125     mJankyRects.reset(new float[mFrameSource.capacity() * 4]);
126 }
127 
destroyData()128 void FrameInfoVisualizer::destroyData() {
129     mFastRects.reset(nullptr);
130     mJankyRects.reset(nullptr);
131 }
132 
initializeRects(const int baseline,const int width)133 void FrameInfoVisualizer::initializeRects(const int baseline, const int width) {
134     // Target the 95% mark for the current frame
135     float right = width * .95;
136     float baseLineWidth = right / mFrameSource.capacity();
137     mNumFastRects = 0;
138     mNumJankyRects = 0;
139     int fast_i = 0, janky_i = 0;
140     // Set the bottom of all the shapes to the baseline
141     for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) {
142         if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
143             continue;
144         }
145         float lineWidth = baseLineWidth;
146         float* rect;
147         int ri;
148         // Rects are LTRB
149         if (mFrameSource[fi].totalDuration() <= FRAME_THRESHOLD_NS) {
150             rect = mFastRects.get();
151             ri = fast_i;
152             fast_i += 4;
153             mNumFastRects++;
154         } else {
155             rect = mJankyRects.get();
156             ri = janky_i;
157             janky_i += 4;
158             mNumJankyRects++;
159             lineWidth *= 2;
160         }
161 
162         rect[ri + 0] = right - lineWidth;
163         rect[ri + 1] = baseline;
164         rect[ri + 2] = right;
165         rect[ri + 3] = baseline;
166         right -= lineWidth;
167     }
168 }
169 
nextBarSegment(FrameInfoIndex start,FrameInfoIndex end)170 void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) {
171     int fast_i = (mNumFastRects - 1) * 4;
172     int janky_i = (mNumJankyRects - 1) * 4;;
173     for (size_t fi = 0; fi < mFrameSource.size(); fi++) {
174         if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
175             continue;
176         }
177 
178         float* rect;
179         int ri;
180         // Rects are LTRB
181         if (mFrameSource[fi].totalDuration() <= FRAME_THRESHOLD_NS) {
182             rect = mFastRects.get();
183             ri = fast_i;
184             fast_i -= 4;
185         } else {
186             rect = mJankyRects.get();
187             ri = janky_i;
188             janky_i -= 4;
189         }
190 
191         // Set the bottom to the old top (build upwards)
192         rect[ri + 3] = rect[ri + 1];
193         // Move the top up by the duration
194         rect[ri + 1] -= mVerticalUnit * durationMS(fi, start, end);
195     }
196 }
197 
drawGraph(IProfileRenderer & renderer)198 void FrameInfoVisualizer::drawGraph(IProfileRenderer& renderer) {
199     SkPaint paint;
200     for (size_t i = 0; i < Bar.size(); i++) {
201         nextBarSegment(Bar[i].start, Bar[i].end);
202         paint.setColor(Bar[i].color & BAR_FAST_MASK);
203         renderer.drawRects(mFastRects.get(), mNumFastRects * 4, paint);
204         paint.setColor(Bar[i].color & BAR_JANKY_MASK);
205         renderer.drawRects(mJankyRects.get(), mNumJankyRects * 4, paint);
206     }
207 }
208 
drawThreshold(IProfileRenderer & renderer)209 void FrameInfoVisualizer::drawThreshold(IProfileRenderer& renderer) {
210     SkPaint paint;
211     paint.setColor(THRESHOLD_COLOR);
212     float yLocation = renderer.getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
213     renderer.drawRect(0.0f,
214             yLocation - mThresholdStroke/2,
215             renderer.getViewportWidth(),
216             yLocation + mThresholdStroke/2,
217             paint);
218 }
219 
consumeProperties()220 bool 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 
dumpData(int fd)241 void 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                 durationMS(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart),
258                 durationMS(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart),
259                 durationMS(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
260                 durationMS(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
261     }
262 
263     fflush(file);
264 }
265 
266 } /* namespace uirenderer */
267 } /* namespace android */
268