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 SyntheticModeManager(DisplayManagerFlags flags)41 public SyntheticModeManager(DisplayManagerFlags flags) { 42 mSynthetic60HzModesEnabled = flags.isSynthetic60HzModesEnabled(); 43 } 44 45 /** 46 * creates display supportedModes array, exposed to applications 47 */ createAppSupportedModes(DisplayDeviceConfig config, Display.Mode[] modes)48 public Display.Mode[] createAppSupportedModes(DisplayDeviceConfig config, 49 Display.Mode[] modes) { 50 if (!config.isVrrSupportEnabled() || !mSynthetic60HzModesEnabled) { 51 return modes; 52 } 53 List<Display.Mode> appSupportedModes = new ArrayList<>(); 54 Map<Size, int[]> sizes = new LinkedHashMap<>(); 55 int nextModeId = 0; 56 // exclude "real" 60Hz modes and below for VRR displays, 57 // they will be replaced with synthetic 60Hz mode 58 // for VRR display there should be "real" mode with rr > 60Hz 59 for (Display.Mode mode : modes) { 60 if (mode.getRefreshRate() > SYNTHETIC_MODE_HIGH_BOUNDARY) { 61 appSupportedModes.add(mode); 62 } 63 if (mode.getModeId() > nextModeId) { 64 nextModeId = mode.getModeId(); 65 } 66 67 float divisor = mode.getVsyncRate() / SYNTHETIC_MODE_REFRESH_RATE; 68 boolean is60HzAchievable = Math.abs(divisor - Math.round(divisor)) < FLOAT_TOLERANCE; 69 if (is60HzAchievable) { 70 sizes.put(new Size(mode.getPhysicalWidth(), mode.getPhysicalHeight()), 71 mode.getSupportedHdrTypes()); 72 } 73 } 74 // even if VRR display does not have 60Hz mode, we are still adding synthetic 60Hz mode 75 // for each screen size 76 // vsync rate, alternativeRates and hdrTypes are not important for synthetic mode, 77 // only refreshRate and size are used for DisplayModeDirector votes. 78 for (Map.Entry<Size, int[]> entry: sizes.entrySet()) { 79 nextModeId++; 80 Size size = entry.getKey(); 81 int[] hdrTypes = entry.getValue(); 82 appSupportedModes.add( 83 new Display.Mode(nextModeId, size.getWidth(), size.getHeight(), 60f, 60f, true, 84 new float[0], hdrTypes)); 85 } 86 Display.Mode[] appSupportedModesArr = new Display.Mode[appSupportedModes.size()]; 87 return appSupportedModes.toArray(appSupportedModesArr); 88 } 89 } 90