John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 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 | */ |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 16 | #include "FrameInfoVisualizer.h" |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 17 | |
| 18 | #include "OpenGLRenderer.h" |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 19 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 20 | #include <cutils/compiler.h> |
John Reck | bf3c602 | 2015-06-02 15:55:00 -0700 | [diff] [blame] | 21 | #include <array> |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 22 | |
Chris Craik | 2507c34 | 2015-05-04 14:36:49 -0700 | [diff] [blame] | 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 |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 25 | |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 26 | #define PROFILE_DRAW_WIDTH 3 |
| 27 | #define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2 |
| 28 | #define PROFILE_DRAW_DP_PER_MS 7 |
| 29 | |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 30 | // Must be NUM_ELEMENTS in size |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 31 | static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d; |
| 32 | static const SkColor THRESHOLD_COLOR = 0xff5faa4d; |
John Reck | bf3c602 | 2015-06-02 15:55:00 -0700 | [diff] [blame] | 33 | static const SkColor BAR_ALPHA = 0xCF000000; |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 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 | |
| 39 | namespace android { |
| 40 | namespace uirenderer { |
| 41 | |
John Reck | bf3c602 | 2015-06-02 15:55:00 -0700 | [diff] [blame] | 42 | struct BarSegment { |
| 43 | FrameInfoIndex start; |
| 44 | FrameInfoIndex end; |
| 45 | SkColor color; |
| 46 | }; |
| 47 | |
| 48 | static 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 | |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 60 | static int dpToPx(int dp, float density) { |
| 61 | return (int) (dp * density + 0.5f); |
| 62 | } |
| 63 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 64 | FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source) |
| 65 | : mFrameSource(source) { |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 66 | setDensity(1); |
| 67 | } |
| 68 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 69 | FrameInfoVisualizer::~FrameInfoVisualizer() { |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 70 | destroyData(); |
| 71 | } |
| 72 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 73 | void FrameInfoVisualizer::setDensity(float density) { |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 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 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 82 | void FrameInfoVisualizer::unionDirty(SkRect* dirty) { |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 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) { |
John Reck | 23d307c | 2014-10-27 12:38:48 -0700 | [diff] [blame] | 87 | mDirtyRegion = *dirty; |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 88 | dirty->setEmpty(); |
| 89 | } |
| 90 | } |
| 91 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 92 | void FrameInfoVisualizer::draw(OpenGLRenderer* canvas) { |
John Reck | 23d307c | 2014-10-27 12:38:48 -0700 | [diff] [blame] | 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 | } |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 103 | } |
| 104 | |
Chris Craik | 2507c34 | 2015-05-04 14:36:49 -0700 | [diff] [blame] | 105 | if (mType == ProfileType::Bars) { |
John Reck | bf3c602 | 2015-06-02 15:55:00 -0700 | [diff] [blame] | 106 | initializeRects(canvas->getViewportHeight()); |
John Reck | 23d307c | 2014-10-27 12:38:48 -0700 | [diff] [blame] | 107 | drawGraph(canvas); |
John Reck | bf3c602 | 2015-06-02 15:55:00 -0700 | [diff] [blame] | 108 | drawCurrentFrame(canvas->getViewportHeight(), canvas); |
John Reck | 23d307c | 2014-10-27 12:38:48 -0700 | [diff] [blame] | 109 | drawThreshold(canvas); |
| 110 | } |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 111 | } |
| 112 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 113 | void FrameInfoVisualizer::createData() { |
| 114 | if (mRects.get()) return; |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 115 | |
John Reck | bf3c602 | 2015-06-02 15:55:00 -0700 | [diff] [blame] | 116 | mRects.reset(new float[mFrameSource.capacity() * 4]); |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 117 | } |
| 118 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 119 | void FrameInfoVisualizer::destroyData() { |
| 120 | mRects.reset(nullptr); |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 121 | } |
| 122 | |
John Reck | bf3c602 | 2015-06-02 15:55:00 -0700 | [diff] [blame] | 123 | void 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 | } |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 134 | } |
| 135 | |
John Reck | bf3c602 | 2015-06-02 15:55:00 -0700 | [diff] [blame] | 136 | void 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); |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 148 | } |
| 149 | } |
| 150 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 151 | void FrameInfoVisualizer::drawGraph(OpenGLRenderer* canvas) { |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 152 | SkPaint paint; |
John Reck | bf3c602 | 2015-06-02 15:55:00 -0700 | [diff] [blame] | 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); |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 157 | } |
| 158 | } |
| 159 | |
John Reck | bf3c602 | 2015-06-02 15:55:00 -0700 | [diff] [blame] | 160 | void FrameInfoVisualizer::drawCurrentFrame(const int baseline, OpenGLRenderer* canvas) { |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 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); |
John Reck | bf3c602 | 2015-06-02 15:55:00 -0700 | [diff] [blame] | 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); |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 171 | } |
| 172 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 173 | void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) { |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 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 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 185 | bool FrameInfoVisualizer::consumeProperties() { |
John Reck | 23d307c | 2014-10-27 12:38:48 -0700 | [diff] [blame] | 186 | bool changed = false; |
Chris Craik | 2507c34 | 2015-05-04 14:36:49 -0700 | [diff] [blame] | 187 | ProfileType newType = Properties::getProfileType(); |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 188 | if (newType != mType) { |
| 189 | mType = newType; |
Chris Craik | 2507c34 | 2015-05-04 14:36:49 -0700 | [diff] [blame] | 190 | if (mType == ProfileType::None) { |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 191 | destroyData(); |
| 192 | } else { |
| 193 | createData(); |
| 194 | } |
John Reck | 23d307c | 2014-10-27 12:38:48 -0700 | [diff] [blame] | 195 | changed = true; |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 196 | } |
Chris Craik | 2507c34 | 2015-05-04 14:36:49 -0700 | [diff] [blame] | 197 | |
| 198 | bool showDirty = Properties::showDirtyRegions; |
John Reck | 23d307c | 2014-10-27 12:38:48 -0700 | [diff] [blame] | 199 | if (showDirty != mShowDirtyRegions) { |
| 200 | mShowDirtyRegions = showDirty; |
| 201 | changed = true; |
| 202 | } |
| 203 | return changed; |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 204 | } |
| 205 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 206 | void FrameInfoVisualizer::dumpData(int fd) { |
John Reck | 23d307c | 2014-10-27 12:38:48 -0700 | [diff] [blame] | 207 | RETURN_IF_PROFILING_DISABLED(); |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 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 | |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 213 | FILE *file = fdopen(fd, "a"); |
| 214 | fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n"); |
| 215 | |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 216 | for (size_t i = 0; i < mFrameSource.size(); i++) { |
| 217 | if (mFrameSource[i][FrameInfoIndex::kIntendedVsync] <= mLastFrameLogged) { |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 218 | continue; |
| 219 | } |
John Reck | 4c9e59d | 2015-05-12 07:17:50 -0700 | [diff] [blame] | 220 | mLastFrameLogged = mFrameSource[i][FrameInfoIndex::kIntendedVsync]; |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 221 | fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n", |
John Reck | bf3c602 | 2015-06-02 15:55:00 -0700 | [diff] [blame] | 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)); |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 226 | } |
John Reck | fe5e7b7 | 2014-05-23 17:42:28 -0700 | [diff] [blame] | 227 | |
| 228 | fflush(file); |
| 229 | } |
| 230 | |
| 231 | } /* namespace uirenderer */ |
| 232 | } /* namespace android */ |