Skip sending HWUI hints when frame does not draw
Sending HWUI ADPF CPU hints when the frame is not drawn can undercount
the actual workload duration and lead to inadequate boosting. This
patch prevents this by not sending hints when the frame is not drawn.
Bug: b/239677101
Test: manual
Change-Id: Ia308f59740c83977df02dff6d9f9289f2b8d439e
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 6a0c5a8..d09bc47 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -472,11 +472,11 @@
mRenderThread.pushBackFrameCallback(this);
}
-nsecs_t CanvasContext::draw() {
+std::optional<nsecs_t> CanvasContext::draw() {
if (auto grContext = getGrContext()) {
if (grContext->abandoned()) {
LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw");
- return 0;
+ return std::nullopt;
}
}
SkRect dirty;
@@ -498,7 +498,7 @@
std::invoke(func, false /* didProduceBuffer */);
}
mFrameCommitCallbacks.clear();
- return 0;
+ return std::nullopt;
}
ScopedActiveContext activeContext(this);
@@ -543,6 +543,8 @@
}
bool requireSwap = false;
+ bool didDraw = false;
+
int error = OK;
bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult.success, windowDirty,
mCurrentFrameInfo, &requireSwap);
@@ -553,7 +555,7 @@
mIsDirty = false;
if (requireSwap) {
- bool didDraw = true;
+ didDraw = true;
// Handle any swapchain errors
error = mNativeSurface->getAndClearError();
if (error == TIMED_OUT) {
@@ -649,7 +651,9 @@
}
mRenderThread.cacheManager().onFrameCompleted();
- return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
+ return didDraw ? std::make_optional(
+ mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration))
+ : std::nullopt;
}
void CanvasContext::reportMetricsWithPresentTime() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 748ab96..db96cfb 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -138,7 +138,7 @@
bool makeCurrent();
void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
// Returns the DequeueBufferDuration.
- nsecs_t draw();
+ std::optional<nsecs_t> draw();
void destroy();
// IFrameCallback, Choreographer-driven frame callback entry point
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 03f02de..dc7676c 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -19,6 +19,7 @@
#include <dlfcn.h>
#include <gui/TraceUtils.h>
#include <utils/Log.h>
+
#include <algorithm>
#include "../DeferredLayerUpdater.h"
@@ -28,6 +29,7 @@
#include "CanvasContext.h"
#include "RenderThread.h"
#include "thread/CommonPool.h"
+#include "utils/TimeUtils.h"
namespace android {
namespace uirenderer {
@@ -146,6 +148,7 @@
bool canUnblockUiThread;
bool canDrawThisFrame;
+ bool didDraw = false;
{
TreeInfo info(TreeInfo::MODE_FULL, *mContext);
info.forceDrawFrame = mForceDrawFrame;
@@ -188,7 +191,9 @@
nsecs_t dequeueBufferDuration = 0;
if (CC_LIKELY(canDrawThisFrame)) {
- dequeueBufferDuration = context->draw();
+ std::optional<nsecs_t> drawResult = context->draw();
+ didDraw = drawResult.has_value();
+ dequeueBufferDuration = drawResult.value_or(0);
} else {
// Do a flush in case syncFrameState performed any texture uploads. Since we skipped
// the draw() call, those uploads (or deletes) will end up sitting in the queue.
@@ -209,8 +214,9 @@
}
if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
- constexpr int64_t kSanityCheckLowerBound = 100000; // 0.1ms
- constexpr int64_t kSanityCheckUpperBound = 10000000000; // 10s
+
+ constexpr int64_t kSanityCheckLowerBound = 100_us;
+ constexpr int64_t kSanityCheckUpperBound = 10_s;
int64_t targetWorkDuration = frameDeadline - intendedVsync;
targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
if (targetWorkDuration > kSanityCheckLowerBound &&
@@ -219,12 +225,15 @@
mLastTargetWorkDuration = targetWorkDuration;
mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration);
}
- int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
- int64_t actualDuration = frameDuration -
- (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
- dequeueBufferDuration;
- if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
- mHintSessionWrapper->reportActualWorkDuration(actualDuration);
+
+ if (didDraw) {
+ int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
+ int64_t actualDuration = frameDuration -
+ (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+ dequeueBufferDuration;
+ if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
+ mHintSessionWrapper->reportActualWorkDuration(actualDuration);
+ }
}
mLastDequeueBufferDuration = dequeueBufferDuration;