FrameInfoVisualizer.cpp revision 41300274cf8efde2ca95d3c767b214d1edb97f8d
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 31static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d; 32static const SkColor THRESHOLD_COLOR = 0xff5faa4d; 33static const SkColor BAR_FAST_ALPHA = 0x8F000000; 34static const SkColor BAR_JANKY_ALPHA = 0xDF000000; 35 36// We could get this from TimeLord and use the actual frame interval, but 37// this is good enough 38#define FRAME_THRESHOLD 16 39#define FRAME_THRESHOLD_NS 16000000 40 41namespace android { 42namespace uirenderer { 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, 0x00796B }, 52 { FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, 0x388E3C }, 53 { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, 0x689F38}, 54 { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, 0x2196F3}, 55 { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, 0x4FC3F7}, 56 { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, 0xF44336}, 57 { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, 0xFF9800}, 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(OpenGLRenderer* canvas) { 92 RETURN_IF_DISABLED(); 93 94 if (mShowDirtyRegions) { 95 mFlashToggle = !mFlashToggle; 96 if (mFlashToggle) { 97 SkPaint paint; 98 paint.setColor(0x7fff0000); 99 canvas->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(canvas->getViewportHeight(), canvas->getViewportWidth()); 115 drawGraph(canvas); 116 drawThreshold(canvas); 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 * duration(fi, start, end); 194 } 195} 196 197void FrameInfoVisualizer::drawGraph(OpenGLRenderer* canvas) { 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_ALPHA); 202 canvas->drawRects(mFastRects.get(), mNumFastRects * 4, &paint); 203 paint.setColor(Bar[i].color | BAR_JANKY_ALPHA); 204 canvas->drawRects(mJankyRects.get(), mNumJankyRects * 4, &paint); 205 } 206} 207 208void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) { 209 SkPaint paint; 210 paint.setColor(THRESHOLD_COLOR); 211 paint.setStrokeWidth(mThresholdStroke); 212 213 float pts[4]; 214 pts[0] = 0.0f; 215 pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit); 216 pts[2] = canvas->getViewportWidth(); 217 canvas->drawLines(pts, 4, &paint); 218} 219 220bool 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 241void 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 duration(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart), 258 duration(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart), 259 duration(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers), 260 duration(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted)); 261 } 262 263 fflush(file); 264} 265 266} /* namespace uirenderer */ 267} /* namespace android */ 268