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