Teach JankTracker about new swap behaviors
Bug: 30440166
If we are using HWC2, there's a change in timing
when in triple buffering with the pipelined offsets.
This changes JankTracker to recognize that and silently
erase that from the total duration
Change-Id: Ib1fd4209070f17dbd2baed707c8cf73fb11c3cf2
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index c9d4af6..366ef71 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -113,6 +113,10 @@
-DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" \
-Wall -Wno-unused-parameter -Wunreachable-code -Werror
+ifeq ($(TARGET_USES_HWC2),true)
+ hwui_cflags += -DUSE_HWC2
+endif
+
# GCC false-positives on this warning, and since we -Werror that's
# a problem
hwui_cflags += -Wno-free-nonheap-object
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 41e2233..09b3945 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -35,8 +35,14 @@
"IssueDrawCommandsStart",
"SwapBuffers",
"FrameCompleted",
+ "DequeueBufferDuration",
+ "QueueBufferDuration",
};
+static_assert((sizeof(FrameInfoNames)/sizeof(FrameInfoNames[0]))
+ == static_cast<int>(FrameInfoIndex::NumIndexes),
+ "size mismatch: FrameInfoNames doesn't match the enum!");
+
void FrameInfo::importUiThreadInfo(int64_t* info) {
memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
}
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 0baca39..1fe5459 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -48,6 +48,9 @@
SwapBuffers,
FrameCompleted,
+ DequeueBufferDuration,
+ QueueBufferDuration,
+
// Must be the last value!
NumIndexes
};
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index ebe9c42..ed6b211e 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -16,6 +16,7 @@
#include "JankTracker.h"
#include "Properties.h"
+#include "utils/TimeUtils.h"
#include <algorithm>
#include <cutils/ashmem.h>
@@ -119,11 +120,27 @@
return index;
}
-JankTracker::JankTracker(nsecs_t frameIntervalNanos) {
+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();
+ nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps);
+#if USE_HWC2
+ nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms);
+ nsecs_t offsetDelta = sfOffset - displayInfo.appVsyncOffset;
+ // There are two different offset cases. If the offsetDelta is positive
+ // and small, then the intention is to give apps extra time by leveraging
+ // pipelining between the UI & RT threads. If the offsetDelta is large or
+ // negative, the intention is to subtract time from the total duration
+ // in which case we can't afford to wait for dequeueBuffer blockage.
+ if (offsetDelta <= 4_ms && offsetDelta >= 0) {
+ // SF will begin composition at VSYNC-app + offsetDelta. If we are triple
+ // buffered, this is the expected time at which dequeueBuffer will
+ // return due to the staggering of VSYNC-app & VSYNC-sf.
+ mDequeueTimeForgiveness = offsetDelta + 4_ms;
+ }
+#endif
setFrameInterval(frameIntervalNanos);
}
@@ -213,6 +230,19 @@
mData->totalFrameCount++;
// Fast-path for jank-free frames
int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted);
+ if (mDequeueTimeForgiveness
+ && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) {
+ nsecs_t expectedDequeueDuration =
+ mDequeueTimeForgiveness + frame[FrameInfoIndex::Vsync]
+ - frame[FrameInfoIndex::IssueDrawCommandsStart];
+ if (expectedDequeueDuration > 0) {
+ // Forgive only up to the expected amount, but not more than
+ // the actual time spent blocked.
+ nsecs_t forgiveAmount = std::min(expectedDequeueDuration,
+ frame[FrameInfoIndex::DequeueBufferDuration]);
+ totalDuration -= forgiveAmount;
+ }
+ }
uint32_t framebucket = frameCountIndexForFrameTime(totalDuration);
// Keep the fast path as fast as possible.
if (CC_LIKELY(totalDuration < mFrameInterval)) {
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 84b8c3f..126025c 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -21,6 +21,7 @@
#include "utils/RingBuffer.h"
#include <cutils/compiler.h>
+#include <ui/DisplayInfo.h>
#include <array>
#include <memory>
@@ -56,7 +57,7 @@
// TODO: Replace DrawProfiler with this
class JankTracker {
public:
- JankTracker(nsecs_t frameIntervalNanos);
+ JankTracker(const DisplayInfo& displayInfo);
~JankTracker();
void addFrame(const FrameInfo& frame);
@@ -79,6 +80,14 @@
std::array<int64_t, NUM_BUCKETS> mThresholds;
int64_t mFrameInterval;
+ // The amount of time we will erase from the total duration to account
+ // for SF vsync offsets with HWC2 blocking dequeueBuffers.
+ // (Vsync + mDequeueBlockTolerance) is the point at which we expect
+ // SF to have released the buffer normally, so we will forgive up to that
+ // point in time by comparing to (IssueDrawCommandsStart + DequeueDuration)
+ // 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;
};
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 4b9d7d5..812fdf7 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -67,7 +67,7 @@
, mEglManager(thread.eglManager())
, mOpaque(!translucent)
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
- , mJankTracker(thread.timeLord().frameIntervalNanos())
+ , mJankTracker(thread.mainDisplayInfo())
, mProfiler(mFrames)
, mContentDrawBounds(0, 0, 0, 0) {
mRenderNodes.emplace_back(rootRenderNode);
@@ -284,11 +284,6 @@
if (CC_LIKELY(mSwapHistory.size())) {
nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
SwapHistory& lastSwap = mSwapHistory.back();
- int durationUs;
- mNativeSurface->query(NATIVE_WINDOW_LAST_DEQUEUE_DURATION, &durationUs);
- lastSwap.dequeueDuration = us2ns(durationUs);
- mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs);
- lastSwap.queueDuration = us2ns(durationUs);
nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
// The slight fudge-factor is to deal with cases where
// the vsync was estimated due to being slow handling the signal.
@@ -565,6 +560,15 @@
swap.damage = screenDirty;
swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC);
swap.vsyncTime = mRenderThread.timeLord().latestVsync();
+ int durationUs;
+ mNativeSurface->query(NATIVE_WINDOW_LAST_DEQUEUE_DURATION, &durationUs);
+ swap.dequeueDuration = us2ns(durationUs);
+ mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs);
+ swap.queueDuration = us2ns(durationUs);
+ mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration)
+ = swap.dequeueDuration;
+ mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration)
+ = swap.queueDuration;
mHaveNewSurface = false;
mFrameNumber = -1;
}
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 9fb30c9..f4b4416 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -190,7 +190,7 @@
initializeDisplayEventReceiver();
mEglManager = new EglManager(*this);
mRenderState = new RenderState(*this);
- mJankTracker = new JankTracker(frameIntervalNanos);
+ mJankTracker = new JankTracker(mDisplayInfo);
}
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
diff --git a/libs/hwui/utils/TimeUtils.h b/libs/hwui/utils/TimeUtils.h
index 8d42d7e..ce181b7 100644
--- a/libs/hwui/utils/TimeUtils.h
+++ b/libs/hwui/utils/TimeUtils.h
@@ -21,10 +21,18 @@
namespace android {
namespace uirenderer {
+constexpr nsecs_t operator"" _s (unsigned long long s) {
+ return seconds_to_nanoseconds(s);
+}
+
constexpr nsecs_t operator"" _ms (unsigned long long ms) {
return milliseconds_to_nanoseconds(ms);
}
+constexpr nsecs_t operator"" _us (unsigned long long us) {
+ return microseconds_to_nanoseconds(us);
+}
+
} /* namespace uirenderer */
} /* namespace android */