Merge "Unify DrawProfiler/JankStats" into mnc-dev
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 @@
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.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 <utils/Timers.h>
-
-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/DrawProfiler.cpp b/libs/hwui/FrameInfoVisualizer.cpp
similarity index 60%
rename from libs/hwui/DrawProfiler.cpp
rename to libs/hwui/FrameInfoVisualizer.cpp
index 7addef9..0411742 100644
--- a/libs/hwui/DrawProfiler.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -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
@@ -55,15 +51,16 @@
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);
@@ -72,35 +69,7 @@
}
}
-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.
@@ -110,7 +79,7 @@
}
}
-void DrawProfiler::draw(OpenGLRenderer* canvas) {
+void FrameInfoVisualizer::draw(OpenGLRenderer* canvas) {
RETURN_IF_DISABLED();
if (mShowDirtyRegions) {
@@ -131,27 +100,21 @@
}
}
-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;
@@ -160,40 +123,40 @@
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);
@@ -205,7 +168,7 @@
canvas->drawLines(pts, 4, &paint);
}
-bool DrawProfiler::consumeProperties() {
+bool FrameInfoVisualizer::consumeProperties() {
bool changed = false;
ProfileType newType = Properties::getProfileType();
if (newType != mType) {
@@ -226,29 +189,25 @@
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);
}
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 <utils/Timers.h>
+
+#include <memory>
+
+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<FrameInfo, 120> 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<float*> 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 e993f74..26d8bf7 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -109,20 +109,6 @@
#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 @@
, 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 @@
return;
}
- profiler().markPlaybackStart();
mCurrentFrameInfo->markIssueDrawCommandsStart();
EGLint width, height;
@@ -251,8 +251,6 @@
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 @@
mCurrentFrameInfo->markFrameCompleted();
mJankTracker.addFrame(*mCurrentFrameInfo);
mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
- profiler().finishFrame();
}
// Called by choreographer to do an RT-driven animation
@@ -278,7 +275,6 @@
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 @@
void stopDrawing();
void notifyFramePending();
- DrawProfiler& profiler() { return mProfiler; }
+ FrameInfoVisualizer& profiler() { return mProfiler; }
void dumpFrames(int fd);
void resetFrameStats();
@@ -140,12 +140,12 @@
const sp<RenderNode> mRootRenderNode;
- DrawProfiler mProfiler;
FrameInfo* mCurrentFrameInfo = nullptr;
- // Ring buffer large enough for 1 second worth of frames
- RingBuffer<FrameInfo, 60> mFrames;
+ // Ring buffer large enough for 2 seconds worth of frames
+ RingBuffer<FrameInfo, 120> mFrames;
std::string mName;
JankTracker mJankTracker;
+ FrameInfoVisualizer mProfiler;
std::set<RenderNode*> 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::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 @@
RingBuffer() {}
~RingBuffer() {}
- size_t capacity() { return SIZE; }
+ constexpr size_t capacity() { return SIZE; }
size_t size() { return mCount; }
T& next() {