1 /*
2 * Copyright (c) 2023 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 "hgm_frame_rate_manager.h"
17
18 #include "hgm_core.h"
19 #include "hgm_log.h"
20 #include "rs_trace.h"
21
22 namespace OHOS {
23 namespace Rosen {
24 namespace {
25 constexpr float MARGIN = 0.00001;
26 constexpr int MIN_DRAWING_FPS = 10;
27 }
28
UniProcessData(const FrameRateRangeData & data)29 void HgmFrameRateManager::UniProcessData(const FrameRateRangeData& data)
30 {
31 auto screenId = data.screenId;
32 if (screenId == INVALID_SCREEN_ID) {
33 return;
34 }
35
36 FrameRateRange finalRange;
37 finalRange.Merge(data.rsRange);
38 for (auto appRange : data.multiAppRange) {
39 finalRange.Merge(appRange.second);
40 }
41 if (!finalRange.IsValid()) {
42 return;
43 }
44
45 CalcRefreshRate(screenId, finalRange);
46 rsFrameRate_ = GetDrawingFrameRate(currRefreshRate_, finalRange);
47 RS_TRACE_NAME("HgmFrameRateManager:UniProcessData refreshRate:" +
48 std::to_string(static_cast<int>(currRefreshRate_)) + ", rsFrameRate:" +
49 std::to_string(static_cast<int>(rsFrameRate_)) + ", finalRange=(" +
50 std::to_string(static_cast<int>(finalRange.min_)) + ", " +
51 std::to_string(static_cast<int>(finalRange.max_)) + ", " +
52 std::to_string(static_cast<int>(finalRange.preferred_)) + ")");
53
54 for (auto appRange : data.multiAppRange) {
55 multiAppFrameRate_[appRange.first] = GetDrawingFrameRate(currRefreshRate_, finalRange);
56 RS_TRACE_NAME("multiAppFrameRate:pid=" +
57 std::to_string(static_cast<int>(appRange.first)) + ", appRange=(" +
58 std::to_string(static_cast<int>(appRange.second.min_)) + ", " +
59 std::to_string(static_cast<int>(appRange.second.max_)) + ", " +
60 std::to_string(static_cast<int>(appRange.second.preferred_)) + "), frameRate=" +
61 std::to_string(static_cast<int>(multiAppFrameRate_[appRange.first])));
62 }
63 // [Temporary func]: Switch refresh rate immediately, func will be removed in the future.
64 ExecuteSwitchRefreshRate(screenId);
65 }
66
CalcRefreshRate(const ScreenId id,const FrameRateRange & range)67 void HgmFrameRateManager::CalcRefreshRate(const ScreenId id, const FrameRateRange& range)
68 {
69 // Find current refreshRate by FrameRateRange. For example:
70 // 1. FrameRateRange[min, max, preferred] is [24, 48, 48], supported refreshRates
71 // of current screen are {30, 60, 90}, the result should be 30, not 60.
72 // 2. FrameRateRange[min, max, preferred] is [150, 150, 150], supported refreshRates
73 // of current screen are {30, 60, 90}, the result will be 90.
74 auto supportRefreshRateVec = HgmCore::Instance().GetScreenSupportedRefreshRates(id);
75 std::sort(supportRefreshRateVec.begin(), supportRefreshRateVec.end());
76 auto iter = std::lower_bound(supportRefreshRateVec.begin(), supportRefreshRateVec.end(), range.preferred_);
77 if (iter != supportRefreshRateVec.end()) {
78 currRefreshRate_ = *iter;
79 if (currRefreshRate_ > static_cast<uint32_t>(range.max_) &&
80 (iter - supportRefreshRateVec.begin()) > 0) {
81 iter--;
82 if (*iter >= static_cast<uint32_t>(range.min_) &&
83 *iter <= static_cast<uint32_t>(range.max_)) {
84 currRefreshRate_ = *iter;
85 }
86 }
87 } else {
88 currRefreshRate_ = supportRefreshRateVec.back();
89 }
90 }
91
GetDrawingFrameRate(const uint32_t refreshRate,const FrameRateRange & range)92 uint32_t HgmFrameRateManager::GetDrawingFrameRate(const uint32_t refreshRate, const FrameRateRange& range)
93 {
94 // We will find a drawing fps, which is divisible by refreshRate.
95 // If the refreshRate is 60, the options of drawing fps are 60, 30, 15, 12, etc.
96 // 1. The preferred fps is divisible by refreshRate.
97 const float currRefreshRate = static_cast<float>(refreshRate);
98 const float preferredFps = static_cast<float>(range.preferred_);
99 if (std::fmodf(currRefreshRate, range.preferred_) < MARGIN) {
100 return static_cast<uint32_t>(preferredFps);
101 }
102
103 // 2. FrameRateRange is not dynamic, we will find the closest drawing fps to preferredFps.
104 // e.g. If the FrameRateRange of a surfaceNode is [50, 50, 50], the refreshRate is
105 // 90, the drawing fps of the surfaceNode should be 45.
106 if (!range.IsDynamic()) {
107 return static_cast<uint32_t>(currRefreshRate / std::round(refreshRate / preferredFps));
108 }
109
110 // 3. FrameRateRange is dynamic. We will find a divisible result in the range if possible.
111 // If several divisible options are in the range, the smoother, the better.
112 // The KPI of "smooth" is the ratio of lack.
113 // e.g. The preferred fps is 58, the refreshRate is 60. When the drawing fps is 60,
114 // we lack the least(the ratio is 2/60).
115 // The preferred fps is 34, the refreshRate is 60, the drawing fps will be 30(the ratio is 4/30).
116 int divisor = 1;
117 float drawingFps = currRefreshRate;
118 float dividedFps = currRefreshRate;
119 float currRatio = std::abs(dividedFps - preferredFps) / preferredFps;
120 float ratio = currRatio;
121 while (dividedFps > MIN_DRAWING_FPS - MARGIN) {
122 if (dividedFps < range.min_) {
123 break;
124 }
125 if (dividedFps > range.max_) {
126 divisor++;
127 float preDividedFps = dividedFps;
128 dividedFps = currRefreshRate / static_cast<float>(divisor);
129 // If we cannot find a divisible result, the closer to the preferred, the better.
130 // e.g.FrameRateRange is [50, 80, 80], refreshrate is
131 // 90, the drawing frame rate is 90.
132 if (dividedFps < range.min_ && (preferredFps - dividedFps) >
133 (preDividedFps - preferredFps)) {
134 drawingFps = preDividedFps;
135 break;
136 }
137 currRatio = std::abs(dividedFps - preferredFps) / preferredFps;
138 if (currRatio < ratio) {
139 ratio = currRatio;
140 drawingFps = dividedFps;
141 }
142 continue;
143 }
144 float remainder = std::min(std::fmodf(preferredFps, dividedFps),
145 std::fmodf(std::abs(dividedFps - preferredFps), dividedFps));
146 currRatio = remainder / dividedFps;
147 // When currRatio is almost zero, dividedFps is the perfect result
148 if (currRatio < MARGIN) {
149 drawingFps = dividedFps;
150 break;
151 }
152
153 if (currRatio < ratio) {
154 ratio = currRatio;
155 drawingFps = dividedFps;
156 }
157 divisor++;
158 dividedFps = currRefreshRate / static_cast<float>(divisor);
159 }
160 return static_cast<uint32_t>(drawingFps);
161 }
162
ExecuteSwitchRefreshRate(const ScreenId id)163 void HgmFrameRateManager::ExecuteSwitchRefreshRate(const ScreenId id)
164 {
165 static bool refreshRateSwitch = system::GetBoolParameter("persist.hgm.refreshrate.enabled", false);
166 if (!refreshRateSwitch) {
167 HGM_LOGD("HgmFrameRateManager: refreshRateSwitch is off, currRefreshRate is %{public}d", currRefreshRate_);
168 return;
169 }
170 uint32_t lcdRefreshRate = HgmCore::Instance().GetScreenCurrentRefreshRate(id);
171 if (currRefreshRate_ != lcdRefreshRate) {
172 HGM_LOGD("HgmFrameRateManager: current refreshRate is %{public}d",
173 static_cast<int>(currRefreshRate_));
174 int status = HgmCore::Instance().SetScreenRefreshRate(id, 0, currRefreshRate_);
175 if (status < EXEC_SUCCESS) {
176 currRefreshRate_ = lcdRefreshRate;
177 HGM_LOGE("HgmFrameRateManager: failed to set refreshRate %{public}d, screenId %{public}d",
178 static_cast<int>(currRefreshRate_), static_cast<int>(id));
179 }
180 }
181 }
182
Reset()183 void HgmFrameRateManager::Reset()
184 {
185 currRefreshRate_ = -1;
186 rsFrameRate_ = -1;
187 multiAppFrameRate_.clear();
188 }
189 } // namespace Rosen
190 } // namespace OHOS