1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "HintSessionWrapper.h"
18
19 #include <dlfcn.h>
20 #include <private/performance_hint_private.h>
21 #include <utils/Log.h>
22
23 #include <chrono>
24 #include <vector>
25
26 #include "../Properties.h"
27 #include "thread/CommonPool.h"
28
29 using namespace std::chrono_literals;
30
31 namespace android {
32 namespace uirenderer {
33 namespace renderthread {
34
35 #define BIND_APH_METHOD(name) \
36 name = (decltype(name))dlsym(handle_, "APerformanceHint_" #name); \
37 LOG_ALWAYS_FATAL_IF(name == nullptr, "Failed to find required symbol APerformanceHint_" #name)
38
init()39 void HintSessionWrapper::HintSessionBinding::init() {
40 if (mInitialized) return;
41
42 void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
43 LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
44
45 BIND_APH_METHOD(getManager);
46 BIND_APH_METHOD(createSession);
47 BIND_APH_METHOD(closeSession);
48 BIND_APH_METHOD(updateTargetWorkDuration);
49 BIND_APH_METHOD(reportActualWorkDuration);
50 BIND_APH_METHOD(sendHint);
51
52 mInitialized = true;
53 }
54
HintSessionWrapper(pid_t uiThreadId,pid_t renderThreadId)55 HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
56 : mUiThreadId(uiThreadId)
57 , mRenderThreadId(renderThreadId)
58 , mBinding(std::make_shared<HintSessionBinding>()) {}
59
~HintSessionWrapper()60 HintSessionWrapper::~HintSessionWrapper() {
61 destroy();
62 }
63
destroy()64 void HintSessionWrapper::destroy() {
65 if (mHintSessionFuture.valid()) {
66 mHintSession = mHintSessionFuture.get();
67 }
68 if (mHintSession) {
69 mBinding->closeSession(mHintSession);
70 mSessionValid = true;
71 mHintSession = nullptr;
72 }
73 }
74
init()75 bool HintSessionWrapper::init() {
76 if (mHintSession != nullptr) return true;
77
78 // If we're waiting for the session
79 if (mHintSessionFuture.valid()) {
80 // If the session is here
81 if (mHintSessionFuture.wait_for(0s) == std::future_status::ready) {
82 mHintSession = mHintSessionFuture.get();
83 if (mHintSession != nullptr) {
84 mSessionValid = true;
85 return true;
86 }
87 }
88 return false;
89 }
90
91 // If it broke last time we tried this, shouldn't be running, or
92 // has bad argument values, don't even bother
93 if (!mSessionValid || !Properties::useHintManager || !Properties::isDrawingEnabled() ||
94 mUiThreadId < 0 || mRenderThreadId < 0) {
95 return false;
96 }
97
98 // Assume that if we return before the end, it broke
99 mSessionValid = false;
100
101 mBinding->init();
102
103 APerformanceHintManager* manager = mBinding->getManager();
104 if (!manager) return false;
105
106 std::vector<pid_t> tids = CommonPool::getThreadIds();
107 tids.push_back(mUiThreadId);
108 tids.push_back(mRenderThreadId);
109
110 // Use a placeholder target value to initialize,
111 // this will always be replaced elsewhere before it gets used
112 int64_t defaultTargetDurationNanos = 16666667;
113 mHintSessionFuture = CommonPool::async([=, this, tids = std::move(tids)] {
114 return mBinding->createSession(manager, tids.data(), tids.size(),
115 defaultTargetDurationNanos);
116 });
117 return false;
118 }
119
updateTargetWorkDuration(long targetWorkDurationNanos)120 void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) {
121 if (!init()) return;
122 targetWorkDurationNanos = targetWorkDurationNanos * Properties::targetCpuTimePercentage / 100;
123 if (targetWorkDurationNanos != mLastTargetWorkDuration &&
124 targetWorkDurationNanos > kSanityCheckLowerBound &&
125 targetWorkDurationNanos < kSanityCheckUpperBound) {
126 mLastTargetWorkDuration = targetWorkDurationNanos;
127 mBinding->updateTargetWorkDuration(mHintSession, targetWorkDurationNanos);
128 }
129 mLastFrameNotification = systemTime();
130 }
131
reportActualWorkDuration(long actualDurationNanos)132 void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
133 if (!init()) return;
134 mResetsSinceLastReport = 0;
135 if (actualDurationNanos > kSanityCheckLowerBound &&
136 actualDurationNanos < kSanityCheckUpperBound) {
137 mBinding->reportActualWorkDuration(mHintSession, actualDurationNanos);
138 }
139 }
140
sendLoadResetHint()141 void HintSessionWrapper::sendLoadResetHint() {
142 static constexpr int kMaxResetsSinceLastReport = 2;
143 if (!init()) return;
144 nsecs_t now = systemTime();
145 if (now - mLastFrameNotification > kResetHintTimeout &&
146 mResetsSinceLastReport <= kMaxResetsSinceLastReport) {
147 ++mResetsSinceLastReport;
148 mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET));
149 }
150 mLastFrameNotification = now;
151 }
152
sendLoadIncreaseHint()153 void HintSessionWrapper::sendLoadIncreaseHint() {
154 if (!init()) return;
155 mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_UP));
156 }
157
158 } /* namespace renderthread */
159 } /* namespace uirenderer */
160 } /* namespace android */
161