diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 92d53da602b76..113a4779712f6 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -202,6 +202,7 @@ cc_defaults { "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 f9671ed437710..9d11828194441 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.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(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(sizeof(ProfileData))) { - ALOGW("Ashmem region is too small! Received %d, required %u", - regionSize, static_cast(sizeof(ProfileData))); - return; - } - ProfileData* newData = reinterpret_cast( - 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(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(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 2c567b361a15c..e56c0791d3a47 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 @@ 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& 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 mFrames; }; } /* namespace uirenderer */ diff --git a/libs/hwui/ProfileDataContainer.cpp b/libs/hwui/ProfileDataContainer.cpp new file mode 100644 index 0000000000000..cbf3eb390f536 --- /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 +#include + +#include + +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(sizeof(ProfileData))) { + ALOGW("Ashmem region is too small! Received %d, required %u", + regionSize, static_cast(sizeof(ProfileData))); + return; + } + ProfileData* newData = reinterpret_cast( + 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 0000000000000..d2de24188c7c9 --- /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 7799248830162..5d7f5948b0ece 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -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(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(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 b1f405040a590..aa6d2f3513d7b 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 @@ private: std::vector< sp > mRenderNodes; FrameInfo* mCurrentFrameInfo = nullptr; - // Ring buffer large enough for 2 seconds worth of frames - RingBuffer mFrames; std::string mName; JankTracker mJankTracker; FrameInfoVisualizer mProfiler; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 80c2955400d8e..370cf525fa58f 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -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(static_cast( - 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; } diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 13af2c4d15e85..72a428f1c70c7 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -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; diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index d9842572d7cd3..bef47b3e27c5c 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -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 mGrContext;