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() \
26     if (CC_LIKELY(mType == ProfileType::None)) return
27 #define RETURN_IF_DISABLED() \
28     if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return
29 
30 #define PROFILE_DRAW_WIDTH 3
31 #define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
32 #define PROFILE_DRAW_DP_PER_MS 7
33 
34 namespace android {
35 namespace uirenderer {
36 
37 // Must be NUM_ELEMENTS in size
38 static const SkColor THRESHOLD_COLOR = Color::Green_500;
39 static const SkColor BAR_FAST_MASK = 0x8FFFFFFF;
40 static const SkColor BAR_JANKY_MASK = 0xDFFFFFFF;
41 
42 // We could get this from TimeLord and use the actual frame interval, but
43 // this is good enough
44 #define FRAME_THRESHOLD 16
45 #define FRAME_THRESHOLD_NS 16000000
46 
47 struct BarSegment {
48     FrameInfoIndex start;
49     FrameInfoIndex end;
50     SkColor color;
51 };
52 
53 static const std::array<BarSegment, 7> Bar{{
54         {FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, Color::Teal_700},
55         {FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart,
56          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) : mFrameSource(source) {
69     setDensity(1);
70 }
71 
~FrameInfoVisualizer()72 FrameInfoVisualizer::~FrameInfoVisualizer() {
73     destroyData();
74 }
75 
setDensity(float density)76 void FrameInfoVisualizer::setDensity(float density) {
77     if (CC_UNLIKELY(mDensity != density)) {
78         mDensity = density;
79         mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
80         mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
81     }
82 }
83 
unionDirty(SkRect * dirty)84 void FrameInfoVisualizer::unionDirty(SkRect* dirty) {
85     RETURN_IF_DISABLED();
86     // Not worth worrying about minimizing the dirty region for debugging, so just
87     // dirty the entire viewport.
88     if (dirty) {
89         mDirtyRegion = *dirty;
90         dirty->setEmpty();
91     }
92 }
93 
draw(IProfileRenderer & renderer)94 void FrameInfoVisualizer::draw(IProfileRenderer& renderer) {
95     RETURN_IF_DISABLED();
96 
97     if (mShowDirtyRegions) {
98         mFlashToggle = !mFlashToggle;
99         if (mFlashToggle) {
100             SkPaint paint;
101             paint.setColor(0x7fff0000);
102             renderer.drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop, mDirtyRegion.fRight,
103                               mDirtyRegion.fBottom, paint);
104         }
105     }
106 
107     if (mType == ProfileType::Bars) {
108         // Patch up the current frame to pretend we ended here. CanvasContext
109         // will overwrite these values with the real ones after we return.
110         // This is a bit nicer looking than the vague green bar, as we have
111         // valid data for almost all the stages and a very good idea of what
112         // the issue stage will look like, too
113         FrameInfo& info = mFrameSource.back();
114         info.markSwapBuffers();
115         info.markFrameCompleted();
116 
117         initializeRects(renderer.getViewportHeight(), renderer.getViewportWidth());
118         drawGraph(renderer);
119         drawThreshold(renderer);
120     }
121 }
122 
createData()123 void FrameInfoVisualizer::createData() {
124     if (mFastRects.get()) return;
125 
126     mFastRects.reset(new float[mFrameSource.capacity() * 4]);
127     mJankyRects.reset(new float[mFrameSource.capacity() * 4]);
128 }
129 
destroyData()130 void FrameInfoVisualizer::destroyData() {
131     mFastRects.reset(nullptr);
132     mJankyRects.reset(nullptr);
133 }
134 
initializeRects(const int baseline,const int width)135 void FrameInfoVisualizer::initializeRects(const int baseline, const int width) {
136     // Target the 95% mark for the current frame
137     float right = width * .95;
138     float baseLineWidth = right / mFrameSource.capacity();
139     mNumFastRects = 0;
140     mNumJankyRects = 0;
141     int fast_i = 0, janky_i = 0;
142     // Set the bottom of all the shapes to the baseline
143     for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) {
144         if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
145             continue;
146         }
147         float lineWidth = baseLineWidth;
148         float* rect;
149         int ri;
150         // Rects are LTRB
151         if (mFrameSource[fi].totalDuration() <= FRAME_THRESHOLD_NS) {
152             rect = mFastRects.get();
153             ri = fast_i;
154             fast_i += 4;
155             mNumFastRects++;
156         } else {
157             rect = mJankyRects.get();
158             ri = janky_i;
159             janky_i += 4;
160             mNumJankyRects++;
161             lineWidth *= 2;
162         }
163 
164         rect[ri + 0] = right - lineWidth;
165         rect[ri + 1] = baseline;
166         rect[ri + 2] = right;
167         rect[ri + 3] = baseline;
168         right -= lineWidth;
169     }
170 }
171 
nextBarSegment(FrameInfoIndex start,FrameInfoIndex end)172 void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) {
173     int fast_i = (mNumFastRects - 1) * 4;
174     int janky_i = (mNumJankyRects - 1) * 4;
175     ;
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(IProfileRenderer & renderer)201 void FrameInfoVisualizer::drawGraph(IProfileRenderer& 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(IProfileRenderer & renderer)212 void FrameInfoVisualizer::drawThreshold(IProfileRenderer& renderer) {
213     SkPaint paint;
214     paint.setColor(THRESHOLD_COLOR);
215     float yLocation = renderer.getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
216     renderer.drawRect(0.0f, yLocation - mThresholdStroke / 2, renderer.getViewportWidth(),
217                       yLocation + mThresholdStroke / 2, 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     dprintf(fd, "\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         dprintf(fd, "\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 
263 } /* namespace uirenderer */
264 } /* namespace android */
265