1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "frame_report.h"
17
18 #include <dlfcn.h>
19 #include <cstdio>
20 #include <securec.h>
21 #include <unistd.h>
22 #include <hilog/log.h>
23
24 #include "parameter.h"
25 #include "parameters.h"
26
27 namespace OHOS {
28 namespace Rosen {
29
30 static constexpr OHOS::HiviewDFX::HiLogLabel LOG_LABEL = { LOG_CORE, 0xD001404, "FrameReport" };
31 #define LOGF(...) (void)OHOS::HiviewDFX::HiLog::Fatal(LOG_LABEL, __VA_ARGS__)
32 #define LOGE(...) (void)OHOS::HiviewDFX::HiLog::Error(LOG_LABEL, __VA_ARGS__)
33 #define LOGW(...) (void)OHOS::HiviewDFX::HiLog::Warn(LOG_LABEL, __VA_ARGS__)
34 #define LOGI(...) (void)OHOS::HiviewDFX::HiLog::Info(LOG_LABEL, __VA_ARGS__)
35 #define LOGD(...) (void)OHOS::HiviewDFX::HiLog::Debug(LOG_LABEL, __VA_ARGS__)
36
37 #if (defined(__aarch64__) || defined(__x86_64__))
38 const std::string FRAME_AWARE_SO_PATH = "/system/lib64/libframe_ui_intf.z.so";
39 #else
40 const std::string FRAME_AWARE_SO_PATH = "/system/lib/libframe_ui_intf.z.so";
41 #endif
42
43 constexpr int SCHEDULE_MSG_BUFFER_SIZE = 48;
44 constexpr int REPORT_BUFFER_SIZE = 256;
45
46 constexpr int THOUSAND_COUNT = 1000;
47 constexpr int MIN_GAME_FPS = 10;
48 constexpr int SEND_FPS_FRAME_NUM = 100;
49 constexpr int64_t VALID_TIME_INTERVAL = 800000000;
50 constexpr int MIN_FRAME_NUM = 40;
51 constexpr int64_t ONE_SECOND = 1000000000;
52 const std::vector<int> LEVEL_TO_TARGET_FPS({ 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 90, 120, 144 });
53 const int MAX_FRAME_LEVEL(LEVEL_TO_TARGET_FPS.size() - 1);
54 const std::vector<int> REAL_FPS_TO_LEVEL({ 0, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 90, 120 });
55 constexpr const char *SWITCH_TEXT = "debug.graphic.framereport";
56
GetInstance()57 FrameReport& FrameReport::GetInstance()
58 {
59 static FrameReport instance;
60 return instance;
61 }
62
FrameReport()63 FrameReport::FrameReport()
64 {
65 int ret = WatchParameter(SWITCH_TEXT, SwitchFunction, this);
66 if (ret) {
67 LOGW("WatchParameter failed");
68 }
69 ret = LoadLibrary();
70 if (!ret) {
71 LOGE("dlopen libframe_ui_intf.so failed");
72 return;
73 }
74 }
75
~FrameReport()76 FrameReport::~FrameReport()
77 {
78 CloseLibrary();
79 }
80
SwitchFunction(const char * key,const char * value,void * context)81 void FrameReport::SwitchFunction(const char *key, const char *value, void *context)
82 {
83 auto &that = *reinterpret_cast<FrameReport *>(context);
84 that.gameScene_ = std::atoi(value) != 0;
85 LOGI("SwitchFunction value %{public}s that.gameScene_ %{public}d ",
86 value, that.gameScene_);
87 }
88
IsReportBySurfaceName(std::string & name)89 bool FrameReport::IsReportBySurfaceName(std::string& name)
90 {
91 bool isSurface = name.find("Surface") != std::string::npos;
92 return isSurface;
93 }
94
IsGameScene() const95 bool FrameReport::IsGameScene() const
96 {
97 return gameScene_;
98 }
99
SetGameScene(bool gameScene)100 void FrameReport::SetGameScene(bool gameScene)
101 {
102 gameScene_ = gameScene;
103 LOGI("SetGameScene gameScene_ %{public}d ", gameScene_);
104 }
105
CalculateGameFps(int64_t timestamp)106 void FrameReport::CalculateGameFps(int64_t timestamp)
107 {
108 DeleteInvalidTimes(timestamp);
109 gameTimeStamps_.PushElement(timestamp);
110 gameLastTimeStamp_ = timestamp;
111 GetTargetGameFps();
112
113 if ((targetFps_ > 0 && targetFps_ != lastTargetFps_) || (frameNumCnt_ > SEND_FPS_FRAME_NUM)) {
114 SendGameTargetFps(targetFps_);
115 lastTargetFps_ = targetFps_;
116 frameNumCnt_ = 0;
117 }
118 frameNumCnt_++;
119 }
120
DeleteInvalidTimes(int64_t timestamp)121 void FrameReport::DeleteInvalidTimes(int64_t timestamp)
122 {
123 int64_t headTimestamp = std::get<int64_t>(gameTimeStamps_.GetTailElement());
124 if ((timestamp - headTimestamp) > VALID_TIME_INTERVAL) {
125 gameTimeStamps_.ClearArray();
126 }
127 return;
128 }
129
GetTargetGameFps()130 void FrameReport::GetTargetGameFps()
131 {
132 targetFps_ = 0;
133 FpsCalculator curFps;
134 curFps.frameNum = gameTimeStamps_.ArraySize();
135
136 if (curFps.frameNum < MIN_FRAME_NUM) {
137 return;
138 }
139 curFps.duration =
140 std::get<int64_t>(gameTimeStamps_.GetTailElement()) - std::get<int64_t>(gameTimeStamps_.GetHeadElement());
141 curFps.realFps =
142 static_cast<float>(ONE_SECOND) * static_cast<float>(curFps.frameNum - 1) / static_cast<float>(curFps.duration);
143 curFps.fps = static_cast<int>(curFps.realFps) + 1;
144
145 targetFps_ = FindStage(curFps.fps);
146
147 LOGD("GetTargetGameFps targetFps_ %{public}d curFps.fps %{public}d curFps.realFps %{public}f", targetFps_,
148 curFps.fps, curFps.realFps);
149 }
150
FindStage(const int fps) const151 int FrameReport::FindStage(const int fps) const
152 {
153 int validFps = (fps <= MIN_GAME_FPS) ? MIN_GAME_FPS : fps;
154 auto it = lower_bound(REAL_FPS_TO_LEVEL.begin(), REAL_FPS_TO_LEVEL.end(), validFps);
155 int level = it - REAL_FPS_TO_LEVEL.begin() - 1;
156 level = (level < 0) ? 0 : level;
157 level = (level > MAX_FRAME_LEVEL) ? MAX_FRAME_LEVEL : level;
158 return LEVEL_TO_TARGET_FPS[level];
159 }
160
SetLastSwapBufferTime(int64_t lastSwapBufferTime)161 void FrameReport::SetLastSwapBufferTime(int64_t lastSwapBufferTime)
162 {
163 if (!IsReportBySurfaceName(name_)) {
164 return;
165 }
166 lastSwapBufferTime_ = lastSwapBufferTime;
167
168 int64_t time = std::chrono::duration_cast<std::chrono::nanoseconds>(
169 std::chrono::steady_clock::now().time_since_epoch()).count();
170 CalculateGameFps(time);
171 }
172
SetDequeueBufferTime(std::string & name,int64_t dequeueBufferTime)173 void FrameReport::SetDequeueBufferTime(std::string& name, int64_t dequeueBufferTime)
174 {
175 if (!IsReportBySurfaceName(name)) {
176 return;
177 }
178 name_ = name;
179 dequeueBufferTime_ = dequeueBufferTime;
180 }
181
SetQueueBufferTime(std::string & name,int64_t queueBufferTime)182 void FrameReport::SetQueueBufferTime(std::string& name, int64_t queueBufferTime)
183 {
184 if (!IsReportBySurfaceName(name)) {
185 return;
186 }
187 queueBufferTime_ = queueBufferTime;
188 }
189
SetPendingBufferNum(std::string & name,int32_t pendingBufferNum)190 void FrameReport::SetPendingBufferNum(std::string& name, int32_t pendingBufferNum)
191 {
192 if (!IsReportBySurfaceName(name)) {
193 return;
194 }
195 pendingBufferNum_ = pendingBufferNum;
196 }
197
LoadLibrary()198 bool FrameReport::LoadLibrary()
199 {
200 if (!schedSoLoaded_) {
201 schedHandle_ = dlopen(FRAME_AWARE_SO_PATH.c_str(), RTLD_LAZY);
202 if (schedHandle_ == nullptr) {
203 LOGE("dlopen libframe_ui_intf.so failed! error = %{public}s", dlerror());
204 return false;
205 }
206 schedSoLoaded_ = true;
207 }
208 LOGI("load library success!");
209 return true;
210 }
211
CloseLibrary()212 void FrameReport::CloseLibrary()
213 {
214 if (schedHandle_ != nullptr) {
215 if (dlclose(schedHandle_) != 0) {
216 LOGE("libframe_ui_intf.so close failed!\n");
217 return;
218 }
219 }
220 schedHandle_ = nullptr;
221 schedSoLoaded_ = false;
222 LOGI("libframe_ui_intf.so close success!\n");
223 }
224
LoadSymbol(const char * symName)225 void* FrameReport::LoadSymbol(const char* symName)
226 {
227 if (!schedSoLoaded_) {
228 LOGE("libframe_ui_intf.so not loaded.\n");
229 return nullptr;
230 }
231
232 void *funcSym = dlsym(schedHandle_, symName);
233 if (funcSym == nullptr) {
234 LOGE("Get %{public}s symbol failed: %{public}s\n", symName, dlerror());
235 return nullptr;
236 }
237 return funcSym;
238 }
239
CurTime(int type,const std::string & message,int length)240 int FrameReport::CurTime(int type, const std::string& message, int length)
241 {
242 int ret = -1;
243 if (curTimeFunc_ == nullptr) {
244 curTimeFunc_ = LoadSymbol("CurTime");
245 }
246 if (curTimeFunc_ != nullptr) {
247 auto curTimeFunc = reinterpret_cast<int (*)(int, const std::string&, int)>(curTimeFunc_);
248 ret = curTimeFunc(type, message, length);
249 } else {
250 LOGE("load CurTime function failed!");
251 }
252 return ret;
253 }
254
SchedMsg(int type,const std::string & message,int length)255 int FrameReport::SchedMsg(int type, const std::string& message, int length)
256 {
257 int ret = -1;
258 if (schedMsgFunc_ == nullptr) {
259 schedMsgFunc_ = LoadSymbol("SchedMsg");
260 }
261 if (schedMsgFunc_ != nullptr) {
262 auto schedMsgFunc = reinterpret_cast<int (*)(int, const std::string&, int)>(schedMsgFunc_);
263 ret = schedMsgFunc(type, message, length);
264 } else {
265 LOGE("load SchedMsg function failed!");
266 }
267 return ret;
268 }
269
Report(std::string & name)270 void FrameReport::Report(std::string& name)
271 {
272 if (!IsReportBySurfaceName(name)) {
273 return;
274 }
275
276 char msg[REPORT_BUFFER_SIZE] = { 0 };
277
278 int ret = sprintf_s(msg, sizeof(msg),
279 "{\"swapBufferTime\":\"%d\",\"pendingBufferNum\":\"%d\",\"dequeueBufferTime\":\"%d\","
280 "\"queueBufferTime\":\"%d\", \"skipHint\":\"%d\"}",
281 static_cast<int>(lastSwapBufferTime_ / THOUSAND_COUNT), pendingBufferNum_,
282 static_cast<int>(dequeueBufferTime_ / THOUSAND_COUNT), static_cast<int>(queueBufferTime_ / THOUSAND_COUNT),
283 skipHintStatus_);
284 if (ret == -1) {
285 return;
286 }
287 std::string bfMsg(msg);
288 LOGD("Report bfMsg %{public}s ", bfMsg.c_str());
289 #ifdef AI_SCHED_ENABLE
290 ret = CurTime(1, bfMsg, bfMsg.size());
291 if (ret) {
292 LOGW("hwsched sf time failed");
293 } else {
294 LOGD("hwsched sf time succ");
295 }
296 #endif
297 }
298
SendGameTargetFps(int32_t fps)299 void FrameReport::SendGameTargetFps(int32_t fps)
300 {
301 char msg[SCHEDULE_MSG_BUFFER_SIZE] = { 0 };
302 int ret = sprintf_s(msg, sizeof(msg), "{\"agpFPS\":\"%d\"}", fps);
303 if (ret == -1) {
304 return;
305 }
306
307 std::string bfMsg(msg);
308 LOGD("SendGameTargetFps bfMsg %{public}s ", bfMsg.c_str());
309 #ifdef AI_SCHED_ENABLE
310 ret = SchedMsg(1, bfMsg, bfMsg.size());
311 if (ret) {
312 LOGW("hwsched game fps failed");
313 } else {
314 LOGD("hwsched game fps succ");
315 }
316 #endif
317 }
318
319 } // namespace Rosen
320 } // namespace OHOS