• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "DrawProfiler.h"
17 
18 #include <cutils/compiler.h>
19 
20 #include "OpenGLRenderer.h"
21 #include "Properties.h"
22 
23 #define DEFAULT_MAX_FRAMES 128
24 
25 #define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == kNone)) return
26 #define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone && !mShowDirtyRegions)) return
27 
28 #define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f)
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 // Number of floats we want to display from FrameTimingData
35 // If this is changed make sure to update the indexes below
36 #define NUM_ELEMENTS 4
37 
38 #define RECORD_INDEX 0
39 #define PREPARE_INDEX 1
40 #define PLAYBACK_INDEX 2
41 #define SWAPBUFFERS_INDEX 3
42 
43 // Must be NUM_ELEMENTS in size
44 static const SkColor ELEMENT_COLORS[] = { 0xcf3e66cc, 0xcf8f00ff, 0xcfdc3912, 0xcfe69800 };
45 static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d;
46 static const SkColor THRESHOLD_COLOR = 0xff5faa4d;
47 
48 // We could get this from TimeLord and use the actual frame interval, but
49 // this is good enough
50 #define FRAME_THRESHOLD 16
51 
52 namespace android {
53 namespace uirenderer {
54 
dpToPx(int dp,float density)55 static int dpToPx(int dp, float density) {
56     return (int) (dp * density + 0.5f);
57 }
58 
DrawProfiler()59 DrawProfiler::DrawProfiler()
60         : mType(kNone)
61         , mDensity(0)
62         , mData(NULL)
63         , mDataSize(0)
64         , mCurrentFrame(-1)
65         , mPreviousTime(0)
66         , mVerticalUnit(0)
67         , mHorizontalUnit(0)
68         , mThresholdStroke(0)
69         , mShowDirtyRegions(false)
70         , mFlashToggle(false) {
71     setDensity(1);
72 }
73 
~DrawProfiler()74 DrawProfiler::~DrawProfiler() {
75     destroyData();
76 }
77 
setDensity(float density)78 void DrawProfiler::setDensity(float density) {
79     if (CC_UNLIKELY(mDensity != density)) {
80         mDensity = density;
81         mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
82         mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
83         mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
84     }
85 }
86 
startFrame(nsecs_t recordDurationNanos)87 void DrawProfiler::startFrame(nsecs_t recordDurationNanos) {
88     RETURN_IF_PROFILING_DISABLED();
89     mData[mCurrentFrame].record = NANOS_TO_MILLIS_FLOAT(recordDurationNanos);
90     mPreviousTime = systemTime(CLOCK_MONOTONIC);
91 }
92 
markPlaybackStart()93 void DrawProfiler::markPlaybackStart() {
94     RETURN_IF_PROFILING_DISABLED();
95     nsecs_t now = systemTime(CLOCK_MONOTONIC);
96     mData[mCurrentFrame].prepare = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
97     mPreviousTime = now;
98 }
99 
markPlaybackEnd()100 void DrawProfiler::markPlaybackEnd() {
101     RETURN_IF_PROFILING_DISABLED();
102     nsecs_t now = systemTime(CLOCK_MONOTONIC);
103     mData[mCurrentFrame].playback = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
104     mPreviousTime = now;
105 }
106 
finishFrame()107 void DrawProfiler::finishFrame() {
108     RETURN_IF_PROFILING_DISABLED();
109     nsecs_t now = systemTime(CLOCK_MONOTONIC);
110     mData[mCurrentFrame].swapBuffers = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
111     mPreviousTime = now;
112     mCurrentFrame = (mCurrentFrame + 1) % mDataSize;
113 }
114 
unionDirty(SkRect * dirty)115 void DrawProfiler::unionDirty(SkRect* dirty) {
116     RETURN_IF_DISABLED();
117     // Not worth worrying about minimizing the dirty region for debugging, so just
118     // dirty the entire viewport.
119     if (dirty) {
120         mDirtyRegion = *dirty;
121         dirty->setEmpty();
122     }
123 }
124 
draw(OpenGLRenderer * canvas)125 void DrawProfiler::draw(OpenGLRenderer* canvas) {
126     RETURN_IF_DISABLED();
127 
128     if (mShowDirtyRegions) {
129         mFlashToggle = !mFlashToggle;
130         if (mFlashToggle) {
131             SkPaint paint;
132             paint.setColor(0x7fff0000);
133             canvas->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
134                     mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint);
135         }
136     }
137 
138     if (mType == kBars) {
139         prepareShapes(canvas->getViewportHeight());
140         drawGraph(canvas);
141         drawCurrentFrame(canvas);
142         drawThreshold(canvas);
143     }
144 }
145 
createData()146 void DrawProfiler::createData() {
147     if (mData) return;
148 
149     mDataSize = property_get_int32(PROPERTY_PROFILE_MAXFRAMES, DEFAULT_MAX_FRAMES);
150     if (mDataSize <= 0) mDataSize = 1;
151     if (mDataSize > 4096) mDataSize = 4096; // Reasonable maximum
152     mData = (FrameTimingData*) calloc(mDataSize, sizeof(FrameTimingData));
153     mRects = new float*[NUM_ELEMENTS];
154     for (int i = 0; i < NUM_ELEMENTS; i++) {
155         // 4 floats per rect
156         mRects[i] = (float*) calloc(mDataSize, 4 * sizeof(float));
157     }
158     mCurrentFrame = 0;
159 }
160 
destroyData()161 void DrawProfiler::destroyData() {
162     delete mData;
163     mData = NULL;
164 }
165 
addRect(Rect & r,float data,float * shapeOutput)166 void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) {
167     r.top = r.bottom - (data * mVerticalUnit);
168     shapeOutput[0] = r.left;
169     shapeOutput[1] = r.top;
170     shapeOutput[2] = r.right;
171     shapeOutput[3] = r.bottom;
172     r.bottom = r.top;
173 }
174 
prepareShapes(const int baseline)175 void DrawProfiler::prepareShapes(const int baseline) {
176     Rect r;
177     r.right = mHorizontalUnit;
178     for (int i = 0; i < mDataSize; i++) {
179         const int shapeIndex = i * 4;
180         r.bottom = baseline;
181         addRect(r, mData[i].record, mRects[RECORD_INDEX] + shapeIndex);
182         addRect(r, mData[i].prepare, mRects[PREPARE_INDEX] + shapeIndex);
183         addRect(r, mData[i].playback, mRects[PLAYBACK_INDEX] + shapeIndex);
184         addRect(r, mData[i].swapBuffers, mRects[SWAPBUFFERS_INDEX] + shapeIndex);
185         r.translate(mHorizontalUnit, 0);
186     }
187 }
188 
drawGraph(OpenGLRenderer * canvas)189 void DrawProfiler::drawGraph(OpenGLRenderer* canvas) {
190     SkPaint paint;
191     for (int i = 0; i < NUM_ELEMENTS; i++) {
192         paint.setColor(ELEMENT_COLORS[i]);
193         canvas->drawRects(mRects[i], mDataSize * 4, &paint);
194     }
195 }
196 
drawCurrentFrame(OpenGLRenderer * canvas)197 void DrawProfiler::drawCurrentFrame(OpenGLRenderer* canvas) {
198     // This draws a solid rect over the entirety of the current frame's shape
199     // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1]
200     // which will therefore fully overlap the previously drawn rects
201     SkPaint paint;
202     paint.setColor(CURRENT_FRAME_COLOR);
203     const int i = mCurrentFrame * 4;
204     canvas->drawRect(mRects[0][i], mRects[NUM_ELEMENTS-1][i+1], mRects[0][i+2],
205             mRects[0][i+3], &paint);
206 }
207 
drawThreshold(OpenGLRenderer * canvas)208 void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) {
209     SkPaint paint;
210     paint.setColor(THRESHOLD_COLOR);
211     paint.setStrokeWidth(mThresholdStroke);
212 
213     float pts[4];
214     pts[0] = 0.0f;
215     pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
216     pts[2] = canvas->getViewportWidth();
217     canvas->drawLines(pts, 4, &paint);
218 }
219 
loadRequestedProfileType()220 DrawProfiler::ProfileType DrawProfiler::loadRequestedProfileType() {
221     ProfileType type = kNone;
222     char buf[PROPERTY_VALUE_MAX] = {'\0',};
223     if (property_get(PROPERTY_PROFILE, buf, "") > 0) {
224         if (!strcmp(buf, PROPERTY_PROFILE_VISUALIZE_BARS)) {
225             type = kBars;
226         } else if (!strcmp(buf, "true")) {
227             type = kConsole;
228         }
229     }
230     return type;
231 }
232 
loadSystemProperties()233 bool DrawProfiler::loadSystemProperties() {
234     bool changed = false;
235     ProfileType newType = loadRequestedProfileType();
236     if (newType != mType) {
237         mType = newType;
238         if (mType == kNone) {
239             destroyData();
240         } else {
241             createData();
242         }
243         changed = true;
244     }
245     bool showDirty = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false);
246     if (showDirty != mShowDirtyRegions) {
247         mShowDirtyRegions = showDirty;
248         changed = true;
249     }
250     return changed;
251 }
252 
dumpData(int fd)253 void DrawProfiler::dumpData(int fd) {
254     RETURN_IF_PROFILING_DISABLED();
255 
256     // This method logs the last N frames (where N is <= mDataSize) since the
257     // last call to dumpData(). In other words if there's a dumpData(), draw frame,
258     // dumpData(), the last dumpData() should only log 1 frame.
259 
260     const FrameTimingData emptyData = {0, 0, 0, 0};
261 
262     FILE *file = fdopen(fd, "a");
263     fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
264 
265     for (int frameOffset = 1; frameOffset <= mDataSize; frameOffset++) {
266         int i = (mCurrentFrame + frameOffset) % mDataSize;
267         if (!memcmp(mData + i, &emptyData, sizeof(FrameTimingData))) {
268             continue;
269         }
270         fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
271                 mData[i].record, mData[i].prepare, mData[i].playback, mData[i].swapBuffers);
272     }
273     // reset the buffer
274     memset(mData, 0, sizeof(FrameTimingData) * mDataSize);
275     mCurrentFrame = 0;
276 
277     fflush(file);
278 }
279 
280 } /* namespace uirenderer */
281 } /* namespace android */
282