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