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