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