Switch HWUI to use native performance hint API

Test: None
Bug: 194204196
Change-Id: I80dfdb5d56921c465406cc4534e82738c668d46d
This commit is contained in:
Bo Liu
2021-07-17 16:42:13 -04:00
parent 4426772eab
commit 6a3fc60f43
6 changed files with 137 additions and 181 deletions

View File

@@ -44,8 +44,6 @@
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
#include <pthread.h>
#include <algorithm>
#include <atomic>
#include <vector>
@@ -60,10 +58,6 @@ using namespace android::uirenderer::renderthread;
struct {
jclass clazz;
jmethodID invokePictureCapturedCallback;
jmethodID createHintSession;
jmethodID updateTargetWorkDuration;
jmethodID reportActualWorkDuration;
jmethodID closeHintSession;
} gHardwareRenderer;
struct {
@@ -90,14 +84,6 @@ static JNIEnv* getenv(JavaVM* vm) {
return env;
}
static bool hasExceptionAndClear(JNIEnv* env) {
if (GraphicsJNI::hasException(env)) {
env->ExceptionClear();
return true;
}
return false;
}
typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface);
ANW_fromSurface fromSurface;
@@ -147,67 +133,6 @@ private:
}
};
class HintSessionWrapper : public LightRefBase<HintSessionWrapper> {
public:
static sp<HintSessionWrapper> create(JNIEnv* env, RenderProxy* proxy) {
if (!Properties::useHintManager) return nullptr;
// Include UI thread (self), render thread, and thread pool.
std::vector<int> tids = CommonPool::getThreadIds();
tids.push_back(proxy->getRenderThreadTid());
tids.push_back(pthread_gettid_np(pthread_self()));
jintArray tidsArray = env->NewIntArray(tids.size());
if (hasExceptionAndClear(env)) return nullptr;
env->SetIntArrayRegion(tidsArray, 0, tids.size(), reinterpret_cast<jint*>(tids.data()));
if (hasExceptionAndClear(env)) return nullptr;
jobject session = env->CallStaticObjectMethod(
gHardwareRenderer.clazz, gHardwareRenderer.createHintSession, tidsArray);
if (hasExceptionAndClear(env) || !session) return nullptr;
return new HintSessionWrapper(env, session);
}
~HintSessionWrapper() {
if (!mSession) return;
JNIEnv* env = getenv(mVm);
env->CallStaticVoidMethod(gHardwareRenderer.clazz, gHardwareRenderer.closeHintSession,
mSession);
hasExceptionAndClear(env);
env->DeleteGlobalRef(mSession);
mSession = nullptr;
}
void updateTargetWorkDuration(long targetDurationNanos) {
if (!mSession) return;
JNIEnv* env = getenv(mVm);
env->CallStaticVoidMethod(gHardwareRenderer.clazz,
gHardwareRenderer.updateTargetWorkDuration, mSession,
static_cast<jlong>(targetDurationNanos));
hasExceptionAndClear(env);
}
void reportActualWorkDuration(long actualDurationNanos) {
if (!mSession) return;
JNIEnv* env = getenv(mVm);
env->CallStaticVoidMethod(gHardwareRenderer.clazz,
gHardwareRenderer.reportActualWorkDuration, mSession,
static_cast<jlong>(actualDurationNanos));
hasExceptionAndClear(env);
}
private:
HintSessionWrapper(JNIEnv* env, jobject jobject) {
env->GetJavaVM(&mVm);
if (jobject) {
mSession = env->NewGlobalRef(jobject);
LOG_ALWAYS_FATAL_IF(!mSession, "Failed to make global ref");
}
}
JavaVM* mVm = nullptr;
jobject mSession = nullptr;
};
static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) {
RenderProxy::rotateProcessStatsBuffer();
}
@@ -235,12 +160,6 @@ static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject claz
RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
ContextFactoryImpl factory(rootRenderNode);
RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory);
sp<HintSessionWrapper> wrapper = HintSessionWrapper::create(env, proxy);
if (wrapper) {
proxy->setHintSessionCallbacks(
[wrapper](int64_t nanos) { wrapper->updateTargetWorkDuration(nanos); },
[wrapper](int64_t nanos) { wrapper->reportActualWorkDuration(nanos); });
}
return (jlong) proxy;
}
@@ -1059,18 +978,6 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) {
gHardwareRenderer.invokePictureCapturedCallback = GetStaticMethodIDOrDie(env, hardwareRenderer,
"invokePictureCapturedCallback",
"(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V");
gHardwareRenderer.createHintSession =
GetStaticMethodIDOrDie(env, hardwareRenderer, "createHintSession",
"([I)Landroid/os/PerformanceHintManager$Session;");
gHardwareRenderer.updateTargetWorkDuration =
GetStaticMethodIDOrDie(env, hardwareRenderer, "updateTargetWorkDuration",
"(Landroid/os/PerformanceHintManager$Session;J)V");
gHardwareRenderer.reportActualWorkDuration =
GetStaticMethodIDOrDie(env, hardwareRenderer, "reportActualWorkDuration",
"(Landroid/os/PerformanceHintManager$Session;J)V");
gHardwareRenderer.closeHintSession =
GetStaticMethodIDOrDie(env, hardwareRenderer, "closeHintSession",
"(Landroid/os/PerformanceHintManager$Session;)V");
jclass aSurfaceTransactionCallbackClass =
FindClassOrDie(env, "android/graphics/HardwareRenderer$ASurfaceTransactionCallback");

View File

@@ -16,6 +16,7 @@
#include "DrawFrameTask.h"
#include <dlfcn.h>
#include <gui/TraceUtils.h>
#include <utils/Log.h>
#include <algorithm>
@@ -26,11 +27,63 @@
#include "../RenderNode.h"
#include "CanvasContext.h"
#include "RenderThread.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_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_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_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_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)
@@ -39,17 +92,13 @@ DrawFrameTask::DrawFrameTask()
DrawFrameTask::~DrawFrameTask() {}
void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
RenderNode* targetNode) {
void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
int32_t uiThreadId, int32_t renderThreadId) {
mRenderThread = thread;
mContext = context;
mTargetNode = targetNode;
}
void DrawFrameTask::setHintSessionCallbacks(std::function<void(int64_t)> updateTargetWorkDuration,
std::function<void(int64_t)> reportActualWorkDuration) {
mUpdateTargetWorkDuration = std::move(updateTargetWorkDuration);
mReportActualWorkDuration = std::move(reportActualWorkDuration);
mUiThreadId = uiThreadId;
mRenderThreadId = renderThreadId;
}
void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -144,27 +193,25 @@ void DrawFrameTask::run() {
unblockUiThread();
}
// These member callbacks are effectively const as they are set once during init, so it's safe
// to use these directly instead of making local copies.
if (mUpdateTargetWorkDuration && mReportActualWorkDuration) {
constexpr int64_t kSanityCheckLowerBound = 100000; // 0.1ms
constexpr int64_t kSanityCheckUpperBound = 10000000000; // 10s
int64_t targetWorkDuration = frameDeadline - intendedVsync;
targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
if (targetWorkDuration > kSanityCheckLowerBound &&
targetWorkDuration < kSanityCheckUpperBound &&
targetWorkDuration != mLastTargetWorkDuration) {
mLastTargetWorkDuration = targetWorkDuration;
mUpdateTargetWorkDuration(targetWorkDuration);
}
int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
int64_t actualDuration = frameDuration -
(std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
dequeueBufferDuration;
if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
mReportActualWorkDuration(actualDuration);
}
if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
constexpr int64_t kSanityCheckLowerBound = 100000; // 0.1ms
constexpr int64_t kSanityCheckUpperBound = 10000000000; // 10s
int64_t targetWorkDuration = frameDeadline - intendedVsync;
targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
if (targetWorkDuration > kSanityCheckLowerBound &&
targetWorkDuration < kSanityCheckUpperBound &&
targetWorkDuration != mLastTargetWorkDuration) {
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);
}
mLastDequeueBufferDuration = dequeueBufferDuration;
}
@@ -216,6 +263,44 @@ void DrawFrameTask::unblockUiThread() {
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);
}
}
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */

View File

@@ -16,8 +16,10 @@
#ifndef DRAWFRAMETASK_H
#define DRAWFRAMETASK_H
#include <optional>
#include <vector>
#include <performance_hint_private.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/StrongPointer.h>
@@ -60,9 +62,8 @@ public:
DrawFrameTask();
virtual ~DrawFrameTask();
void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
void setHintSessionCallbacks(std::function<void(int64_t)> updateTargetWorkDuration,
std::function<void(int64_t)> reportActualWorkDuration);
void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
int32_t uiThreadId, int32_t renderThreadId);
void setContentDrawBounds(int left, int top, int right, int bottom) {
mContentDrawBounds.set(left, top, right, bottom);
}
@@ -85,6 +86,18 @@ public:
}
private:
class HintSessionWrapper {
public:
HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId);
~HintSessionWrapper();
void updateTargetWorkDuration(long targetDurationNanos);
void reportActualWorkDuration(long actualDurationNanos);
private:
APerformanceHintSession* mHintSession = nullptr;
};
void postAndWait();
bool syncFrameState(TreeInfo& info);
void unblockUiThread();
@@ -95,6 +108,8 @@ private:
RenderThread* mRenderThread;
CanvasContext* mContext;
RenderNode* mTargetNode = nullptr;
int32_t mUiThreadId = -1;
int32_t mRenderThreadId = -1;
Rect mContentDrawBounds;
/*********************************************
@@ -112,8 +127,7 @@ private:
nsecs_t mLastDequeueBufferDuration = 0;
nsecs_t mLastTargetWorkDuration = 0;
std::function<void(int64_t)> mUpdateTargetWorkDuration;
std::function<void(int64_t)> mReportActualWorkDuration;
std::optional<HintSessionWrapper> mHintSessionWrapper;
};
} /* namespace renderthread */

View File

@@ -29,6 +29,8 @@
#include "utils/Macros.h"
#include "utils/TimeUtils.h"
#include <pthread.h>
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -39,7 +41,8 @@ RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
});
mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
pthread_gettid_np(pthread_self()), getRenderThreadTid());
}
RenderProxy::~RenderProxy() {
@@ -48,7 +51,7 @@ RenderProxy::~RenderProxy() {
void RenderProxy::destroyContext() {
if (mContext) {
mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
mDrawFrameTask.setContext(nullptr, nullptr, nullptr, -1, -1);
// 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; });
@@ -76,12 +79,6 @@ void RenderProxy::setName(const char* name) {
mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); });
}
void RenderProxy::setHintSessionCallbacks(std::function<void(int64_t)> updateTargetWorkDuration,
std::function<void(int64_t)> reportActualWorkDuration) {
mDrawFrameTask.setHintSessionCallbacks(std::move(updateTargetWorkDuration),
std::move(reportActualWorkDuration));
}
void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
if (window) { ANativeWindow_acquire(window); }
mRenderThread.queue().post([this, win = window, enableTimeout]() mutable {

View File

@@ -71,8 +71,6 @@ public:
void setSwapBehavior(SwapBehavior swapBehavior);
bool loadSystemProperties();
void setName(const char* name);
void setHintSessionCallbacks(std::function<void(int64_t)> updateTargetWorkDuration,
std::function<void(int64_t)> reportActualWorkDuration);
void setSurface(ANativeWindow* window, bool enableTimeout = true);
void setSurfaceControl(ASurfaceControl* surfaceControl);