Merge "Move frame history into jank tracker"
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 92d53da6..113a477 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -202,6 +202,7 @@
"PathTessellator.cpp",
"PixelBuffer.cpp",
"ProfileData.cpp",
+ "ProfileDataContainer.cpp",
"ProfileRenderer.cpp",
"Program.cpp",
"ProgramCache.cpp",
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index f9671ed4..9d11828 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -65,11 +65,8 @@
// and filter it out of the frame profile data
static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync;
-JankTracker::JankTracker(const DisplayInfo& displayInfo) {
- // By default this will use malloc memory. It may be moved later to ashmem
- // if there is shared space for it and a request comes in to do that.
- mData = new ProfileData;
- reset();
+JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo) {
+ mGlobalData = globalData;
nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps);
#if USE_HWC2
nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms);
@@ -89,60 +86,6 @@
setFrameInterval(frameIntervalNanos);
}
-JankTracker::~JankTracker() {
- freeData();
-}
-
-void JankTracker::freeData() {
- if (mIsMapped) {
- munmap(mData, sizeof(ProfileData));
- } else {
- delete mData;
- }
- mIsMapped = false;
- mData = nullptr;
-}
-
-void JankTracker::rotateStorage() {
- // If we are mapped we want to stop using the ashmem backend and switch to malloc
- // We are expecting a switchStorageToAshmem call to follow this, but it's not guaranteed
- // If we aren't sitting on top of ashmem then just do a reset() as it's functionally
- // equivalent do a free, malloc, reset.
- if (mIsMapped) {
- freeData();
- mData = new ProfileData;
- }
- reset();
-}
-
-void JankTracker::switchStorageToAshmem(int ashmemfd) {
- int regionSize = ashmem_get_size_region(ashmemfd);
- if (regionSize < 0) {
- int err = errno;
- ALOGW("Failed to get ashmem region size from fd %d, err %d %s", ashmemfd, err, strerror(err));
- return;
- }
- if (regionSize < static_cast<int>(sizeof(ProfileData))) {
- ALOGW("Ashmem region is too small! Received %d, required %u",
- regionSize, static_cast<unsigned int>(sizeof(ProfileData)));
- return;
- }
- ProfileData* newData = reinterpret_cast<ProfileData*>(
- mmap(NULL, sizeof(ProfileData), PROT_READ | PROT_WRITE,
- MAP_SHARED, ashmemfd, 0));
- if (newData == MAP_FAILED) {
- int err = errno;
- ALOGW("Failed to move profile data to ashmem fd %d, error = %d",
- ashmemfd, err);
- return;
- }
-
- newData->mergeWith(*mData);
- freeData();
- mData = newData;
- mIsMapped = true;
-}
-
void JankTracker::setFrameInterval(nsecs_t frameInterval) {
mFrameInterval = frameInterval;
mThresholds[kMissedVsync] = 1;
@@ -166,7 +109,7 @@
}
-void JankTracker::addFrame(const FrameInfo& frame) {
+void JankTracker::finishFrame(const FrameInfo& frame) {
// Fast-path for jank-free frames
int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted);
if (mDequeueTimeForgiveness
@@ -187,6 +130,7 @@
}
LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64, totalDuration);
mData->reportFrame(totalDuration);
+ (*mGlobalData)->reportFrame(totalDuration);
// Keep the fast path as fast as possible.
if (CC_LIKELY(totalDuration < mFrameInterval)) {
@@ -199,11 +143,13 @@
}
mData->reportJank();
+ (*mGlobalData)->reportJank();
for (int i = 0; i < NUM_BUCKETS; i++) {
int64_t delta = frame.duration(COMPARISONS[i].start, COMPARISONS[i].end);
if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
mData->reportJankType((JankType) i);
+ (*mGlobalData)->reportJankType((JankType) i);
}
}
}
@@ -228,8 +174,31 @@
dprintf(fd, "\n");
}
+void JankTracker::dumpFrames(int fd) {
+ FILE* file = fdopen(fd, "a");
+ fprintf(file, "\n\n---PROFILEDATA---\n");
+ for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
+ fprintf(file, "%s", FrameInfoNames[i].c_str());
+ fprintf(file, ",");
+ }
+ for (size_t i = 0; i < mFrames.size(); i++) {
+ FrameInfo& frame = mFrames[i];
+ if (frame[FrameInfoIndex::SyncStart] == 0) {
+ continue;
+ }
+ fprintf(file, "\n");
+ for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) {
+ fprintf(file, "%" PRId64 ",", frame[i]);
+ }
+ }
+ fprintf(file, "\n---PROFILEDATA---\n\n");
+ fflush(file);
+}
+
void JankTracker::reset() {
+ mFrames.clear();
mData->reset();
+ (*mGlobalData)->reset();
sFrameStart = Properties::filterOutTestOverhead
? FrameInfoIndex::HandleInputStart
: FrameInfoIndex::IntendedVsync;
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 2c567b3..e56c079 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -18,6 +18,7 @@
#include "FrameInfo.h"
#include "ProfileData.h"
+#include "ProfileDataContainer.h"
#include "renderthread/TimeLord.h"
#include "utils/RingBuffer.h"
@@ -48,26 +49,25 @@
// TODO: Replace DrawProfiler with this
class JankTracker {
public:
- explicit JankTracker(const DisplayInfo& displayInfo);
- ~JankTracker();
+ explicit JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo);
void setDescription(JankTrackerType type, const std::string&& name) {
mDescription.type = type;
mDescription.name = name;
}
- void addFrame(const FrameInfo& frame);
+ FrameInfo* startFrame() { return &mFrames.next(); }
+ void finishFrame(const FrameInfo& frame);
- void dump(int fd) { dumpData(fd, &mDescription, mData); }
+ void dumpStats(int fd) { dumpData(fd, &mDescription, mData.get()); }
+ void dumpFrames(int fd);
void reset();
- void rotateStorage();
- void switchStorageToAshmem(int ashmemfd);
-
- uint32_t findPercentile(int p) { return mData->findPercentile(p); }
+ // Exposed for FrameInfoVisualizer
+ // TODO: Figure out a better way to handle this
+ RingBuffer<FrameInfo, 120>& frames() { return mFrames; }
private:
- void freeData();
void setFrameInterval(nsecs_t frameIntervalNanos);
static void dumpData(int fd, const ProfileDataDescription* description, const ProfileData* data);
@@ -82,9 +82,12 @@
// This is only used if we are in pipelined mode and are using HWC2,
// otherwise it's 0.
nsecs_t mDequeueTimeForgiveness = 0;
- ProfileData* mData;
- bool mIsMapped = false;
+ ProfileDataContainer mData;
+ ProfileDataContainer* mGlobalData;
ProfileDataDescription mDescription;
+
+ // Ring buffer large enough for 2 seconds worth of frames
+ RingBuffer<FrameInfo, 120> mFrames;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/ProfileDataContainer.cpp b/libs/hwui/ProfileDataContainer.cpp
new file mode 100644
index 0000000..cbf3eb3
--- /dev/null
+++ b/libs/hwui/ProfileDataContainer.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 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 "ProfileDataContainer.h"
+
+#include <log/log.h>
+#include <cutils/ashmem.h>
+
+#include <sys/mman.h>
+
+namespace android {
+namespace uirenderer {
+
+void ProfileDataContainer::freeData() {
+ if (mIsMapped) {
+ munmap(mData, sizeof(ProfileData));
+ } else {
+ delete mData;
+ }
+ mIsMapped = false;
+ mData = nullptr;
+}
+
+void ProfileDataContainer::rotateStorage() {
+ // If we are mapped we want to stop using the ashmem backend and switch to malloc
+ // We are expecting a switchStorageToAshmem call to follow this, but it's not guaranteed
+ // If we aren't sitting on top of ashmem then just do a reset() as it's functionally
+ // equivalent do a free, malloc, reset.
+ if (mIsMapped) {
+ freeData();
+ mData = new ProfileData;
+ }
+ mData->reset();
+}
+
+void ProfileDataContainer::switchStorageToAshmem(int ashmemfd) {
+ int regionSize = ashmem_get_size_region(ashmemfd);
+ if (regionSize < 0) {
+ int err = errno;
+ ALOGW("Failed to get ashmem region size from fd %d, err %d %s", ashmemfd, err, strerror(err));
+ return;
+ }
+ if (regionSize < static_cast<int>(sizeof(ProfileData))) {
+ ALOGW("Ashmem region is too small! Received %d, required %u",
+ regionSize, static_cast<unsigned int>(sizeof(ProfileData)));
+ return;
+ }
+ ProfileData* newData = reinterpret_cast<ProfileData*>(
+ mmap(NULL, sizeof(ProfileData), PROT_READ | PROT_WRITE,
+ MAP_SHARED, ashmemfd, 0));
+ if (newData == MAP_FAILED) {
+ int err = errno;
+ ALOGW("Failed to move profile data to ashmem fd %d, error = %d",
+ ashmemfd, err);
+ return;
+ }
+
+ newData->mergeWith(*mData);
+ freeData();
+ mData = newData;
+ mIsMapped = true;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
\ No newline at end of file
diff --git a/libs/hwui/ProfileDataContainer.h b/libs/hwui/ProfileDataContainer.h
new file mode 100644
index 0000000..d2de241
--- /dev/null
+++ b/libs/hwui/ProfileDataContainer.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#include "ProfileData.h"
+#include "utils/Macros.h"
+
+namespace android {
+namespace uirenderer {
+
+class ProfileDataContainer {
+ PREVENT_COPY_AND_ASSIGN(ProfileDataContainer);
+public:
+ explicit ProfileDataContainer() {}
+
+ ~ProfileDataContainer() { freeData(); }
+
+ void rotateStorage();
+ void switchStorageToAshmem(int ashmemfd);
+
+ ProfileData* get() { return mData; }
+ ProfileData* operator->() { return mData; }
+
+private:
+ void freeData();
+
+ // By default this will use malloc memory. It may be moved later to ashmem
+ // if there is shared space for it and a request comes in to do that.
+ ProfileData* mData = new ProfileData;
+ bool mIsMapped = false;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
\ No newline at end of file
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 7799248..5d7f594 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -142,8 +142,8 @@
: mRenderThread(thread)
, mOpaque(!translucent)
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
- , mJankTracker(thread.mainDisplayInfo())
- , mProfiler(mFrames)
+ , mJankTracker(&thread.globalProfileData(), thread.mainDisplayInfo())
+ , mProfiler(mJankTracker.frames())
, mContentDrawBounds(0, 0, 0, 0)
, mRenderPipeline(std::move(renderPipeline)) {
rootRenderNode->makeRoot();
@@ -321,7 +321,7 @@
// If the previous frame was dropped we don't need to hold onto it, so
// just keep using the previous frame's structure instead
if (!wasSkipped(mCurrentFrameInfo)) {
- mCurrentFrameInfo = &mFrames.next();
+ mCurrentFrameInfo = mJankTracker.startFrame();
}
mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
@@ -485,8 +485,7 @@
}
#endif
- mJankTracker.addFrame(*mCurrentFrameInfo);
- mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
+ mJankTracker.finishFrame(*mCurrentFrameInfo);
if (CC_UNLIKELY(mFrameMetricsReporter.get() != nullptr)) {
mFrameMetricsReporter->reportFrameMetrics(mCurrentFrameInfo->data());
}
@@ -625,30 +624,12 @@
}
void CanvasContext::dumpFrames(int fd) {
- mJankTracker.dump(fd);
- FILE* file = fdopen(fd, "a");
- fprintf(file, "\n\n---PROFILEDATA---\n");
- for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
- fprintf(file, "%s", FrameInfoNames[i].c_str());
- fprintf(file, ",");
- }
- for (size_t i = 0; i < mFrames.size(); i++) {
- FrameInfo& frame = mFrames[i];
- if (frame[FrameInfoIndex::SyncStart] == 0) {
- continue;
- }
- fprintf(file, "\n");
- for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) {
- fprintf(file, "%" PRId64 ",", frame[i]);
- }
- }
- fprintf(file, "\n---PROFILEDATA---\n\n");
- fflush(file);
+ mJankTracker.dumpStats(fd);
+ mJankTracker.dumpFrames(fd);
}
void CanvasContext::resetFrameStats() {
- mFrames.clear();
- mRenderThread.jankTracker().reset();
+ mJankTracker.reset();
}
void CanvasContext::setName(const std::string&& name) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index b1f4050..aa6d2f3 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -29,7 +29,6 @@
#include "RenderNode.h"
#include "thread/Task.h"
#include "thread/TaskProcessor.h"
-#include "utils/RingBuffer.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
@@ -253,8 +252,6 @@
std::vector< sp<RenderNode> > mRenderNodes;
FrameInfo* mCurrentFrameInfo = nullptr;
- // Ring buffer large enough for 2 seconds worth of frames
- RingBuffer<FrameInfo, 120> mFrames;
std::string mName;
JankTracker mJankTracker;
FrameInfoVisualizer mProfiler;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 80c2955..370cf52 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -427,12 +427,12 @@
if (args->dumpFlags & DumpFlags::FrameStats) {
args->context->dumpFrames(args->fd);
}
+ if (args->dumpFlags & DumpFlags::JankStats) {
+ args->thread->globalProfileData()->dump(args->fd);
+ }
if (args->dumpFlags & DumpFlags::Reset) {
args->context->resetFrameStats();
}
- if (args->dumpFlags & DumpFlags::JankStats) {
- args->thread->jankTracker().dump(args->fd);
- }
return nullptr;
}
@@ -458,7 +458,7 @@
CREATE_BRIDGE2(frameTimePercentile, RenderThread* thread, int percentile) {
return reinterpret_cast<void*>(static_cast<uintptr_t>(
- args->thread->jankTracker().findPercentile(args->percentile)));
+ args->thread->globalProfileData()->findPercentile(args->percentile)));
}
uint32_t RenderProxy::frameTimePercentile(int p) {
@@ -483,7 +483,7 @@
}
CREATE_BRIDGE2(setProcessStatsBuffer, RenderThread* thread, int fd) {
- args->thread->jankTracker().switchStorageToAshmem(args->fd);
+ args->thread->globalProfileData().switchStorageToAshmem(args->fd);
close(args->fd);
return nullptr;
}
@@ -497,7 +497,7 @@
}
CREATE_BRIDGE1(rotateProcessStatsBuffer, RenderThread* thread) {
- args->thread->jankTracker().rotateStorage();
+ args->thread->globalProfileData().rotateStorage();
return nullptr;
}
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 13af2c4..72a428f 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -200,13 +200,12 @@
initializeDisplayEventReceiver();
mEglManager = new EglManager(*this);
mRenderState = new RenderState(*this);
- mJankTracker = new JankTracker(mDisplayInfo);
mVkManager = new VulkanManager(*this);
mCacheManager = new CacheManager(mDisplayInfo);
}
void RenderThread::dumpGraphicsMemory(int fd) {
- jankTracker().dump(fd);
+ globalProfileData()->dump(fd);
String8 cachesOutput;
String8 pipeline;
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index d984257..bef47b3 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -97,7 +97,7 @@
TimeLord& timeLord() { return mTimeLord; }
RenderState& renderState() const { return *mRenderState; }
EglManager& eglManager() const { return *mEglManager; }
- JankTracker& jankTracker() { return *mJankTracker; }
+ ProfileDataContainer& globalProfileData() { return mGlobalProfileData; }
Readback& readback();
const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; }
@@ -160,7 +160,7 @@
RenderState* mRenderState;
EglManager* mEglManager;
- JankTracker* mJankTracker = nullptr;
+ ProfileDataContainer mGlobalProfileData;
Readback* mReadback = nullptr;
sk_sp<GrContext> mGrContext;