1 /* 2 * Copyright (C) 2024 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.display.mode; 18 19 import android.util.Size; 20 import android.view.Display; 21 22 import com.android.server.display.DisplayDeviceConfig; 23 import com.android.server.display.feature.DisplayManagerFlags; 24 25 import java.util.ArrayList; 26 import java.util.LinkedHashMap; 27 import java.util.List; 28 import java.util.Map; 29 30 /** 31 * When selected by app synthetic modes will only affect render rate switch rather than mode switch 32 */ 33 public class SyntheticModeManager { 34 private static final float FLOAT_TOLERANCE = 0.01f; 35 private static final float SYNTHETIC_MODE_REFRESH_RATE = 60f; 36 private static final float SYNTHETIC_MODE_HIGH_BOUNDARY = 37 SYNTHETIC_MODE_REFRESH_RATE + FLOAT_TOLERANCE; 38 39 private final boolean mSynthetic60HzModesEnabled; 40 private final boolean mHasArrSupportEnabled; 41 SyntheticModeManager(DisplayManagerFlags flags)42 public SyntheticModeManager(DisplayManagerFlags flags) { 43 mSynthetic60HzModesEnabled = flags.isSynthetic60HzModesEnabled(); 44 mHasArrSupportEnabled = flags.hasArrSupportFlag(); 45 } 46 47 /** 48 * creates display supportedModes array, exposed to applications 49 */ createAppSupportedModes(DisplayDeviceConfig config, Display.Mode[] modes, boolean hasArrSupport)50 public Display.Mode[] createAppSupportedModes(DisplayDeviceConfig config, 51 Display.Mode[] modes, boolean hasArrSupport) { 52 // TODO(b/361433651) Remove config.isVrrSupportEnabled once hasArrSupport is rolled out 53 boolean isArrSupported = 54 mHasArrSupportEnabled ? hasArrSupport : config.isVrrSupportEnabled(); 55 if (!isArrSupported || !mSynthetic60HzModesEnabled) { 56 return modes; 57 } 58 List<Display.Mode> appSupportedModes = new ArrayList<>(); 59 Map<Size, int[]> sizes = new LinkedHashMap<>(); 60 int nextModeId = 0; 61 // exclude "real" 60Hz modes and below for VRR displays, 62 // they will be replaced with synthetic 60Hz mode 63 // for VRR display there should be "real" mode with rr > 60Hz 64 for (Display.Mode mode : modes) { 65 if (mode.getRefreshRate() > SYNTHETIC_MODE_HIGH_BOUNDARY) { 66 appSupportedModes.add(mode); 67 } 68 if (mode.getModeId() > nextModeId) { 69 nextModeId = mode.getModeId(); 70 } 71 72 float divisor = mode.getVsyncRate() / SYNTHETIC_MODE_REFRESH_RATE; 73 boolean is60HzAchievable = Math.abs(divisor - Math.round(divisor)) < FLOAT_TOLERANCE; 74 if (is60HzAchievable) { 75 sizes.put(new Size(mode.getPhysicalWidth(), mode.getPhysicalHeight()), 76 mode.getSupportedHdrTypes()); 77 } 78 } 79 // even if VRR display does not have 60Hz mode, we are still adding synthetic 60Hz mode 80 // for each screen size 81 // vsync rate, alternativeRates and hdrTypes are not important for synthetic mode, 82 // only refreshRate and size are used for DisplayModeDirector votes. 83 for (Map.Entry<Size, int[]> entry: sizes.entrySet()) { 84 nextModeId++; 85 Size size = entry.getKey(); 86 int[] hdrTypes = entry.getValue(); 87 appSupportedModes.add( 88 new Display.Mode(nextModeId, size.getWidth(), size.getHeight(), 60f, 60f, true, 89 new float[0], hdrTypes)); 90 } 91 Display.Mode[] appSupportedModesArr = new Display.Mode[appSupportedModes.size()]; 92 return appSupportedModes.toArray(appSupportedModesArr); 93 } 94 } 95