blob: 9557cb09a4c6c24aeb00160fe138922a4b65bc92 [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
18#include "OpenGLRenderer.h"
John Reckfe5e7b72014-05-23 17:42:28 -070019
John Reck4c9e59d2015-05-12 07:17:50 -070020#include <cutils/compiler.h>
John Reckbf3c6022015-06-02 15:55:00 -070021#include <array>
John Reckfe5e7b72014-05-23 17:42:28 -070022
Chris Craik2507c342015-05-04 14:36:49 -070023#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
John Reckfe5e7b72014-05-23 17:42:28 -070025
John Reckfe5e7b72014-05-23 17:42:28 -070026#define PROFILE_DRAW_WIDTH 3
27#define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
28#define PROFILE_DRAW_DP_PER_MS 7
29
John Reckfe5e7b72014-05-23 17:42:28 -070030// Must be NUM_ELEMENTS in size
John Reckfe5e7b72014-05-23 17:42:28 -070031static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d;
32static const SkColor THRESHOLD_COLOR = 0xff5faa4d;
John Reckbf3c6022015-06-02 15:55:00 -070033static const SkColor BAR_ALPHA = 0xCF000000;
John Reckfe5e7b72014-05-23 17:42:28 -070034
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
39namespace android {
40namespace uirenderer {
41
John Reckbf3c6022015-06-02 15:55:00 -070042struct BarSegment {
43 FrameInfoIndex start;
44 FrameInfoIndex end;
45 SkColor color;
46};
47
48static const std::array<BarSegment,9> Bar {{
Chris Craik1b54fb22015-06-02 17:40:58 -070049 { FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync, 0x00695C },
50 { FrameInfoIndex::Vsync, FrameInfoIndex::HandleInputStart, 0x00796B },
51 { FrameInfoIndex::HandleInputStart, FrameInfoIndex::AnimationStart, 0x00897B },
52 { FrameInfoIndex::AnimationStart, FrameInfoIndex::PerformTraversalsStart, 0x009688 },
53 { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, 0x26A69A},
54 { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, 0x2196F3},
55 { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, 0x4FC3F7},
56 { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, 0xF44336},
57 { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, 0xFF9800},
John Reckbf3c6022015-06-02 15:55:00 -070058}};
59
John Reckfe5e7b72014-05-23 17:42:28 -070060static int dpToPx(int dp, float density) {
61 return (int) (dp * density + 0.5f);
62}
63
John Reck4c9e59d2015-05-12 07:17:50 -070064FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source)
65 : mFrameSource(source) {
John Reckfe5e7b72014-05-23 17:42:28 -070066 setDensity(1);
67}
68
John Reck4c9e59d2015-05-12 07:17:50 -070069FrameInfoVisualizer::~FrameInfoVisualizer() {
John Reckfe5e7b72014-05-23 17:42:28 -070070 destroyData();
71}
72
John Reck4c9e59d2015-05-12 07:17:50 -070073void FrameInfoVisualizer::setDensity(float density) {
John Reckfe5e7b72014-05-23 17:42:28 -070074 if (CC_UNLIKELY(mDensity != density)) {
75 mDensity = density;
76 mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
77 mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
78 mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
79 }
80}
81
John Reck4c9e59d2015-05-12 07:17:50 -070082void FrameInfoVisualizer::unionDirty(SkRect* dirty) {
John Reckfe5e7b72014-05-23 17:42:28 -070083 RETURN_IF_DISABLED();
84 // Not worth worrying about minimizing the dirty region for debugging, so just
85 // dirty the entire viewport.
86 if (dirty) {
John Reck23d307c2014-10-27 12:38:48 -070087 mDirtyRegion = *dirty;
John Reckfe5e7b72014-05-23 17:42:28 -070088 dirty->setEmpty();
89 }
90}
91
John Reck4c9e59d2015-05-12 07:17:50 -070092void FrameInfoVisualizer::draw(OpenGLRenderer* canvas) {
John Reck23d307c2014-10-27 12:38:48 -070093 RETURN_IF_DISABLED();
94
95 if (mShowDirtyRegions) {
96 mFlashToggle = !mFlashToggle;
97 if (mFlashToggle) {
98 SkPaint paint;
99 paint.setColor(0x7fff0000);
100 canvas->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
101 mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint);
102 }
John Reckfe5e7b72014-05-23 17:42:28 -0700103 }
104
Chris Craik2507c342015-05-04 14:36:49 -0700105 if (mType == ProfileType::Bars) {
John Reckbf3c6022015-06-02 15:55:00 -0700106 initializeRects(canvas->getViewportHeight());
John Reck23d307c2014-10-27 12:38:48 -0700107 drawGraph(canvas);
John Reckbf3c6022015-06-02 15:55:00 -0700108 drawCurrentFrame(canvas->getViewportHeight(), canvas);
John Reck23d307c2014-10-27 12:38:48 -0700109 drawThreshold(canvas);
110 }
John Reckfe5e7b72014-05-23 17:42:28 -0700111}
112
John Reck4c9e59d2015-05-12 07:17:50 -0700113void FrameInfoVisualizer::createData() {
114 if (mRects.get()) return;
John Reckfe5e7b72014-05-23 17:42:28 -0700115
John Reckbf3c6022015-06-02 15:55:00 -0700116 mRects.reset(new float[mFrameSource.capacity() * 4]);
John Reckfe5e7b72014-05-23 17:42:28 -0700117}
118
John Reck4c9e59d2015-05-12 07:17:50 -0700119void FrameInfoVisualizer::destroyData() {
120 mRects.reset(nullptr);
John Reckfe5e7b72014-05-23 17:42:28 -0700121}
122
John Reckbf3c6022015-06-02 15:55:00 -0700123void FrameInfoVisualizer::initializeRects(const int baseline) {
124 float left = 0;
125 // Set the bottom of all the shapes to the baseline
126 for (size_t i = 0; i < (mFrameSource.capacity() * 4); i += 4) {
127 // Rects are LTRB
128 mRects[i + 0] = left;
129 mRects[i + 1] = baseline;
130 left += mHorizontalUnit;
131 mRects[i + 2] = left;
132 mRects[i + 3] = baseline;
133 }
John Reckfe5e7b72014-05-23 17:42:28 -0700134}
135
John Reckbf3c6022015-06-02 15:55:00 -0700136void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) {
137 for (size_t fi = 0, ri = 0; fi < mFrameSource.size(); fi++, ri += 4) {
138 // TODO: Skipped frames will leave little holes in the graph, but this
139 // is better than bogus and freaky lines, so...
Chris Craik1b54fb22015-06-02 17:40:58 -0700140 if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
John Reckbf3c6022015-06-02 15:55:00 -0700141 continue;
142 }
143
144 // Set the bottom to the old top (build upwards)
145 mRects[ri + 3] = mRects[ri + 1];
146 // Move the top up by the duration
147 mRects[ri + 1] -= mVerticalUnit * duration(fi, start, end);
John Reckfe5e7b72014-05-23 17:42:28 -0700148 }
149}
150
John Reck4c9e59d2015-05-12 07:17:50 -0700151void FrameInfoVisualizer::drawGraph(OpenGLRenderer* canvas) {
John Reckfe5e7b72014-05-23 17:42:28 -0700152 SkPaint paint;
John Reckbf3c6022015-06-02 15:55:00 -0700153 for (size_t i = 0; i < Bar.size(); i++) {
154 paint.setColor(Bar[i].color | BAR_ALPHA);
155 nextBarSegment(Bar[i].start, Bar[i].end);
156 canvas->drawRects(mRects.get(), (mFrameSource.size() - 1) * 4, &paint);
John Reckfe5e7b72014-05-23 17:42:28 -0700157 }
158}
159
John Reckbf3c6022015-06-02 15:55:00 -0700160void FrameInfoVisualizer::drawCurrentFrame(const int baseline, OpenGLRenderer* canvas) {
John Reckfe5e7b72014-05-23 17:42:28 -0700161 // This draws a solid rect over the entirety of the current frame's shape
162 // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1]
163 // which will therefore fully overlap the previously drawn rects
164 SkPaint paint;
165 paint.setColor(CURRENT_FRAME_COLOR);
John Reckbf3c6022015-06-02 15:55:00 -0700166 size_t fi = mFrameSource.size() - 1;
167 size_t ri = fi * 4;
168 float top = baseline - (mVerticalUnit * duration(fi,
Chris Craik1b54fb22015-06-02 17:40:58 -0700169 FrameInfoIndex::IntendedVsync, FrameInfoIndex::IssueDrawCommandsStart));
John Reckbf3c6022015-06-02 15:55:00 -0700170 canvas->drawRect(mRects[ri], top, mRects[ri + 2], baseline, &paint);
John Reckfe5e7b72014-05-23 17:42:28 -0700171}
172
John Reck4c9e59d2015-05-12 07:17:50 -0700173void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) {
John Reckfe5e7b72014-05-23 17:42:28 -0700174 SkPaint paint;
175 paint.setColor(THRESHOLD_COLOR);
176 paint.setStrokeWidth(mThresholdStroke);
177
178 float pts[4];
179 pts[0] = 0.0f;
180 pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
181 pts[2] = canvas->getViewportWidth();
182 canvas->drawLines(pts, 4, &paint);
183}
184
John Reck4c9e59d2015-05-12 07:17:50 -0700185bool FrameInfoVisualizer::consumeProperties() {
John Reck23d307c2014-10-27 12:38:48 -0700186 bool changed = false;
Chris Craik2507c342015-05-04 14:36:49 -0700187 ProfileType newType = Properties::getProfileType();
John Reckfe5e7b72014-05-23 17:42:28 -0700188 if (newType != mType) {
189 mType = newType;
Chris Craik2507c342015-05-04 14:36:49 -0700190 if (mType == ProfileType::None) {
John Reckfe5e7b72014-05-23 17:42:28 -0700191 destroyData();
192 } else {
193 createData();
194 }
John Reck23d307c2014-10-27 12:38:48 -0700195 changed = true;
John Reckfe5e7b72014-05-23 17:42:28 -0700196 }
Chris Craik2507c342015-05-04 14:36:49 -0700197
198 bool showDirty = Properties::showDirtyRegions;
John Reck23d307c2014-10-27 12:38:48 -0700199 if (showDirty != mShowDirtyRegions) {
200 mShowDirtyRegions = showDirty;
201 changed = true;
202 }
203 return changed;
John Reckfe5e7b72014-05-23 17:42:28 -0700204}
205
John Reck4c9e59d2015-05-12 07:17:50 -0700206void FrameInfoVisualizer::dumpData(int fd) {
John Reck23d307c2014-10-27 12:38:48 -0700207 RETURN_IF_PROFILING_DISABLED();
John Reckfe5e7b72014-05-23 17:42:28 -0700208
209 // This method logs the last N frames (where N is <= mDataSize) since the
210 // last call to dumpData(). In other words if there's a dumpData(), draw frame,
211 // dumpData(), the last dumpData() should only log 1 frame.
212
John Reckfe5e7b72014-05-23 17:42:28 -0700213 FILE *file = fdopen(fd, "a");
214 fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
215
John Reck4c9e59d2015-05-12 07:17:50 -0700216 for (size_t i = 0; i < mFrameSource.size(); i++) {
Chris Craik1b54fb22015-06-02 17:40:58 -0700217 if (mFrameSource[i][FrameInfoIndex::IntendedVsync] <= mLastFrameLogged) {
John Reckfe5e7b72014-05-23 17:42:28 -0700218 continue;
219 }
Chris Craik1b54fb22015-06-02 17:40:58 -0700220 mLastFrameLogged = mFrameSource[i][FrameInfoIndex::IntendedVsync];
John Reckfe5e7b72014-05-23 17:42:28 -0700221 fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
Chris Craik1b54fb22015-06-02 17:40:58 -0700222 duration(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart),
223 duration(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart),
224 duration(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
225 duration(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
John Reckfe5e7b72014-05-23 17:42:28 -0700226 }
John Reckfe5e7b72014-05-23 17:42:28 -0700227
228 fflush(file);
229}
230
231} /* namespace uirenderer */
232} /* namespace android */