Home | History | Annotate | Download | only in hwui
      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 
     55 static int dpToPx(int dp, float density) {
     56     return (int) (dp * density + 0.5f);
     57 }
     58 
     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 
     74 DrawProfiler::~DrawProfiler() {
     75     destroyData();
     76 }
     77 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    161 void DrawProfiler::destroyData() {
    162     delete mData;
    163     mData = NULL;
    164 }
    165 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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