blob: 7addef9d6759e8c01ee0baeb3f728540176f8e7e [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 */
16#include "DrawProfiler.h"
17
18#include <cutils/compiler.h>
19
20#include "OpenGLRenderer.h"
John Reckfe5e7b72014-05-23 17:42:28 -070021
22#define DEFAULT_MAX_FRAMES 128
23
Chris Craik2507c342015-05-04 14:36:49 -070024#define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == ProfileType::None)) return
25#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return
John Reckfe5e7b72014-05-23 17:42:28 -070026
27#define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f)
28
29#define PROFILE_DRAW_WIDTH 3
30#define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
31#define PROFILE_DRAW_DP_PER_MS 7
32
33// Number of floats we want to display from FrameTimingData
34// If this is changed make sure to update the indexes below
35#define NUM_ELEMENTS 4
36
37#define RECORD_INDEX 0
38#define PREPARE_INDEX 1
39#define PLAYBACK_INDEX 2
40#define SWAPBUFFERS_INDEX 3
41
42// Must be NUM_ELEMENTS in size
43static const SkColor ELEMENT_COLORS[] = { 0xcf3e66cc, 0xcf8f00ff, 0xcfdc3912, 0xcfe69800 };
44static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d;
45static const SkColor THRESHOLD_COLOR = 0xff5faa4d;
46
47// We could get this from TimeLord and use the actual frame interval, but
48// this is good enough
49#define FRAME_THRESHOLD 16
50
51namespace android {
52namespace uirenderer {
53
54static int dpToPx(int dp, float density) {
55 return (int) (dp * density + 0.5f);
56}
57
Chris Craik2507c342015-05-04 14:36:49 -070058DrawProfiler::DrawProfiler() {
John Reckfe5e7b72014-05-23 17:42:28 -070059 setDensity(1);
60}
61
62DrawProfiler::~DrawProfiler() {
63 destroyData();
64}
65
66void DrawProfiler::setDensity(float density) {
67 if (CC_UNLIKELY(mDensity != density)) {
68 mDensity = density;
69 mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
70 mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
71 mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
72 }
73}
74
75void DrawProfiler::startFrame(nsecs_t recordDurationNanos) {
John Reck23d307c2014-10-27 12:38:48 -070076 RETURN_IF_PROFILING_DISABLED();
John Reckfe5e7b72014-05-23 17:42:28 -070077 mData[mCurrentFrame].record = NANOS_TO_MILLIS_FLOAT(recordDurationNanos);
78 mPreviousTime = systemTime(CLOCK_MONOTONIC);
79}
80
81void DrawProfiler::markPlaybackStart() {
John Reck23d307c2014-10-27 12:38:48 -070082 RETURN_IF_PROFILING_DISABLED();
John Reckfe5e7b72014-05-23 17:42:28 -070083 nsecs_t now = systemTime(CLOCK_MONOTONIC);
84 mData[mCurrentFrame].prepare = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
85 mPreviousTime = now;
86}
87
88void DrawProfiler::markPlaybackEnd() {
John Reck23d307c2014-10-27 12:38:48 -070089 RETURN_IF_PROFILING_DISABLED();
John Reckfe5e7b72014-05-23 17:42:28 -070090 nsecs_t now = systemTime(CLOCK_MONOTONIC);
91 mData[mCurrentFrame].playback = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
92 mPreviousTime = now;
93}
94
95void DrawProfiler::finishFrame() {
John Reck23d307c2014-10-27 12:38:48 -070096 RETURN_IF_PROFILING_DISABLED();
John Reckfe5e7b72014-05-23 17:42:28 -070097 nsecs_t now = systemTime(CLOCK_MONOTONIC);
98 mData[mCurrentFrame].swapBuffers = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
99 mPreviousTime = now;
100 mCurrentFrame = (mCurrentFrame + 1) % mDataSize;
101}
102
John Recke4267ea2014-06-03 15:53:15 -0700103void DrawProfiler::unionDirty(SkRect* dirty) {
John Reckfe5e7b72014-05-23 17:42:28 -0700104 RETURN_IF_DISABLED();
105 // Not worth worrying about minimizing the dirty region for debugging, so just
106 // dirty the entire viewport.
107 if (dirty) {
John Reck23d307c2014-10-27 12:38:48 -0700108 mDirtyRegion = *dirty;
John Reckfe5e7b72014-05-23 17:42:28 -0700109 dirty->setEmpty();
110 }
111}
112
113void DrawProfiler::draw(OpenGLRenderer* canvas) {
John Reck23d307c2014-10-27 12:38:48 -0700114 RETURN_IF_DISABLED();
115
116 if (mShowDirtyRegions) {
117 mFlashToggle = !mFlashToggle;
118 if (mFlashToggle) {
119 SkPaint paint;
120 paint.setColor(0x7fff0000);
121 canvas->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
122 mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint);
123 }
John Reckfe5e7b72014-05-23 17:42:28 -0700124 }
125
Chris Craik2507c342015-05-04 14:36:49 -0700126 if (mType == ProfileType::Bars) {
John Reck23d307c2014-10-27 12:38:48 -0700127 prepareShapes(canvas->getViewportHeight());
128 drawGraph(canvas);
129 drawCurrentFrame(canvas);
130 drawThreshold(canvas);
131 }
John Reckfe5e7b72014-05-23 17:42:28 -0700132}
133
134void DrawProfiler::createData() {
135 if (mData) return;
136
137 mDataSize = property_get_int32(PROPERTY_PROFILE_MAXFRAMES, DEFAULT_MAX_FRAMES);
138 if (mDataSize <= 0) mDataSize = 1;
139 if (mDataSize > 4096) mDataSize = 4096; // Reasonable maximum
140 mData = (FrameTimingData*) calloc(mDataSize, sizeof(FrameTimingData));
141 mRects = new float*[NUM_ELEMENTS];
142 for (int i = 0; i < NUM_ELEMENTS; i++) {
143 // 4 floats per rect
144 mRects[i] = (float*) calloc(mDataSize, 4 * sizeof(float));
145 }
146 mCurrentFrame = 0;
147}
148
149void DrawProfiler::destroyData() {
150 delete mData;
Chris Craikd41c4d82015-01-05 15:51:13 -0800151 mData = nullptr;
John Reckfe5e7b72014-05-23 17:42:28 -0700152}
153
154void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) {
155 r.top = r.bottom - (data * mVerticalUnit);
156 shapeOutput[0] = r.left;
157 shapeOutput[1] = r.top;
158 shapeOutput[2] = r.right;
159 shapeOutput[3] = r.bottom;
160 r.bottom = r.top;
161}
162
163void DrawProfiler::prepareShapes(const int baseline) {
164 Rect r;
165 r.right = mHorizontalUnit;
166 for (int i = 0; i < mDataSize; i++) {
167 const int shapeIndex = i * 4;
168 r.bottom = baseline;
169 addRect(r, mData[i].record, mRects[RECORD_INDEX] + shapeIndex);
170 addRect(r, mData[i].prepare, mRects[PREPARE_INDEX] + shapeIndex);
171 addRect(r, mData[i].playback, mRects[PLAYBACK_INDEX] + shapeIndex);
172 addRect(r, mData[i].swapBuffers, mRects[SWAPBUFFERS_INDEX] + shapeIndex);
173 r.translate(mHorizontalUnit, 0);
174 }
175}
176
177void DrawProfiler::drawGraph(OpenGLRenderer* canvas) {
178 SkPaint paint;
179 for (int i = 0; i < NUM_ELEMENTS; i++) {
180 paint.setColor(ELEMENT_COLORS[i]);
181 canvas->drawRects(mRects[i], mDataSize * 4, &paint);
182 }
183}
184
185void DrawProfiler::drawCurrentFrame(OpenGLRenderer* canvas) {
186 // This draws a solid rect over the entirety of the current frame's shape
187 // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1]
188 // which will therefore fully overlap the previously drawn rects
189 SkPaint paint;
190 paint.setColor(CURRENT_FRAME_COLOR);
191 const int i = mCurrentFrame * 4;
192 canvas->drawRect(mRects[0][i], mRects[NUM_ELEMENTS-1][i+1], mRects[0][i+2],
193 mRects[0][i+3], &paint);
194}
195
196void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) {
197 SkPaint paint;
198 paint.setColor(THRESHOLD_COLOR);
199 paint.setStrokeWidth(mThresholdStroke);
200
201 float pts[4];
202 pts[0] = 0.0f;
203 pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
204 pts[2] = canvas->getViewportWidth();
205 canvas->drawLines(pts, 4, &paint);
206}
207
Chris Craik2507c342015-05-04 14:36:49 -0700208bool DrawProfiler::consumeProperties() {
John Reck23d307c2014-10-27 12:38:48 -0700209 bool changed = false;
Chris Craik2507c342015-05-04 14:36:49 -0700210 ProfileType newType = Properties::getProfileType();
John Reckfe5e7b72014-05-23 17:42:28 -0700211 if (newType != mType) {
212 mType = newType;
Chris Craik2507c342015-05-04 14:36:49 -0700213 if (mType == ProfileType::None) {
John Reckfe5e7b72014-05-23 17:42:28 -0700214 destroyData();
215 } else {
216 createData();
217 }
John Reck23d307c2014-10-27 12:38:48 -0700218 changed = true;
John Reckfe5e7b72014-05-23 17:42:28 -0700219 }
Chris Craik2507c342015-05-04 14:36:49 -0700220
221 bool showDirty = Properties::showDirtyRegions;
John Reck23d307c2014-10-27 12:38:48 -0700222 if (showDirty != mShowDirtyRegions) {
223 mShowDirtyRegions = showDirty;
224 changed = true;
225 }
226 return changed;
John Reckfe5e7b72014-05-23 17:42:28 -0700227}
228
229void DrawProfiler::dumpData(int fd) {
John Reck23d307c2014-10-27 12:38:48 -0700230 RETURN_IF_PROFILING_DISABLED();
John Reckfe5e7b72014-05-23 17:42:28 -0700231
232 // This method logs the last N frames (where N is <= mDataSize) since the
233 // last call to dumpData(). In other words if there's a dumpData(), draw frame,
234 // dumpData(), the last dumpData() should only log 1 frame.
235
236 const FrameTimingData emptyData = {0, 0, 0, 0};
237
238 FILE *file = fdopen(fd, "a");
239 fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
240
241 for (int frameOffset = 1; frameOffset <= mDataSize; frameOffset++) {
242 int i = (mCurrentFrame + frameOffset) % mDataSize;
243 if (!memcmp(mData + i, &emptyData, sizeof(FrameTimingData))) {
244 continue;
245 }
246 fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
247 mData[i].record, mData[i].prepare, mData[i].playback, mData[i].swapBuffers);
248 }
249 // reset the buffer
250 memset(mData, 0, sizeof(FrameTimingData) * mDataSize);
251 mCurrentFrame = 0;
252
253 fflush(file);
254}
255
256} /* namespace uirenderer */
257} /* namespace android */