diff options
author | John Reck <jreck@google.com> | 2015-05-12 07:17:50 -0700 |
---|---|---|
committer | John Reck <jreck@google.com> | 2015-05-12 11:02:07 -0700 |
commit | 4c9e59d03c2bca38001225b79d01740b8999adfb (patch) | |
tree | 140ff328b9966bdd974a7868c631eebdc1fea76f /libs/hwui/FrameInfoVisualizer.cpp | |
parent | 4bd4121d584e16c700ba08828c675b7f906ff331 (diff) | |
download | frameworks_base-4c9e59d03c2bca38001225b79d01740b8999adfb.zip frameworks_base-4c9e59d03c2bca38001225b79d01740b8999adfb.tar.gz frameworks_base-4c9e59d03c2bca38001225b79d01740b8999adfb.tar.bz2 |
Unify DrawProfiler/JankStats
Bug: 20822400
Change-Id: I24345c3120440bfce14e8cbe7e880b39f10b744a
Diffstat (limited to 'libs/hwui/FrameInfoVisualizer.cpp')
-rw-r--r-- | libs/hwui/FrameInfoVisualizer.cpp | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp new file mode 100644 index 0000000..0411742 --- /dev/null +++ b/libs/hwui/FrameInfoVisualizer.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "FrameInfoVisualizer.h" + +#include "OpenGLRenderer.h" + +#include <cutils/compiler.h> + +#define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == ProfileType::None)) return +#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return + +#define PROFILE_DRAW_WIDTH 3 +#define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2 +#define PROFILE_DRAW_DP_PER_MS 7 + +// Number of floats we want to display from FrameTimingData +// If this is changed make sure to update the indexes below +#define NUM_ELEMENTS 4 + +#define RECORD_INDEX 0 +#define PREPARE_INDEX 1 +#define PLAYBACK_INDEX 2 +#define SWAPBUFFERS_INDEX 3 + +// Must be NUM_ELEMENTS in size +static const SkColor ELEMENT_COLORS[] = { 0xcf3e66cc, 0xcf8f00ff, 0xcfdc3912, 0xcfe69800 }; +static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d; +static const SkColor THRESHOLD_COLOR = 0xff5faa4d; + +// We could get this from TimeLord and use the actual frame interval, but +// this is good enough +#define FRAME_THRESHOLD 16 + +namespace android { +namespace uirenderer { + +static int dpToPx(int dp, float density) { + return (int) (dp * density + 0.5f); +} + +FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source) + : mFrameSource(source) { + setDensity(1); +} + +FrameInfoVisualizer::~FrameInfoVisualizer() { + destroyData(); +} + +void FrameInfoVisualizer::setDensity(float density) { + if (CC_UNLIKELY(mDensity != density)) { + mDensity = density; + mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density); + mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density); + mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density); + } +} + +void FrameInfoVisualizer::unionDirty(SkRect* dirty) { + RETURN_IF_DISABLED(); + // Not worth worrying about minimizing the dirty region for debugging, so just + // dirty the entire viewport. + if (dirty) { + mDirtyRegion = *dirty; + dirty->setEmpty(); + } +} + +void FrameInfoVisualizer::draw(OpenGLRenderer* canvas) { + RETURN_IF_DISABLED(); + + if (mShowDirtyRegions) { + mFlashToggle = !mFlashToggle; + if (mFlashToggle) { + SkPaint paint; + paint.setColor(0x7fff0000); + canvas->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop, + mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint); + } + } + + if (mType == ProfileType::Bars) { + prepareShapes(canvas->getViewportHeight()); + drawGraph(canvas); + drawCurrentFrame(canvas); + drawThreshold(canvas); + } +} + +void FrameInfoVisualizer::createData() { + if (mRects.get()) return; + + mRects.reset(new float*[mFrameSource.capacity()]); + for (int i = 0; i < NUM_ELEMENTS; i++) { + // 4 floats per rect + mRects.get()[i] = (float*) calloc(mFrameSource.capacity(), 4 * sizeof(float)); + } +} + +void FrameInfoVisualizer::destroyData() { + mRects.reset(nullptr); +} + +void FrameInfoVisualizer::addRect(Rect& r, float data, float* shapeOutput) { + r.top = r.bottom - (data * mVerticalUnit); + shapeOutput[0] = r.left; + shapeOutput[1] = r.top; + shapeOutput[2] = r.right; + shapeOutput[3] = r.bottom; + r.bottom = r.top; +} + +void FrameInfoVisualizer::prepareShapes(const int baseline) { + Rect r; + r.right = mHorizontalUnit; + for (size_t i = 0; i < mFrameSource.size(); i++) { + const int shapeIndex = i * 4; + r.bottom = baseline; + addRect(r, recordDuration(i), mRects.get()[RECORD_INDEX] + shapeIndex); + addRect(r, prepareDuration(i), mRects.get()[PREPARE_INDEX] + shapeIndex); + addRect(r, issueDrawDuration(i), mRects.get()[PLAYBACK_INDEX] + shapeIndex); + addRect(r, swapBuffersDuration(i), mRects.get()[SWAPBUFFERS_INDEX] + shapeIndex); + r.translate(mHorizontalUnit, 0); + } +} + +void FrameInfoVisualizer::drawGraph(OpenGLRenderer* canvas) { + SkPaint paint; + for (int i = 0; i < NUM_ELEMENTS; i++) { + paint.setColor(ELEMENT_COLORS[i]); + canvas->drawRects(mRects.get()[i], mFrameSource.capacity() * 4, &paint); + } +} + +void FrameInfoVisualizer::drawCurrentFrame(OpenGLRenderer* canvas) { + // This draws a solid rect over the entirety of the current frame's shape + // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1] + // which will therefore fully overlap the previously drawn rects + SkPaint paint; + paint.setColor(CURRENT_FRAME_COLOR); + const int i = (mFrameSource.size() - 1) * 4; + canvas->drawRect(mRects.get()[0][i], mRects.get()[NUM_ELEMENTS-1][i+1], + mRects.get()[0][i+2], mRects.get()[0][i+3], &paint); +} + +void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) { + SkPaint paint; + paint.setColor(THRESHOLD_COLOR); + paint.setStrokeWidth(mThresholdStroke); + + float pts[4]; + pts[0] = 0.0f; + pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit); + pts[2] = canvas->getViewportWidth(); + canvas->drawLines(pts, 4, &paint); +} + +bool FrameInfoVisualizer::consumeProperties() { + bool changed = false; + ProfileType newType = Properties::getProfileType(); + if (newType != mType) { + mType = newType; + if (mType == ProfileType::None) { + destroyData(); + } else { + createData(); + } + changed = true; + } + + bool showDirty = Properties::showDirtyRegions; + if (showDirty != mShowDirtyRegions) { + mShowDirtyRegions = showDirty; + changed = true; + } + return changed; +} + +void FrameInfoVisualizer::dumpData(int fd) { + RETURN_IF_PROFILING_DISABLED(); + + // This method logs the last N frames (where N is <= mDataSize) since the + // last call to dumpData(). In other words if there's a dumpData(), draw frame, + // dumpData(), the last dumpData() should only log 1 frame. + + FILE *file = fdopen(fd, "a"); + fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n"); + + for (size_t i = 0; i < mFrameSource.size(); i++) { + if (mFrameSource[i][FrameInfoIndex::kIntendedVsync] <= mLastFrameLogged) { + continue; + } + mLastFrameLogged = mFrameSource[i][FrameInfoIndex::kIntendedVsync]; + fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n", + recordDuration(i), prepareDuration(i), + issueDrawDuration(i), swapBuffersDuration(i)); + } + + fflush(file); +} + +} /* namespace uirenderer */ +} /* namespace android */ |