Merge "Move frame history into jank tracker"
This commit is contained in:
@@ -202,6 +202,7 @@ cc_defaults {
|
||||
"PathTessellator.cpp",
|
||||
"PixelBuffer.cpp",
|
||||
"ProfileData.cpp",
|
||||
"ProfileDataContainer.cpp",
|
||||
"ProfileRenderer.cpp",
|
||||
"Program.cpp",
|
||||
"ProgramCache.cpp",
|
||||
|
||||
@@ -65,11 +65,8 @@ static const int64_t EXEMPT_FRAMES_FLAGS = FrameInfoFlags::SurfaceCanvas;
|
||||
// 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 @@ JankTracker::JankTracker(const DisplayInfo& displayInfo) {
|
||||
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::setFrameInterval(nsecs_t frameInterval) {
|
||||
|
||||
}
|
||||
|
||||
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 @@ void JankTracker::addFrame(const FrameInfo& frame) {
|
||||
}
|
||||
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 @@ void JankTracker::addFrame(const FrameInfo& frame) {
|
||||
}
|
||||
|
||||
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 @@ void JankTracker::dumpData(int fd, const ProfileDataDescription* description, co
|
||||
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;
|
||||
|
||||
@@ -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 @@ struct ProfileDataDescription {
|
||||
// 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 @@ private:
|
||||
// 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 */
|
||||
|
||||
78
libs/hwui/ProfileDataContainer.cpp
Normal file
78
libs/hwui/ProfileDataContainer.cpp
Normal file
@@ -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 */
|
||||
48
libs/hwui/ProfileDataContainer.h
Normal file
48
libs/hwui/ProfileDataContainer.h
Normal file
@@ -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 */
|
||||
@@ -142,8 +142,8 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
|
||||
: 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 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
|
||||
// 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 @@ void CanvasContext::draw() {
|
||||
}
|
||||
#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 @@ DeferredLayerUpdater* CanvasContext::createTextureLayer() {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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 @@ private:
|
||||
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;
|
||||
|
||||
@@ -427,12 +427,12 @@ CREATE_BRIDGE4(dumpProfileInfo, CanvasContext* context, RenderThread* thread,
|
||||
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 @@ void RenderProxy::resetProfileInfo() {
|
||||
|
||||
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 @@ void RenderProxy::dumpGraphicsMemory(int fd) {
|
||||
}
|
||||
|
||||
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 @@ void RenderProxy::setProcessStatsBuffer(int fd) {
|
||||
}
|
||||
|
||||
CREATE_BRIDGE1(rotateProcessStatsBuffer, RenderThread* thread) {
|
||||
args->thread->jankTracker().rotateStorage();
|
||||
args->thread->globalProfileData().rotateStorage();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -200,13 +200,12 @@ void RenderThread::initThreadLocals() {
|
||||
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;
|
||||
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
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 @@ private:
|
||||
RenderState* mRenderState;
|
||||
EglManager* mEglManager;
|
||||
|
||||
JankTracker* mJankTracker = nullptr;
|
||||
ProfileDataContainer mGlobalProfileData;
|
||||
Readback* mReadback = nullptr;
|
||||
|
||||
sk_sp<GrContext> mGrContext;
|
||||
|
||||
Reference in New Issue
Block a user