Merge "Move frame history into jank tracker"

This commit is contained in:
John Reck
2017-07-06 16:34:04 +00:00
committed by Android (Google) Code Review
10 changed files with 186 additions and 110 deletions

View File

@@ -202,6 +202,7 @@ cc_defaults {
"PathTessellator.cpp",
"PixelBuffer.cpp",
"ProfileData.cpp",
"ProfileDataContainer.cpp",
"ProfileRenderer.cpp",
"Program.cpp",
"ProgramCache.cpp",

View File

@@ -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;

View File

@@ -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 */

View 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 */

View 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 */

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;