1 /*
2 * Copyright (c) 2025 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 #include "hgm_soft_vsync_manager.h"
16
17 #include "animation/rs_frame_rate_range.h"
18 #include "common/rs_optional_trace.h"
19 #include "hgm_command.h"
20 #include "hgm_core.h"
21 #include "hgm_voter.h"
22 #include "rs_trace.h"
23
24 namespace OHOS {
25 namespace Rosen {
26 namespace {
27 const std::string VOTER_NAME[] = {
28 "VOTER_VRATE",
29 "VOTER_VIDEOCALL",
30 "VOTER_GAMEFRAMEINSERTION",
31 "VOTER_HIGH",
32 "VOTER_MID",
33 "VOTER_LOW",
34 };
35 constexpr uint32_t SOFT_NATIVE_VSYNC_FRAME_RATE_TYPE = 6;
36 const std::string VRATE_CONTROL_MINIFPS = "minifps";
37 }
38
39 // LCOV_EXCL_START
HgmSoftVSyncManager()40 HgmSoftVSyncManager::HgmSoftVSyncManager()
41 {
42 HGM_LOGI("Construction of HgmSoftVSyncManager");
43 }
44
InitController(std::weak_ptr<HgmVSyncGeneratorController> controller,sptr<VSyncDistributor> appDistributor)45 void HgmSoftVSyncManager::InitController(std::weak_ptr<HgmVSyncGeneratorController> controller,
46 sptr<VSyncDistributor> appDistributor)
47 {
48 controller_ = controller;
49 appDistributor_ = appDistributor;
50 }
51
HandleLinkers()52 void HgmSoftVSyncManager::HandleLinkers()
53 {
54 // Clear votes for non-existent linkers
55 for (auto iter = linkerVoteMap_.begin(); iter != linkerVoteMap_.end();) {
56 if (appFrameRateLinkers_.count(iter->first) == 0) {
57 iter = linkerVoteMap_.erase(iter);
58 } else {
59 ++iter;
60 }
61 }
62
63 winLinkerMap_.clear();
64 vsyncLinkerMap_.clear();
65 for (auto linker : appFrameRateLinkers_) {
66 if (linker.second == nullptr) {
67 continue;
68 }
69
70 winLinkerMap_[linker.second->GetWindowNodeId()] = linker.first;
71
72 vsyncLinkerMap_.try_emplace(linker.second->GetVsyncName(), std::vector<FrameRateLinkerId>{});
73 vsyncLinkerMap_[linker.second->GetVsyncName()].emplace_back(linker.first);
74 }
75 }
76
CollectFrameRateChange(FrameRateRange finalRange,std::shared_ptr<RSRenderFrameRateLinker> rsFrameRateLinker,const FrameRateLinkerMap & appFrameRateLinkers,const uint32_t currRefreshRate)77 bool HgmSoftVSyncManager::CollectFrameRateChange(FrameRateRange finalRange,
78 std::shared_ptr<RSRenderFrameRateLinker> rsFrameRateLinker,
79 const FrameRateLinkerMap& appFrameRateLinkers,
80 const uint32_t currRefreshRate)
81 {
82 auto sharedController = controller_.lock();
83 if (sharedController == nullptr) {
84 HGM_LOGE("no valid controller, cannot work correctly, maybe Init() wasn't executed correctly.");
85 return false;
86 }
87 Reset();
88 bool frameRateChanged = false;
89 bool controllerRateChanged = false;
90 auto rsFrameRate = HgmSoftVSyncManager::GetDrawingFrameRate(currRefreshRate, finalRange);
91 controllerRate_ = rsFrameRate > 0 ? rsFrameRate : sharedController->GetCurrentRate();
92 if (controllerRate_ != sharedController->GetCurrentRate()) {
93 rsFrameRateLinker->SetFrameRate(controllerRate_);
94 controllerRateChanged = true;
95 frameRateChanged = true;
96 }
97
98 RS_TRACE_NAME_FMT("CollectFrameRateChange rsFrameRate: %d, finalRange = (%d, %d, %d)",
99 rsFrameRate, finalRange.min_, finalRange.max_, finalRange.preferred_);
100 RS_TRACE_INT("PreferredFrameRate", static_cast<int>(finalRange.preferred_));
101
102 appChangeData_.clear();
103 for (auto linker : appFrameRateLinkers) {
104 if (linker.second == nullptr) {
105 continue;
106 }
107 auto expectedRange = linker.second->GetExpectedRange();
108 CalcAppFrameRate(linker, expectedRange, frameRateChanged, controllerRateChanged, currRefreshRate);
109 }
110 return frameRateChanged;
111 }
112
CalcAppFrameRate(const std::pair<FrameRateLinkerId,std::shared_ptr<RSRenderFrameRateLinker>> & linker,FrameRateRange & expectedRange,bool & frameRateChanged,bool controllerRateChanged,const uint32_t currRefreshRate)113 void HgmSoftVSyncManager::CalcAppFrameRate(
114 const std::pair<FrameRateLinkerId, std::shared_ptr<RSRenderFrameRateLinker>>& linker,
115 FrameRateRange& expectedRange,
116 bool& frameRateChanged,
117 bool controllerRateChanged,
118 const uint32_t currRefreshRate)
119 {
120 bool isChanged = false;
121 if (HgmEnergyConsumptionPolicy::Instance().GetVideoCallFrameRate(
122 ExtractPid(linker.first), linker.second->GetVsyncName(), expectedRange)) {
123 isChanged = true;
124 }
125 if (CollectVRateChange(linker.first, expectedRange)) {
126 isChanged = true;
127 }
128 if (!isChanged && appVoteData_.count(linker.first)) {
129 expectedRange.preferred_ = static_cast<int32_t>(appVoteData_[linker.first]);
130 }
131 auto appFrameRate = isPerformanceFirst_ && expectedRange.type_ != SOFT_NATIVE_VSYNC_FRAME_RATE_TYPE ?
132 OLED_NULL_HZ : HgmSoftVSyncManager::GetDrawingFrameRate(currRefreshRate, expectedRange);
133 if (CollectGameRateDiscountChange(linker.first, expectedRange, currRefreshRate)) {
134 appFrameRate = static_cast<uint32_t>(expectedRange.preferred_);
135 }
136 if (appFrameRate != linker.second->GetFrameRate() || controllerRateChanged) {
137 linker.second->SetFrameRate(appFrameRate);
138 appChangeData_[linker.second->GetId()] = appFrameRate;
139 HGM_LOGD("HgmSoftVSyncManager: appChangeData linkerId = %{public}" PRIu64 ", %{public}d",
140 linker.second->GetId(), appFrameRate);
141 frameRateChanged = true;
142 }
143 if (expectedRange.min_ == OLED_NULL_HZ && expectedRange.preferred_ == OLED_NULL_HZ &&
144 (expectedRange.max_ == OLED_144_HZ || expectedRange.max_ == OLED_NULL_HZ)) {
145 return;
146 }
147 RS_TRACE_NAME_FMT("HgmSoftVSyncManager::UniProcessData multiAppFrameRate: pid = %d, linkerId = %ld, "\
148 "appFrameRate = %d, appRange = (%d, %d, %d)", ExtractPid(linker.first), linker.second->GetId(),
149 appFrameRate, expectedRange.min_, expectedRange.max_, expectedRange.preferred_);
150 }
151
GetDrawingFrameRate(const uint32_t refreshRate,const FrameRateRange & range)152 uint32_t HgmSoftVSyncManager::GetDrawingFrameRate(const uint32_t refreshRate, const FrameRateRange& range)
153 {
154 if (refreshRate == 0 || range.preferred_ <= 0) {
155 return 0;
156 }
157
158 uint32_t preferredFps = static_cast<uint32_t>(range.preferred_);
159 if (!range.IsValid() || preferredFps > refreshRate) {
160 return refreshRate;
161 }
162
163 // find the smallest value in range of [preferredFps, refreshRate], which can exactly divide refreshRate
164 uint32_t divisor = refreshRate / preferredFps;
165 while (divisor > 1) {
166 if (refreshRate % divisor == 0) {
167 break;
168 }
169 divisor--;
170 }
171 return refreshRate / divisor;
172 }
173
GetVRateMiniFPS(const std::shared_ptr<PolicyConfigData> & configData)174 void HgmSoftVSyncManager::GetVRateMiniFPS(const std::shared_ptr<PolicyConfigData>& configData)
175 {
176 if (configData == nullptr) {
177 HGM_LOGE("GetVRateMiniFPS configData is nullptr use dafault value");
178 return;
179 }
180 if (configData->vRateControlList_.find(VRATE_CONTROL_MINIFPS) == configData->vRateControlList_.end()) {
181 HGM_LOGE("GetVRateMiniFPS VRATE_CONTROL_MINIFPS config is invalid use dafault value");
182 return;
183 }
184 if (!XMLParser::IsNumber(configData->vRateControlList_[VRATE_CONTROL_MINIFPS])) {
185 HGM_LOGE("GetVRateMiniFPS VRATE_CONTROL_MINIFPS config is Is Not Number use dafault value");
186 return;
187 }
188
189 vrateControlMinifpsValue_ = static_cast<int32_t>(std::stoi(configData->vRateControlList_[VRATE_CONTROL_MINIFPS]));
190 HGM_LOGI("GetVRateMiniFPS vrateControlMinifpsValue:%{public}d", vrateControlMinifpsValue_);
191 }
192
CollectVRateChange(uint64_t linkerId,FrameRateRange & finalRange)193 bool HgmSoftVSyncManager::CollectVRateChange(uint64_t linkerId, FrameRateRange& finalRange)
194 {
195 auto iter = vRatesMap_.find(linkerId);
196 if (iter == vRatesMap_.end()) {
197 RS_OPTIONAL_TRACE_NAME_FMT("CollectVRateChange not find pid = %d linkerId = %" PRIu64 " return",
198 ExtractPid(linkerId), linkerId);
199 HGM_LOGD("CollectVRateChange not find pid = %{public}d linkerId = %{public}" PRIu64 " return",
200 ExtractPid(linkerId), linkerId);
201 return false;
202 }
203 if (iter->second == 1 || iter->second == 0) {
204 RS_OPTIONAL_TRACE_NAME_FMT("CollectVRateChange pid = %d , linkerId = %" PRIu64 ", vrate = %d return",
205 ExtractPid(linkerId), linkerId, iter->second);
206 HGM_LOGD("CollectVRateChange linkerId = %{public}" PRIu64 ",vrate = %{public}d return",
207 linkerId, iter->second);
208 return false;
209 }
210 int32_t& appFrameRate = finalRange.preferred_;
211 // finalRange.preferred_ is 0 means that the appframerate want to be changed by self.
212 if (appFrameRate != 0 && (finalRange.type_ != DRAG_SCENE_FRAME_RATE_TYPE ||
213 (finalRange.type_ == DRAG_SCENE_FRAME_RATE_TYPE && /* ArkUI Vote */
214 iter->second != std::numeric_limits<int>::max()))) { /* invisible window */
215 RS_OPTIONAL_TRACE_NAME_FMT("CollectVRateChange pid = %d , linkerId = %" PRIu64 ", vrate = %d return because "
216 "appFrameRate[%d] changed by self, not arkui vote[%u], not invisble window", ExtractPid(linkerId),
217 linkerId, iter->second, appFrameRate, finalRange.type_);
218 HGM_LOGD("CollectVRateChange linkerId = %{public}" PRIu64 ",vrate = %{public}d return "
219 "appFrameRate[%{public}d] because changed by self, not arkui vote[%{public}u], not invisble window",
220 linkerId, iter->second, appFrameRate, finalRange.type_);
221 return false;
222 }
223
224 appFrameRate = static_cast<int>(controllerRate_) / iter->second;
225 if (iter->second == std::numeric_limits<int>::max()) {
226 // invisible window 1 means app need not refreshing
227 appFrameRate = 1;
228 }
229
230 // vrate is int::max means app need not refreshing
231 if (appFrameRate == 0 && vrateControlMinifpsValue_ >= 0) {
232 appFrameRate = vrateControlMinifpsValue_;
233 }
234
235 finalRange.min_ = OLED_NULL_HZ;
236 finalRange.max_ = OLED_144_HZ;
237
238 RS_TRACE_NAME_FMT("CollectVRateChange modification pid = %d , linkerIdS = %" PRIu64 ",appFrameRate = %d,"
239 " vrate = %d, rsFrameRate = %u", ExtractPid(linkerId), linkerId, appFrameRate, iter->second, controllerRate_);
240 HGM_LOGD("CollectVRateChange modification linkerId = %{public}" PRIu64 ",appFrameRate = %{public}d,"
241 " vrate = %{public}d, rsFrameRate = %{public}u", linkerId, appFrameRate, iter->second, controllerRate_);
242 return true;
243 }
244
GetSoftAppChangeData()245 std::vector<std::pair<FrameRateLinkerId, uint32_t>> HgmSoftVSyncManager::GetSoftAppChangeData()
246 {
247 std::vector<std::pair<FrameRateLinkerId, uint32_t>> appData(appChangeData_.begin(), appChangeData_.end());
248 return appData;
249 }
250
Reset()251 void HgmSoftVSyncManager::Reset()
252 {
253 controllerRate_ = 0;
254 appChangeData_.clear();
255 }
256
UniProcessDataForLtpo(const std::map<uint64_t,int> & vRatesMap,const FrameRateLinkerMap & appFrameRateLinkers)257 void HgmSoftVSyncManager::UniProcessDataForLtpo(const std::map<uint64_t, int>& vRatesMap,
258 const FrameRateLinkerMap& appFrameRateLinkers)
259 {
260 vRatesMap_ = vRatesMap;
261 appFrameRateLinkers_ = appFrameRateLinkers;
262 HandleLinkers();
263 }
264
SetQosVSyncRate(const uint32_t currRefreshRate,const FrameRateLinkerMap & appFrameRateLinkers)265 void HgmSoftVSyncManager::SetQosVSyncRate(const uint32_t currRefreshRate,
266 const FrameRateLinkerMap& appFrameRateLinkers)
267 {
268 for (const auto& data : appChangeData_) {
269 auto linkerId = data.first;
270 auto prefer = data.second;
271
272 auto linker = appFrameRateLinkers.find(linkerId);
273 if (linker != appFrameRateLinkers.end() && appDistributor_ != nullptr && linker->second != nullptr) {
274 // perfer equals 0 means keeping the original rhythm without skipping, just pass 1
275 appDistributor_->SetQosVSyncRateByConnId(linker->second->GetId(),
276 prefer == 0 ? 1 : currRefreshRate / prefer);
277 }
278 }
279 }
280
CollectGameRateDiscountChange(uint64_t linkerId,FrameRateRange & expectedRange,const uint32_t currRefreshRate)281 bool HgmSoftVSyncManager::CollectGameRateDiscountChange(uint64_t linkerId, FrameRateRange& expectedRange,
282 const uint32_t currRefreshRate)
283 {
284 auto iter = gameRateDiscountMap_.find(linkerId);
285 if (iter == gameRateDiscountMap_.end()) {
286 return false;
287 }
288 if (iter->second == 1 || iter->second == 0) {
289 return false;
290 }
291 if (expectedRange.preferred_ == 0 && currRefreshRate == 0) {
292 return false;
293 }
294 int32_t tmpPreferred = expectedRange.preferred_;
295 int32_t tmpMin = expectedRange.min_;
296 int32_t tmpMax = expectedRange.max_;
297 if (expectedRange.preferred_ == 0) {
298 expectedRange.preferred_ = static_cast<int32_t>(currRefreshRate) / static_cast<int32_t>(iter->second);
299 } else {
300 expectedRange.preferred_ = std::min(expectedRange.preferred_, static_cast<int32_t>(currRefreshRate)) /
301 static_cast<int32_t>(iter->second);
302 }
303 expectedRange.min_ = expectedRange.preferred_;
304 expectedRange.max_ = expectedRange.preferred_;
305
306 int32_t drawingFrameRate = static_cast<int32_t>(GetDrawingFrameRate(currRefreshRate, expectedRange));
307 if (drawingFrameRate != expectedRange.preferred_) {
308 RS_TRACE_NAME_FMT("CollectGameRateDiscountChange failed, linkerId=%" PRIu64
309 ", rateDiscount=%d, preferred=%d drawingFrameRate=%d currRefreshRate=%d",
310 linkerId, iter->second, expectedRange.preferred_, drawingFrameRate, static_cast<int32_t>(currRefreshRate));
311 HGM_LOGD("CollectGameRateDiscountChange failed, linkerId=%{public}" PRIu64
312 ", rateDiscount=%{public}d, preferred=%{public}d drawingFrameRate=%{public}d currRefreshRate=%{public}d",
313 linkerId, iter->second, expectedRange.preferred_, drawingFrameRate, static_cast<int32_t>(currRefreshRate));
314 expectedRange.preferred_ = tmpPreferred;
315 expectedRange.min_ = tmpMin;
316 expectedRange.max_ = tmpMax;
317 return false;
318 }
319 RS_TRACE_NAME_FMT("CollectGameRateDiscountChange linkerId=%" PRIu64
320 ", rateDiscount=%d, preferred=%d currRefreshRate=%d",
321 linkerId, iter->second, expectedRange.preferred_, static_cast<int32_t>(currRefreshRate));
322 HGM_LOGD("CollectGameRateDiscountChange succeed, linkerId=%{public}" PRIu64
323 ", rateDiscount=%{public}d, preferred=%{public}d currRefreshRate=%{public}d",
324 linkerId, iter->second, expectedRange.preferred_, static_cast<int32_t>(currRefreshRate));
325 return true;
326 }
327
EraseGameRateDiscountMap(pid_t pid)328 void HgmSoftVSyncManager::EraseGameRateDiscountMap(pid_t pid)
329 {
330 for (auto iter = gameRateDiscountMap_.begin(); iter != gameRateDiscountMap_.end(); ++iter) {
331 if (ExtractPid(iter->first) == pid) {
332 gameRateDiscountMap_.erase(iter);
333 break;
334 }
335 }
336 }
337
SetVsyncRateDiscountLTPO(const std::vector<uint64_t> & linkerIds,uint32_t rateDiscount)338 void HgmSoftVSyncManager::SetVsyncRateDiscountLTPO(const std::vector<uint64_t>& linkerIds, uint32_t rateDiscount)
339 {
340 for (auto linkerId : linkerIds) {
341 gameRateDiscountMap_[linkerId] = rateDiscount;
342 }
343 }
344 // LCOV_EXCL_STOP
345
DeliverSoftVote(FrameRateLinkerId linkerId,const VoteInfo & voteInfo,bool eventStatus)346 void HgmSoftVSyncManager::DeliverSoftVote(FrameRateLinkerId linkerId, const VoteInfo& voteInfo, bool eventStatus)
347 {
348 FrameRateRange frameRateRange(voteInfo.min, voteInfo.max, voteInfo.max);
349 if (!frameRateRange.IsValid()) {
350 return;
351 }
352
353 if (linkerVoteMap_.find(linkerId) == linkerVoteMap_.end()) {
354 std::vector<std::string> voters(std::begin(VOTER_NAME), std::end(VOTER_NAME));
355 linkerVoteMap_.try_emplace(linkerId, std::make_shared<HgmVoter>(voters));
356 }
357
358 auto hgmVoter = linkerVoteMap_[linkerId];
359 if (hgmVoter && hgmVoter->DeliverVote(voteInfo, eventStatus)) {
360 VoteInfo voteInfo = hgmVoter->ProcessVote();
361 appVoteData_[linkerId] = voteInfo.max;
362 }
363 }
364
SetWindowExpectedRefreshRate(pid_t pid,const std::unordered_map<WindowId,EventInfo> & voters)365 void HgmSoftVSyncManager::SetWindowExpectedRefreshRate(pid_t pid,
366 const std::unordered_map<WindowId, EventInfo>& voters)
367 {
368 for (const auto& voter : voters) {
369 WindowId winId = voter.first;
370 EventInfo eventInfo = voter.second;
371
372 auto winLinker = winLinkerMap_.find(winId);
373 if (winLinker != winLinkerMap_.end()) {
374 FrameRateLinkerId linkerId = winLinker->second;
375 DeliverSoftVote(
376 linkerId,
377 {eventInfo.eventName, eventInfo.minRefreshRate, eventInfo.maxRefreshRate, pid},
378 eventInfo.eventStatus);
379 }
380 }
381 }
382
SetWindowExpectedRefreshRate(pid_t pid,const std::unordered_map<VsyncName,EventInfo> & voters)383 void HgmSoftVSyncManager::SetWindowExpectedRefreshRate(pid_t pid,
384 const std::unordered_map<VsyncName, EventInfo>& voters)
385 {
386 for (const auto& voter : voters) {
387 VsyncName vsyncName = voter.first;
388 EventInfo eventInfo = voter.second;
389
390 auto vsyncLinker = vsyncLinkerMap_.find(vsyncName);
391 if (vsyncLinker != vsyncLinkerMap_.end()) {
392 std::vector<FrameRateLinkerId> linkerIds = vsyncLinker->second;
393 for (const auto& linkerId : linkerIds) {
394 DeliverSoftVote(
395 linkerId,
396 {eventInfo.eventName, eventInfo.minRefreshRate, eventInfo.maxRefreshRate, pid},
397 eventInfo.eventStatus);
398 }
399 }
400 }
401 }
402 }
403 }