FrameInfoVisualizer.cpp revision bf3c602284f9a344faf185c3a5e94a264ba44c4f
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_ALPHA = 0xCF000000; 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 39namespace android { 40namespace uirenderer { 41 42struct BarSegment { 43 FrameInfoIndex start; 44 FrameInfoIndex end; 45 SkColor color; 46}; 47 48static const std::array<BarSegment,9> Bar {{ 49 { FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kVsync, 0x00695C }, 50 { FrameInfoIndex::kVsync, FrameInfoIndex::kHandleInputStart, 0x00796B }, 51 { FrameInfoIndex::kHandleInputStart, FrameInfoIndex::kAnimationStart, 0x00897B }, 52 { FrameInfoIndex::kAnimationStart, FrameInfoIndex::kPerformTraversalsStart, 0x009688 }, 53 { FrameInfoIndex::kPerformTraversalsStart, FrameInfoIndex::kDrawStart, 0x26A69A}, 54 { FrameInfoIndex::kDrawStart, FrameInfoIndex::kSyncStart, 0x2196F3}, 55 { FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart, 0x4FC3F7}, 56 { FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kSwapBuffers, 0xF44336}, 57 { FrameInfoIndex::kSwapBuffers, FrameInfoIndex::kFrameCompleted, 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 mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, 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(OpenGLRenderer* canvas) { 93 RETURN_IF_DISABLED(); 94 95 if (mShowDirtyRegions) { 96 mFlashToggle = !mFlashToggle; 97 if (mFlashToggle) { 98 SkPaint paint; 99 paint.setColor(0x7fff0000); 100 canvas->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop, 101 mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint); 102 } 103 } 104 105 if (mType == ProfileType::Bars) { 106 initializeRects(canvas->getViewportHeight()); 107 drawGraph(canvas); 108 drawCurrentFrame(canvas->getViewportHeight(), canvas); 109 drawThreshold(canvas); 110 } 111} 112 113void FrameInfoVisualizer::createData() { 114 if (mRects.get()) return; 115 116 mRects.reset(new float[mFrameSource.capacity() * 4]); 117} 118 119void FrameInfoVisualizer::destroyData() { 120 mRects.reset(nullptr); 121} 122 123void FrameInfoVisualizer::initializeRects(const int baseline) { 124 float left = 0; 125 // Set the bottom of all the shapes to the baseline 126 for (size_t i = 0; i < (mFrameSource.capacity() * 4); i += 4) { 127 // Rects are LTRB 128 mRects[i + 0] = left; 129 mRects[i + 1] = baseline; 130 left += mHorizontalUnit; 131 mRects[i + 2] = left; 132 mRects[i + 3] = baseline; 133 } 134} 135 136void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) { 137 for (size_t fi = 0, ri = 0; fi < mFrameSource.size(); fi++, ri += 4) { 138 // TODO: Skipped frames will leave little holes in the graph, but this 139 // is better than bogus and freaky lines, so... 140 if (mFrameSource[fi][FrameInfoIndex::kFlags] & FrameInfoFlags::kSkippedFrame) { 141 continue; 142 } 143 144 // Set the bottom to the old top (build upwards) 145 mRects[ri + 3] = mRects[ri + 1]; 146 // Move the top up by the duration 147 mRects[ri + 1] -= mVerticalUnit * duration(fi, start, end); 148 } 149} 150 151void FrameInfoVisualizer::drawGraph(OpenGLRenderer* canvas) { 152 SkPaint paint; 153 for (size_t i = 0; i < Bar.size(); i++) { 154 paint.setColor(Bar[i].color | BAR_ALPHA); 155 nextBarSegment(Bar[i].start, Bar[i].end); 156 canvas->drawRects(mRects.get(), (mFrameSource.size() - 1) * 4, &paint); 157 } 158} 159 160void FrameInfoVisualizer::drawCurrentFrame(const int baseline, OpenGLRenderer* canvas) { 161 // This draws a solid rect over the entirety of the current frame's shape 162 // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1] 163 // which will therefore fully overlap the previously drawn rects 164 SkPaint paint; 165 paint.setColor(CURRENT_FRAME_COLOR); 166 size_t fi = mFrameSource.size() - 1; 167 size_t ri = fi * 4; 168 float top = baseline - (mVerticalUnit * duration(fi, 169 FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kIssueDrawCommandsStart)); 170 canvas->drawRect(mRects[ri], top, mRects[ri + 2], baseline, &paint); 171} 172 173void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) { 174 SkPaint paint; 175 paint.setColor(THRESHOLD_COLOR); 176 paint.setStrokeWidth(mThresholdStroke); 177 178 float pts[4]; 179 pts[0] = 0.0f; 180 pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit); 181 pts[2] = canvas->getViewportWidth(); 182 canvas->drawLines(pts, 4, &paint); 183} 184 185bool FrameInfoVisualizer::consumeProperties() { 186 bool changed = false; 187 ProfileType newType = Properties::getProfileType(); 188 if (newType != mType) { 189 mType = newType; 190 if (mType == ProfileType::None) { 191 destroyData(); 192 } else { 193 createData(); 194 } 195 changed = true; 196 } 197 198 bool showDirty = Properties::showDirtyRegions; 199 if (showDirty != mShowDirtyRegions) { 200 mShowDirtyRegions = showDirty; 201 changed = true; 202 } 203 return changed; 204} 205 206void FrameInfoVisualizer::dumpData(int fd) { 207 RETURN_IF_PROFILING_DISABLED(); 208 209 // This method logs the last N frames (where N is <= mDataSize) since the 210 // last call to dumpData(). In other words if there's a dumpData(), draw frame, 211 // dumpData(), the last dumpData() should only log 1 frame. 212 213 FILE *file = fdopen(fd, "a"); 214 fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n"); 215 216 for (size_t i = 0; i < mFrameSource.size(); i++) { 217 if (mFrameSource[i][FrameInfoIndex::kIntendedVsync] <= mLastFrameLogged) { 218 continue; 219 } 220 mLastFrameLogged = mFrameSource[i][FrameInfoIndex::kIntendedVsync]; 221 fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n", 222 duration(i, FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kSyncStart), 223 duration(i, FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart), 224 duration(i, FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kSwapBuffers), 225 duration(i, FrameInfoIndex::kSwapBuffers, FrameInfoIndex::kFrameCompleted)); 226 } 227 228 fflush(file); 229} 230 231} /* namespace uirenderer */ 232} /* namespace android */ 233