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 32namespace android { 33namespace uirenderer { 34 35// Must be NUM_ELEMENTS in size 36static const SkColor THRESHOLD_COLOR = Color::Green_500; 37static const SkColor BAR_FAST_MASK = 0x8FFFFFFF; 38static 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 45struct BarSegment { 46 FrameInfoIndex start; 47 FrameInfoIndex end; 48 SkColor color; 49}; 50 51static 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 61static int dpToPx(int dp, float density) { 62 return (int) (dp * density + 0.5f); 63} 64 65FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source) 66 : mFrameSource(source) { 67 setDensity(1); 68} 69 70FrameInfoVisualizer::~FrameInfoVisualizer() { 71 destroyData(); 72} 73 74void 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 82void 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 92void 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 121void 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 128void FrameInfoVisualizer::destroyData() { 129 mFastRects.reset(nullptr); 130 mJankyRects.reset(nullptr); 131} 132 133void 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 170void 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 198void 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 209void 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 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 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