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