Refactor HintSessionWrapper and move into CanvasContext
* Move HintSessionWrapper into its own file
* Refactor HintSessionWrapper initialization and lifecycle
* Move HintSessionWrapper instance into CanvasContext
* Have HintSessionWrapper do its own validity checks
* Have HintSessionWrapper implicitly initialize when called
Bug: b/260267128
Bug: b/260224719
Test: manual
Change-Id: I69f89bb5ef601d1b5d9fca1a5da62256f2556da8
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 88cfed9..fcc0126 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -570,6 +570,7 @@
"renderthread/VulkanSurface.cpp",
"renderthread/RenderProxy.cpp",
"renderthread/RenderThread.cpp",
+ "renderthread/HintSessionWrapper.cpp",
"service/GraphicsStatsService.cpp",
"thread/CommonPool.cpp",
"utils/GLUtils.cpp",
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 564ee4f..b15b6cb 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -104,6 +104,7 @@
set(FrameInfoIndex::AnimationStart) = vsyncTime;
set(FrameInfoIndex::PerformTraversalsStart) = vsyncTime;
set(FrameInfoIndex::DrawStart) = vsyncTime;
+ set(FrameInfoIndex::FrameStartTime) = vsyncTime;
set(FrameInfoIndex::FrameDeadline) = frameDeadline;
set(FrameInfoIndex::FrameInterval) = frameInterval;
return *this;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d09bc47..64839d0 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -71,16 +71,19 @@
} /* namespace */
CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
- RenderNode* rootRenderNode, IContextFactory* contextFactory) {
+ RenderNode* rootRenderNode, IContextFactory* contextFactory,
+ int32_t uiThreadId, int32_t renderThreadId) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::SkiaGL:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
- std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
+ std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread),
+ uiThreadId, renderThreadId);
case RenderPipelineType::SkiaVulkan:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
- std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread));
+ std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread),
+ uiThreadId, renderThreadId);
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
break;
@@ -110,7 +113,8 @@
CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory,
- std::unique_ptr<IRenderPipeline> renderPipeline)
+ std::unique_ptr<IRenderPipeline> renderPipeline, pid_t uiThreadId,
+ pid_t renderThreadId)
: mRenderThread(thread)
, mGenerationID(0)
, mOpaque(!translucent)
@@ -118,7 +122,8 @@
, mJankTracker(&thread.globalProfileData())
, mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
, mContentDrawBounds(0, 0, 0, 0)
- , mRenderPipeline(std::move(renderPipeline)) {
+ , mRenderPipeline(std::move(renderPipeline))
+ , mHintSessionWrapper(uiThreadId, renderThreadId) {
mRenderThread.cacheManager().registerCanvasContext(this);
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
@@ -472,16 +477,22 @@
mRenderThread.pushBackFrameCallback(this);
}
-std::optional<nsecs_t> CanvasContext::draw() {
+void CanvasContext::draw() {
if (auto grContext = getGrContext()) {
if (grContext->abandoned()) {
LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw");
- return std::nullopt;
+ return;
}
}
SkRect dirty;
mDamageAccumulator.finish(&dirty);
+ // reset syncDelayDuration each time we draw
+ nsecs_t syncDelayDuration = mSyncDelayDuration;
+ nsecs_t idleDuration = mIdleDuration;
+ mSyncDelayDuration = 0;
+ mIdleDuration = 0;
+
if (!Properties::isDrawingEnabled() ||
(dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
@@ -498,7 +509,7 @@
std::invoke(func, false /* didProduceBuffer */);
}
mFrameCommitCallbacks.clear();
- return std::nullopt;
+ return;
}
ScopedActiveContext activeContext(this);
@@ -650,10 +661,25 @@
}
}
+ int64_t intendedVsync = mCurrentFrameInfo->get(FrameInfoIndex::IntendedVsync);
+ int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
+ int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
+
+ mHintSessionWrapper.updateTargetWorkDuration(frameDeadline - intendedVsync);
+
+ if (didDraw) {
+ int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+ int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
+ int64_t actualDuration = frameDuration -
+ (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+ dequeueBufferDuration - idleDuration;
+ mHintSessionWrapper.reportActualWorkDuration(actualDuration);
+ }
+
+ mLastDequeueBufferDuration = dequeueBufferDuration;
+
mRenderThread.cacheManager().onFrameCompleted();
- return didDraw ? std::make_optional(
- mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration))
- : std::nullopt;
+ return;
}
void CanvasContext::reportMetricsWithPresentTime() {
@@ -766,6 +792,8 @@
// Called by choreographer to do an RT-driven animation
void CanvasContext::doFrame() {
if (!mRenderPipeline->isSurfaceReady()) return;
+ mIdleDuration =
+ systemTime(SYSTEM_TIME_MONOTONIC) - mRenderThread.timeLord().computeFrameTimeNanos();
prepareAndDraw(nullptr);
}
@@ -974,6 +1002,14 @@
}
}
+void CanvasContext::sendLoadResetHint() {
+ mHintSessionWrapper.sendLoadResetHint();
+}
+
+void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
+ mSyncDelayDuration = duration;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index db96cfb..e875c42 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -16,22 +16,6 @@
#pragma once
-#include "DamageAccumulator.h"
-#include "FrameInfo.h"
-#include "FrameInfoVisualizer.h"
-#include "FrameMetricsReporter.h"
-#include "IContextFactory.h"
-#include "IRenderPipeline.h"
-#include "JankTracker.h"
-#include "LayerUpdateQueue.h"
-#include "Lighting.h"
-#include "ReliableSurface.h"
-#include "RenderNode.h"
-#include "renderthread/RenderTask.h"
-#include "renderthread/RenderThread.h"
-#include "utils/RingBuffer.h"
-#include "ColorMode.h"
-
#include <SkBitmap.h>
#include <SkRect.h>
#include <SkSize.h>
@@ -46,6 +30,23 @@
#include <utility>
#include <vector>
+#include "ColorMode.h"
+#include "DamageAccumulator.h"
+#include "FrameInfo.h"
+#include "FrameInfoVisualizer.h"
+#include "FrameMetricsReporter.h"
+#include "HintSessionWrapper.h"
+#include "IContextFactory.h"
+#include "IRenderPipeline.h"
+#include "JankTracker.h"
+#include "LayerUpdateQueue.h"
+#include "Lighting.h"
+#include "ReliableSurface.h"
+#include "RenderNode.h"
+#include "renderthread/RenderTask.h"
+#include "renderthread/RenderThread.h"
+#include "utils/RingBuffer.h"
+
namespace android {
namespace uirenderer {
@@ -66,7 +67,8 @@
class CanvasContext : public IFrameCallback {
public:
static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
- IContextFactory* contextFactory);
+ IContextFactory* contextFactory, pid_t uiThreadId,
+ pid_t renderThreadId);
virtual ~CanvasContext();
/**
@@ -138,7 +140,7 @@
bool makeCurrent();
void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
// Returns the DequeueBufferDuration.
- std::optional<nsecs_t> draw();
+ void draw();
void destroy();
// IFrameCallback, Choreographer-driven frame callback entry point
@@ -214,9 +216,14 @@
static CanvasContext* getActiveContext();
+ void sendLoadResetHint();
+
+ void setSyncDelayDuration(nsecs_t duration);
+
private:
CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
- IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
+ IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline,
+ pid_t uiThreadId, pid_t renderThreadId);
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
@@ -330,6 +337,11 @@
std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
std::function<void()> mPrepareSurfaceControlForWebviewCallback;
+
+ HintSessionWrapper mHintSessionWrapper;
+ nsecs_t mLastDequeueBufferDuration = 0;
+ nsecs_t mSyncDelayDuration = 0;
+ nsecs_t mIdleDuration = 0;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index cb30614..1cc82fd 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -16,7 +16,6 @@
#include "DrawFrameTask.h"
-#include <dlfcn.h>
#include <gui/TraceUtils.h>
#include <utils/Log.h>
@@ -28,70 +27,11 @@
#include "../RenderNode.h"
#include "CanvasContext.h"
#include "RenderThread.h"
-#include "thread/CommonPool.h"
-#include "utils/TimeUtils.h"
namespace android {
namespace uirenderer {
namespace renderthread {
-namespace {
-
-typedef APerformanceHintManager* (*APH_getManager)();
-typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
- size_t, int64_t);
-typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);
-typedef void (*APH_closeSession)(APerformanceHintSession* session);
-
-bool gAPerformanceHintBindingInitialized = false;
-APH_getManager gAPH_getManagerFn = nullptr;
-APH_createSession gAPH_createSessionFn = nullptr;
-APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
-APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
-APH_sendHint gAPH_sendHintFn = nullptr;
-APH_closeSession gAPH_closeSessionFn = nullptr;
-
-void ensureAPerformanceHintBindingInitialized() {
- if (gAPerformanceHintBindingInitialized) return;
-
- void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
- LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
-
- gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
- LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
- "Failed to find required symbol APerformanceHint_getManager!");
-
- gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
- LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_createSession!");
-
- gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
- handle_, "APerformanceHint_updateTargetWorkDuration");
- LOG_ALWAYS_FATAL_IF(
- gAPH_updateTargetWorkDurationFn == nullptr,
- "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
-
- gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
- handle_, "APerformanceHint_reportActualWorkDuration");
- LOG_ALWAYS_FATAL_IF(
- gAPH_reportActualWorkDurationFn == nullptr,
- "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
-
- gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
- LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
- "Failed to find required symbol APerformanceHint_sendHint!");
-
- gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
- LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_closeSession!");
-
- gAPerformanceHintBindingInitialized = true;
-}
-
-} // namespace
-
DrawFrameTask::DrawFrameTask()
: mRenderThread(nullptr)
, mContext(nullptr)
@@ -100,13 +40,11 @@
DrawFrameTask::~DrawFrameTask() {}
-void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
- int32_t uiThreadId, int32_t renderThreadId) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
+ RenderNode* targetNode) {
mRenderThread = thread;
mContext = context;
mTargetNode = targetNode;
- mUiThreadId = uiThreadId;
- mRenderThreadId = renderThreadId;
}
void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -150,11 +88,11 @@
void DrawFrameTask::run() {
const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
- nsecs_t syncDelayDuration = systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued;
+
+ mContext->setSyncDelayDuration(systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued);
bool canUnblockUiThread;
bool canDrawThisFrame;
- bool didDraw = false;
{
TreeInfo info(TreeInfo::MODE_FULL, *mContext);
info.forceDrawFrame = mForceDrawFrame;
@@ -175,9 +113,6 @@
std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
mFrameCallback = nullptr;
mFrameCompleteCallback = nullptr;
- int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
- int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
- int64_t frameStartTime = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameStartTime)];
// From this point on anything in "this" is *UNSAFE TO ACCESS*
if (canUnblockUiThread) {
@@ -188,18 +123,15 @@
if (CC_UNLIKELY(frameCallback)) {
context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult,
frameNr = context->getFrameNumber()]() {
- auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr));
+ auto frameCommitCallback = frameCallback(syncResult, frameNr);
if (frameCommitCallback) {
context->addFrameCommitListener(std::move(frameCommitCallback));
}
});
}
- nsecs_t dequeueBufferDuration = 0;
if (CC_LIKELY(canDrawThisFrame)) {
- std::optional<nsecs_t> drawResult = context->draw();
- didDraw = drawResult.has_value();
- dequeueBufferDuration = drawResult.value_or(0);
+ context->draw();
} 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.
@@ -218,41 +150,6 @@
if (!canUnblockUiThread) {
unblockUiThread();
}
-
- if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
-
- 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 &&
- targetWorkDuration < kSanityCheckUpperBound &&
- targetWorkDuration != mLastTargetWorkDuration) {
- mLastTargetWorkDuration = targetWorkDuration;
- mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration);
- }
-
- 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;
-}
-
-void DrawFrameTask::sendLoadResetHint() {
- if (!(Properties::useHintManager && Properties::isDrawingEnabled())) return;
- if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
- nsecs_t now = systemTime();
- if (now - mLastFrameNotification > kResetHintTimeout) {
- mHintSessionWrapper->sendHint(SessionHint::CPU_LOAD_RESET);
- }
- mLastFrameNotification = now;
}
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
@@ -305,50 +202,6 @@
mSignal.signal();
}
-DrawFrameTask::HintSessionWrapper::HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId) {
- if (!Properties::useHintManager) return;
- if (uiThreadId < 0 || renderThreadId < 0) return;
-
- ensureAPerformanceHintBindingInitialized();
-
- APerformanceHintManager* manager = gAPH_getManagerFn();
- if (!manager) return;
-
- std::vector<int32_t> tids = CommonPool::getThreadIds();
- tids.push_back(uiThreadId);
- tids.push_back(renderThreadId);
-
- // DrawFrameTask code will always set a target duration before reporting actual durations.
- // So this is just a placeholder value that's never used.
- int64_t dummyTargetDurationNanos = 16666667;
- mHintSession =
- gAPH_createSessionFn(manager, tids.data(), tids.size(), dummyTargetDurationNanos);
-}
-
-DrawFrameTask::HintSessionWrapper::~HintSessionWrapper() {
- if (mHintSession) {
- gAPH_closeSessionFn(mHintSession);
- }
-}
-
-void DrawFrameTask::HintSessionWrapper::updateTargetWorkDuration(long targetDurationNanos) {
- if (mHintSession) {
- gAPH_updateTargetWorkDurationFn(mHintSession, targetDurationNanos);
- }
-}
-
-void DrawFrameTask::HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
- if (mHintSession) {
- gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
- }
-}
-
-void DrawFrameTask::HintSessionWrapper::sendHint(SessionHint hint) {
- if (mHintSession && Properties::isDrawingEnabled()) {
- gAPH_sendHintFn(mHintSession, static_cast<int>(hint));
- }
-}
-
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 7eae41c..fafab24 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,7 +16,6 @@
#ifndef DRAWFRAMETASK_H
#define DRAWFRAMETASK_H
-#include <android/performance_hint.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/StrongPointer.h>
@@ -28,7 +27,6 @@
#include "../Rect.h"
#include "../TreeInfo.h"
#include "RenderTask.h"
-#include "utils/TimeUtils.h"
namespace android {
namespace uirenderer {
@@ -62,8 +60,7 @@
DrawFrameTask();
virtual ~DrawFrameTask();
- void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
- int32_t uiThreadId, int32_t renderThreadId);
+ void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
void setContentDrawBounds(int left, int top, int right, int bottom) {
mContentDrawBounds.set(left, top, right, bottom);
}
@@ -91,22 +88,7 @@
void forceDrawNextFrame() { mForceDrawFrame = true; }
- void sendLoadResetHint();
-
private:
- class HintSessionWrapper {
- public:
- HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId);
- ~HintSessionWrapper();
-
- void updateTargetWorkDuration(long targetDurationNanos);
- void reportActualWorkDuration(long actualDurationNanos);
- void sendHint(SessionHint hint);
-
- private:
- APerformanceHintSession* mHintSession = nullptr;
- };
-
void postAndWait();
bool syncFrameState(TreeInfo& info);
void unblockUiThread();
@@ -117,8 +99,6 @@
RenderThread* mRenderThread;
CanvasContext* mContext;
RenderNode* mTargetNode = nullptr;
- int32_t mUiThreadId = -1;
- int32_t mRenderThreadId = -1;
Rect mContentDrawBounds;
/*********************************************
@@ -135,13 +115,6 @@
std::function<void(bool)> mFrameCommitCallback;
std::function<void()> mFrameCompleteCallback;
- nsecs_t mLastDequeueBufferDuration = 0;
- nsecs_t mLastTargetWorkDuration = 0;
- std::optional<HintSessionWrapper> mHintSessionWrapper;
-
- nsecs_t mLastFrameNotification = 0;
- nsecs_t kResetHintTimeout = 100_ms;
-
bool mForceDrawFrame = false;
};
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
new file mode 100644
index 0000000..edacef0
--- /dev/null
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 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 "HintSessionWrapper.h"
+
+#include <dlfcn.h>
+#include <utils/Log.h>
+
+#include <vector>
+
+#include "../Properties.h"
+#include "thread/CommonPool.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+namespace {
+
+typedef APerformanceHintManager* (*APH_getManager)();
+typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
+ size_t, int64_t);
+typedef void (*APH_closeSession)(APerformanceHintSession* session);
+typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);
+
+bool gAPerformanceHintBindingInitialized = false;
+APH_getManager gAPH_getManagerFn = nullptr;
+APH_createSession gAPH_createSessionFn = nullptr;
+APH_closeSession gAPH_closeSessionFn = nullptr;
+APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
+APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
+APH_sendHint gAPH_sendHintFn = nullptr;
+
+void ensureAPerformanceHintBindingInitialized() {
+ if (gAPerformanceHintBindingInitialized) return;
+
+ void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+ LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
+
+ gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
+ LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
+ "Failed to find required symbol APerformanceHint_getManager!");
+
+ gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_createSession!");
+
+ gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_closeSession!");
+
+ gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
+ handle_, "APerformanceHint_updateTargetWorkDuration");
+ LOG_ALWAYS_FATAL_IF(
+ gAPH_updateTargetWorkDurationFn == nullptr,
+ "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
+
+ gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
+ handle_, "APerformanceHint_reportActualWorkDuration");
+ LOG_ALWAYS_FATAL_IF(
+ gAPH_reportActualWorkDurationFn == nullptr,
+ "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
+
+ gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
+ LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
+ "Failed to find required symbol APerformanceHint_sendHint!");
+
+ gAPerformanceHintBindingInitialized = true;
+}
+
+} // namespace
+
+HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
+ : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {}
+
+HintSessionWrapper::~HintSessionWrapper() {
+ if (mHintSession) {
+ gAPH_closeSessionFn(mHintSession);
+ }
+}
+
+bool HintSessionWrapper::useHintSession() {
+ if (!Properties::useHintManager || !Properties::isDrawingEnabled()) return false;
+ if (mHintSession) return true;
+ // If session does not exist, create it;
+ // this defers session creation until we try to actually use it.
+ if (!mSessionValid) return false;
+ return init();
+}
+
+bool HintSessionWrapper::init() {
+ if (mUiThreadId < 0 || mRenderThreadId < 0) return false;
+
+ // Assume that if we return before the end, it broke
+ mSessionValid = false;
+
+ ensureAPerformanceHintBindingInitialized();
+
+ APerformanceHintManager* manager = gAPH_getManagerFn();
+ if (!manager) return false;
+
+ std::vector<pid_t> tids = CommonPool::getThreadIds();
+ tids.push_back(mUiThreadId);
+ tids.push_back(mRenderThreadId);
+
+ // Use a placeholder target value to initialize,
+ // this will always be replaced elsewhere before it gets used
+ int64_t defaultTargetDurationNanos = 16666667;
+ mHintSession =
+ gAPH_createSessionFn(manager, tids.data(), tids.size(), defaultTargetDurationNanos);
+
+ mSessionValid = !!mHintSession;
+ return mSessionValid;
+}
+
+void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) {
+ if (!useHintSession()) return;
+ targetWorkDurationNanos = targetWorkDurationNanos * Properties::targetCpuTimePercentage / 100;
+ if (targetWorkDurationNanos != mLastTargetWorkDuration &&
+ targetWorkDurationNanos > kSanityCheckLowerBound &&
+ targetWorkDurationNanos < kSanityCheckUpperBound) {
+ mLastTargetWorkDuration = targetWorkDurationNanos;
+ gAPH_updateTargetWorkDurationFn(mHintSession, targetWorkDurationNanos);
+ }
+ mLastFrameNotification = systemTime();
+}
+
+void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
+ if (!useHintSession()) return;
+ if (actualDurationNanos > kSanityCheckLowerBound &&
+ actualDurationNanos < kSanityCheckUpperBound) {
+ gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
+ }
+}
+
+void HintSessionWrapper::sendLoadResetHint() {
+ if (!useHintSession()) return;
+ nsecs_t now = systemTime();
+ if (now - mLastFrameNotification > kResetHintTimeout) {
+ gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET));
+ }
+ mLastFrameNotification = now;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
new file mode 100644
index 0000000..fcbc101
--- /dev/null
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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 <android/performance_hint.h>
+
+#include "utils/TimeUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+namespace renderthread {
+
+class HintSessionWrapper {
+public:
+ HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId);
+ ~HintSessionWrapper();
+
+ void updateTargetWorkDuration(long targetDurationNanos);
+ void reportActualWorkDuration(long actualDurationNanos);
+ void sendLoadResetHint();
+
+private:
+ bool useHintSession();
+ bool init();
+ APerformanceHintSession* mHintSession = nullptr;
+
+ nsecs_t mLastFrameNotification = 0;
+ nsecs_t mLastTargetWorkDuration = 0;
+
+ pid_t mUiThreadId;
+ pid_t mRenderThreadId;
+
+ bool mSessionValid = true;
+
+ static constexpr nsecs_t kResetHintTimeout = 100_ms;
+ static constexpr int64_t kSanityCheckLowerBound = 100_us;
+ static constexpr int64_t kSanityCheckUpperBound = 10_s;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 03a2bc9..07f5a78 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -42,11 +42,13 @@
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory)
: mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
- mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
- return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
+ pid_t uiThreadId = pthread_gettid_np(pthread_self());
+ pid_t renderThreadId = getRenderThreadTid();
+ mContext = mRenderThread.queue().runSync([=, this]() -> CanvasContext* {
+ return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory,
+ uiThreadId, renderThreadId);
});
- mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
- pthread_gettid_np(pthread_self()), getRenderThreadTid());
+ mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}
RenderProxy::~RenderProxy() {
@@ -55,7 +57,7 @@
void RenderProxy::destroyContext() {
if (mContext) {
- mDrawFrameTask.setContext(nullptr, nullptr, nullptr, -1, -1);
+ mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
// This is also a fence as we need to be certain that there are no
// outstanding mDrawFrame tasks posted before it is destroyed
mRenderThread.queue().runSync([this]() { delete mContext; });
@@ -237,7 +239,7 @@
}
void RenderProxy::notifyCallbackPending() {
- mDrawFrameTask.sendLoadResetHint();
+ mRenderThread.queue().post([this]() { mContext->sendLoadResetHint(); });
}
void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
index 1771c35..88420a5 100644
--- a/libs/hwui/tests/unit/CanvasContextTests.cpp
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -36,7 +36,7 @@
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
ASSERT_FALSE(canvasContext->hasSurface());
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index ec949b8..67b4d2c 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -334,7 +334,7 @@
"A");
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -398,7 +398,7 @@
"A");
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -518,7 +518,7 @@
// prepareTree is required to find, which receivers have backward projected nodes
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -618,7 +618,7 @@
// prepareTree is required to find, which receivers have backward projected nodes
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -634,7 +634,7 @@
static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) {
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 61bd646..80796f4 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -274,7 +274,7 @@
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -310,7 +310,7 @@
});
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
canvasContext->setSurface(nullptr);
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 3d5aca4..f825d7c 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -142,7 +142,7 @@
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -201,7 +201,7 @@
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
// Set up a Surface so that we can position the VectorDrawable offscreen.
test::TestContext testContext;