blob: 3a8e559f6d7ea52a87aa939fef9f7e1e049ea8c4 [file] [log] [blame]
John Reckfe5e7b72014-05-23 17:42:28 -07001/*
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 */
John Reck4c9e59d2015-05-12 07:17:50 -070016#include "FrameInfoVisualizer.h"
John Reckfe5e7b72014-05-23 17:42:28 -070017
Matt Sarettde973072016-10-25 11:07:40 -040018#include "IProfileRenderer.h"
Chris Craik54fa17f2015-11-25 14:14:53 -080019#include "utils/Color.h"
John Reck3c0f5632019-03-15 16:36:01 -070020#include "utils/TimeUtils.h"
John Reckfe5e7b72014-05-23 17:42:28 -070021
John Reck4c9e59d2015-05-12 07:17:50 -070022#include <cutils/compiler.h>
John Reckbf3c6022015-06-02 15:55:00 -070023#include <array>
John Reckfe5e7b72014-05-23 17:42:28 -070024
John Reck1bcacfd2017-11-03 10:12:19 -070025#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
John Reckfe5e7b72014-05-23 17:42:28 -070029
Chris Craik54fa17f2015-11-25 14:14:53 -080030namespace android {
31namespace uirenderer {
32
John Reck3c0f5632019-03-15 16:36:01 -070033static constexpr auto PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
34static constexpr auto PROFILE_DRAW_DP_PER_MS = 7;
John Reckfe5e7b72014-05-23 17:42:28 -070035
John Reck3c0f5632019-03-15 16:36:01 -070036struct Threshold {
37 SkColor color;
38 float percentFrametime;
39};
40
41static constexpr std::array<Threshold, 3> THRESHOLDS{
42 Threshold{.color = Color::Green_500, .percentFrametime = 0.8f},
43 Threshold{.color = Color::Lime_500, .percentFrametime = 1.0f},
44 Threshold{.color = Color::Red_500, .percentFrametime = 1.5f},
45};
46static constexpr SkColor BAR_FAST_MASK = 0x8FFFFFFF;
47static constexpr SkColor BAR_JANKY_MASK = 0xDFFFFFFF;
John Reckfe5e7b72014-05-23 17:42:28 -070048
John Reckbf3c6022015-06-02 15:55:00 -070049struct BarSegment {
50 FrameInfoIndex start;
51 FrameInfoIndex end;
52 SkColor color;
53};
54
John Reck1bcacfd2017-11-03 10:12:19 -070055static const std::array<BarSegment, 7> Bar{{
56 {FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, Color::Teal_700},
57 {FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart,
58 Color::Green_700},
59 {FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, Color::LightGreen_700},
60 {FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, Color::Blue_500},
61 {FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, Color::LightBlue_300},
62 {FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, Color::Red_500},
63 {FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, Color::Orange_500},
John Reckbf3c6022015-06-02 15:55:00 -070064}};
65
John Reckfe5e7b72014-05-23 17:42:28 -070066static int dpToPx(int dp, float density) {
John Reck1bcacfd2017-11-03 10:12:19 -070067 return (int)(dp * density + 0.5f);
John Reckfe5e7b72014-05-23 17:42:28 -070068}
69
John Reck3c0f5632019-03-15 16:36:01 -070070FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source, nsecs_t frameInterval)
71 : mFrameSource(source), mFrameInterval(frameInterval) {
John Reckfe5e7b72014-05-23 17:42:28 -070072 setDensity(1);
John Reck8785ceb2018-10-29 16:45:58 -070073 consumeProperties();
John Reckfe5e7b72014-05-23 17:42:28 -070074}
75
John Reck4c9e59d2015-05-12 07:17:50 -070076FrameInfoVisualizer::~FrameInfoVisualizer() {
John Reckfe5e7b72014-05-23 17:42:28 -070077 destroyData();
78}
79
John Reck4c9e59d2015-05-12 07:17:50 -070080void FrameInfoVisualizer::setDensity(float density) {
John Reckfe5e7b72014-05-23 17:42:28 -070081 if (CC_UNLIKELY(mDensity != density)) {
82 mDensity = density;
John Reck3c0f5632019-03-15 16:36:01 -070083 // We want the vertical units to scale height relative to a baseline 16ms.
84 // This keeps the threshold lines consistent across varying refresh rates
85 mVerticalUnit = static_cast<int>(dpToPx(PROFILE_DRAW_DP_PER_MS, density) * (float)16_ms /
86 (float)mFrameInterval);
John Reckfe5e7b72014-05-23 17:42:28 -070087 mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
88 }
89}
90
John Reck4c9e59d2015-05-12 07:17:50 -070091void FrameInfoVisualizer::unionDirty(SkRect* dirty) {
John Reckfe5e7b72014-05-23 17:42:28 -070092 RETURN_IF_DISABLED();
93 // Not worth worrying about minimizing the dirty region for debugging, so just
94 // dirty the entire viewport.
95 if (dirty) {
John Reck23d307c2014-10-27 12:38:48 -070096 mDirtyRegion = *dirty;
John Reckfe5e7b72014-05-23 17:42:28 -070097 dirty->setEmpty();
98 }
99}
100
Matt Sarettde973072016-10-25 11:07:40 -0400101void FrameInfoVisualizer::draw(IProfileRenderer& renderer) {
John Reck23d307c2014-10-27 12:38:48 -0700102 RETURN_IF_DISABLED();
103
104 if (mShowDirtyRegions) {
105 mFlashToggle = !mFlashToggle;
106 if (mFlashToggle) {
107 SkPaint paint;
108 paint.setColor(0x7fff0000);
John Reck1bcacfd2017-11-03 10:12:19 -0700109 renderer.drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop, mDirtyRegion.fRight,
110 mDirtyRegion.fBottom, paint);
John Reck23d307c2014-10-27 12:38:48 -0700111 }
John Reckfe5e7b72014-05-23 17:42:28 -0700112 }
113
Chris Craik2507c342015-05-04 14:36:49 -0700114 if (mType == ProfileType::Bars) {
John Reck41300272015-06-03 14:42:34 -0700115 // Patch up the current frame to pretend we ended here. CanvasContext
116 // will overwrite these values with the real ones after we return.
117 // This is a bit nicer looking than the vague green bar, as we have
118 // valid data for almost all the stages and a very good idea of what
119 // the issue stage will look like, too
120 FrameInfo& info = mFrameSource.back();
121 info.markSwapBuffers();
122 info.markFrameCompleted();
123
Matt Sarettde973072016-10-25 11:07:40 -0400124 initializeRects(renderer.getViewportHeight(), renderer.getViewportWidth());
Chris Craik1dfa0702016-03-04 15:59:24 -0800125 drawGraph(renderer);
126 drawThreshold(renderer);
John Reck23d307c2014-10-27 12:38:48 -0700127 }
John Reckfe5e7b72014-05-23 17:42:28 -0700128}
129
John Reck4c9e59d2015-05-12 07:17:50 -0700130void FrameInfoVisualizer::createData() {
John Reck41300272015-06-03 14:42:34 -0700131 if (mFastRects.get()) return;
John Reckfe5e7b72014-05-23 17:42:28 -0700132
John Reck41300272015-06-03 14:42:34 -0700133 mFastRects.reset(new float[mFrameSource.capacity() * 4]);
134 mJankyRects.reset(new float[mFrameSource.capacity() * 4]);
John Reckfe5e7b72014-05-23 17:42:28 -0700135}
136
John Reck4c9e59d2015-05-12 07:17:50 -0700137void FrameInfoVisualizer::destroyData() {
John Reck41300272015-06-03 14:42:34 -0700138 mFastRects.reset(nullptr);
139 mJankyRects.reset(nullptr);
John Reckfe5e7b72014-05-23 17:42:28 -0700140}
141
John Reck41300272015-06-03 14:42:34 -0700142void FrameInfoVisualizer::initializeRects(const int baseline, const int width) {
143 // Target the 95% mark for the current frame
144 float right = width * .95;
145 float baseLineWidth = right / mFrameSource.capacity();
146 mNumFastRects = 0;
147 mNumJankyRects = 0;
148 int fast_i = 0, janky_i = 0;
John Reckbf3c6022015-06-02 15:55:00 -0700149 // Set the bottom of all the shapes to the baseline
John Reck41300272015-06-03 14:42:34 -0700150 for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) {
151 if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
152 continue;
153 }
154 float lineWidth = baseLineWidth;
155 float* rect;
156 int ri;
John Reckbf3c6022015-06-02 15:55:00 -0700157 // Rects are LTRB
John Reck3c0f5632019-03-15 16:36:01 -0700158 if (mFrameSource[fi].totalDuration() <= mFrameInterval) {
John Reck41300272015-06-03 14:42:34 -0700159 rect = mFastRects.get();
160 ri = fast_i;
161 fast_i += 4;
162 mNumFastRects++;
163 } else {
164 rect = mJankyRects.get();
165 ri = janky_i;
166 janky_i += 4;
167 mNumJankyRects++;
168 lineWidth *= 2;
169 }
170
171 rect[ri + 0] = right - lineWidth;
172 rect[ri + 1] = baseline;
173 rect[ri + 2] = right;
174 rect[ri + 3] = baseline;
175 right -= lineWidth;
John Reckbf3c6022015-06-02 15:55:00 -0700176 }
John Reckfe5e7b72014-05-23 17:42:28 -0700177}
178
John Reckbf3c6022015-06-02 15:55:00 -0700179void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) {
John Reck41300272015-06-03 14:42:34 -0700180 int fast_i = (mNumFastRects - 1) * 4;
John Reck1bcacfd2017-11-03 10:12:19 -0700181 int janky_i = (mNumJankyRects - 1) * 4;
182 ;
John Reck41300272015-06-03 14:42:34 -0700183 for (size_t fi = 0; fi < mFrameSource.size(); fi++) {
Chris Craik1b54fb22015-06-02 17:40:58 -0700184 if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
John Reckbf3c6022015-06-02 15:55:00 -0700185 continue;
186 }
187
John Reck41300272015-06-03 14:42:34 -0700188 float* rect;
189 int ri;
190 // Rects are LTRB
John Reck3c0f5632019-03-15 16:36:01 -0700191 if (mFrameSource[fi].totalDuration() <= mFrameInterval) {
John Reck41300272015-06-03 14:42:34 -0700192 rect = mFastRects.get();
193 ri = fast_i;
194 fast_i -= 4;
195 } else {
196 rect = mJankyRects.get();
197 ri = janky_i;
198 janky_i -= 4;
199 }
200
John Reckbf3c6022015-06-02 15:55:00 -0700201 // Set the bottom to the old top (build upwards)
John Reck41300272015-06-03 14:42:34 -0700202 rect[ri + 3] = rect[ri + 1];
John Reckbf3c6022015-06-02 15:55:00 -0700203 // Move the top up by the duration
John Reckbe3fba02015-07-06 13:49:58 -0700204 rect[ri + 1] -= mVerticalUnit * durationMS(fi, start, end);
John Reckfe5e7b72014-05-23 17:42:28 -0700205 }
206}
207
Matt Sarettde973072016-10-25 11:07:40 -0400208void FrameInfoVisualizer::drawGraph(IProfileRenderer& renderer) {
John Reckfe5e7b72014-05-23 17:42:28 -0700209 SkPaint paint;
John Reckbf3c6022015-06-02 15:55:00 -0700210 for (size_t i = 0; i < Bar.size(); i++) {
John Reckbf3c6022015-06-02 15:55:00 -0700211 nextBarSegment(Bar[i].start, Bar[i].end);
Chris Craik54fa17f2015-11-25 14:14:53 -0800212 paint.setColor(Bar[i].color & BAR_FAST_MASK);
Matt Sarettde973072016-10-25 11:07:40 -0400213 renderer.drawRects(mFastRects.get(), mNumFastRects * 4, paint);
Chris Craik54fa17f2015-11-25 14:14:53 -0800214 paint.setColor(Bar[i].color & BAR_JANKY_MASK);
Matt Sarettde973072016-10-25 11:07:40 -0400215 renderer.drawRects(mJankyRects.get(), mNumJankyRects * 4, paint);
John Reckfe5e7b72014-05-23 17:42:28 -0700216 }
217}
218
Matt Sarettde973072016-10-25 11:07:40 -0400219void FrameInfoVisualizer::drawThreshold(IProfileRenderer& renderer) {
John Reckfe5e7b72014-05-23 17:42:28 -0700220 SkPaint paint;
John Reck3c0f5632019-03-15 16:36:01 -0700221 for (auto& t : THRESHOLDS) {
222 paint.setColor(t.color);
223 float yLocation = renderer.getViewportHeight() -
224 (ns2ms(mFrameInterval) * t.percentFrametime * mVerticalUnit);
225 renderer.drawRect(0.0f, yLocation - mThresholdStroke / 2, renderer.getViewportWidth(),
226 yLocation + mThresholdStroke / 2, paint);
227 }
John Reckfe5e7b72014-05-23 17:42:28 -0700228}
229
John Reck4c9e59d2015-05-12 07:17:50 -0700230bool FrameInfoVisualizer::consumeProperties() {
John Reck23d307c2014-10-27 12:38:48 -0700231 bool changed = false;
Chris Craik2507c342015-05-04 14:36:49 -0700232 ProfileType newType = Properties::getProfileType();
John Reckfe5e7b72014-05-23 17:42:28 -0700233 if (newType != mType) {
234 mType = newType;
Chris Craik2507c342015-05-04 14:36:49 -0700235 if (mType == ProfileType::None) {
John Reckfe5e7b72014-05-23 17:42:28 -0700236 destroyData();
237 } else {
238 createData();
239 }
John Reck23d307c2014-10-27 12:38:48 -0700240 changed = true;
John Reckfe5e7b72014-05-23 17:42:28 -0700241 }
Chris Craik2507c342015-05-04 14:36:49 -0700242
243 bool showDirty = Properties::showDirtyRegions;
John Reck23d307c2014-10-27 12:38:48 -0700244 if (showDirty != mShowDirtyRegions) {
245 mShowDirtyRegions = showDirty;
246 changed = true;
247 }
248 return changed;
John Reckfe5e7b72014-05-23 17:42:28 -0700249}
250
John Reck4c9e59d2015-05-12 07:17:50 -0700251void FrameInfoVisualizer::dumpData(int fd) {
John Reck23d307c2014-10-27 12:38:48 -0700252 RETURN_IF_PROFILING_DISABLED();
John Reckfe5e7b72014-05-23 17:42:28 -0700253
254 // This method logs the last N frames (where N is <= mDataSize) since the
255 // last call to dumpData(). In other words if there's a dumpData(), draw frame,
256 // dumpData(), the last dumpData() should only log 1 frame.
257
John Reck47f5c3a2017-11-13 11:32:39 -0800258 dprintf(fd, "\n\tDraw\tPrepare\tProcess\tExecute\n");
John Reckfe5e7b72014-05-23 17:42:28 -0700259
John Reck4c9e59d2015-05-12 07:17:50 -0700260 for (size_t i = 0; i < mFrameSource.size(); i++) {
Chris Craik1b54fb22015-06-02 17:40:58 -0700261 if (mFrameSource[i][FrameInfoIndex::IntendedVsync] <= mLastFrameLogged) {
John Reckfe5e7b72014-05-23 17:42:28 -0700262 continue;
263 }
Chris Craik1b54fb22015-06-02 17:40:58 -0700264 mLastFrameLogged = mFrameSource[i][FrameInfoIndex::IntendedVsync];
John Reck47f5c3a2017-11-13 11:32:39 -0800265 dprintf(fd, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
John Reckbe3fba02015-07-06 13:49:58 -0700266 durationMS(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart),
267 durationMS(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart),
268 durationMS(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
269 durationMS(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
John Reckfe5e7b72014-05-23 17:42:28 -0700270 }
John Reckfe5e7b72014-05-23 17:42:28 -0700271}
272
273} /* namespace uirenderer */
274} /* namespace android */