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 35namespace android { 36namespace uirenderer { 37 38// Must be NUM_ELEMENTS in size 39static const SkColor THRESHOLD_COLOR = Color::Green_500; 40static const SkColor BAR_FAST_MASK = 0x8FFFFFFF; 41static 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 48struct BarSegment { 49 FrameInfoIndex start; 50 FrameInfoIndex end; 51 SkColor color; 52}; 53 54static 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 64static int dpToPx(int dp, float density) { 65 return (int) (dp * density + 0.5f); 66} 67 68FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source) 69 : mFrameSource(source) { 70 setDensity(1); 71} 72 73FrameInfoVisualizer::~FrameInfoVisualizer() { 74 destroyData(); 75} 76 77void 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 85void 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 95void 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 124void 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 131void FrameInfoVisualizer::destroyData() { 132 mFastRects.reset(nullptr); 133 mJankyRects.reset(nullptr); 134} 135 136void 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 173void 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 201void 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 212void 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 223bool 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 244void 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