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