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 "FrameInfoVisualizer.h"
     17 
     18 #include "OpenGLRenderer.h"
     19 
     20 #include <cutils/compiler.h>
     21 #include <array>
     22 
     23 #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
     25 
     26 #define PROFILE_DRAW_WIDTH 3
     27 #define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
     28 #define PROFILE_DRAW_DP_PER_MS 7
     29 
     30 // Must be NUM_ELEMENTS in size
     31 static const SkColor THRESHOLD_COLOR = 0xff5faa4d;
     32 static const SkColor BAR_FAST_ALPHA = 0x8F000000;
     33 static const SkColor BAR_JANKY_ALPHA = 0xDF000000;
     34 
     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 #define FRAME_THRESHOLD_NS 16000000
     39 
     40 namespace android {
     41 namespace uirenderer {
     42 
     43 struct BarSegment {
     44     FrameInfoIndex start;
     45     FrameInfoIndex end;
     46     SkColor color;
     47 };
     48 
     49 static const std::array<BarSegment,7> Bar {{
     50     { FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, 0x00796B },
     51     { FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, 0x388E3C },
     52     { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, 0x689F38},
     53     { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, 0x2196F3},
     54     { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, 0x4FC3F7},
     55     { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, 0xF44336},
     56     { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, 0xFF9800},
     57 }};
     58 
     59 static int dpToPx(int dp, float density) {
     60     return (int) (dp * density + 0.5f);
     61 }
     62 
     63 FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source)
     64         : mFrameSource(source) {
     65     setDensity(1);
     66 }
     67 
     68 FrameInfoVisualizer::~FrameInfoVisualizer() {
     69     destroyData();
     70 }
     71 
     72 void FrameInfoVisualizer::setDensity(float density) {
     73     if (CC_UNLIKELY(mDensity != density)) {
     74         mDensity = density;
     75         mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
     76         mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
     77     }
     78 }
     79 
     80 void FrameInfoVisualizer::unionDirty(SkRect* dirty) {
     81     RETURN_IF_DISABLED();
     82     // Not worth worrying about minimizing the dirty region for debugging, so just
     83     // dirty the entire viewport.
     84     if (dirty) {
     85         mDirtyRegion = *dirty;
     86         dirty->setEmpty();
     87     }
     88 }
     89 
     90 void FrameInfoVisualizer::draw(OpenGLRenderer* canvas) {
     91     RETURN_IF_DISABLED();
     92 
     93     if (mShowDirtyRegions) {
     94         mFlashToggle = !mFlashToggle;
     95         if (mFlashToggle) {
     96             SkPaint paint;
     97             paint.setColor(0x7fff0000);
     98             canvas->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
     99                     mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint);
    100         }
    101     }
    102 
    103     if (mType == ProfileType::Bars) {
    104         // Patch up the current frame to pretend we ended here. CanvasContext
    105         // will overwrite these values with the real ones after we return.
    106         // This is a bit nicer looking than the vague green bar, as we have
    107         // valid data for almost all the stages and a very good idea of what
    108         // the issue stage will look like, too
    109         FrameInfo& info = mFrameSource.back();
    110         info.markSwapBuffers();
    111         info.markFrameCompleted();
    112 
    113         initializeRects(canvas->getViewportHeight(), canvas->getViewportWidth());
    114         drawGraph(canvas);
    115         drawThreshold(canvas);
    116     }
    117 }
    118 
    119 void FrameInfoVisualizer::createData() {
    120     if (mFastRects.get()) return;
    121 
    122     mFastRects.reset(new float[mFrameSource.capacity() * 4]);
    123     mJankyRects.reset(new float[mFrameSource.capacity() * 4]);
    124 }
    125 
    126 void FrameInfoVisualizer::destroyData() {
    127     mFastRects.reset(nullptr);
    128     mJankyRects.reset(nullptr);
    129 }
    130 
    131 void FrameInfoVisualizer::initializeRects(const int baseline, const int width) {
    132     // Target the 95% mark for the current frame
    133     float right = width * .95;
    134     float baseLineWidth = right / mFrameSource.capacity();
    135     mNumFastRects = 0;
    136     mNumJankyRects = 0;
    137     int fast_i = 0, janky_i = 0;
    138     // Set the bottom of all the shapes to the baseline
    139     for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) {
    140         if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
    141             continue;
    142         }
    143         float lineWidth = baseLineWidth;
    144         float* rect;
    145         int ri;
    146         // Rects are LTRB
    147         if (mFrameSource[fi].totalDuration() <= FRAME_THRESHOLD_NS) {
    148             rect = mFastRects.get();
    149             ri = fast_i;
    150             fast_i += 4;
    151             mNumFastRects++;
    152         } else {
    153             rect = mJankyRects.get();
    154             ri = janky_i;
    155             janky_i += 4;
    156             mNumJankyRects++;
    157             lineWidth *= 2;
    158         }
    159 
    160         rect[ri + 0] = right - lineWidth;
    161         rect[ri + 1] = baseline;
    162         rect[ri + 2] = right;
    163         rect[ri + 3] = baseline;
    164         right -= lineWidth;
    165     }
    166 }
    167 
    168 void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) {
    169     int fast_i = (mNumFastRects - 1) * 4;
    170     int janky_i = (mNumJankyRects - 1) * 4;;
    171     for (size_t fi = 0; fi < mFrameSource.size(); fi++) {
    172         if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
    173             continue;
    174         }
    175 
    176         float* rect;
    177         int ri;
    178         // Rects are LTRB
    179         if (mFrameSource[fi].totalDuration() <= FRAME_THRESHOLD_NS) {
    180             rect = mFastRects.get();
    181             ri = fast_i;
    182             fast_i -= 4;
    183         } else {
    184             rect = mJankyRects.get();
    185             ri = janky_i;
    186             janky_i -= 4;
    187         }
    188 
    189         // Set the bottom to the old top (build upwards)
    190         rect[ri + 3] = rect[ri + 1];
    191         // Move the top up by the duration
    192         rect[ri + 1] -= mVerticalUnit * durationMS(fi, start, end);
    193     }
    194 }
    195 
    196 void FrameInfoVisualizer::drawGraph(OpenGLRenderer* canvas) {
    197     SkPaint paint;
    198     for (size_t i = 0; i < Bar.size(); i++) {
    199         nextBarSegment(Bar[i].start, Bar[i].end);
    200         paint.setColor(Bar[i].color | BAR_FAST_ALPHA);
    201         canvas->drawRects(mFastRects.get(), mNumFastRects * 4, &paint);
    202         paint.setColor(Bar[i].color | BAR_JANKY_ALPHA);
    203         canvas->drawRects(mJankyRects.get(), mNumJankyRects * 4, &paint);
    204     }
    205 }
    206 
    207 void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) {
    208     SkPaint paint;
    209     paint.setColor(THRESHOLD_COLOR);
    210     paint.setStrokeWidth(mThresholdStroke);
    211 
    212     float pts[4];
    213     pts[0] = 0.0f;
    214     pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
    215     pts[2] = canvas->getViewportWidth();
    216     canvas->drawLines(pts, 4, &paint);
    217 }
    218 
    219 bool FrameInfoVisualizer::consumeProperties() {
    220     bool changed = false;
    221     ProfileType newType = Properties::getProfileType();
    222     if (newType != mType) {
    223         mType = newType;
    224         if (mType == ProfileType::None) {
    225             destroyData();
    226         } else {
    227             createData();
    228         }
    229         changed = true;
    230     }
    231 
    232     bool showDirty = Properties::showDirtyRegions;
    233     if (showDirty != mShowDirtyRegions) {
    234         mShowDirtyRegions = showDirty;
    235         changed = true;
    236     }
    237     return changed;
    238 }
    239 
    240 void FrameInfoVisualizer::dumpData(int fd) {
    241     RETURN_IF_PROFILING_DISABLED();
    242 
    243     // This method logs the last N frames (where N is <= mDataSize) since the
    244     // last call to dumpData(). In other words if there's a dumpData(), draw frame,
    245     // dumpData(), the last dumpData() should only log 1 frame.
    246 
    247     FILE *file = fdopen(fd, "a");
    248     fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
    249 
    250     for (size_t i = 0; i < mFrameSource.size(); i++) {
    251         if (mFrameSource[i][FrameInfoIndex::IntendedVsync] <= mLastFrameLogged) {
    252             continue;
    253         }
    254         mLastFrameLogged = mFrameSource[i][FrameInfoIndex::IntendedVsync];
    255         fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
    256                 durationMS(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart),
    257                 durationMS(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart),
    258                 durationMS(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
    259                 durationMS(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
    260     }
    261 
    262     fflush(file);
    263 }
    264 
    265 } /* namespace uirenderer */
    266 } /* namespace android */
    267