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