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() \ 26 if (CC_LIKELY(mType == ProfileType::None)) return 27 #define RETURN_IF_DISABLED() \ 28 if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return 29 30 #define PROFILE_DRAW_WIDTH 3 31 #define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2 32 #define PROFILE_DRAW_DP_PER_MS 7 33 34 namespace android { 35 namespace uirenderer { 36 37 // Must be NUM_ELEMENTS in size 38 static const SkColor THRESHOLD_COLOR = Color::Green_500; 39 static const SkColor BAR_FAST_MASK = 0x8FFFFFFF; 40 static const SkColor BAR_JANKY_MASK = 0xDFFFFFFF; 41 42 // We could get this from TimeLord and use the actual frame interval, but 43 // this is good enough 44 #define FRAME_THRESHOLD 16 45 #define FRAME_THRESHOLD_NS 16000000 46 47 struct BarSegment { 48 FrameInfoIndex start; 49 FrameInfoIndex end; 50 SkColor color; 51 }; 52 53 static const std::array<BarSegment, 7> Bar{{ 54 {FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, Color::Teal_700}, 55 {FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, 56 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) : mFrameSource(source) { 69 setDensity(1); 70 } 71 72 FrameInfoVisualizer::~FrameInfoVisualizer() { 73 destroyData(); 74 } 75 76 void FrameInfoVisualizer::setDensity(float density) { 77 if (CC_UNLIKELY(mDensity != density)) { 78 mDensity = density; 79 mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density); 80 mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density); 81 } 82 } 83 84 void FrameInfoVisualizer::unionDirty(SkRect* dirty) { 85 RETURN_IF_DISABLED(); 86 // Not worth worrying about minimizing the dirty region for debugging, so just 87 // dirty the entire viewport. 88 if (dirty) { 89 mDirtyRegion = *dirty; 90 dirty->setEmpty(); 91 } 92 } 93 94 void FrameInfoVisualizer::draw(IProfileRenderer& renderer) { 95 RETURN_IF_DISABLED(); 96 97 if (mShowDirtyRegions) { 98 mFlashToggle = !mFlashToggle; 99 if (mFlashToggle) { 100 SkPaint paint; 101 paint.setColor(0x7fff0000); 102 renderer.drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop, mDirtyRegion.fRight, 103 mDirtyRegion.fBottom, paint); 104 } 105 } 106 107 if (mType == ProfileType::Bars) { 108 // Patch up the current frame to pretend we ended here. CanvasContext 109 // will overwrite these values with the real ones after we return. 110 // This is a bit nicer looking than the vague green bar, as we have 111 // valid data for almost all the stages and a very good idea of what 112 // the issue stage will look like, too 113 FrameInfo& info = mFrameSource.back(); 114 info.markSwapBuffers(); 115 info.markFrameCompleted(); 116 117 initializeRects(renderer.getViewportHeight(), renderer.getViewportWidth()); 118 drawGraph(renderer); 119 drawThreshold(renderer); 120 } 121 } 122 123 void FrameInfoVisualizer::createData() { 124 if (mFastRects.get()) return; 125 126 mFastRects.reset(new float[mFrameSource.capacity() * 4]); 127 mJankyRects.reset(new float[mFrameSource.capacity() * 4]); 128 } 129 130 void FrameInfoVisualizer::destroyData() { 131 mFastRects.reset(nullptr); 132 mJankyRects.reset(nullptr); 133 } 134 135 void FrameInfoVisualizer::initializeRects(const int baseline, const int width) { 136 // Target the 95% mark for the current frame 137 float right = width * .95; 138 float baseLineWidth = right / mFrameSource.capacity(); 139 mNumFastRects = 0; 140 mNumJankyRects = 0; 141 int fast_i = 0, janky_i = 0; 142 // Set the bottom of all the shapes to the baseline 143 for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) { 144 if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) { 145 continue; 146 } 147 float lineWidth = baseLineWidth; 148 float* rect; 149 int ri; 150 // Rects are LTRB 151 if (mFrameSource[fi].totalDuration() <= FRAME_THRESHOLD_NS) { 152 rect = mFastRects.get(); 153 ri = fast_i; 154 fast_i += 4; 155 mNumFastRects++; 156 } else { 157 rect = mJankyRects.get(); 158 ri = janky_i; 159 janky_i += 4; 160 mNumJankyRects++; 161 lineWidth *= 2; 162 } 163 164 rect[ri + 0] = right - lineWidth; 165 rect[ri + 1] = baseline; 166 rect[ri + 2] = right; 167 rect[ri + 3] = baseline; 168 right -= lineWidth; 169 } 170 } 171 172 void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) { 173 int fast_i = (mNumFastRects - 1) * 4; 174 int janky_i = (mNumJankyRects - 1) * 4; 175 ; 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(IProfileRenderer& 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(IProfileRenderer& renderer) { 213 SkPaint paint; 214 paint.setColor(THRESHOLD_COLOR); 215 float yLocation = renderer.getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit); 216 renderer.drawRect(0.0f, yLocation - mThresholdStroke / 2, renderer.getViewportWidth(), 217 yLocation + mThresholdStroke / 2, paint); 218 } 219 220 bool 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 241 void 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 dprintf(fd, "\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 dprintf(fd, "\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 263 } /* namespace uirenderer */ 264 } /* namespace android */ 265