diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/hwui/Android.mk | 2 | ||||
| -rw-r--r-- | libs/hwui/FrameInfo.cpp | 28 | ||||
| -rw-r--r-- | libs/hwui/FrameInfo.h | 116 | ||||
| -rw-r--r-- | libs/hwui/JankTracker.cpp | 159 | ||||
| -rw-r--r-- | libs/hwui/JankTracker.h | 71 | ||||
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 46 | ||||
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 26 | ||||
| -rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.cpp | 16 | ||||
| -rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.h | 9 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 52 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 8 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderThread.cpp | 9 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderThread.h | 12 | ||||
| -rw-r--r-- | libs/hwui/renderthread/TimeLord.cpp | 8 | ||||
| -rw-r--r-- | libs/hwui/renderthread/TimeLord.h | 4 | ||||
| -rw-r--r-- | libs/hwui/tests/main.cpp | 5 | ||||
| -rw-r--r-- | libs/hwui/utils/Macros.h | 8 | ||||
| -rw-r--r-- | libs/hwui/utils/RingBuffer.h | 71 |
18 files changed, 592 insertions, 58 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 49560ffdd628..507472aa61a8 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -18,6 +18,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) AssetAtlas.cpp \ DamageAccumulator.cpp \ FontRenderer.cpp \ + FrameInfo.cpp \ GammaFontRenderer.cpp \ Caches.cpp \ DisplayList.cpp \ @@ -32,6 +33,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) GradientCache.cpp \ Image.cpp \ Interpolator.cpp \ + JankTracker.cpp \ Layer.cpp \ LayerCache.cpp \ LayerRenderer.cpp \ diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp new file mode 100644 index 000000000000..6da1fa808d0a --- /dev/null +++ b/libs/hwui/FrameInfo.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 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 "FrameInfo.h" + +#include <cstring> + +namespace android { +namespace uirenderer { + +void FrameInfo::importUiThreadInfo(int64_t* info) { + memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t)); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h new file mode 100644 index 000000000000..3c3167721d2b --- /dev/null +++ b/libs/hwui/FrameInfo.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2015 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 FRAMEINFO_H_ +#define FRAMEINFO_H_ + +#include "utils/Macros.h" + +#include <cutils/compiler.h> +#include <utils/Timers.h> + +#include <memory.h> + +namespace android { +namespace uirenderer { + +#define UI_THREAD_FRAME_INFO_SIZE 9 + +HWUI_ENUM(FrameInfoIndex, + kFlags = 0, + kIntendedVsync, + kVsync, + kOldestInputEvent, + kNewestInputEvent, + kHandleInputStart, + kAnimationStart, + kPerformTraversalsStart, + kDrawStart, + // End of UI frame info + + kSyncStart, + kIssueDrawCommandsStart, + kSwapBuffers, + kFrameCompleted, + + // Must be the last value! + kNumIndexes +); + +HWUI_ENUM(FrameInfoFlags, + kWindowLayoutChanged = 1 << 0, + kRTAnimation = 1 << 1, + kSurfaceCanvas = 1 << 2, +); + +class ANDROID_API UiFrameInfoBuilder { +public: + UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) { + memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t)); + } + + UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync) { + mBuffer[FrameInfoIndex::kVsync] = vsyncTime; + mBuffer[FrameInfoIndex::kIntendedVsync] = intendedVsync; + return *this; + } + + UiFrameInfoBuilder& addFlag(FrameInfoFlagsEnum flag) { + mBuffer[FrameInfoIndex::kFlags] |= static_cast<uint64_t>(flag); + return *this; + } + +private: + int64_t* mBuffer; +}; + +class FrameInfo { +public: + void importUiThreadInfo(int64_t* info); + + void markSyncStart() { + mFrameInfo[FrameInfoIndex::kSyncStart] = systemTime(CLOCK_MONOTONIC); + } + + void markIssueDrawCommandsStart() { + mFrameInfo[FrameInfoIndex::kIssueDrawCommandsStart] = systemTime(CLOCK_MONOTONIC); + } + + void markSwapBuffers() { + mFrameInfo[FrameInfoIndex::kSwapBuffers] = systemTime(CLOCK_MONOTONIC); + } + + void markFrameCompleted() { + mFrameInfo[FrameInfoIndex::kFrameCompleted] = systemTime(CLOCK_MONOTONIC); + } + + int64_t operator[](FrameInfoIndexEnum index) const { + if (index == FrameInfoIndex::kNumIndexes) return 0; + return mFrameInfo[static_cast<int>(index)]; + } + + int64_t operator[](int index) const { + if (index < 0 || index >= FrameInfoIndex::kNumIndexes) return 0; + return mFrameInfo[static_cast<int>(index)]; + } + +private: + int64_t mFrameInfo[FrameInfoIndex::kNumIndexes]; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* FRAMEINFO_H_ */ diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp new file mode 100644 index 000000000000..f7c81953b67b --- /dev/null +++ b/libs/hwui/JankTracker.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2015 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 "JankTracker.h" + +#include <algorithm> +#include <cstdio> +#include <inttypes.h> + +namespace android { +namespace uirenderer { + +static const char* JANK_TYPE_NAMES[] = { + "Missed Vsync", + "High input latency", + "Slow UI thread", + "Slow bitmap uploads", + "Slow draw", +}; + +struct Comparison { + FrameInfoIndexEnum start; + FrameInfoIndexEnum end; +}; + +static const Comparison COMPARISONS[] = { + {FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kVsync}, + {FrameInfoIndex::kOldestInputEvent, FrameInfoIndex::kVsync}, + {FrameInfoIndex::kVsync, FrameInfoIndex::kSyncStart}, + {FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart}, + {FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kFrameCompleted}, +}; + +// If the event exceeds 10 seconds throw it away, this isn't a jank event +// it's an ANR and will be handled as such +static const int64_t IGNORE_EXCEEDING = seconds_to_nanoseconds(10); + +/* + * Frames that are exempt from jank metrics. + * First-draw frames, for example, are expected to + * be slow, this is hidden from the user with window animations and + * other tricks + * + * Similarly, we don't track direct-drawing via Surface:lockHardwareCanvas() + * for now + * + * TODO: kSurfaceCanvas can negatively impact other drawing by using up + * time on the RenderThread, figure out how to attribute that as a jank-causer + */ +static const int64_t EXEMPT_FRAMES_FLAGS + = FrameInfoFlags::kWindowLayoutChanged + | FrameInfoFlags::kSurfaceCanvas; + +JankTracker::JankTracker(nsecs_t frameIntervalNanos) { + reset(); + setFrameInterval(frameIntervalNanos); +} + +void JankTracker::setFrameInterval(nsecs_t frameInterval) { + mFrameInterval = frameInterval; + mThresholds[kMissedVsync] = 1; + /* + * Due to interpolation and sample rate differences between the touch + * panel and the display (example, 85hz touch panel driving a 60hz display) + * we call high latency 1.5 * frameinterval + * + * NOTE: Be careful when tuning this! A theoretical 1,000hz touch panel + * on a 60hz display will show kOldestInputEvent - kIntendedVsync of being 15ms + * Thus this must always be larger than frameInterval, or it will fail + */ + mThresholds[kHighInputLatency] = static_cast<int64_t>(1.5 * frameInterval); + + // Note that these do not add up to 1. This is intentional. It's to deal + // with variance in values, and should be sort of an upper-bound on what + // is reasonable to expect. + mThresholds[kSlowUI] = static_cast<int64_t>(.5 * frameInterval); + mThresholds[kSlowSync] = static_cast<int64_t>(.2 * frameInterval); + mThresholds[kSlowRT] = static_cast<int64_t>(.75 * frameInterval); + +} + +void JankTracker::addFrame(const FrameInfo& frame) { + using namespace FrameInfoIndex; + mTotalFrameCount++; + // Fast-path for jank-free frames + int64_t totalDuration = frame[kFrameCompleted] - frame[kIntendedVsync]; + uint32_t framebucket = std::min( + static_cast<typeof sizeof(mFrameCounts)>(ns2ms(totalDuration)), + sizeof(mFrameCounts) / sizeof(mFrameCounts[0])); + // Keep the fast path as fast as possible. + if (CC_LIKELY(totalDuration < mFrameInterval)) { + mFrameCounts[framebucket]++; + return; + } + + if (frame[kFlags] & EXEMPT_FRAMES_FLAGS) { + return; + } + + mFrameCounts[framebucket]++; + mJankFrameCount++; + + for (int i = 0; i < NUM_BUCKETS; i++) { + int64_t delta = frame[COMPARISONS[i].end] - frame[COMPARISONS[i].start]; + if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) { + mBuckets[i].count++; + } + } +} + +void JankTracker::dump(int fd) { + FILE* file = fdopen(fd, "a"); + fprintf(file, "\nFrame stats:"); + fprintf(file, "\n Total frames rendered: %u", mTotalFrameCount); + fprintf(file, "\n Janky frames: %u (%.2f%%)", mJankFrameCount, + (float) mJankFrameCount / (float) mTotalFrameCount * 100.0f); + fprintf(file, "\n 90th percentile: %ums", findPercentile(90)); + fprintf(file, "\n 95th percentile: %ums", findPercentile(95)); + fprintf(file, "\n 99th percentile: %ums", findPercentile(99)); + for (int i = 0; i < NUM_BUCKETS; i++) { + fprintf(file, "\n Number %s: %u", JANK_TYPE_NAMES[i], mBuckets[i].count); + } + fprintf(file, "\n"); + fflush(file); +} + +void JankTracker::reset() { + memset(mBuckets, 0, sizeof(mBuckets)); + memset(mFrameCounts, 0, sizeof(mFrameCounts)); + mTotalFrameCount = 0; + mJankFrameCount = 0; +} + +uint32_t JankTracker::findPercentile(int percentile) { + int pos = percentile * mTotalFrameCount / 100; + int remaining = mTotalFrameCount - pos; + for (int i = sizeof(mFrameCounts) / sizeof(mFrameCounts[0]) - 1; i >= 0; i--) { + remaining -= mFrameCounts[i]; + if (remaining <= 0) { + return i; + } + } + return 0; +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h new file mode 100644 index 000000000000..3d4929b215c6 --- /dev/null +++ b/libs/hwui/JankTracker.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 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 JANKTRACKER_H_ +#define JANKTRACKER_H_ + +#include "FrameInfo.h" +#include "renderthread/TimeLord.h" +#include "utils/RingBuffer.h" + +#include <memory> + +namespace android { +namespace uirenderer { + +enum JankType { + kMissedVsync = 0, + kHighInputLatency, + kSlowUI, + kSlowSync, + kSlowRT, + + // must be last + NUM_BUCKETS, +}; + +struct JankBucket { + // Number of frames that hit this bucket + uint32_t count; +}; + +// TODO: Replace DrawProfiler with this +class JankTracker { +public: + JankTracker(nsecs_t frameIntervalNanos); + + void setFrameInterval(nsecs_t frameIntervalNanos); + + void addFrame(const FrameInfo& frame); + + void dump(int fd); + void reset(); + +private: + uint32_t findPercentile(int p); + + JankBucket mBuckets[NUM_BUCKETS]; + int64_t mThresholds[NUM_BUCKETS]; + uint32_t mFrameCounts[128]; + + int64_t mFrameInterval; + uint32_t mTotalFrameCount; + uint32_t mJankFrameCount; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* JANKTRACKER_H_ */ diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 75bd0676d70a..2eaa7719227c 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -47,7 +47,8 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, , mOpaque(!translucent) , mCanvas(NULL) , mHaveNewSurface(false) - , mRootRenderNode(rootRenderNode) { + , mRootRenderNode(rootRenderNode) + , mCurrentFrameInfo(NULL) { mAnimationContext = contextFactory->createAnimationContext(mRenderThread.timeLord()); mRenderThread.renderState().registerCanvasContext(this); } @@ -152,9 +153,13 @@ void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) { } } -void CanvasContext::prepareTree(TreeInfo& info) { +void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo) { mRenderThread.removeFrameCallback(this); + mCurrentFrameInfo = &mFrames.next(); + mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo); + mCurrentFrameInfo->markSyncStart(); + info.damageAccumulator = &mDamageAccumulator; info.renderer = mCanvas; if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) { @@ -204,6 +209,7 @@ void CanvasContext::draw() { "drawRenderNode called on a context with no canvas or surface!"); profiler().markPlaybackStart(); + mCurrentFrameInfo->markIssueDrawCommandsStart(); SkRect dirty; mDamageAccumulator.finish(&dirty); @@ -241,12 +247,19 @@ void CanvasContext::draw() { 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(); + if (status & DrawGlInfo::kStatusDrew) { swapBuffers(); } else { mEglManager.cancelFrame(); } + // TODO: Use a fence for real completion? + mCurrentFrameInfo->markFrameCompleted(); + mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); profiler().finishFrame(); } @@ -259,9 +272,14 @@ void CanvasContext::doFrame() { ATRACE_CALL(); profiler().startFrame(); + int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; + UiFrameInfoBuilder(frameInfo) + .addFlag(FrameInfoFlags::kRTAnimation) + .setVsync(mRenderThread.timeLord().computeFrameTimeNanos(), + mRenderThread.timeLord().latestVsync()); TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); - prepareTree(info); + prepareTree(info, frameInfo); if (info.out.canDrawThisFrame) { draw(); } @@ -374,6 +392,28 @@ void CanvasContext::setTextureAtlas(RenderThread& thread, thread.eglManager().setTextureAtlas(buffer, map, mapSize); } +void CanvasContext::dumpFrames(int fd) { + FILE* file = fdopen(fd, "a"); + fprintf(file, "\n\n---PROFILEDATA---"); + for (size_t i = 0; i < mFrames.size(); i++) { + FrameInfo& frame = mFrames[i]; + if (frame[FrameInfoIndex::kSyncStart] == 0) { + continue; + } + fprintf(file, "\n"); + for (int i = 0; i < FrameInfoIndex::kNumIndexes; i++) { + fprintf(file, "%" PRId64 ",", frame[i]); + } + } + fprintf(file, "\n---PROFILEDATA---\n\n"); + fflush(file); +} + +void CanvasContext::resetFrameStats() { + mFrames.clear(); + mRenderThread.jankTracker().reset(); +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 0cc2c7c2811a..0e8be9d9ed59 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -17,7 +17,14 @@ #ifndef CANVASCONTEXT_H_ #define CANVASCONTEXT_H_ -#include <set> +#include "DamageAccumulator.h" +#include "DrawProfiler.h" +#include "IContextFactory.h" +#include "FrameInfo.h" +#include "RenderNode.h" +#include "utils/RingBuffer.h" +#include "renderthread/RenderTask.h" +#include "renderthread/RenderThread.h" #include <cutils/compiler.h> #include <EGL/egl.h> @@ -25,14 +32,7 @@ #include <utils/Functor.h> #include <utils/Vector.h> -#include "../DamageAccumulator.h" -#include "../DrawProfiler.h" -#include "../IContextFactory.h" -#include "../RenderNode.h" -#include "RenderTask.h" -#include "RenderThread.h" - -#define FUNCTOR_PROCESS_DELAY 4 +#include <set> namespace android { namespace uirenderer { @@ -75,7 +75,7 @@ public: void setOpaque(bool opaque); void makeCurrent(); void processLayerUpdate(DeferredLayerUpdater* layerUpdater); - void prepareTree(TreeInfo& info); + void prepareTree(TreeInfo& info, int64_t* uiFrameInfo); void draw(); void destroy(); @@ -103,6 +103,9 @@ public: DrawProfiler& profiler() { return mProfiler; } + void dumpFrames(int fd); + void resetFrameStats(); + private: friend class RegisterFrameCallbackTask; // TODO: Replace with something better for layer & other GL object @@ -133,6 +136,9 @@ private: const sp<RenderNode> mRootRenderNode; DrawProfiler mProfiler; + FrameInfo* mCurrentFrameInfo; + // Ring buffer large enough for 1 second worth of frames + RingBuffer<FrameInfo, 60> mFrames; std::set<RenderNode*> mPrefetechedLayers; }; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 97b31a95f278..23a020233f27 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -34,8 +34,6 @@ namespace renderthread { DrawFrameTask::DrawFrameTask() : mRenderThread(NULL) , mContext(NULL) - , mFrameTimeNanos(0) - , mRecordDurationNanos(0) , mDensity(1.0f) // safe enough default , mSyncResult(kSync_OK) { } @@ -68,18 +66,12 @@ void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) { } } -int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) { +int DrawFrameTask::drawFrame() { LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); mSyncResult = kSync_OK; - mFrameTimeNanos = frameTimeNanos; - mRecordDurationNanos = recordDurationNanos; postAndWait(); - // Reset the single-frame data - mFrameTimeNanos = 0; - mRecordDurationNanos = 0; - return mSyncResult; } @@ -93,7 +85,7 @@ void DrawFrameTask::run() { ATRACE_NAME("DrawFrame"); mContext->profiler().setDensity(mDensity); - mContext->profiler().startFrame(mRecordDurationNanos); + mContext->profiler().startFrame(); bool canUnblockUiThread; bool canDrawThisFrame; @@ -122,7 +114,7 @@ void DrawFrameTask::run() { bool DrawFrameTask::syncFrameState(TreeInfo& info) { ATRACE_CALL(); - mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos); + mRenderThread->timeLord().vsyncReceived(mFrameInfo[FrameInfoIndex::kVsync]); mContext->makeCurrent(); Caches::getInstance().textureCache.resetMarkInUse(); @@ -130,7 +122,7 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { mContext->processLayerUpdate(mLayers[i].get()); } mLayers.clear(); - mContext->prepareTree(info); + mContext->prepareTree(info, mFrameInfo); // This is after the prepareTree so that any pending operations // (RenderNode tree state, prefetched layers, etc...) will be flushed. diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 28f6cb26fb57..eccb87f29383 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -25,6 +25,7 @@ #include "RenderTask.h" #include "../Rect.h" +#include "../FrameInfo.h" #include "../TreeInfo.h" namespace android { @@ -62,7 +63,9 @@ public: void removeLayerUpdate(DeferredLayerUpdater* layer); void setDensity(float density) { mDensity = density; } - int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos); + int drawFrame(); + + int64_t* frameInfo() { return mFrameInfo; } virtual void run(); @@ -80,12 +83,12 @@ private: /********************************************* * Single frame data *********************************************/ - nsecs_t mFrameTimeNanos; - nsecs_t mRecordDurationNanos; float mDensity; std::vector< sp<DeferredLayerUpdater> > mLayers; int mSyncResult; + + int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE]; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 6d063a4b0ce0..088c65b939ef 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -16,14 +16,14 @@ #include "RenderProxy.h" -#include "CanvasContext.h" -#include "RenderTask.h" -#include "RenderThread.h" - -#include "../DeferredLayerUpdater.h" -#include "../DisplayList.h" -#include "../LayerRenderer.h" -#include "../Rect.h" +#include "DeferredLayerUpdater.h" +#include "DisplayList.h" +#include "LayerRenderer.h" +#include "Rect.h" +#include "renderthread/CanvasContext.h" +#include "renderthread/RenderTask.h" +#include "renderthread/RenderThread.h" +#include "utils/Macros.h" namespace android { namespace uirenderer { @@ -52,6 +52,11 @@ namespace renderthread { MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ ARGS(method) *args = (ARGS(method) *) task->payload() +HWUI_ENUM(DumpFlags, + kFrameStats = 1 << 0, + kReset = 1 << 1, +); + CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) { return new CanvasContext(*args->thread, args->translucent, @@ -92,7 +97,7 @@ void RenderProxy::destroyContext() { } CREATE_BRIDGE2(setFrameInterval, RenderThread* thread, nsecs_t frameIntervalNanos) { - args->thread->timeLord().setFrameInterval(args->frameIntervalNanos); + args->thread->setFrameInterval(args->frameIntervalNanos); return NULL; } @@ -175,7 +180,8 @@ CREATE_BRIDGE7(setup, CanvasContext* context, int width, int height, } void RenderProxy::setup(int width, int height, const Vector3& lightCenter, float lightRadius, - uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { + uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha, float density) { + mDrawFrameTask.setDensity(density); SETUP_TASK(setup); args->context = mContext; args->width = width; @@ -199,10 +205,12 @@ void RenderProxy::setOpaque(bool opaque) { post(task); } -int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos, - float density) { - mDrawFrameTask.setDensity(density); - return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos); +int64_t* RenderProxy::frameInfo() { + return mDrawFrameTask.frameInfo(); +} + +int RenderProxy::syncAndDrawFrame() { + return mDrawFrameTask.drawFrame(); } CREATE_BRIDGE1(destroy, CanvasContext* context) { @@ -371,19 +379,28 @@ void RenderProxy::notifyFramePending() { mRenderThread.queueAtFront(task); } -CREATE_BRIDGE2(dumpProfileInfo, CanvasContext* context, int fd) { +CREATE_BRIDGE3(dumpProfileInfo, CanvasContext* context, int fd, int dumpFlags) { args->context->profiler().dumpData(args->fd); + + if (args->dumpFlags & DumpFlags::kFrameStats) { + args->context->dumpFrames(args->fd); + } + if (args->dumpFlags & DumpFlags::kReset) { + args->context->resetFrameStats(); + } return NULL; } -void RenderProxy::dumpProfileInfo(int fd) { +void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) { SETUP_TASK(dumpProfileInfo); args->context = mContext; args->fd = fd; + args->dumpFlags = dumpFlags; postAndWait(task); } -CREATE_BRIDGE1(outputLogBuffer, int fd) { +CREATE_BRIDGE2(outputLogBuffer, int fd, RenderThread* thread) { + args->thread->jankTracker().dump(args->fd); RenderNode::outputLogBuffer(args->fd); return NULL; } @@ -391,6 +408,7 @@ CREATE_BRIDGE1(outputLogBuffer, int fd) { void RenderProxy::outputLogBuffer(int fd) { SETUP_TASK(outputLogBuffer); args->fd = fd; + args->thread = &RenderThread::getInstance(); staticPostAndWait(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index fd1fe05ffbdb..d86f1fca466f 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -71,10 +71,10 @@ public: ANDROID_API void updateSurface(const sp<ANativeWindow>& window); ANDROID_API bool pauseSurface(const sp<ANativeWindow>& window); ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius, - uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); + uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha, float density); ANDROID_API void setOpaque(bool opaque); - ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos, - float density); + ANDROID_API int64_t* frameInfo(); + ANDROID_API int syncAndDrawFrame(); ANDROID_API void destroy(); ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion); @@ -95,7 +95,7 @@ public: ANDROID_API void stopDrawing(); ANDROID_API void notifyFramePending(); - ANDROID_API void dumpProfileInfo(int fd); + ANDROID_API void dumpProfileInfo(int fd, int dumpFlags); ANDROID_API static void outputLogBuffer(int fd); ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 84826b7a3bff..7a8c3cee088d 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -143,7 +143,8 @@ RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() , mFrameCallbackTaskPending(false) , mFrameCallbackTask(0) , mRenderState(NULL) - , mEglManager(NULL) { + , mEglManager(NULL) + , mJankTracker(NULL) { mFrameCallbackTask = new DispatchFrameCallbacks(this); mLooper = new Looper(false); run("RenderThread"); @@ -153,6 +154,11 @@ RenderThread::~RenderThread() { LOG_ALWAYS_FATAL("Can't destroy the render thread"); } +void RenderThread::setFrameInterval(nsecs_t frameInterval) { + mTimeLord.setFrameInterval(frameInterval); + mJankTracker->setFrameInterval(frameInterval); +} + void RenderThread::initializeDisplayEventReceiver() { LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?"); mDisplayEventReceiver = new DisplayEventReceiver(); @@ -169,6 +175,7 @@ void RenderThread::initThreadLocals() { initializeDisplayEventReceiver(); mEglManager = new EglManager(*this); mRenderState = new RenderState(*this); + mJankTracker = new JankTracker(mTimeLord.frameIntervalNanos()); } int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 99c2e1527ea2..8a28a35aabc2 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -19,8 +19,8 @@ #include "RenderTask.h" -#include <memory> -#include <set> +#include "../JankTracker.h" +#include "TimeLord.h" #include <cutils/compiler.h> #include <utils/Looper.h> @@ -28,7 +28,8 @@ #include <utils/Singleton.h> #include <utils/Thread.h> -#include "TimeLord.h" +#include <memory> +#include <set> namespace android { @@ -85,9 +86,12 @@ public: // the next vsync. If it is not currently registered this does nothing. void pushBackFrameCallback(IFrameCallback* callback); + void setFrameInterval(nsecs_t frameInterval); + TimeLord& timeLord() { return mTimeLord; } RenderState& renderState() { return *mRenderState; } EglManager& eglManager() { return *mEglManager; } + JankTracker& jankTracker() { return *mJankTracker; } protected: virtual bool threadLoop(); @@ -132,6 +136,8 @@ private: TimeLord mTimeLord; RenderState* mRenderState; EglManager* mEglManager; + + JankTracker* mJankTracker; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp index f18749317ab6..f846d6a44473 100644 --- a/libs/hwui/renderthread/TimeLord.cpp +++ b/libs/hwui/renderthread/TimeLord.cpp @@ -32,7 +32,7 @@ bool TimeLord::vsyncReceived(nsecs_t vsync) { return false; } -nsecs_t TimeLord::computeFrameTimeMs() { +nsecs_t TimeLord::computeFrameTimeNanos() { // Logic copied from Choreographer.java nsecs_t now = systemTime(CLOCK_MONOTONIC); nsecs_t jitterNanos = now - mFrameTimeNanos; @@ -40,7 +40,11 @@ nsecs_t TimeLord::computeFrameTimeMs() { nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos; mFrameTimeNanos = now - lastFrameOffset; } - return nanoseconds_to_milliseconds(mFrameTimeNanos); + return mFrameTimeNanos; +} + +nsecs_t TimeLord::computeFrameTimeMs() { + return nanoseconds_to_milliseconds(computeFrameTimeNanos()); } } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h index 7c155d28feef..5464399234fb 100644 --- a/libs/hwui/renderthread/TimeLord.h +++ b/libs/hwui/renderthread/TimeLord.h @@ -29,9 +29,13 @@ class RenderThread; class TimeLord { public: void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; } + nsecs_t frameIntervalNanos() const { return mFrameIntervalNanos; } + // returns true if the vsync is newer, false if it was rejected for staleness bool vsyncReceived(nsecs_t vsync); + nsecs_t latestVsync() { return mFrameTimeNanos; } nsecs_t computeFrameTimeMs(); + nsecs_t computeFrameTimeNanos(); private: friend class RenderThread; diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp index 2d99e9f7686c..33b8103cdb84 100644 --- a/libs/hwui/tests/main.cpp +++ b/libs/hwui/tests/main.cpp @@ -88,7 +88,7 @@ int main(int argc, char* argv[]) { proxy->initialize(surface); float lightX = width / 2.0; proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)}, - dp(800.0f), 255 * 0.075, 255 * 0.15); + dp(800.0f), 255 * 0.075, 255 * 0.15, gDisplay.density); android::uirenderer::Rect DUMMY; @@ -116,8 +116,7 @@ int main(int argc, char* argv[]) { cards[ci]->mutateStagingProperties().setTranslationY(i); cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } - nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC); - proxy->syncAndDrawFrame(frameTimeNs, 0, gDisplay.density); + proxy->syncAndDrawFrame(); usleep(12000); } diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h index 5b7c87ceef8f..068b32b3fe68 100644 --- a/libs/hwui/utils/Macros.h +++ b/libs/hwui/utils/Macros.h @@ -29,4 +29,12 @@ friend inline int compare_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs); } \ friend inline hash_t hash_type(const Type& entry) { return entry.hash(); } +#define HWUI_ENUM(name, ...) \ + namespace name { \ + enum _##name { \ + __VA_ARGS__ \ + }; \ + } \ + typedef enum name::_##name name##Enum + #endif /* MACROS_H */ diff --git a/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h new file mode 100644 index 000000000000..62b22fdcef42 --- /dev/null +++ b/libs/hwui/utils/RingBuffer.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 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 RINGBUFFER_H_ +#define RINGBUFFER_H_ + +#include "utils/Macros.h" + +#include <stddef.h> + +namespace android { +namespace uirenderer { + +template<class T, size_t SIZE> +class RingBuffer { + PREVENT_COPY_AND_ASSIGN(RingBuffer); + +public: + RingBuffer() : mHead(-1), mCount(0) {} + ~RingBuffer() {} + + size_t capacity() { return SIZE; } + size_t size() { return mCount; } + + T& next() { + mHead = (mHead + 1) % SIZE; + if (mCount < SIZE) { + mCount++; + } + return mBuffer[mHead]; + } + + T& front() { + return this[0]; + } + + T& back() { + return this[size() - 1]; + } + + T& operator[](size_t index) { + return mBuffer[(mHead + index + 1) % mCount]; + } + + void clear() { + mCount = 0; + mHead = -1; + } + +private: + T mBuffer[SIZE]; + int mHead; + size_t mCount; +}; + +}; // namespace uirenderer +}; // namespace android + +#endif /* RINGBUFFER_H_ */ |