From 4c9e59d03c2bca38001225b79d01740b8999adfb Mon Sep 17 00:00:00 2001 From: John Reck Date: Tue, 12 May 2015 07:17:50 -0700 Subject: Unify DrawProfiler/JankStats Bug: 20822400 Change-Id: I24345c3120440bfce14e8cbe7e880b39f10b744a --- libs/hwui/Android.common.mk | 2 +- libs/hwui/DrawProfiler.cpp | 257 ------------------------------- libs/hwui/DrawProfiler.h | 94 ----------- libs/hwui/FrameInfoVisualizer.cpp | 216 ++++++++++++++++++++++++++ libs/hwui/FrameInfoVisualizer.h | 119 ++++++++++++++ libs/hwui/Properties.h | 14 -- libs/hwui/renderthread/CanvasContext.cpp | 8 +- libs/hwui/renderthread/CanvasContext.h | 10 +- libs/hwui/renderthread/DrawFrameTask.cpp | 2 - libs/hwui/utils/RingBuffer.h | 2 +- 10 files changed, 344 insertions(+), 380 deletions(-) delete mode 100644 libs/hwui/DrawProfiler.cpp delete mode 100644 libs/hwui/DrawProfiler.h create mode 100644 libs/hwui/FrameInfoVisualizer.cpp create mode 100644 libs/hwui/FrameInfoVisualizer.h (limited to 'libs') diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk index 8a4e609..3e76656 100644 --- a/libs/hwui/Android.common.mk +++ b/libs/hwui/Android.common.mk @@ -40,11 +40,11 @@ LOCAL_SRC_FILES := \ DisplayList.cpp \ DisplayListCanvas.cpp \ Dither.cpp \ - DrawProfiler.cpp \ Extensions.cpp \ FboCache.cpp \ FontRenderer.cpp \ FrameInfo.cpp \ + FrameInfoVisualizer.cpp \ GammaFontRenderer.cpp \ GlopBuilder.cpp \ GradientCache.cpp \ diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp deleted file mode 100644 index 7addef9..0000000 --- a/libs/hwui/DrawProfiler.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 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 "DrawProfiler.h" - -#include - -#include "OpenGLRenderer.h" - -#define DEFAULT_MAX_FRAMES 128 - -#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 NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f) - -#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); -} - -DrawProfiler::DrawProfiler() { - setDensity(1); -} - -DrawProfiler::~DrawProfiler() { - destroyData(); -} - -void DrawProfiler::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 DrawProfiler::startFrame(nsecs_t recordDurationNanos) { - RETURN_IF_PROFILING_DISABLED(); - mData[mCurrentFrame].record = NANOS_TO_MILLIS_FLOAT(recordDurationNanos); - mPreviousTime = systemTime(CLOCK_MONOTONIC); -} - -void DrawProfiler::markPlaybackStart() { - RETURN_IF_PROFILING_DISABLED(); - nsecs_t now = systemTime(CLOCK_MONOTONIC); - mData[mCurrentFrame].prepare = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime); - mPreviousTime = now; -} - -void DrawProfiler::markPlaybackEnd() { - RETURN_IF_PROFILING_DISABLED(); - nsecs_t now = systemTime(CLOCK_MONOTONIC); - mData[mCurrentFrame].playback = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime); - mPreviousTime = now; -} - -void DrawProfiler::finishFrame() { - RETURN_IF_PROFILING_DISABLED(); - nsecs_t now = systemTime(CLOCK_MONOTONIC); - mData[mCurrentFrame].swapBuffers = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime); - mPreviousTime = now; - mCurrentFrame = (mCurrentFrame + 1) % mDataSize; -} - -void DrawProfiler::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 DrawProfiler::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 DrawProfiler::createData() { - if (mData) return; - - mDataSize = property_get_int32(PROPERTY_PROFILE_MAXFRAMES, DEFAULT_MAX_FRAMES); - if (mDataSize <= 0) mDataSize = 1; - if (mDataSize > 4096) mDataSize = 4096; // Reasonable maximum - mData = (FrameTimingData*) calloc(mDataSize, sizeof(FrameTimingData)); - mRects = new float*[NUM_ELEMENTS]; - for (int i = 0; i < NUM_ELEMENTS; i++) { - // 4 floats per rect - mRects[i] = (float*) calloc(mDataSize, 4 * sizeof(float)); - } - mCurrentFrame = 0; -} - -void DrawProfiler::destroyData() { - delete mData; - mData = nullptr; -} - -void DrawProfiler::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 DrawProfiler::prepareShapes(const int baseline) { - Rect r; - r.right = mHorizontalUnit; - for (int i = 0; i < mDataSize; i++) { - const int shapeIndex = i * 4; - r.bottom = baseline; - addRect(r, mData[i].record, mRects[RECORD_INDEX] + shapeIndex); - addRect(r, mData[i].prepare, mRects[PREPARE_INDEX] + shapeIndex); - addRect(r, mData[i].playback, mRects[PLAYBACK_INDEX] + shapeIndex); - addRect(r, mData[i].swapBuffers, mRects[SWAPBUFFERS_INDEX] + shapeIndex); - r.translate(mHorizontalUnit, 0); - } -} - -void DrawProfiler::drawGraph(OpenGLRenderer* canvas) { - SkPaint paint; - for (int i = 0; i < NUM_ELEMENTS; i++) { - paint.setColor(ELEMENT_COLORS[i]); - canvas->drawRects(mRects[i], mDataSize * 4, &paint); - } -} - -void DrawProfiler::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 = mCurrentFrame * 4; - canvas->drawRect(mRects[0][i], mRects[NUM_ELEMENTS-1][i+1], mRects[0][i+2], - mRects[0][i+3], &paint); -} - -void DrawProfiler::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 DrawProfiler::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 DrawProfiler::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. - - const FrameTimingData emptyData = {0, 0, 0, 0}; - - FILE *file = fdopen(fd, "a"); - fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n"); - - for (int frameOffset = 1; frameOffset <= mDataSize; frameOffset++) { - int i = (mCurrentFrame + frameOffset) % mDataSize; - if (!memcmp(mData + i, &emptyData, sizeof(FrameTimingData))) { - continue; - } - fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n", - mData[i].record, mData[i].prepare, mData[i].playback, mData[i].swapBuffers); - } - // reset the buffer - memset(mData, 0, sizeof(FrameTimingData) * mDataSize); - mCurrentFrame = 0; - - fflush(file); -} - -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h deleted file mode 100644 index ef6101c..0000000 --- a/libs/hwui/DrawProfiler.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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. - */ -#ifndef DRAWPROFILER_H -#define DRAWPROFILER_H - -#include "Properties.h" -#include "Rect.h" - -#include - -namespace android { -namespace uirenderer { - -class OpenGLRenderer; - -class DrawProfiler { -public: - DrawProfiler(); - ~DrawProfiler(); - - bool consumeProperties(); - void setDensity(float density); - - void startFrame(nsecs_t recordDurationNanos = 0); - void markPlaybackStart(); - void markPlaybackEnd(); - void finishFrame(); - - void unionDirty(SkRect* dirty); - void draw(OpenGLRenderer* canvas); - - void dumpData(int fd); - -private: - typedef struct { - float record; - float prepare; - float playback; - float swapBuffers; - } FrameTimingData; - - void createData(); - void destroyData(); - - void addRect(Rect& r, float data, float* shapeOutput); - void prepareShapes(const int baseline); - void drawGraph(OpenGLRenderer* canvas); - void drawCurrentFrame(OpenGLRenderer* canvas); - void drawThreshold(OpenGLRenderer* canvas); - - ProfileType mType = ProfileType::None; - float mDensity = 0; - - FrameTimingData* mData = nullptr; - int mDataSize = 0; - - int mCurrentFrame = -1; - nsecs_t mPreviousTime = 0; - - int mVerticalUnit = 0; - int mHorizontalUnit = 0; - int mThresholdStroke = 0; - - /* - * mRects represents an array of rect shapes, divided into NUM_ELEMENTS - * groups such that each group is drawn with the same paint. - * For example mRects[0] is the array of rect floats suitable for - * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record - * information. - */ - float** mRects = nullptr; - - bool mShowDirtyRegions = false; - SkRect mDirtyRegion; - bool mFlashToggle = false; -}; - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif /* DRAWPROFILER_H */ 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 + +#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 */ diff --git a/libs/hwui/FrameInfoVisualizer.h b/libs/hwui/FrameInfoVisualizer.h new file mode 100644 index 0000000..f62e34d --- /dev/null +++ b/libs/hwui/FrameInfoVisualizer.h @@ -0,0 +1,119 @@ +/* + * 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. + */ +#ifndef DRAWPROFILER_H +#define DRAWPROFILER_H + +#include "FrameInfo.h" +#include "Properties.h" +#include "Rect.h" +#include "utils/RingBuffer.h" + +#include + +#include + +namespace android { +namespace uirenderer { + +class OpenGLRenderer; + +// TODO: This is a bit awkward as it needs to match the thing in CanvasContext +// A better abstraction here would be nice but iterators are painful +// and RingBuffer having the size baked into the template is also painful +// But making DrawProfiler also be templated is ALSO painful +// At least this is a compile failure if this doesn't match, so there's that. +typedef RingBuffer FrameInfoSource; + +class FrameInfoVisualizer { +public: + FrameInfoVisualizer(FrameInfoSource& source); + ~FrameInfoVisualizer(); + + bool consumeProperties(); + void setDensity(float density); + + void unionDirty(SkRect* dirty); + void draw(OpenGLRenderer* canvas); + + void dumpData(int fd); + +private: + void createData(); + void destroyData(); + + void addRect(Rect& r, float data, float* shapeOutput); + void prepareShapes(const int baseline); + void drawGraph(OpenGLRenderer* canvas); + void drawCurrentFrame(OpenGLRenderer* canvas); + void drawThreshold(OpenGLRenderer* canvas); + + static inline float duration(nsecs_t start, nsecs_t end) { + float duration = ((end - start) * 0.000001f); + return duration > 0.0f ? duration : 0.0f; + } + + inline float recordDuration(size_t index) { + return duration( + mFrameSource[index][FrameInfoIndex::kIntendedVsync], + mFrameSource[index][FrameInfoIndex::kSyncStart]); + } + + inline float prepareDuration(size_t index) { + return duration( + mFrameSource[index][FrameInfoIndex::kSyncStart], + mFrameSource[index][FrameInfoIndex::kIssueDrawCommandsStart]); + } + + inline float issueDrawDuration(size_t index) { + return duration( + mFrameSource[index][FrameInfoIndex::kIssueDrawCommandsStart], + mFrameSource[index][FrameInfoIndex::kSwapBuffers]); + } + + inline float swapBuffersDuration(size_t index) { + return duration( + mFrameSource[index][FrameInfoIndex::kSwapBuffers], + mFrameSource[index][FrameInfoIndex::kFrameCompleted]); + } + + ProfileType mType = ProfileType::None; + float mDensity = 0; + + FrameInfoSource& mFrameSource; + + int mVerticalUnit = 0; + int mHorizontalUnit = 0; + int mThresholdStroke = 0; + + /* + * mRects represents an array of rect shapes, divided into NUM_ELEMENTS + * groups such that each group is drawn with the same paint. + * For example mRects[0] is the array of rect floats suitable for + * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record + * information. + */ + std::unique_ptr mRects; + + bool mShowDirtyRegions = false; + SkRect mDirtyRegion; + bool mFlashToggle = false; + nsecs_t mLastFrameLogged = 0; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* DRAWPROFILER_H */ diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index cb5560f..e34444a 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -109,20 +109,6 @@ enum DebugLevel { #define PROPERTY_PROFILE_VISUALIZE_BARS "visual_bars" /** - * System property used to specify the number of frames to be used - * when doing hardware rendering profiling. - * The default value of this property is #PROFILE_MAX_FRAMES. - * - * When profiling is enabled, the adb shell dumpsys gfxinfo command will - * output extra information about the time taken to execute by the last - * frames. - * - * Possible values: - * "60", to set the limit of frames to 60 - */ -#define PROPERTY_PROFILE_MAXFRAMES "debug.hwui.profile.maxframes" - -/** * Used to enable/disable non-rectangular clipping debugging. * * The accepted values are: diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 733e5e0..6d7dcf1 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -46,7 +46,8 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, , mOpaque(!translucent) , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) , mRootRenderNode(rootRenderNode) - , mJankTracker(thread.timeLord().frameIntervalNanos()) { + , mJankTracker(thread.timeLord().frameIntervalNanos()) + , mProfiler(mFrames) { mRenderThread.renderState().registerCanvasContext(this); mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); } @@ -218,7 +219,6 @@ void CanvasContext::draw() { return; } - profiler().markPlaybackStart(); mCurrentFrameInfo->markIssueDrawCommandsStart(); EGLint width, height; @@ -251,8 +251,6 @@ void CanvasContext::draw() { bool drew = mCanvas->finish(); - profiler().markPlaybackEnd(); - // Even if we decided to cancel the frame, from the perspective of jank // metrics the frame was swapped at this point mCurrentFrameInfo->markSwapBuffers(); @@ -267,7 +265,6 @@ void CanvasContext::draw() { mCurrentFrameInfo->markFrameCompleted(); mJankTracker.addFrame(*mCurrentFrameInfo); mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); - profiler().finishFrame(); } // Called by choreographer to do an RT-driven animation @@ -278,7 +275,6 @@ void CanvasContext::doFrame() { ATRACE_CALL(); - profiler().startFrame(); int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; UiFrameInfoBuilder(frameInfo) .addFlag(FrameInfoFlags::kRTAnimation) diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 8163b0f..8d54304 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -18,9 +18,9 @@ #define CANVASCONTEXT_H_ #include "DamageAccumulator.h" -#include "DrawProfiler.h" #include "IContextFactory.h" #include "FrameInfo.h" +#include "FrameInfoVisualizer.h" #include "RenderNode.h" #include "utils/RingBuffer.h" #include "renderthread/RenderTask.h" @@ -103,7 +103,7 @@ public: void stopDrawing(); void notifyFramePending(); - DrawProfiler& profiler() { return mProfiler; } + FrameInfoVisualizer& profiler() { return mProfiler; } void dumpFrames(int fd); void resetFrameStats(); @@ -140,12 +140,12 @@ private: const sp mRootRenderNode; - DrawProfiler mProfiler; FrameInfo* mCurrentFrameInfo = nullptr; - // Ring buffer large enough for 1 second worth of frames - RingBuffer mFrames; + // Ring buffer large enough for 2 seconds worth of frames + RingBuffer mFrames; std::string mName; JankTracker mJankTracker; + FrameInfoVisualizer mProfiler; std::set mPrefetechedLayers; }; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 35391b2..83af4ae 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -83,8 +83,6 @@ void DrawFrameTask::postAndWait() { void DrawFrameTask::run() { ATRACE_NAME("DrawFrame"); - mContext->profiler().startFrame(); - bool canUnblockUiThread; bool canDrawThisFrame; { diff --git a/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h index fc9aec0..6d0a06b 100644 --- a/libs/hwui/utils/RingBuffer.h +++ b/libs/hwui/utils/RingBuffer.h @@ -31,7 +31,7 @@ public: RingBuffer() {} ~RingBuffer() {} - size_t capacity() { return SIZE; } + constexpr size_t capacity() { return SIZE; } size_t size() { return mCount; } T& next() { -- cgit v1.1