xref: /aosp_15_r20/frameworks/base/libs/hwui/renderthread/HintSessionWrapper.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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 <algorithm>
24 #include <chrono>
25 #include <vector>
26 
27 #include "../Properties.h"
28 #include "RenderThread.h"
29 #include "thread/CommonPool.h"
30 
31 using namespace std::chrono_literals;
32 
33 namespace android {
34 namespace uirenderer {
35 namespace renderthread {
36 
37 #define BIND_APH_METHOD(name)                                         \
38     name = (decltype(name))dlsym(handle_, "APerformanceHint_" #name); \
39     LOG_ALWAYS_FATAL_IF(name == nullptr, "Failed to find required symbol APerformanceHint_" #name)
40 
init()41 void HintSessionWrapper::HintSessionBinding::init() {
42     if (mInitialized) return;
43 
44     void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
45     LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
46 
47     BIND_APH_METHOD(getManager);
48     BIND_APH_METHOD(createSessionInternal);
49     BIND_APH_METHOD(closeSession);
50     BIND_APH_METHOD(updateTargetWorkDuration);
51     BIND_APH_METHOD(reportActualWorkDuration);
52     BIND_APH_METHOD(sendHint);
53     BIND_APH_METHOD(setThreads);
54 
55     mInitialized = true;
56 }
57 
HintSessionWrapper(pid_t uiThreadId,pid_t renderThreadId)58 HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
59         : mUiThreadId(uiThreadId)
60         , mRenderThreadId(renderThreadId)
61         , mBinding(std::make_shared<HintSessionBinding>()) {}
62 
~HintSessionWrapper()63 HintSessionWrapper::~HintSessionWrapper() {
64     destroy();
65 }
66 
destroy()67 void HintSessionWrapper::destroy() {
68     if (mHintSessionFuture.has_value()) {
69         mHintSession = mHintSessionFuture->get();
70         mHintSessionFuture = std::nullopt;
71     }
72     if (mSetThreadsFuture.has_value()) {
73         mSetThreadsFuture->wait();
74         mSetThreadsFuture = std::nullopt;
75     }
76     if (mHintSession) {
77         mBinding->closeSession(mHintSession);
78         mSessionValid = true;
79         mHintSession = nullptr;
80     }
81     mResetsSinceLastReport = 0;
82 }
83 
init()84 bool HintSessionWrapper::init() {
85     if (mHintSession != nullptr) return true;
86     // If we're waiting for the session
87     if (mHintSessionFuture.has_value()) {
88         // If the session is here
89         if (mHintSessionFuture->wait_for(0s) == std::future_status::ready) {
90             mHintSession = mHintSessionFuture->get();
91             mHintSessionFuture = std::nullopt;
92             if (mHintSession != nullptr) {
93                 mSessionValid = true;
94                 return true;
95             }
96         }
97         return false;
98     }
99 
100     // If it broke last time we tried this, shouldn't be running, or
101     // has bad argument values, don't even bother
102     if (!mSessionValid || !Properties::useHintManager || !Properties::isDrawingEnabled() ||
103         mUiThreadId < 0 || mRenderThreadId < 0) {
104         return false;
105     }
106 
107     // Assume that if we return before the end, it broke
108     mSessionValid = false;
109 
110     mBinding->init();
111 
112     APerformanceHintManager* manager = mBinding->getManager();
113     if (!manager) return false;
114 
115     mPermanentSessionTids = CommonPool::getThreadIds();
116     mPermanentSessionTids.push_back(mUiThreadId);
117     mPermanentSessionTids.push_back(mRenderThreadId);
118 
119     // Use the cached target value if there is one, otherwise use a default. This is to ensure
120     // the cached target and target in PowerHAL are consistent, and that it updates correctly
121     // whenever there is a change.
122     int64_t targetDurationNanos =
123             mLastTargetWorkDuration == 0 ? kDefaultTargetDuration : mLastTargetWorkDuration;
124     mHintSessionFuture = CommonPool::async([=, this, tids = mPermanentSessionTids] {
125         return mBinding->createSessionInternal(manager, tids.data(), tids.size(),
126                                                targetDurationNanos, SessionTag::HWUI);
127     });
128     return false;
129 }
130 
updateTargetWorkDuration(long targetWorkDurationNanos)131 void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) {
132     if (!init()) return;
133     targetWorkDurationNanos = targetWorkDurationNanos * Properties::targetCpuTimePercentage / 100;
134     if (targetWorkDurationNanos != mLastTargetWorkDuration &&
135         targetWorkDurationNanos > kSanityCheckLowerBound &&
136         targetWorkDurationNanos < kSanityCheckUpperBound) {
137         mLastTargetWorkDuration = targetWorkDurationNanos;
138         mBinding->updateTargetWorkDuration(mHintSession, targetWorkDurationNanos);
139     }
140     mLastFrameNotification = systemTime();
141 }
142 
reportActualWorkDuration(long actualDurationNanos)143 void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
144     if (!init()) return;
145     mResetsSinceLastReport = 0;
146     if (actualDurationNanos > kSanityCheckLowerBound &&
147         actualDurationNanos < kSanityCheckUpperBound) {
148         mBinding->reportActualWorkDuration(mHintSession, actualDurationNanos);
149     }
150     mLastFrameNotification = systemTime();
151 }
152 
setActiveFunctorThreads(std::vector<pid_t> threadIds)153 void HintSessionWrapper::setActiveFunctorThreads(std::vector<pid_t> threadIds) {
154     if (!init()) return;
155     if (!mBinding || !mHintSession) return;
156     // Sort the vector to make sure they're compared as sets.
157     std::sort(threadIds.begin(), threadIds.end());
158     if (threadIds == mActiveFunctorTids) return;
159     mActiveFunctorTids = std::move(threadIds);
160     std::vector<pid_t> combinedTids = mPermanentSessionTids;
161     std::copy(mActiveFunctorTids.begin(), mActiveFunctorTids.end(),
162               std::back_inserter(combinedTids));
163     mSetThreadsFuture = CommonPool::async([this, tids = std::move(combinedTids)] {
164         int ret = mBinding->setThreads(mHintSession, tids.data(), tids.size());
165         ALOGE_IF(ret != 0, "APerformaceHint_setThreads failed: %d", ret);
166         return ret;
167     });
168 }
169 
sendLoadResetHint()170 void HintSessionWrapper::sendLoadResetHint() {
171     static constexpr int kMaxResetsSinceLastReport = 2;
172     if (!init()) return;
173     nsecs_t now = systemTime();
174     if (now - mLastFrameNotification > kResetHintTimeout &&
175         mResetsSinceLastReport <= kMaxResetsSinceLastReport) {
176         ++mResetsSinceLastReport;
177         mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET));
178     }
179     mLastFrameNotification = now;
180 }
181 
sendLoadIncreaseHint()182 void HintSessionWrapper::sendLoadIncreaseHint() {
183     if (!init()) return;
184     mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_UP));
185 }
186 
alive()187 bool HintSessionWrapper::alive() {
188     return mHintSession != nullptr;
189 }
190 
getLastUpdate()191 nsecs_t HintSessionWrapper::getLastUpdate() {
192     return mLastFrameNotification;
193 }
194 
195 // Requires passing in its shared_ptr since it shouldn't own a shared_ptr to itself
delayedDestroy(RenderThread & rt,nsecs_t delay,std::shared_ptr<HintSessionWrapper> wrapperPtr)196 void HintSessionWrapper::delayedDestroy(RenderThread& rt, nsecs_t delay,
197                                         std::shared_ptr<HintSessionWrapper> wrapperPtr) {
198     nsecs_t lastUpdate = wrapperPtr->getLastUpdate();
199     rt.queue().postDelayed(delay, [lastUpdate = lastUpdate, wrapper = wrapperPtr]() mutable {
200         if (wrapper->getLastUpdate() == lastUpdate) {
201             wrapper->destroy();
202         }
203         // Ensure the shared_ptr is killed at the end of the method
204         wrapper = nullptr;
205     });
206 }
207 
208 } /* namespace renderthread */
209 } /* namespace uirenderer */
210 } /* namespace android */
211