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