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; 28 import android.view.SurfaceControl.RefreshRateRange; 29 30 import java.util.HashMap; 31 import java.util.Objects; 32 33 /** 34 * Policy to select a lower refresh rate for the display if applicable. 35 */ 36 class RefreshRatePolicy { 37 38 class PackageRefreshRate { 39 private final HashMap<String, RefreshRateRange> mPackages = new HashMap<>(); 40 add(String s, float minRefreshRate, float maxRefreshRate)41 public void add(String s, float minRefreshRate, float maxRefreshRate) { 42 float minSupportedRefreshRate = 43 Math.max(RefreshRatePolicy.this.mMinSupportedRefreshRate, minRefreshRate); 44 float maxSupportedRefreshRate = 45 Math.min(RefreshRatePolicy.this.mMaxSupportedRefreshRate, maxRefreshRate); 46 47 mPackages.put(s, 48 new RefreshRateRange(minSupportedRefreshRate, maxSupportedRefreshRate)); 49 } 50 get(String s)51 public RefreshRateRange get(String s) { 52 return mPackages.get(s); 53 } 54 remove(String s)55 public void remove(String s) { 56 mPackages.remove(s); 57 } 58 } 59 60 private final DisplayInfo mDisplayInfo; 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 mLowRefreshRateMode = findLowRefreshRateMode(displayInfo); 93 mHighRefreshRateDenylist = denylist; 94 mWmService = wmService; 95 } 96 97 /** 98 * Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the 99 * default mode. 100 */ findLowRefreshRateMode(DisplayInfo displayInfo)101 private Mode findLowRefreshRateMode(DisplayInfo displayInfo) { 102 final Mode defaultMode = displayInfo.getDefaultMode(); 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 return preferredDisplayModeId; 136 } 137 138 /** 139 * Calculate the priority based on whether the window is in focus and whether the application 140 * voted for a specific refresh rate. 141 * 142 * TODO(b/144307188): This is a very basic algorithm version. Explore other signals that might 143 * be useful in edge cases when we are deciding which layer should get priority when deciding 144 * about the refresh rate. 145 */ calculatePriority(WindowState w)146 int calculatePriority(WindowState w) { 147 boolean isFocused = w.isFocused(); 148 int preferredModeId = getPreferredModeId(w); 149 150 if (!isFocused && preferredModeId > 0) { 151 return LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE; 152 } 153 if (isFocused && preferredModeId == 0) { 154 return LAYER_PRIORITY_FOCUSED_WITHOUT_MODE; 155 } 156 if (isFocused && preferredModeId > 0) { 157 return LAYER_PRIORITY_FOCUSED_WITH_MODE; 158 } 159 return LAYER_PRIORITY_UNSET; 160 } 161 162 public static class FrameRateVote { 163 float mRefreshRate; 164 @Surface.FrameRateCompatibility int mCompatibility; 165 @SurfaceControl.FrameRateSelectionStrategy int mSelectionStrategy; 166 167 168 FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility, @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy)169 FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility, 170 @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy) { 171 update(refreshRate, compatibility, selectionStrategy); 172 } 173 FrameRateVote()174 FrameRateVote() { 175 reset(); 176 } 177 update(float refreshRate, @Surface.FrameRateCompatibility int compatibility, @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy)178 boolean update(float refreshRate, @Surface.FrameRateCompatibility int compatibility, 179 @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy) { 180 if (!refreshRateEquals(refreshRate) 181 || mCompatibility != compatibility 182 || mSelectionStrategy != selectionStrategy) { 183 mRefreshRate = refreshRate; 184 mCompatibility = compatibility; 185 mSelectionStrategy = selectionStrategy; 186 return true; 187 } 188 return false; 189 } 190 reset()191 boolean reset() { 192 return update(0, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, 193 SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_PROPAGATE); 194 } 195 196 @Override equals(Object o)197 public boolean equals(Object o) { 198 if (!(o instanceof FrameRateVote)) { 199 return false; 200 } 201 202 FrameRateVote other = (FrameRateVote) o; 203 return refreshRateEquals(other.mRefreshRate) 204 && mCompatibility == other.mCompatibility 205 && mSelectionStrategy == other.mSelectionStrategy; 206 } 207 208 @Override hashCode()209 public int hashCode() { 210 return Objects.hash(mRefreshRate, mCompatibility, mSelectionStrategy); 211 212 } 213 214 @Override toString()215 public String toString() { 216 return "mRefreshRate=" + mRefreshRate + ", mCompatibility=" + mCompatibility 217 + ", mSelectionStrategy=" + mSelectionStrategy; 218 } 219 refreshRateEquals(float refreshRate)220 private boolean refreshRateEquals(float refreshRate) { 221 return mRefreshRate <= refreshRate + RefreshRateRange.FLOAT_TOLERANCE 222 && mRefreshRate >= refreshRate - RefreshRateRange.FLOAT_TOLERANCE; 223 } 224 } 225 updateFrameRateVote(WindowState w)226 boolean updateFrameRateVote(WindowState w) { 227 @DisplayManager.SwitchingType int refreshRateSwitchingType = 228 mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType(); 229 230 // If refresh rate switching is disabled there is no point to set the frame rate on the 231 // surface as the refresh rate will be limited by display manager to a single value 232 // and SurfaceFlinger wouldn't be able to change it anyways. 233 if (refreshRateSwitchingType == SWITCHING_TYPE_NONE) { 234 return w.mFrameRateVote.reset(); 235 } 236 237 // If insets animation is running, do not convey the preferred app refresh rate to let VRI 238 // to control the refresh rate. 239 if (w.isInsetsAnimationRunning()) { 240 return w.mFrameRateVote.reset(); 241 } 242 243 // If the app set a preferredDisplayModeId, the preferred refresh rate is the refresh rate 244 // of that mode id. 245 if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) { 246 final int preferredModeId = w.mAttrs.preferredDisplayModeId; 247 if (preferredModeId > 0) { 248 for (Display.Mode mode : mDisplayInfo.appsSupportedModes) { 249 if (preferredModeId == mode.getModeId()) { 250 return w.mFrameRateVote.update(mode.getRefreshRate(), 251 Surface.FRAME_RATE_COMPATIBILITY_EXACT, 252 SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); 253 } 254 } 255 } 256 } 257 258 if (w.mAttrs.preferredRefreshRate > 0) { 259 return w.mFrameRateVote.update(w.mAttrs.preferredRefreshRate, 260 Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, 261 SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); 262 } 263 264 // If the app didn't set a preferred mode id or refresh rate, but it is part of the deny 265 // list, we return the low refresh rate as the preferred one. 266 if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) { 267 final String packageName = w.getOwningPackage(); 268 if (mHighRefreshRateDenylist.isDenylisted(packageName)) { 269 return w.mFrameRateVote.update(mLowRefreshRateMode.getRefreshRate(), 270 Surface.FRAME_RATE_COMPATIBILITY_EXACT, 271 SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); 272 } 273 } 274 275 return w.mFrameRateVote.reset(); 276 } 277 getPreferredMinRefreshRate(WindowState w)278 float getPreferredMinRefreshRate(WindowState w) { 279 // If app is animating, it's not able to control refresh rate because we want the animation 280 // to run in default refresh rate. 281 if (w.isAnimationRunningSelfOrParent() || w.isInsetsAnimationRunning()) { 282 return 0; 283 } 284 285 if (w.mAttrs.preferredMinDisplayRefreshRate > 0) { 286 return w.mAttrs.preferredMinDisplayRefreshRate; 287 } 288 289 String packageName = w.getOwningPackage(); 290 // If app is using Camera, we set both the min and max refresh rate to the camera's 291 // preferred refresh rate to make sure we don't end up with a refresh rate lower 292 // than the camera capture rate, which will lead to dropping camera frames. 293 RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName); 294 if (range != null) { 295 return range.min; 296 } 297 298 return 0; 299 } 300 getPreferredMaxRefreshRate(WindowState w)301 float getPreferredMaxRefreshRate(WindowState w) { 302 // If app is animating, it's not able to control refresh rate because we want the animation 303 // to run in default refresh rate. 304 if (w.isAnimationRunningSelfOrParent() || w.isInsetsAnimationRunning()) { 305 return 0; 306 } 307 308 if (w.mAttrs.preferredMaxDisplayRefreshRate > 0) { 309 return w.mAttrs.preferredMaxDisplayRefreshRate; 310 } 311 312 final String packageName = w.getOwningPackage(); 313 // If app is using Camera, force it to default (lower) refresh rate. 314 RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName); 315 if (range != null) { 316 return range.max; 317 } 318 319 return 0; 320 } 321 } 322