• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }