Loading libs/hwui/Android.common.mk +1 −1 Original line number Diff line number Diff line Loading @@ -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 \ Loading libs/hwui/DrawProfiler.cpp→libs/hwui/FrameInfoVisualizer.cpp +35 −76 Original line number Diff line number Diff line Loading @@ -13,19 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ #include "DrawProfiler.h" #include <cutils/compiler.h> #include "FrameInfoVisualizer.h" #include "OpenGLRenderer.h" #define DEFAULT_MAX_FRAMES 128 #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 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 Loading Loading @@ -55,15 +51,16 @@ static int dpToPx(int dp, float density) { return (int) (dp * density + 0.5f); } DrawProfiler::DrawProfiler() { FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source) : mFrameSource(source) { setDensity(1); } DrawProfiler::~DrawProfiler() { FrameInfoVisualizer::~FrameInfoVisualizer() { destroyData(); } void DrawProfiler::setDensity(float density) { void FrameInfoVisualizer::setDensity(float density) { if (CC_UNLIKELY(mDensity != density)) { mDensity = density; mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density); Loading @@ -72,35 +69,7 @@ void DrawProfiler::setDensity(float 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) { void FrameInfoVisualizer::unionDirty(SkRect* dirty) { RETURN_IF_DISABLED(); // Not worth worrying about minimizing the dirty region for debugging, so just // dirty the entire viewport. Loading @@ -110,7 +79,7 @@ void DrawProfiler::unionDirty(SkRect* dirty) { } } void DrawProfiler::draw(OpenGLRenderer* canvas) { void FrameInfoVisualizer::draw(OpenGLRenderer* canvas) { RETURN_IF_DISABLED(); if (mShowDirtyRegions) { Loading @@ -131,27 +100,21 @@ void DrawProfiler::draw(OpenGLRenderer* canvas) { } } void DrawProfiler::createData() { if (mData) return; void FrameInfoVisualizer::createData() { if (mRects.get()) 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]; mRects.reset(new float*[mFrameSource.capacity()]); for (int i = 0; i < NUM_ELEMENTS; i++) { // 4 floats per rect mRects[i] = (float*) calloc(mDataSize, 4 * sizeof(float)); mRects.get()[i] = (float*) calloc(mFrameSource.capacity(), 4 * sizeof(float)); } mCurrentFrame = 0; } void DrawProfiler::destroyData() { delete mData; mData = nullptr; void FrameInfoVisualizer::destroyData() { mRects.reset(nullptr); } void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) { void FrameInfoVisualizer::addRect(Rect& r, float data, float* shapeOutput) { r.top = r.bottom - (data * mVerticalUnit); shapeOutput[0] = r.left; shapeOutput[1] = r.top; Loading @@ -160,40 +123,40 @@ void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) { r.bottom = r.top; } void DrawProfiler::prepareShapes(const int baseline) { void FrameInfoVisualizer::prepareShapes(const int baseline) { Rect r; r.right = mHorizontalUnit; for (int i = 0; i < mDataSize; i++) { for (size_t i = 0; i < mFrameSource.size(); 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); 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 DrawProfiler::drawGraph(OpenGLRenderer* canvas) { void FrameInfoVisualizer::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); canvas->drawRects(mRects.get()[i], mFrameSource.capacity() * 4, &paint); } } void DrawProfiler::drawCurrentFrame(OpenGLRenderer* canvas) { 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 = mCurrentFrame * 4; canvas->drawRect(mRects[0][i], mRects[NUM_ELEMENTS-1][i+1], mRects[0][i+2], mRects[0][i+3], &paint); 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 DrawProfiler::drawThreshold(OpenGLRenderer* canvas) { void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) { SkPaint paint; paint.setColor(THRESHOLD_COLOR); paint.setStrokeWidth(mThresholdStroke); Loading @@ -205,7 +168,7 @@ void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) { canvas->drawLines(pts, 4, &paint); } bool DrawProfiler::consumeProperties() { bool FrameInfoVisualizer::consumeProperties() { bool changed = false; ProfileType newType = Properties::getProfileType(); if (newType != mType) { Loading @@ -226,29 +189,25 @@ bool DrawProfiler::consumeProperties() { return changed; } void DrawProfiler::dumpData(int fd) { 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. 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))) { 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", mData[i].record, mData[i].prepare, mData[i].playback, mData[i].swapBuffers); recordDuration(i), prepareDuration(i), issueDrawDuration(i), swapBuffersDuration(i)); } // reset the buffer memset(mData, 0, sizeof(FrameTimingData) * mDataSize); mCurrentFrame = 0; fflush(file); } Loading libs/hwui/DrawProfiler.h→libs/hwui/FrameInfoVisualizer.h +46 −21 Original line number Diff line number Diff line Loading @@ -16,42 +16,41 @@ #ifndef DRAWPROFILER_H #define DRAWPROFILER_H #include "FrameInfo.h" #include "Properties.h" #include "Rect.h" #include "utils/RingBuffer.h" #include <utils/Timers.h> #include <memory> namespace android { namespace uirenderer { class OpenGLRenderer; class DrawProfiler { // 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<FrameInfo, 120> FrameInfoSource; class FrameInfoVisualizer { public: DrawProfiler(); ~DrawProfiler(); FrameInfoVisualizer(FrameInfoSource& source); ~FrameInfoVisualizer(); 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(); Loading @@ -61,14 +60,39 @@ private: 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; FrameTimingData* mData = nullptr; int mDataSize = 0; int mCurrentFrame = -1; nsecs_t mPreviousTime = 0; FrameInfoSource& mFrameSource; int mVerticalUnit = 0; int mHorizontalUnit = 0; Loading @@ -81,11 +105,12 @@ private: * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record * information. */ float** mRects = nullptr; std::unique_ptr<float*> mRects; bool mShowDirtyRegions = false; SkRect mDirtyRegion; bool mFlashToggle = false; nsecs_t mLastFrameLogged = 0; }; } /* namespace uirenderer */ Loading libs/hwui/Properties.h +0 −14 Original line number Diff line number Diff line Loading @@ -108,20 +108,6 @@ enum DebugLevel { #define PROPERTY_PROFILE "debug.hwui.profile" #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. * Loading libs/hwui/renderthread/CanvasContext.cpp +2 −6 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -218,7 +219,6 @@ void CanvasContext::draw() { return; } profiler().markPlaybackStart(); mCurrentFrameInfo->markIssueDrawCommandsStart(); EGLint width, height; Loading Loading @@ -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(); Loading @@ -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 Loading @@ -278,7 +275,6 @@ void CanvasContext::doFrame() { ATRACE_CALL(); profiler().startFrame(); int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; UiFrameInfoBuilder(frameInfo) .addFlag(FrameInfoFlags::kRTAnimation) Loading Loading
libs/hwui/Android.common.mk +1 −1 Original line number Diff line number Diff line Loading @@ -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 \ Loading
libs/hwui/DrawProfiler.cpp→libs/hwui/FrameInfoVisualizer.cpp +35 −76 Original line number Diff line number Diff line Loading @@ -13,19 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ #include "DrawProfiler.h" #include <cutils/compiler.h> #include "FrameInfoVisualizer.h" #include "OpenGLRenderer.h" #define DEFAULT_MAX_FRAMES 128 #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 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 Loading Loading @@ -55,15 +51,16 @@ static int dpToPx(int dp, float density) { return (int) (dp * density + 0.5f); } DrawProfiler::DrawProfiler() { FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source) : mFrameSource(source) { setDensity(1); } DrawProfiler::~DrawProfiler() { FrameInfoVisualizer::~FrameInfoVisualizer() { destroyData(); } void DrawProfiler::setDensity(float density) { void FrameInfoVisualizer::setDensity(float density) { if (CC_UNLIKELY(mDensity != density)) { mDensity = density; mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density); Loading @@ -72,35 +69,7 @@ void DrawProfiler::setDensity(float 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) { void FrameInfoVisualizer::unionDirty(SkRect* dirty) { RETURN_IF_DISABLED(); // Not worth worrying about minimizing the dirty region for debugging, so just // dirty the entire viewport. Loading @@ -110,7 +79,7 @@ void DrawProfiler::unionDirty(SkRect* dirty) { } } void DrawProfiler::draw(OpenGLRenderer* canvas) { void FrameInfoVisualizer::draw(OpenGLRenderer* canvas) { RETURN_IF_DISABLED(); if (mShowDirtyRegions) { Loading @@ -131,27 +100,21 @@ void DrawProfiler::draw(OpenGLRenderer* canvas) { } } void DrawProfiler::createData() { if (mData) return; void FrameInfoVisualizer::createData() { if (mRects.get()) 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]; mRects.reset(new float*[mFrameSource.capacity()]); for (int i = 0; i < NUM_ELEMENTS; i++) { // 4 floats per rect mRects[i] = (float*) calloc(mDataSize, 4 * sizeof(float)); mRects.get()[i] = (float*) calloc(mFrameSource.capacity(), 4 * sizeof(float)); } mCurrentFrame = 0; } void DrawProfiler::destroyData() { delete mData; mData = nullptr; void FrameInfoVisualizer::destroyData() { mRects.reset(nullptr); } void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) { void FrameInfoVisualizer::addRect(Rect& r, float data, float* shapeOutput) { r.top = r.bottom - (data * mVerticalUnit); shapeOutput[0] = r.left; shapeOutput[1] = r.top; Loading @@ -160,40 +123,40 @@ void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) { r.bottom = r.top; } void DrawProfiler::prepareShapes(const int baseline) { void FrameInfoVisualizer::prepareShapes(const int baseline) { Rect r; r.right = mHorizontalUnit; for (int i = 0; i < mDataSize; i++) { for (size_t i = 0; i < mFrameSource.size(); 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); 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 DrawProfiler::drawGraph(OpenGLRenderer* canvas) { void FrameInfoVisualizer::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); canvas->drawRects(mRects.get()[i], mFrameSource.capacity() * 4, &paint); } } void DrawProfiler::drawCurrentFrame(OpenGLRenderer* canvas) { 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 = mCurrentFrame * 4; canvas->drawRect(mRects[0][i], mRects[NUM_ELEMENTS-1][i+1], mRects[0][i+2], mRects[0][i+3], &paint); 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 DrawProfiler::drawThreshold(OpenGLRenderer* canvas) { void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) { SkPaint paint; paint.setColor(THRESHOLD_COLOR); paint.setStrokeWidth(mThresholdStroke); Loading @@ -205,7 +168,7 @@ void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) { canvas->drawLines(pts, 4, &paint); } bool DrawProfiler::consumeProperties() { bool FrameInfoVisualizer::consumeProperties() { bool changed = false; ProfileType newType = Properties::getProfileType(); if (newType != mType) { Loading @@ -226,29 +189,25 @@ bool DrawProfiler::consumeProperties() { return changed; } void DrawProfiler::dumpData(int fd) { 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. 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))) { 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", mData[i].record, mData[i].prepare, mData[i].playback, mData[i].swapBuffers); recordDuration(i), prepareDuration(i), issueDrawDuration(i), swapBuffersDuration(i)); } // reset the buffer memset(mData, 0, sizeof(FrameTimingData) * mDataSize); mCurrentFrame = 0; fflush(file); } Loading
libs/hwui/DrawProfiler.h→libs/hwui/FrameInfoVisualizer.h +46 −21 Original line number Diff line number Diff line Loading @@ -16,42 +16,41 @@ #ifndef DRAWPROFILER_H #define DRAWPROFILER_H #include "FrameInfo.h" #include "Properties.h" #include "Rect.h" #include "utils/RingBuffer.h" #include <utils/Timers.h> #include <memory> namespace android { namespace uirenderer { class OpenGLRenderer; class DrawProfiler { // 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<FrameInfo, 120> FrameInfoSource; class FrameInfoVisualizer { public: DrawProfiler(); ~DrawProfiler(); FrameInfoVisualizer(FrameInfoSource& source); ~FrameInfoVisualizer(); 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(); Loading @@ -61,14 +60,39 @@ private: 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; FrameTimingData* mData = nullptr; int mDataSize = 0; int mCurrentFrame = -1; nsecs_t mPreviousTime = 0; FrameInfoSource& mFrameSource; int mVerticalUnit = 0; int mHorizontalUnit = 0; Loading @@ -81,11 +105,12 @@ private: * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record * information. */ float** mRects = nullptr; std::unique_ptr<float*> mRects; bool mShowDirtyRegions = false; SkRect mDirtyRegion; bool mFlashToggle = false; nsecs_t mLastFrameLogged = 0; }; } /* namespace uirenderer */ Loading
libs/hwui/Properties.h +0 −14 Original line number Diff line number Diff line Loading @@ -108,20 +108,6 @@ enum DebugLevel { #define PROPERTY_PROFILE "debug.hwui.profile" #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. * Loading
libs/hwui/renderthread/CanvasContext.cpp +2 −6 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -218,7 +219,6 @@ void CanvasContext::draw() { return; } profiler().markPlaybackStart(); mCurrentFrameInfo->markIssueDrawCommandsStart(); EGLint width, height; Loading Loading @@ -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(); Loading @@ -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 Loading @@ -278,7 +275,6 @@ void CanvasContext::doFrame() { ATRACE_CALL(); profiler().startFrame(); int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; UiFrameInfoBuilder(frameInfo) .addFlag(FrameInfoFlags::kRTAnimation) Loading