1 /* 2 * Copyright (C) 2019 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 package com.android.server.wm; 18 19 import static android.hardware.display.DisplayManager.SWITCHING_TYPE_NONE; 20 import static android.hardware.display.DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY; 21 22 import static com.android.window.flags.Flags.explicitRefreshRateHints; 23 24 import android.hardware.display.DisplayManager; 25 import android.view.Display; 26 import android.view.Display.Mode; 27 import android.view.DisplayInfo; 28 import android.view.Surface; 29 import android.view.SurfaceControl; 30 import android.view.SurfaceControl.RefreshRateRange; 31 32 import java.util.HashMap; 33 import java.util.Objects; 34 35 /** 36 * Policy to select a lower refresh rate for the display if applicable. 37 */ 38 class RefreshRatePolicy { 39 40 class PackageRefreshRate { 41 private final HashMap<String, RefreshRateRange> mPackages = new HashMap<>(); 42 add(String s, float minRefreshRate, float maxRefreshRate)43 public void add(String s, float minRefreshRate, float maxRefreshRate) { 44 float minSupportedRefreshRate = 45 Math.max(RefreshRatePolicy.this.mMinSupportedRefreshRate, minRefreshRate); 46 float maxSupportedRefreshRate = 47 Math.min(RefreshRatePolicy.this.mMaxSupportedRefreshRate, maxRefreshRate); 48 49 mPackages.put(s, 50 new RefreshRateRange(minSupportedRefreshRate, maxSupportedRefreshRate)); 51 } 52 get(String s)53 public RefreshRateRange get(String s) { 54 return mPackages.get(s); 55 } 56 remove(String s)57 public void remove(String s) { 58 mPackages.remove(s); 59 } 60 } 61 62 private final DisplayInfo mDisplayInfo; 63 private final Mode mDefaultMode; 64 private final Mode mLowRefreshRateMode; 65 private final PackageRefreshRate mNonHighRefreshRatePackages = new PackageRefreshRate(); 66 private final HighRefreshRateDenylist mHighRefreshRateDenylist; 67 private final WindowManagerService mWmService; 68 private float mMinSupportedRefreshRate; 69 private float mMaxSupportedRefreshRate; 70 71 /** 72 * The following constants represent priority of the window. SF uses this information when 73 * deciding which window has a priority when deciding about the refresh rate of the screen. 74 * Priority 0 is considered the highest priority. -1 means that the priority is unset. 75 */ 76 static final int LAYER_PRIORITY_UNSET = -1; 77 /** Windows that are in focus and voted for the preferred mode ID have the highest priority. */ 78 static final int LAYER_PRIORITY_FOCUSED_WITH_MODE = 0; 79 /** 80 * This is a default priority for all windows that are in focus, but have not requested a 81 * specific mode ID. 82 */ 83 static final int LAYER_PRIORITY_FOCUSED_WITHOUT_MODE = 1; 84 /** 85 * Windows that are not in focus, but voted for a specific mode ID should be 86 * acknowledged by SF. For example, there are two applications in a split screen. 87 * One voted for a given mode ID, and the second one doesn't care. Even though the 88 * second one might be in focus, we can honor the mode ID of the first one. 89 */ 90 static final int LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE = 2; 91 RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo, HighRefreshRateDenylist denylist)92 RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo, 93 HighRefreshRateDenylist denylist) { 94 mDisplayInfo = displayInfo; 95 mDefaultMode = displayInfo.getDefaultMode(); 96 mLowRefreshRateMode = findLowRefreshRateMode(displayInfo, mDefaultMode); 97 mHighRefreshRateDenylist = denylist; 98 mWmService = wmService; 99 } 100 101 /** 102 * Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the 103 * default mode. 104 */ findLowRefreshRateMode(DisplayInfo displayInfo, Mode defaultMode)105 private Mode findLowRefreshRateMode(DisplayInfo displayInfo, Mode defaultMode) { 106 float[] refreshRates = displayInfo.getDefaultRefreshRates(); 107 float bestRefreshRate = defaultMode.getRefreshRate(); 108 mMinSupportedRefreshRate = bestRefreshRate; 109 mMaxSupportedRefreshRate = bestRefreshRate; 110 for (int i = refreshRates.length - 1; i >= 0; i--) { 111 mMinSupportedRefreshRate = Math.min(mMinSupportedRefreshRate, refreshRates[i]); 112 mMaxSupportedRefreshRate = Math.max(mMaxSupportedRefreshRate, refreshRates[i]); 113 114 if (refreshRates[i] >= 60f && refreshRates[i] < bestRefreshRate) { 115 bestRefreshRate = refreshRates[i]; 116 } 117 } 118 return displayInfo.findDefaultModeByRefreshRate(bestRefreshRate); 119 } 120 addRefreshRateRangeForPackage(String packageName, float minRefreshRate, float maxRefreshRate)121 void addRefreshRateRangeForPackage(String packageName, 122 float minRefreshRate, float maxRefreshRate) { 123 mNonHighRefreshRatePackages.add(packageName, minRefreshRate, maxRefreshRate); 124 mWmService.requestTraversal(); 125 } 126 removeRefreshRateRangeForPackage(String packageName)127 void removeRefreshRateRangeForPackage(String packageName) { 128 mNonHighRefreshRatePackages.remove(packageName); 129 mWmService.requestTraversal(); 130 } 131 getPreferredModeId(WindowState w)132 int getPreferredModeId(WindowState w) { 133 final int preferredDisplayModeId = w.mAttrs.preferredDisplayModeId; 134 if (preferredDisplayModeId <= 0) { 135 // Unspecified, use default mode. 136 return 0; 137 } 138 139 // If app is animating, it's not able to control refresh rate because we want the animation 140 // to run in default refresh rate. But if the display size of default mode is different 141 // from the using preferred mode, then still keep the preferred mode to avoid disturbing 142 // the animation. 143 if (!explicitRefreshRateHints() && w.isAnimationRunningSelfOrParent()) { 144 Display.Mode preferredMode = null; 145 for (Display.Mode mode : mDisplayInfo.supportedModes) { 146 if (preferredDisplayModeId == mode.getModeId()) { 147 preferredMode = mode; 148 break; 149 } 150 } 151 if (preferredMode != null) { 152 final int pW = preferredMode.getPhysicalWidth(); 153 final int pH = preferredMode.getPhysicalHeight(); 154 if ((pW != mDefaultMode.getPhysicalWidth() 155 || pH != mDefaultMode.getPhysicalHeight()) 156 && pW == mDisplayInfo.getNaturalWidth() 157 && pH == mDisplayInfo.getNaturalHeight()) { 158 // Prefer not to change display size when animating. 159 return preferredDisplayModeId; 160 } 161 } 162 return 0; 163 } 164 165 return preferredDisplayModeId; 166 } 167 168 /** 169 * Calculate the priority based on whether the window is in focus and whether the application 170 * voted for a specific refresh rate. 171 * 172 * TODO(b/144307188): This is a very basic algorithm version. Explore other signals that might 173 * be useful in edge cases when we are deciding which layer should get priority when deciding 174 * about the refresh rate. 175 */ calculatePriority(WindowState w)176 int calculatePriority(WindowState w) { 177 boolean isFocused = w.isFocused(); 178 int preferredModeId = getPreferredModeId(w); 179 180 if (!isFocused && preferredModeId > 0) { 181 return LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE; 182 } 183 if (isFocused && preferredModeId == 0) { 184 return LAYER_PRIORITY_FOCUSED_WITHOUT_MODE; 185 } 186 if (isFocused && preferredModeId > 0) { 187 return LAYER_PRIORITY_FOCUSED_WITH_MODE; 188 } 189 return LAYER_PRIORITY_UNSET; 190 } 191 192 public static class FrameRateVote { 193 float mRefreshRate; 194 @Surface.FrameRateCompatibility int mCompatibility; 195 @SurfaceControl.FrameRateSelectionStrategy int mSelectionStrategy; 196 197 198 FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility, @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy)199 FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility, 200 @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy) { 201 update(refreshRate, compatibility, selectionStrategy); 202 } 203 FrameRateVote()204 FrameRateVote() { 205 reset(); 206 } 207 update(float refreshRate, @Surface.FrameRateCompatibility int compatibility, @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy)208 boolean update(float refreshRate, @Surface.FrameRateCompatibility int compatibility, 209 @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy) { 210 if (!refreshRateEquals(refreshRate) 211 || mCompatibility != compatibility 212 || mSelectionStrategy != selectionStrategy) { 213 mRefreshRate = refreshRate; 214 mCompatibility = compatibility; 215 mSelectionStrategy = selectionStrategy; 216 return true; 217 } 218 return false; 219 } 220 reset()221 boolean reset() { 222 return update(0, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, 223 SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_PROPAGATE); 224 } 225 226 @Override equals(Object o)227 public boolean equals(Object o) { 228 if (!(o instanceof FrameRateVote)) { 229 return false; 230 } 231 232 FrameRateVote other = (FrameRateVote) o; 233 return refreshRateEquals(other.mRefreshRate) 234 && mCompatibility == other.mCompatibility 235 && mSelectionStrategy == other.mSelectionStrategy; 236 } 237 238 @Override hashCode()239 public int hashCode() { 240 return Objects.hash(mRefreshRate, mCompatibility, mSelectionStrategy); 241 242 } 243 244 @Override toString()245 public String toString() { 246 return "mRefreshRate=" + mRefreshRate + ", mCompatibility=" + mCompatibility 247 + ", mSelectionStrategy=" + mSelectionStrategy; 248 } 249 refreshRateEquals(float refreshRate)250 private boolean refreshRateEquals(float refreshRate) { 251 return mRefreshRate <= refreshRate + RefreshRateRange.FLOAT_TOLERANCE 252 && mRefreshRate >= refreshRate - RefreshRateRange.FLOAT_TOLERANCE; 253 } 254 } 255 updateFrameRateVote(WindowState w)256 boolean updateFrameRateVote(WindowState w) { 257 @DisplayManager.SwitchingType int refreshRateSwitchingType = 258 mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType(); 259 260 // If refresh rate switching is disabled there is no point to set the frame rate on the 261 // surface as the refresh rate will be limited by display manager to a single value 262 // and SurfaceFlinger wouldn't be able to change it anyways. 263 if (refreshRateSwitchingType == SWITCHING_TYPE_NONE) { 264 return w.mFrameRateVote.reset(); 265 } 266 267 // If app is animating, it's not able to control refresh rate because we want the animation 268 // to run in default refresh rate. 269 if (!explicitRefreshRateHints() && w.isAnimationRunningSelfOrParent()) { 270 return w.mFrameRateVote.reset(); 271 } 272 273 // If the app set a preferredDisplayModeId, the preferred refresh rate is the refresh rate 274 // of that mode id. 275 if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) { 276 final int preferredModeId = w.mAttrs.preferredDisplayModeId; 277 if (preferredModeId > 0) { 278 for (Display.Mode mode : mDisplayInfo.appsSupportedModes) { 279 if (preferredModeId == mode.getModeId()) { 280 return w.mFrameRateVote.update(mode.getRefreshRate(), 281 Surface.FRAME_RATE_COMPATIBILITY_EXACT, 282 SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); 283 } 284 } 285 } 286 } 287 288 if (w.mAttrs.preferredRefreshRate > 0) { 289 return w.mFrameRateVote.update(w.mAttrs.preferredRefreshRate, 290 Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, 291 SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); 292 } 293 294 // If the app didn't set a preferred mode id or refresh rate, but it is part of the deny 295 // list, we return the low refresh rate as the preferred one. 296 if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) { 297 final String packageName = w.getOwningPackage(); 298 if (mHighRefreshRateDenylist.isDenylisted(packageName)) { 299 return w.mFrameRateVote.update(mLowRefreshRateMode.getRefreshRate(), 300 Surface.FRAME_RATE_COMPATIBILITY_EXACT, 301 SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); 302 } 303 } 304 305 return w.mFrameRateVote.reset(); 306 } 307 getPreferredMinRefreshRate(WindowState w)308 float getPreferredMinRefreshRate(WindowState w) { 309 // If app is animating, it's not able to control refresh rate because we want the animation 310 // to run in default refresh rate. 311 if (w.isAnimationRunningSelfOrParent()) { 312 return 0; 313 } 314 315 if (w.mAttrs.preferredMinDisplayRefreshRate > 0) { 316 return w.mAttrs.preferredMinDisplayRefreshRate; 317 } 318 319 String packageName = w.getOwningPackage(); 320 // If app is using Camera, we set both the min and max refresh rate to the camera's 321 // preferred refresh rate to make sure we don't end up with a refresh rate lower 322 // than the camera capture rate, which will lead to dropping camera frames. 323 RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName); 324 if (range != null) { 325 return range.min; 326 } 327 328 return 0; 329 } 330 getPreferredMaxRefreshRate(WindowState w)331 float getPreferredMaxRefreshRate(WindowState w) { 332 // If app is animating, it's not able to control refresh rate because we want the animation 333 // to run in default refresh rate. 334 if (w.isAnimationRunningSelfOrParent()) { 335 return 0; 336 } 337 338 if (w.mAttrs.preferredMaxDisplayRefreshRate > 0) { 339 return w.mAttrs.preferredMaxDisplayRefreshRate; 340 } 341 342 final String packageName = w.getOwningPackage(); 343 // If app is using Camera, force it to default (lower) refresh rate. 344 RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName); 345 if (range != null) { 346 return range.max; 347 } 348 349 return 0; 350 } 351 } 352