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