DisplayModeDirector: separate the display refresh rate and the render frame rate

Add an additional dimension to DisplayModeDirector for render frame rate
in addition to the display physical refresh rate. DisplayModeDirector aggregates the requests for both render frame rates and physical refresh rates, and sets the policy to SurfaceFlinger.
SurfaceFlinger will select the display refresh rate based on the physical refresh rate range, and schedule applications based on the render frame rate range.
This is a change over the previous logic where the render frame rate and
physical refresh rate were always the same for SurfaceFlinger.

Test: atest LocalDisplayAdapterTest DisplayModeDirectorTest
Bug: 241460058
Change-Id: I9563257c7268a731a3919ffa368f7b8ae777eb99
This commit is contained in:
Ady Abraham
2022-09-12 16:29:24 -07:00
parent a18bf66324
commit b878a206e8
9 changed files with 1326 additions and 541 deletions

View File

@@ -24,11 +24,11 @@ import android.hardware.SensorManager;
import android.os.Handler;
import android.os.PowerManager;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.SurfaceControl.RefreshRateRange;
import android.view.SurfaceControl.Transaction;
import android.window.DisplayWindowPolicyController;
import android.window.ScreenCapture;
@@ -36,7 +36,6 @@ import android.window.ScreenCapture;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
@@ -650,72 +649,6 @@ public abstract class DisplayManagerInternal {
void onDisplayGroupChanged(int groupId);
}
/**
* Information about the min and max refresh rate DM would like to set the display to.
*/
public static final class RefreshRateRange {
public static final String TAG = "RefreshRateRange";
// The tolerance within which we consider something approximately equals.
public static final float FLOAT_TOLERANCE = 0.01f;
/**
* The lowest desired refresh rate.
*/
public float min;
/**
* The highest desired refresh rate.
*/
public float max;
public RefreshRateRange() {}
public RefreshRateRange(float min, float max) {
if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) {
Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : "
+ min + " " + max);
this.min = this.max = 0;
return;
}
if (min > max) {
// Min and max are within epsilon of each other, but in the wrong order.
float t = min;
min = max;
max = t;
}
this.min = min;
this.max = max;
}
/**
* Checks whether the two objects have the same values.
*/
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof RefreshRateRange)) {
return false;
}
RefreshRateRange refreshRateRange = (RefreshRateRange) other;
return (min == refreshRateRange.min && max == refreshRateRange.max);
}
@Override
public int hashCode() {
return Objects.hash(min, max);
}
@Override
public String toString() {
return "(" + min + " " + max + ")";
}
}
/**
* Describes a limitation on a display's refresh rate. Includes the allowed refresh rate
* range as well as information about when it applies, such as high-brightness-mode.

View File

@@ -67,6 +67,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import android.view.Surface.OutOfResourcesException;
@@ -1673,6 +1674,146 @@ public final class SurfaceControl implements Parcelable {
return nativeGetDisplayedContentSample(displayToken, maxFrames, timestamp);
}
/**
* Information about the min and max refresh rate DM would like to set the display to.
* @hide
*/
public static final class RefreshRateRange {
public static final String TAG = "RefreshRateRange";
// The tolerance within which we consider something approximately equals.
public static final float FLOAT_TOLERANCE = 0.01f;
/**
* The lowest desired refresh rate.
*/
public float min;
/**
* The highest desired refresh rate.
*/
public float max;
public RefreshRateRange() {}
public RefreshRateRange(float min, float max) {
if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) {
Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : "
+ min + " " + max);
this.min = this.max = 0;
return;
}
if (min > max) {
// Min and max are within epsilon of each other, but in the wrong order.
float t = min;
min = max;
max = t;
}
this.min = min;
this.max = max;
}
/**
* Checks whether the two objects have the same values.
*/
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof RefreshRateRange)) {
return false;
}
RefreshRateRange refreshRateRange = (RefreshRateRange) other;
return (min == refreshRateRange.min && max == refreshRateRange.max);
}
@Override
public int hashCode() {
return Objects.hash(min, max);
}
@Override
public String toString() {
return "(" + min + " " + max + ")";
}
/**
* Copies the supplied object's values to this object.
*/
public void copyFrom(RefreshRateRange other) {
this.min = other.min;
this.max = other.max;
}
}
/**
* Information about the ranges of refresh rates for the display physical refresh rates and the
* render frame rate DM would like to set the policy to.
* @hide
*/
public static final class RefreshRateRanges {
public static final String TAG = "RefreshRateRanges";
/**
* The range of refresh rates that the display should run at.
*/
public final RefreshRateRange physical;
/**
* The range of refresh rates that apps should render at.
*/
public final RefreshRateRange render;
public RefreshRateRanges() {
physical = new RefreshRateRange();
render = new RefreshRateRange();
}
public RefreshRateRanges(RefreshRateRange physical, RefreshRateRange render) {
this.physical = new RefreshRateRange(physical.min, physical.max);
this.render = new RefreshRateRange(render.min, render.max);
}
/**
* Checks whether the two objects have the same values.
*/
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof RefreshRateRanges)) {
return false;
}
RefreshRateRanges rates = (RefreshRateRanges) other;
return physical.equals(rates.physical) && render.equals(
rates.render);
}
@Override
public int hashCode() {
return Objects.hash(physical, render);
}
@Override
public String toString() {
return "physical: " + physical + " render: " + render;
}
/**
* Copies the supplied object's values to this object.
*/
public void copyFrom(RefreshRateRanges other) {
this.physical.copyFrom(other.physical);
this.render.copyFrom(other.render);
}
}
/**
* Contains information about desired display configuration.
@@ -1681,45 +1822,50 @@ public final class SurfaceControl implements Parcelable {
*/
public static final class DesiredDisplayModeSpecs {
public int defaultMode;
/**
* The primary refresh rate range represents display manager's general guidance on the
* display configs surface flinger will consider when switching refresh rates. Unless
* surface flinger has a specific reason to do otherwise, it will stay within this range.
*/
public float primaryRefreshRateMin;
public float primaryRefreshRateMax;
/**
* The app request refresh rate range allows surface flinger to consider more display
* configs when switching refresh rates. Although surface flinger will generally stay within
* the primary range, specific considerations, such as layer frame rate settings specified
* via the setFrameRate() api, may cause surface flinger to go outside the primary
* range. Surface flinger never goes outside the app request range. The app request range
* will be greater than or equal to the primary refresh rate range, never smaller.
*/
public float appRequestRefreshRateMin;
public float appRequestRefreshRateMax;
/**
* If true this will allow switching between modes in different display configuration
* groups. This way the user may see visual interruptions when the display mode changes.
*/
public boolean allowGroupSwitching;
public DesiredDisplayModeSpecs() {}
/**
* The primary physical and render refresh rate ranges represent display manager's general
* guidance on the display configs surface flinger will consider when switching refresh
* rates and scheduling the frame rate. Unless surface flinger has a specific reason to do
* otherwise, it will stay within this range.
*/
public final RefreshRateRanges primaryRanges;
/**
* The app request physical and render refresh rate ranges allow surface flinger to consider
* more display configs when switching refresh rates. Although surface flinger will
* generally stay within the primary range, specific considerations, such as layer frame
* rate settings specified via the setFrameRate() api, may cause surface flinger to go
* outside the primary range. Surface flinger never goes outside the app request range.
* The app request range will be greater than or equal to the primary refresh rate range,
* never smaller.
*/
public final RefreshRateRanges appRequestRanges;
public DesiredDisplayModeSpecs() {
this.primaryRanges = new RefreshRateRanges();
this.appRequestRanges = new RefreshRateRanges();
}
public DesiredDisplayModeSpecs(DesiredDisplayModeSpecs other) {
this.primaryRanges = new RefreshRateRanges();
this.appRequestRanges = new RefreshRateRanges();
copyFrom(other);
}
public DesiredDisplayModeSpecs(int defaultMode, boolean allowGroupSwitching,
float primaryRefreshRateMin, float primaryRefreshRateMax,
float appRequestRefreshRateMin, float appRequestRefreshRateMax) {
RefreshRateRanges primaryRanges, RefreshRateRanges appRequestRanges) {
this.defaultMode = defaultMode;
this.allowGroupSwitching = allowGroupSwitching;
this.primaryRefreshRateMin = primaryRefreshRateMin;
this.primaryRefreshRateMax = primaryRefreshRateMax;
this.appRequestRefreshRateMin = appRequestRefreshRateMin;
this.appRequestRefreshRateMax = appRequestRefreshRateMax;
this.primaryRanges =
new RefreshRateRanges(primaryRanges.physical, primaryRanges.render);
this.appRequestRanges =
new RefreshRateRanges(appRequestRanges.physical, appRequestRanges.render);
}
@Override
@@ -1732,10 +1878,9 @@ public final class SurfaceControl implements Parcelable {
*/
public boolean equals(DesiredDisplayModeSpecs other) {
return other != null && defaultMode == other.defaultMode
&& primaryRefreshRateMin == other.primaryRefreshRateMin
&& primaryRefreshRateMax == other.primaryRefreshRateMax
&& appRequestRefreshRateMin == other.appRequestRefreshRateMin
&& appRequestRefreshRateMax == other.appRequestRefreshRateMax;
&& allowGroupSwitching == other.allowGroupSwitching
&& primaryRanges.equals(other.primaryRanges)
&& appRequestRanges.equals(other.appRequestRanges);
}
@Override
@@ -1748,18 +1893,17 @@ public final class SurfaceControl implements Parcelable {
*/
public void copyFrom(DesiredDisplayModeSpecs other) {
defaultMode = other.defaultMode;
primaryRefreshRateMin = other.primaryRefreshRateMin;
primaryRefreshRateMax = other.primaryRefreshRateMax;
appRequestRefreshRateMin = other.appRequestRefreshRateMin;
appRequestRefreshRateMax = other.appRequestRefreshRateMax;
allowGroupSwitching = other.allowGroupSwitching;
primaryRanges.copyFrom(other.primaryRanges);
appRequestRanges.copyFrom(other.appRequestRanges);
}
@Override
public String toString() {
return String.format("defaultConfig=%d primaryRefreshRateRange=[%.0f %.0f]"
+ " appRequestRefreshRateRange=[%.0f %.0f]",
defaultMode, primaryRefreshRateMin, primaryRefreshRateMax,
appRequestRefreshRateMin, appRequestRefreshRateMax);
return "defaultMode=" + defaultMode
+ " allowGroupSwitching=" + allowGroupSwitching
+ " primaryRanges=" + primaryRanges
+ " appRequestRanges=" + appRequestRanges;
}
}

View File

@@ -187,15 +187,27 @@ static struct {
jfieldID white;
} gDisplayPrimariesClassInfo;
static struct {
jclass clazz;
jmethodID ctor;
jfieldID min;
jfieldID max;
} gRefreshRateRangeClassInfo;
static struct {
jclass clazz;
jmethodID ctor;
jfieldID physical;
jfieldID render;
} gRefreshRateRangesClassInfo;
static struct {
jclass clazz;
jmethodID ctor;
jfieldID defaultMode;
jfieldID allowGroupSwitching;
jfieldID primaryRefreshRateMin;
jfieldID primaryRefreshRateMax;
jfieldID appRequestRefreshRateMin;
jfieldID appRequestRefreshRateMax;
jfieldID primaryRanges;
jfieldID appRequestRanges;
} gDesiredDisplayModeSpecsClassInfo;
static struct {
@@ -1190,6 +1202,39 @@ static jobject nativeGetDynamicDisplayInfo(JNIEnv* env, jclass clazz, jobject to
return object;
}
struct RefreshRateRange {
const float min;
const float max;
RefreshRateRange(float min, float max) : min(min), max(max) {}
RefreshRateRange(JNIEnv* env, jobject obj)
: min(env->GetFloatField(obj, gRefreshRateRangeClassInfo.min)),
max(env->GetFloatField(obj, gRefreshRateRangeClassInfo.max)) {}
jobject toJava(JNIEnv* env) const {
return env->NewObject(gRefreshRateRangeClassInfo.clazz, gRefreshRateRangeClassInfo.ctor,
min, max);
}
};
struct RefreshRateRanges {
const RefreshRateRange physical;
const RefreshRateRange render;
RefreshRateRanges(RefreshRateRange physical, RefreshRateRange render)
: physical(physical), render(render) {}
RefreshRateRanges(JNIEnv* env, jobject obj)
: physical(env, env->GetObjectField(obj, gRefreshRateRangesClassInfo.physical)),
render(env, env->GetObjectField(obj, gRefreshRateRangesClassInfo.render)) {}
jobject toJava(JNIEnv* env) const {
return env->NewObject(gRefreshRateRangesClassInfo.clazz, gRefreshRateRangesClassInfo.ctor,
physical.toJava(env), render.toJava(env));
}
};
static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
jobject DesiredDisplayModeSpecs) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
@@ -1200,25 +1245,23 @@ static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobj
jboolean allowGroupSwitching =
env->GetBooleanField(DesiredDisplayModeSpecs,
gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching);
jfloat primaryRefreshRateMin =
env->GetFloatField(DesiredDisplayModeSpecs,
gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMin);
jfloat primaryRefreshRateMax =
env->GetFloatField(DesiredDisplayModeSpecs,
gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMax);
jfloat appRequestRefreshRateMin =
env->GetFloatField(DesiredDisplayModeSpecs,
gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMin);
jfloat appRequestRefreshRateMax =
env->GetFloatField(DesiredDisplayModeSpecs,
gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax);
size_t result = SurfaceComposerClient::setDesiredDisplayModeSpecs(token, defaultMode,
allowGroupSwitching,
primaryRefreshRateMin,
primaryRefreshRateMax,
appRequestRefreshRateMin,
appRequestRefreshRateMax);
const jobject primaryRangesObject =
env->GetObjectField(DesiredDisplayModeSpecs,
gDesiredDisplayModeSpecsClassInfo.primaryRanges);
const jobject appRequestRangesObject =
env->GetObjectField(DesiredDisplayModeSpecs,
gDesiredDisplayModeSpecsClassInfo.appRequestRanges);
const RefreshRateRanges primaryRanges(env, primaryRangesObject);
const RefreshRateRanges appRequestRanges(env, appRequestRangesObject);
size_t result =
SurfaceComposerClient::setDesiredDisplayModeSpecs(token, defaultMode,
allowGroupSwitching,
primaryRanges.physical.min,
primaryRanges.physical.max,
appRequestRanges.physical.min,
appRequestRanges.physical.max);
return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
}
@@ -1228,22 +1271,31 @@ static jobject nativeGetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobje
ui::DisplayModeId defaultMode;
bool allowGroupSwitching;
float primaryRefreshRateMin;
float primaryRefreshRateMax;
float appRequestRefreshRateMin;
float appRequestRefreshRateMax;
float primaryPhysicalRefreshRateMin;
float primaryPhysicalRefreshRateMax;
float appRequestPhysicalRefreshRateMin;
float appRequestPhysicalRefreshRateMax;
if (SurfaceComposerClient::getDesiredDisplayModeSpecs(token, &defaultMode, &allowGroupSwitching,
&primaryRefreshRateMin,
&primaryRefreshRateMax,
&appRequestRefreshRateMin,
&appRequestRefreshRateMax) != NO_ERROR) {
&primaryPhysicalRefreshRateMin,
&primaryPhysicalRefreshRateMax,
&appRequestPhysicalRefreshRateMin,
&appRequestPhysicalRefreshRateMax) !=
NO_ERROR) {
return nullptr;
}
const RefreshRateRange primaryPhysicalRange(primaryPhysicalRefreshRateMin,
primaryPhysicalRefreshRateMax);
const RefreshRateRange appRequestPhysicalRange(appRequestPhysicalRefreshRateMin,
appRequestPhysicalRefreshRateMax);
// TODO(b/241460058): populate the render ranges
const RefreshRateRanges primaryRanges(primaryPhysicalRange, primaryPhysicalRange);
const RefreshRateRanges appRequestRanges(appRequestPhysicalRange, appRequestPhysicalRange);
return env->NewObject(gDesiredDisplayModeSpecsClassInfo.clazz,
gDesiredDisplayModeSpecsClassInfo.ctor, defaultMode, allowGroupSwitching,
primaryRefreshRateMin, primaryRefreshRateMax, appRequestRefreshRateMin,
appRequestRefreshRateMax);
primaryRanges.toJava(env), appRequestRanges.toJava(env));
}
static jobject nativeGetDisplayNativePrimaries(JNIEnv* env, jclass, jobject tokenObj) {
@@ -2235,23 +2287,45 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gDisplayPrimariesClassInfo.white = GetFieldIDOrDie(env, displayPrimariesClazz, "white",
"Landroid/view/SurfaceControl$CieXyz;");
jclass RefreshRateRangeClazz =
FindClassOrDie(env, "android/view/SurfaceControl$RefreshRateRange");
gRefreshRateRangeClassInfo.clazz = MakeGlobalRefOrDie(env, RefreshRateRangeClazz);
gRefreshRateRangeClassInfo.ctor =
GetMethodIDOrDie(env, gRefreshRateRangeClassInfo.clazz, "<init>", "(FF)V");
gRefreshRateRangeClassInfo.min = GetFieldIDOrDie(env, RefreshRateRangeClazz, "min", "F");
gRefreshRateRangeClassInfo.max = GetFieldIDOrDie(env, RefreshRateRangeClazz, "max", "F");
jclass RefreshRateRangesClazz =
FindClassOrDie(env, "android/view/SurfaceControl$RefreshRateRanges");
gRefreshRateRangesClassInfo.clazz = MakeGlobalRefOrDie(env, RefreshRateRangesClazz);
gRefreshRateRangesClassInfo.ctor =
GetMethodIDOrDie(env, gRefreshRateRangesClassInfo.clazz, "<init>",
"(Landroid/view/SurfaceControl$RefreshRateRange;Landroid/view/"
"SurfaceControl$RefreshRateRange;)V");
gRefreshRateRangesClassInfo.physical =
GetFieldIDOrDie(env, RefreshRateRangesClazz, "physical",
"Landroid/view/SurfaceControl$RefreshRateRange;");
gRefreshRateRangesClassInfo.render =
GetFieldIDOrDie(env, RefreshRateRangesClazz, "render",
"Landroid/view/SurfaceControl$RefreshRateRange;");
jclass DesiredDisplayModeSpecsClazz =
FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayModeSpecs");
gDesiredDisplayModeSpecsClassInfo.clazz = MakeGlobalRefOrDie(env, DesiredDisplayModeSpecsClazz);
gDesiredDisplayModeSpecsClassInfo.ctor =
GetMethodIDOrDie(env, gDesiredDisplayModeSpecsClassInfo.clazz, "<init>", "(IZFFFF)V");
GetMethodIDOrDie(env, gDesiredDisplayModeSpecsClassInfo.clazz, "<init>",
"(IZLandroid/view/SurfaceControl$RefreshRateRanges;Landroid/view/"
"SurfaceControl$RefreshRateRanges;)V");
gDesiredDisplayModeSpecsClassInfo.defaultMode =
GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "defaultMode", "I");
gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching =
GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "allowGroupSwitching", "Z");
gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMin =
GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRefreshRateMin", "F");
gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMax =
GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRefreshRateMax", "F");
gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMin =
GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMin", "F");
gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax =
GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMax", "F");
gDesiredDisplayModeSpecsClassInfo.primaryRanges =
GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRanges",
"Landroid/view/SurfaceControl$RefreshRateRanges;");
gDesiredDisplayModeSpecsClassInfo.appRequestRanges =
GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRanges",
"Landroid/view/SurfaceControl$RefreshRateRanges;");
jclass jankDataClazz =
FindClassOrDie(env, "android/view/SurfaceControl$JankData");

View File

@@ -117,6 +117,7 @@ import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.RefreshRateRange;
import android.window.DisplayWindowPolicyController;
import android.window.ScreenCapture;

View File

@@ -33,7 +33,6 @@ import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.net.Uri;
import android.os.Handler;
@@ -49,6 +48,7 @@ import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.DeviceConfigInterface;
import android.provider.Settings;
import android.sysprop.DisplayProperties;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -58,6 +58,8 @@ import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl.RefreshRateRange;
import android.view.SurfaceControl.RefreshRateRanges;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -135,6 +137,9 @@ public class DisplayModeDirector {
private boolean mAlwaysRespectAppRequest;
// TODO(b/241447632): remove the flag once SF changes are ready
private final boolean mRenderFrameRateIsPhysicalRefreshRate;
/**
* The allowed refresh rate switching type. This is used by SurfaceFlinger.
*/
@@ -170,6 +175,7 @@ public class DisplayModeDirector {
mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler(),
mDeviceConfigDisplaySettings);
mAlwaysRespectAppRequest = false;
mRenderFrameRateIsPhysicalRefreshRate = injector.renderFrameRateIsPhysicalRefreshRate();
}
/**
@@ -230,12 +236,30 @@ public class DisplayModeDirector {
}
}
}
if (mRenderFrameRateIsPhysicalRefreshRate) {
for (int i = 0; i < votes.size(); i++) {
Vote vote = votes.valueAt(i);
vote.refreshRateRanges.physical.min = Math.max(vote.refreshRateRanges.physical.min,
vote.refreshRateRanges.render.min);
vote.refreshRateRanges.physical.max = Math.min(vote.refreshRateRanges.physical.max,
vote.refreshRateRanges.render.max);
vote.refreshRateRanges.render.min = Math.max(vote.refreshRateRanges.physical.min,
vote.refreshRateRanges.render.min);
vote.refreshRateRanges.render.max = Math.min(vote.refreshRateRanges.physical.max,
vote.refreshRateRanges.render.max);
}
}
return votes;
}
private static final class VoteSummary {
public float minRefreshRate;
public float maxRefreshRate;
public float minPhysicalRefreshRate;
public float maxPhysicalRefreshRate;
public float minRenderFrameRate;
public float maxRenderFrameRate;
public int width;
public int height;
public boolean disableRefreshRateSwitching;
@@ -246,8 +270,10 @@ public class DisplayModeDirector {
}
public void reset() {
minRefreshRate = 0f;
maxRefreshRate = Float.POSITIVE_INFINITY;
minPhysicalRefreshRate = 0f;
maxPhysicalRefreshRate = Float.POSITIVE_INFINITY;
minRenderFrameRate = 0f;
maxRenderFrameRate = Float.POSITIVE_INFINITY;
width = Vote.INVALID_SIZE;
height = Vote.INVALID_SIZE;
disableRefreshRateSwitching = false;
@@ -270,9 +296,25 @@ public class DisplayModeDirector {
if (vote == null) {
continue;
}
// For refresh rates, just use the tightest bounds of all the votes
summary.minRefreshRate = Math.max(summary.minRefreshRate, vote.refreshRateRange.min);
summary.maxRefreshRate = Math.min(summary.maxRefreshRate, vote.refreshRateRange.max);
// For physical refresh rates, just use the tightest bounds of all the votes.
// The refresh rate cannot be lower than the minimal render frame rate.
final float minPhysicalRefreshRate = Math.max(vote.refreshRateRanges.physical.min,
vote.refreshRateRanges.render.min);
summary.minPhysicalRefreshRate = Math.max(summary.minPhysicalRefreshRate,
minPhysicalRefreshRate);
summary.maxPhysicalRefreshRate = Math.min(summary.maxPhysicalRefreshRate,
vote.refreshRateRanges.physical.max);
// Same goes to render frame rate, but frame rate cannot exceed the max physical
// refresh rate
final float maxRenderFrameRate = Math.min(vote.refreshRateRanges.render.max,
vote.refreshRateRanges.physical.max);
summary.minRenderFrameRate = Math.max(summary.minRenderFrameRate,
vote.refreshRateRanges.render.min);
summary.maxRenderFrameRate = Math.min(summary.maxRenderFrameRate, maxRenderFrameRate);
// For display size, disable refresh rate switching and base mode refresh rate use only
// the first vote we come across (i.e. the highest priority vote that includes the
// attribute).
@@ -287,6 +329,20 @@ public class DisplayModeDirector {
if (summary.baseModeRefreshRate == 0f && vote.baseModeRefreshRate > 0f) {
summary.baseModeRefreshRate = vote.baseModeRefreshRate;
}
if (mLoggingEnabled) {
Slog.w(TAG, "Vote summary for priority "
+ Vote.priorityToString(priority)
+ ": width=" + summary.width
+ ", height=" + summary.height
+ ", minPhysicalRefreshRate=" + summary.minPhysicalRefreshRate
+ ", maxPhysicalRefreshRate=" + summary.maxPhysicalRefreshRate
+ ", minRenderFrameRate=" + summary.minRenderFrameRate
+ ", maxRenderFrameRate=" + summary.maxRenderFrameRate
+ ", disableRefreshRateSwitching="
+ summary.disableRefreshRateSwitching
+ ", baseModeRefreshRate=" + summary.baseModeRefreshRate);
}
}
}
@@ -346,8 +402,12 @@ public class DisplayModeDirector {
+ " and constraints: "
+ "width=" + primarySummary.width
+ ", height=" + primarySummary.height
+ ", minRefreshRate=" + primarySummary.minRefreshRate
+ ", maxRefreshRate=" + primarySummary.maxRefreshRate
+ ", minPhysicalRefreshRate="
+ primarySummary.minPhysicalRefreshRate
+ ", maxPhysicalRefreshRate="
+ primarySummary.maxPhysicalRefreshRate
+ ", minRenderFrameRate=" + primarySummary.minRenderFrameRate
+ ", maxRenderFrameRate=" + primarySummary.maxRenderFrameRate
+ ", disableRefreshRateSwitching="
+ primarySummary.disableRefreshRateSwitching
+ ", baseModeRefreshRate=" + primarySummary.baseModeRefreshRate);
@@ -361,8 +421,10 @@ public class DisplayModeDirector {
+ " and with the following constraints: "
+ "width=" + primarySummary.width
+ ", height=" + primarySummary.height
+ ", minRefreshRate=" + primarySummary.minRefreshRate
+ ", maxRefreshRate=" + primarySummary.maxRefreshRate
+ ", minPhysicalRefreshRate=" + primarySummary.minPhysicalRefreshRate
+ ", maxPhysicalRefreshRate=" + primarySummary.maxPhysicalRefreshRate
+ ", minRenderFrameRate=" + primarySummary.minRenderFrameRate
+ ", maxRenderFrameRate=" + primarySummary.maxRenderFrameRate
+ ", disableRefreshRateSwitching="
+ primarySummary.disableRefreshRateSwitching
+ ", baseModeRefreshRate=" + primarySummary.baseModeRefreshRate);
@@ -373,25 +435,53 @@ public class DisplayModeDirector {
lowestConsideredPriority++;
}
if (mLoggingEnabled) {
Slog.i(TAG,
"Primary physical range: ["
+ primarySummary.minPhysicalRefreshRate
+ " "
+ primarySummary.maxPhysicalRefreshRate
+ "] render frame rate range: ["
+ primarySummary.minRenderFrameRate
+ " "
+ primarySummary.maxRenderFrameRate
+ "]");
}
VoteSummary appRequestSummary = new VoteSummary();
summarizeVotes(
votes,
Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF,
Vote.MAX_PRIORITY,
appRequestSummary);
appRequestSummary.minRefreshRate =
Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate);
appRequestSummary.maxRefreshRate =
Math.max(appRequestSummary.maxRefreshRate, primarySummary.maxRefreshRate);
appRequestSummary.minPhysicalRefreshRate =
Math.min(appRequestSummary.minPhysicalRefreshRate,
primarySummary.minPhysicalRefreshRate);
appRequestSummary.maxPhysicalRefreshRate =
Math.max(appRequestSummary.maxPhysicalRefreshRate,
primarySummary.maxPhysicalRefreshRate);
appRequestSummary.minRenderFrameRate =
Math.min(appRequestSummary.minRenderFrameRate,
primarySummary.minRenderFrameRate);
appRequestSummary.maxRenderFrameRate =
Math.max(appRequestSummary.maxRenderFrameRate,
primarySummary.maxRenderFrameRate);
if (mLoggingEnabled) {
Slog.i(TAG,
String.format("App request range: [%.0f %.0f]",
appRequestSummary.minRefreshRate,
appRequestSummary.maxRefreshRate));
"App request range: ["
+ appRequestSummary.minPhysicalRefreshRate
+ " "
+ appRequestSummary.maxPhysicalRefreshRate
+ "] Frame rate range: ["
+ appRequestSummary.minRenderFrameRate
+ " "
+ appRequestSummary.maxRenderFrameRate
+ "]");
}
// Select the base mode id based on the base mode refresh rate, if available, since this
// will be the mode id the app voted for.
// Select the base mode id based on the base mode physical refresh rate,
// if available, since this will be the mode id the
// app voted for.
Display.Mode baseMode = null;
for (Display.Mode availableMode : availableModes) {
if (primarySummary.baseModeRefreshRate
@@ -427,18 +517,26 @@ public class DisplayModeDirector {
+ ", supported modes = " + Arrays.toString(modes));
float fps = defaultMode.getRefreshRate();
final RefreshRateRange range = new RefreshRateRange(fps, fps);
final RefreshRateRanges ranges = new RefreshRateRanges(range, range);
return new DesiredDisplayModeSpecs(defaultMode.getModeId(),
/*allowGroupSwitching */ false,
new RefreshRateRange(fps, fps),
new RefreshRateRange(fps, fps));
ranges, ranges);
}
if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
|| primarySummary.disableRefreshRateSwitching) {
float fps = baseMode.getRefreshRate();
primarySummary.minRefreshRate = primarySummary.maxRefreshRate = fps;
primarySummary.minPhysicalRefreshRate = primarySummary.maxPhysicalRefreshRate = fps;
if (mRenderFrameRateIsPhysicalRefreshRate) {
primarySummary.minRenderFrameRate = primarySummary.maxRenderFrameRate = fps;
}
if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
appRequestSummary.minRefreshRate = appRequestSummary.maxRefreshRate = fps;
primarySummary.minRenderFrameRate = primarySummary.maxRenderFrameRate = fps;
appRequestSummary.minPhysicalRefreshRate =
appRequestSummary.maxPhysicalRefreshRate = fps;
appRequestSummary.minRenderFrameRate =
appRequestSummary.maxRenderFrameRate = fps;
}
}
@@ -447,15 +545,34 @@ public class DisplayModeDirector {
return new DesiredDisplayModeSpecs(baseMode.getModeId(),
allowGroupSwitching,
new RefreshRateRange(
primarySummary.minRefreshRate, primarySummary.maxRefreshRate),
new RefreshRateRange(
appRequestSummary.minRefreshRate, appRequestSummary.maxRefreshRate));
new RefreshRateRanges(
new RefreshRateRange(
primarySummary.minPhysicalRefreshRate,
primarySummary.maxPhysicalRefreshRate),
new RefreshRateRange(
primarySummary.minRenderFrameRate,
primarySummary.maxRenderFrameRate)),
new RefreshRateRanges(
new RefreshRateRange(
appRequestSummary.minPhysicalRefreshRate,
appRequestSummary.maxPhysicalRefreshRate),
new RefreshRateRange(
appRequestSummary.minRenderFrameRate,
appRequestSummary.maxRenderFrameRate)));
}
}
private ArrayList<Display.Mode> filterModes(Display.Mode[] supportedModes,
VoteSummary summary) {
if (summary.minRenderFrameRate > summary.maxRenderFrameRate + FLOAT_TOLERANCE) {
if (mLoggingEnabled) {
Slog.w(TAG, "Vote summary resulted in empty set (invalid frame rate range)"
+ ": minRenderFrameRate=" + summary.minRenderFrameRate
+ ", maxRenderFrameRate=" + summary.maxRenderFrameRate);
}
return new ArrayList<>();
}
ArrayList<Display.Mode> availableModes = new ArrayList<>();
boolean missingBaseModeRefreshRate = summary.baseModeRefreshRate > 0f;
for (Display.Mode mode : supportedModes) {
@@ -470,21 +587,44 @@ public class DisplayModeDirector {
}
continue;
}
final float refreshRate = mode.getRefreshRate();
final float physicalRefreshRate = mode.getRefreshRate();
// Some refresh rates are calculated based on frame timings, so they aren't *exactly*
// equal to expected refresh rate. Given that, we apply a bit of tolerance to this
// comparison.
if (refreshRate < (summary.minRefreshRate - FLOAT_TOLERANCE)
|| refreshRate > (summary.maxRefreshRate + FLOAT_TOLERANCE)) {
if (physicalRefreshRate < (summary.minPhysicalRefreshRate - FLOAT_TOLERANCE)
|| physicalRefreshRate > (summary.maxPhysicalRefreshRate + FLOAT_TOLERANCE)) {
if (mLoggingEnabled) {
Slog.w(TAG, "Discarding mode " + mode.getModeId()
+ ", outside refresh rate bounds"
+ ": minRefreshRate=" + summary.minRefreshRate
+ ", maxRefreshRate=" + summary.maxRefreshRate
+ ", modeRefreshRate=" + refreshRate);
+ ": minPhysicalRefreshRate=" + summary.minPhysicalRefreshRate
+ ", maxPhysicalRefreshRate=" + summary.maxPhysicalRefreshRate
+ ", modeRefreshRate=" + physicalRefreshRate);
}
continue;
}
// Check whether the render frame rate range is achievable by the mode's physical
// refresh rate, meaning that if a divisor of the physical refresh rate is in range
// of the render frame rate.
// For example for the render frame rate [50, 70]:
// - 120Hz is in range as we can render at 60hz by skipping every other frame,
// which is within the render rate range
// - 90hz is not in range as none of the even divisors (i.e. 90, 45, 30)
// fall within the acceptable render range.
final int divisor = (int) Math.ceil(physicalRefreshRate / summary.maxRenderFrameRate);
float adjustedPhysicalRefreshRate = physicalRefreshRate / divisor;
if (adjustedPhysicalRefreshRate < (summary.minRenderFrameRate - FLOAT_TOLERANCE)) {
if (mLoggingEnabled) {
Slog.w(TAG, "Discarding mode " + mode.getModeId()
+ " with adjusted refresh rate: " + adjustedPhysicalRefreshRate
+ ", outside frame rate bounds"
+ ": minRenderFrameRate=" + summary.minRenderFrameRate
+ ", maxRenderFrameRate=" + summary.maxRenderFrameRate
+ ", modePhysicalRefreshRate=" + physicalRefreshRate);
}
continue;
}
availableModes.add(mode);
if (mode.getRefreshRate() >= summary.baseModeRefreshRate - FLOAT_TOLERANCE
&& mode.getRefreshRate() <= summary.baseModeRefreshRate + FLOAT_TOLERANCE) {
@@ -854,30 +994,30 @@ public class DisplayModeDirector {
public boolean allowGroupSwitching;
/**
* The primary refresh rate range.
* The primary refresh rate ranges.
*/
public final RefreshRateRange primaryRefreshRateRange;
public final RefreshRateRanges primary;
/**
* The app request refresh rate range. Lower priority considerations won't be included in
* The app request refresh rate ranges. Lower priority considerations won't be included in
* this range, allowing SurfaceFlinger to consider additional refresh rates for apps that
* call setFrameRate(). This range will be greater than or equal to the primary refresh rate
* range, never smaller.
*/
public final RefreshRateRange appRequestRefreshRateRange;
public final RefreshRateRanges appRequest;
public DesiredDisplayModeSpecs() {
primaryRefreshRateRange = new RefreshRateRange();
appRequestRefreshRateRange = new RefreshRateRange();
primary = new RefreshRateRanges();
appRequest = new RefreshRateRanges();
}
public DesiredDisplayModeSpecs(int baseModeId,
boolean allowGroupSwitching,
@NonNull RefreshRateRange primaryRefreshRateRange,
@NonNull RefreshRateRange appRequestRefreshRateRange) {
@NonNull RefreshRateRanges primary,
@NonNull RefreshRateRanges appRequest) {
this.baseModeId = baseModeId;
this.allowGroupSwitching = allowGroupSwitching;
this.primaryRefreshRateRange = primaryRefreshRateRange;
this.appRequestRefreshRateRange = appRequestRefreshRateRange;
this.primary = primary;
this.appRequest = appRequest;
}
/**
@@ -886,12 +1026,12 @@ public class DisplayModeDirector {
@Override
public String toString() {
return String.format("baseModeId=%d allowGroupSwitching=%b"
+ " primaryRefreshRateRange=[%.0f %.0f]"
+ " appRequestRefreshRateRange=[%.0f %.0f]",
baseModeId, allowGroupSwitching, primaryRefreshRateRange.min,
primaryRefreshRateRange.max, appRequestRefreshRateRange.min,
appRequestRefreshRateRange.max);
+ " primary=%s"
+ " appRequest=%s",
baseModeId, allowGroupSwitching, primary.toString(),
appRequest.toString());
}
/**
* Checks whether the two objects have the same values.
*/
@@ -913,11 +1053,11 @@ public class DisplayModeDirector {
if (allowGroupSwitching != desiredDisplayModeSpecs.allowGroupSwitching) {
return false;
}
if (!primaryRefreshRateRange.equals(desiredDisplayModeSpecs.primaryRefreshRateRange)) {
if (!primary.equals(desiredDisplayModeSpecs.primary)) {
return false;
}
if (!appRequestRefreshRateRange.equals(
desiredDisplayModeSpecs.appRequestRefreshRateRange)) {
if (!appRequest.equals(
desiredDisplayModeSpecs.appRequest)) {
return false;
}
return true;
@@ -925,8 +1065,7 @@ public class DisplayModeDirector {
@Override
public int hashCode() {
return Objects.hash(baseModeId, allowGroupSwitching, primaryRefreshRateRange,
appRequestRefreshRateRange);
return Objects.hash(baseModeId, allowGroupSwitching, primary, appRequest);
}
/**
@@ -935,18 +1074,24 @@ public class DisplayModeDirector {
public void copyFrom(DesiredDisplayModeSpecs other) {
baseModeId = other.baseModeId;
allowGroupSwitching = other.allowGroupSwitching;
primaryRefreshRateRange.min = other.primaryRefreshRateRange.min;
primaryRefreshRateRange.max = other.primaryRefreshRateRange.max;
appRequestRefreshRateRange.min = other.appRequestRefreshRateRange.min;
appRequestRefreshRateRange.max = other.appRequestRefreshRateRange.max;
primary.physical.min = other.primary.physical.min;
primary.physical.max = other.primary.physical.max;
primary.render.min = other.primary.render.min;
primary.render.max = other.primary.render.max;
appRequest.physical.min = other.appRequest.physical.min;
appRequest.physical.max = other.appRequest.physical.max;
appRequest.render.min = other.appRequest.render.min;
appRequest.render.max = other.appRequest.render.max;
}
}
@VisibleForTesting
static final class Vote {
// DEFAULT_FRAME_RATE votes for [0, DEFAULT]. As the lowest priority vote, it's overridden
// by all other considerations. It acts to set a default frame rate for a device.
public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;
// DEFAULT_RENDER_FRAME_RATE votes for render frame rate [0, DEFAULT]. As the lowest
// priority vote, it's overridden by all other considerations. It acts to set a default
// frame rate for a device.
public static final int PRIORITY_DEFAULT_RENDER_FRAME_RATE = 0;
// PRIORITY_FLICKER_REFRESH_RATE votes for a single refresh rate like [60,60], [90,90] or
// null. It is used to set a preferred refresh rate value in case the higher priority votes
@@ -956,16 +1101,16 @@ public class DisplayModeDirector {
// High-brightness-mode may need a specific range of refresh-rates to function properly.
public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;
// SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
// SETTING_MIN_RENDER_FRAME_RATE is used to propose a lower bound of the render frame rate.
// It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 3;
public static final int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;
// APP_REQUEST_REFRESH_RATE_RANGE is used to for internal apps to limit the refresh
// rate in certain cases, mostly to preserve power.
// APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render
// frame rate in certain cases, mostly to preserve power.
// @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
// @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
// It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
public static final int PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE = 4;
public static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 4;
// We split the app request into different priorities in case we can satisfy one desire
// without the other.
@@ -983,11 +1128,12 @@ public class DisplayModeDirector {
public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 5;
public static final int PRIORITY_APP_REQUEST_SIZE = 6;
// SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
// of low priority voters. It votes [0, max(PEAK, MIN)]
public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 7;
// SETTING_PEAK_RENDER_FRAME_RATE has a high priority and will restrict the bounds of the
// rest of low priority voters. It votes [0, max(PEAK, MIN)]
public static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 7;
// LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
// LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
// Settings.Global.LOW_POWER_MODE is on.
public static final int PRIORITY_LOW_POWER_MODE = 8;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
@@ -1010,13 +1156,13 @@ public class DisplayModeDirector {
// Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
// APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
public static final int MIN_PRIORITY = PRIORITY_DEFAULT_REFRESH_RATE;
public static final int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE;
public static final int MAX_PRIORITY = PRIORITY_UDFPS;
// The cutoff for the app request refresh rate range. Votes with priorities lower than this
// value will not be considered when constructing the app request refresh rate range.
public static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE;
PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE;
/**
* A value signifying an invalid width or height in a vote.
@@ -1032,9 +1178,9 @@ public class DisplayModeDirector {
*/
public final int height;
/**
* Information about the min and max refresh rate DM would like to set the display to.
* Information about the refresh rate frame rate ranges DM would like to set the display to.
*/
public final RefreshRateRange refreshRateRange;
public final RefreshRateRanges refreshRateRanges;
/**
* Whether refresh rate switching should be disabled (i.e. the refresh rate range is
@@ -1048,34 +1194,48 @@ public class DisplayModeDirector {
*/
public final float baseModeRefreshRate;
public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate,
public static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) {
return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate, 0,
Float.POSITIVE_INFINITY,
minRefreshRate == maxRefreshRate, 0f);
}
public static Vote forRenderFrameRates(float minFrameRate, float maxFrameRate) {
return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, minFrameRate,
maxFrameRate,
false, 0f);
}
public static Vote forSize(int width, int height) {
return new Vote(width, height, 0f, Float.POSITIVE_INFINITY, false,
return new Vote(width, height, 0, Float.POSITIVE_INFINITY, 0, Float.POSITIVE_INFINITY,
false,
0f);
}
public static Vote forDisableRefreshRateSwitching() {
return new Vote(INVALID_SIZE, INVALID_SIZE, 0f, Float.POSITIVE_INFINITY, true,
return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
Float.POSITIVE_INFINITY, true,
0f);
}
public static Vote forBaseModeRefreshRate(float baseModeRefreshRate) {
return new Vote(INVALID_SIZE, INVALID_SIZE, 0f, Float.POSITIVE_INFINITY, false,
return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
Float.POSITIVE_INFINITY, false,
baseModeRefreshRate);
}
private Vote(int width, int height,
float minRefreshRate, float maxRefreshRate,
float minPhysicalRefreshRate,
float maxPhysicalRefreshRate,
float minRenderFrameRate,
float maxRenderFrameRate,
boolean disableRefreshRateSwitching,
float baseModeRefreshRate) {
this.width = width;
this.height = height;
this.refreshRateRange =
new RefreshRateRange(minRefreshRate, maxRefreshRate);
this.refreshRateRanges = new RefreshRateRanges(
new RefreshRateRange(minPhysicalRefreshRate, maxPhysicalRefreshRate),
new RefreshRateRange(minRenderFrameRate, maxRenderFrameRate));
this.disableRefreshRateSwitching = disableRefreshRateSwitching;
this.baseModeRefreshRate = baseModeRefreshRate;
}
@@ -1084,11 +1244,11 @@ public class DisplayModeDirector {
switch (priority) {
case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
case PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE:
return "PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE";
case PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE:
return "PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE";
case PRIORITY_APP_REQUEST_SIZE:
return "PRIORITY_APP_REQUEST_SIZE";
case PRIORITY_DEFAULT_REFRESH_RATE:
case PRIORITY_DEFAULT_RENDER_FRAME_RATE:
return "PRIORITY_DEFAULT_REFRESH_RATE";
case PRIORITY_FLICKER_REFRESH_RATE:
return "PRIORITY_FLICKER_REFRESH_RATE";
@@ -1104,10 +1264,10 @@ public class DisplayModeDirector {
return "PRIORITY_SKIN_TEMPERATURE";
case PRIORITY_UDFPS:
return "PRIORITY_UDFPS";
case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
case PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE:
return "PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE";
case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE:
return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE";
default:
return Integer.toString(priority);
}
@@ -1116,11 +1276,10 @@ public class DisplayModeDirector {
@Override
public String toString() {
return "Vote{"
+ "width=" + width + ", height=" + height
+ ", minRefreshRate=" + refreshRateRange.min
+ ", maxRefreshRate=" + refreshRateRange.max
+ ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
+ ", baseModeRefreshRate=" + baseModeRefreshRate + "}";
+ "width=" + width + ", height=" + height
+ ", refreshRateRanges=" + refreshRateRanges
+ ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
+ ", baseModeRefreshRate=" + baseModeRefreshRate + "}";
}
}
@@ -1237,7 +1396,7 @@ public class DisplayModeDirector {
Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
final Vote vote;
if (inLowPowerMode) {
vote = Vote.forRefreshRates(0f, 60f);
vote = Vote.forRenderFrameRates(0f, 60f);
} else {
vote = null;
}
@@ -1262,13 +1421,14 @@ public class DisplayModeDirector {
// than necessary, and we should improve it. See b/156304339 for more info.
Vote peakVote = peakRefreshRate == 0f
? null
: Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate));
updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, peakVote);
updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY));
: Vote.forRenderFrameRates(0f, Math.max(minRefreshRate, peakRefreshRate));
updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, peakVote);
updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(minRefreshRate, Float.POSITIVE_INFINITY));
Vote defaultVote =
defaultRefreshRate == 0f ? null : Vote.forRefreshRates(0f, defaultRefreshRate);
updateVoteLocked(Vote.PRIORITY_DEFAULT_REFRESH_RATE, defaultVote);
defaultRefreshRate == 0f
? null : Vote.forRenderFrameRates(0f, defaultRefreshRate);
updateVoteLocked(Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE, defaultVote);
float maxRefreshRate;
if (peakRefreshRate == 0f && defaultRefreshRate == 0f) {
@@ -1374,13 +1534,15 @@ public class DisplayModeDirector {
if (refreshRateRange != null) {
mAppPreferredRefreshRateRangeByDisplay.put(displayId, refreshRateRange);
vote = Vote.forRefreshRates(refreshRateRange.min, refreshRateRange.max);
vote = Vote.forRenderFrameRates(refreshRateRange.min, refreshRateRange.max);
} else {
mAppPreferredRefreshRateRangeByDisplay.remove(displayId);
vote = null;
}
synchronized (mLock) {
updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE, vote);
updateVoteLocked(displayId,
Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
vote);
}
}
@@ -1956,6 +2118,7 @@ public class DisplayModeDirector {
return false;
}
private void onBrightnessChangedLocked() {
Vote refreshRateVote = null;
Vote refreshRateSwitchingVote = null;
@@ -1969,7 +2132,7 @@ public class DisplayModeDirector {
boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
if (insideLowZone) {
refreshRateVote =
Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
Vote.forPhysicalRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
}
@@ -1977,7 +2140,8 @@ public class DisplayModeDirector {
&& isInsideHighZone(mBrightness, mAmbientLux);
if (insideHighZone) {
refreshRateVote =
Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
Vote.forPhysicalRefreshRates(mRefreshRateInHighZone,
mRefreshRateInHighZone);
refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
}
@@ -2222,7 +2386,7 @@ public class DisplayModeDirector {
maxRefreshRate = mode.getRefreshRate();
}
}
vote = Vote.forRefreshRates(maxRefreshRate, maxRefreshRate);
vote = Vote.forPhysicalRefreshRates(maxRefreshRate, maxRefreshRate);
} else {
vote = null;
}
@@ -2303,7 +2467,7 @@ public class DisplayModeDirector {
mDisplayManagerInternal.getRefreshRateForDisplayAndSensor(
displayId, mProximitySensorName, mProximitySensorType);
if (rate != null) {
vote = Vote.forRefreshRates(rate.min, rate.max);
vote = Vote.forPhysicalRefreshRates(rate.min, rate.max);
}
}
mBallotBox.vote(displayId, Vote.PRIORITY_PROXIMITY, vote);
@@ -2472,7 +2636,7 @@ public class DisplayModeDirector {
if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
// Device resource properties take priority over DisplayDeviceConfig
if (mRefreshRateInHbmSunlight > 0) {
vote = Vote.forRefreshRates(mRefreshRateInHbmSunlight,
vote = Vote.forPhysicalRefreshRates(mRefreshRateInHbmSunlight,
mRefreshRateInHbmSunlight);
} else {
final List<RefreshRateLimitation> limits =
@@ -2480,7 +2644,7 @@ public class DisplayModeDirector {
for (int i = 0; limits != null && i < limits.size(); i++) {
final RefreshRateLimitation limitation = limits.get(i);
if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
vote = Vote.forRefreshRates(limitation.range.min,
vote = Vote.forPhysicalRefreshRates(limitation.range.min,
limitation.range.max);
break;
}
@@ -2490,7 +2654,7 @@ public class DisplayModeDirector {
mRefreshRateInHbmHdr > 0) {
// HBM for HDR vote isn't supported through DisplayDeviceConfig yet, so look for
// a vote from Device properties
vote = Vote.forRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr);
vote = Vote.forPhysicalRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr);
} else {
Slog.w(TAG, "Unexpected HBM mode " + hbmMode + " for display ID " + displayId);
}
@@ -2528,7 +2692,7 @@ public class DisplayModeDirector {
}
final Vote vote;
if (mStatus >= Temperature.THROTTLING_CRITICAL) {
vote = Vote.forRefreshRates(0f, 60f);
vote = Vote.forRenderFrameRates(0f, 60f);
} else {
vote = null;
}
@@ -2741,6 +2905,8 @@ public class DisplayModeDirector {
boolean isDozeState(Display d);
IThermalService getThermalService();
boolean renderFrameRateIsPhysicalRefreshRate();
}
@VisibleForTesting
@@ -2794,6 +2960,12 @@ public class DisplayModeDirector {
ServiceManager.getService(Context.THERMAL_SERVICE));
}
@Override
public boolean renderFrameRateIsPhysicalRefreshRate() {
return DisplayProperties
.debug_render_frame_rate_is_physical_refresh_rate().orElse(true);
}
private DisplayManager getDisplayManager() {
if (mDisplayManager == null) {
mDisplayManager = mContext.getSystemService(DisplayManager.class);

View File

@@ -387,14 +387,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// list of available modes will take care of updating display mode specs.
if (activeBaseMode == INVALID_MODE_ID
|| mDisplayModeSpecs.baseModeId != activeBaseMode
|| mDisplayModeSpecs.primaryRefreshRateRange.min
!= modeSpecs.primaryRefreshRateMin
|| mDisplayModeSpecs.primaryRefreshRateRange.max
!= modeSpecs.primaryRefreshRateMax
|| mDisplayModeSpecs.appRequestRefreshRateRange.min
!= modeSpecs.appRequestRefreshRateMin
|| mDisplayModeSpecs.appRequestRefreshRateRange.max
!= modeSpecs.appRequestRefreshRateMax) {
|| !mDisplayModeSpecs.primary.equals(modeSpecs.primaryRanges)
|| !mDisplayModeSpecs.appRequest.equals(modeSpecs.appRequestRanges)) {
mDisplayModeSpecsInvalid = true;
sendTraversalRequestLocked();
}
@@ -997,10 +991,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
getDisplayTokenLocked(),
new SurfaceControl.DesiredDisplayModeSpecs(baseSfModeId,
mDisplayModeSpecs.allowGroupSwitching,
mDisplayModeSpecs.primaryRefreshRateRange.min,
mDisplayModeSpecs.primaryRefreshRateRange.max,
mDisplayModeSpecs.appRequestRefreshRateRange.min,
mDisplayModeSpecs.appRequestRefreshRateRange.max)));
mDisplayModeSpecs.primary,
mDisplayModeSpecs.appRequest)));
}
}

View File

@@ -19,10 +19,10 @@ package com.android.server.wm;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.view.Display;
import android.view.Display.Mode;
import android.view.DisplayInfo;
import android.view.SurfaceControl.RefreshRateRange;
import java.util.HashMap;

View File

@@ -35,7 +35,6 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -43,6 +42,8 @@ import android.os.Looper;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.SurfaceControl;
import android.view.SurfaceControl.RefreshRateRange;
import android.view.SurfaceControl.RefreshRateRanges;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -75,6 +76,11 @@ public class LocalDisplayAdapterTest {
private static final int PORT_A = 0;
private static final int PORT_B = 0x80;
private static final int PORT_C = 0xFF;
private static final float REFRESH_RATE = 60f;
private static final RefreshRateRange REFRESH_RATE_RANGE =
new RefreshRateRange(REFRESH_RATE, REFRESH_RATE);
private static final RefreshRateRanges REFRESH_RATE_RANGES =
new RefreshRateRanges(REFRESH_RATE_RANGE, REFRESH_RATE_RANGE);
private static final long HANDLER_WAIT_MS = 100;
@@ -697,16 +703,14 @@ public class LocalDisplayAdapterTest {
new DisplayModeDirector.DesiredDisplayModeSpecs(
/*baseModeId*/ baseModeId,
/*allowGroupSwitching*/ false,
new RefreshRateRange(60f, 60f),
new RefreshRateRange(60f, 60f)
REFRESH_RATE_RANGES, REFRESH_RATE_RANGES
));
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token,
new SurfaceControl.DesiredDisplayModeSpecs(
/* baseModeId */ 0,
/* allowGroupSwitching */ false,
/* primaryRange */ 60f, 60f,
/* appRange */ 60f, 60f
REFRESH_RATE_RANGES, REFRESH_RATE_RANGES
));
// Change the display
@@ -732,8 +736,7 @@ public class LocalDisplayAdapterTest {
new DisplayModeDirector.DesiredDisplayModeSpecs(
/*baseModeId*/ baseModeId,
/*allowGroupSwitching*/ false,
new RefreshRateRange(60f, 60f),
new RefreshRateRange(60f, 60f)
REFRESH_RATE_RANGES, REFRESH_RATE_RANGES
));
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
@@ -743,8 +746,7 @@ public class LocalDisplayAdapterTest {
new SurfaceControl.DesiredDisplayModeSpecs(
/* baseModeId */ 2,
/* allowGroupSwitching */ false,
/* primaryRange */ 60f, 60f,
/* appRange */ 60f, 60f
REFRESH_RATE_RANGES, REFRESH_RATE_RANGES
));
}
@@ -922,12 +924,11 @@ public class LocalDisplayAdapterTest {
}
public SurfaceControl.DesiredDisplayModeSpecs desiredDisplayModeSpecs =
new SurfaceControl.DesiredDisplayModeSpecs(/* defaultMode */ 0,
/* allowGroupSwitching */ false,
/* primaryRefreshRateMin */ 60.f,
/* primaryRefreshRateMax */ 60.f,
/* appRefreshRateMin */ 60.f,
/* appRefreshRateMax */60.f);
new SurfaceControl.DesiredDisplayModeSpecs(
/* defaultMode */ 0,
/* allowGroupSwitching */ false,
REFRESH_RATE_RANGES, REFRESH_RATE_RANGES
);
private FakeDisplay(int port) {
address = createDisplayAddress(port);