diff options
| author | 2022-10-24 17:46:26 +0000 | |
|---|---|---|
| committer | 2022-10-24 17:46:26 +0000 | |
| commit | 1ff67245399dbb6ed19f926b6eeb25995577d00c (patch) | |
| tree | b7556e3a54e3f8f0aaa263a872477f5413803685 | |
| parent | 5dc888399c4e6bc976f0862b273dd8dec32082de (diff) | |
| parent | b878a206e8fb976f7ca8b2969a5c2ddb4fc24cc3 (diff) | |
Merge "DisplayModeDirector: separate the display refresh rate and the render frame rate"
9 files changed, 1329 insertions, 544 deletions
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 00bccc686919..6b3e67309f22 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -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; /** @@ -651,72 +650,6 @@ public abstract class DisplayManagerInternal { } /** - * 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. */ diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index c46f33a4a786..06851de95f82 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -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. @@ -1682,44 +1823,49 @@ 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. + * 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 float primaryRefreshRateMin; - public float primaryRefreshRateMax; + public boolean allowGroupSwitching; + /** - * 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. + * 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 float appRequestRefreshRateMin; - public float appRequestRefreshRateMax; + public final RefreshRateRanges primaryRanges; /** - * 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. + * 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 boolean allowGroupSwitching; + public final RefreshRateRanges appRequestRanges; - public DesiredDisplayModeSpecs() {} + 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; } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index e36815c4beb9..8fd040189f8b 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -190,12 +190,24 @@ static struct { 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"); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 5eb15e09f09e..7e80b7d5b0ac 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -119,6 +119,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; diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 912b1b2e8da2..22a47393b3d6 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -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)); - } - - // 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. + "App request range: [" + + appRequestSummary.minPhysicalRefreshRate + + " " + + appRequestSummary.maxPhysicalRefreshRate + + "] Frame rate range: [" + + appRequestSummary.minRenderFrameRate + + " " + + appRequestSummary.maxRenderFrameRate + + "]"); + } + + // 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); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 002209e32304..2c2075d33bb9 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -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))); } } diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java index f3713eb7f474..ccc71bb8c537 100644 --- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java +++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java @@ -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; diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index 3866da33a24e..d41ac7021077 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -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); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 18dd264558ac..f8dd4436fac0 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -57,7 +57,6 @@ import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation; -import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.hardware.fingerprint.IUdfpsHbmListener; import android.os.Handler; import android.os.IThermalEventListener; @@ -71,10 +70,11 @@ import android.test.mock.MockContentResolver; import android.util.Slog; import android.util.SparseArray; import android.view.Display; +import android.view.SurfaceControl.RefreshRateRange; +import android.view.SurfaceControl.RefreshRateRanges; import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.util.Preconditions; @@ -105,8 +105,11 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(JUnitParamsRunner.class) public class DisplayModeDirectorTest { // The tolerance within which we consider something approximately equals. private static final String TAG = "DisplayModeDirectorTest"; @@ -154,8 +157,6 @@ public class DisplayModeDirectorTest { private DisplayModeDirector createDirectorFromRefreshRateArray( float[] refreshRates, int baseModeId, float defaultRefreshRate) { - DisplayModeDirector director = - new DisplayModeDirector(mContext, mHandler, mInjector); Display.Mode[] modes = new Display.Mode[refreshRates.length]; Display.Mode defaultMode = null; for (int i = 0; i < refreshRates.length; i++) { @@ -194,13 +195,24 @@ public class DisplayModeDirectorTest { } @Test - public void testDisplayModeVoting() { + @Parameters({ + "true", + "false" + }) + public void testDisplayModeVoting(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); // With no votes present, DisplayModeDirector should allow any refresh rate. - DesiredDisplayModeSpecs modeSpecs = - createDirectorFromFpsRange(60, 90).getDesiredDisplayModeSpecs(DISPLAY_ID); + DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + DesiredDisplayModeSpecs modeSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(modeSpecs.baseModeId).isEqualTo(60); - assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(0f); - assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(Float.POSITIVE_INFINITY); + assertThat(modeSpecs.primary.physical.min).isEqualTo(0f); + assertThat(modeSpecs.primary.physical.max).isEqualTo(Float.POSITIVE_INFINITY); + assertThat(modeSpecs.primary.render.min).isEqualTo(0f); + assertThat(modeSpecs.primary.render.max).isEqualTo(Float.POSITIVE_INFINITY); + assertThat(modeSpecs.appRequest.physical.min).isEqualTo(0f); + assertThat(modeSpecs.appRequest.physical.max).isEqualTo(Float.POSITIVE_INFINITY); + assertThat(modeSpecs.appRequest.render.min).isEqualTo(0f); + assertThat(modeSpecs.appRequest.render.max).isEqualTo(Float.POSITIVE_INFINITY); int numPriorities = DisplayModeDirector.Vote.MAX_PRIORITY - DisplayModeDirector.Vote.MIN_PRIORITY + 1; @@ -210,21 +222,49 @@ public class DisplayModeDirectorTest { { int minFps = 60; int maxFps = 90; - DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + director = createDirectorFromFpsRange(60, 90); assertTrue(2 * numPriorities < maxFps - minFps + 1); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); for (int i = 0; i < numPriorities; i++) { int priority = Vote.MIN_PRIORITY + i; - votes.put(priority, Vote.forRefreshRates(minFps + i, maxFps - i)); + votes.put(priority, Vote.forPhysicalRefreshRates(minFps + i, maxFps - i)); director.injectVotesByDisplay(votesByDisplay); modeSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(modeSpecs.baseModeId).isEqualTo(minFps + i); - assertThat(modeSpecs.primaryRefreshRateRange.min) + assertThat(modeSpecs.primary.physical.min) .isEqualTo((float) (minFps + i)); - assertThat(modeSpecs.primaryRefreshRateRange.max) + assertThat(modeSpecs.primary.physical.max) .isEqualTo((float) (maxFps - i)); + if (frameRateIsRefreshRate) { + assertThat(modeSpecs.primary.render.min) + .isEqualTo((float) (minFps + i)); + } else { + assertThat(modeSpecs.primary.render.min).isZero(); + } + assertThat(modeSpecs.primary.render.max) + .isEqualTo((float) (maxFps - i)); + if (priority >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF) { + assertThat(modeSpecs.appRequest.physical.min) + .isEqualTo((float) (minFps + i)); + assertThat(modeSpecs.appRequest.physical.max) + .isEqualTo((float) (maxFps - i)); + if (frameRateIsRefreshRate) { + assertThat(modeSpecs.appRequest.render.min).isEqualTo( + (float) (minFps + i)); + } else { + assertThat(modeSpecs.appRequest.render.min).isZero(); + } + assertThat(modeSpecs.appRequest.render.max).isEqualTo( + (float) (maxFps - i)); + } else { + assertThat(modeSpecs.appRequest.physical.min).isZero(); + assertThat(modeSpecs.appRequest.physical.max).isPositiveInfinity(); + assertThat(modeSpecs.appRequest.render.min).isZero(); + assertThat(modeSpecs.appRequest.render.max).isPositiveInfinity(); + } + } } @@ -232,42 +272,53 @@ public class DisplayModeDirectorTest { // presence of higher priority votes. { assertTrue(numPriorities >= 2); - DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + director = createDirectorFromFpsRange(60, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); - votes.put(Vote.MAX_PRIORITY, Vote.forRefreshRates(65, 85)); - votes.put(Vote.MIN_PRIORITY, Vote.forRefreshRates(70, 80)); + votes.put(Vote.MAX_PRIORITY, Vote.forPhysicalRefreshRates(65, 85)); + votes.put(Vote.MIN_PRIORITY, Vote.forPhysicalRefreshRates(70, 80)); director.injectVotesByDisplay(votesByDisplay); modeSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(modeSpecs.baseModeId).isEqualTo(70); - assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(70f); - assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(80f); + assertThat(modeSpecs.primary.physical.min).isEqualTo(70f); + assertThat(modeSpecs.primary.physical.max).isEqualTo(80f); } } @Test - public void testVotingWithFloatingPointErrors() { + @Parameters({ + "true", + "false" + }) + public void testVotingWithFloatingPointErrors(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); DisplayModeDirector director = createDirectorFromFpsRange(50, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); float error = FLOAT_TOLERANCE / 4; - votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(0, 60)); + votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(0, 60)); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, - Vote.forRefreshRates(60 + error, 60 + error)); + Vote.forPhysicalRefreshRates(60 + error, 60 + error)); votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, - Vote.forRefreshRates(60 - error, 60 - error)); + Vote.forPhysicalRefreshRates(60 - error, 60 - error)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60); assertThat(desiredSpecs.baseModeId).isEqualTo(60); } @Test - public void testFlickerHasLowerPriorityThanUserAndRangeIsSingle() { + @Parameters({ + "true", + "false" + }) + public void testFlickerHasLowerPriorityThanUserAndRangeIsSingle( + boolean frameRateIsRefreshRate) { assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE < Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE @@ -276,6 +327,7 @@ public class DisplayModeDirectorTest { assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH > Vote.PRIORITY_LOW_POWER_MODE); + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); Display.Mode[] modes = new Display.Mode[4]; modes[0] = new Display.Mode( /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60); @@ -295,14 +347,14 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), appRequestedMode.getPhysicalHeight())); - votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(60, 60)); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(2); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max) - .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primaryRefreshRateRange.min); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.max) + .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primary.physical.min); votes.clear(); appRequestedMode = modes[3]; @@ -310,14 +362,14 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), appRequestedMode.getPhysicalHeight())); - votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(90, 90)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(90, 90)); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(4); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(desiredSpecs.primaryRefreshRateRange.max) - .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primaryRefreshRateRange.min); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.physical.max) + .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primary.physical.min); votes.clear(); appRequestedMode = modes[3]; @@ -325,14 +377,14 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), appRequestedMode.getPhysicalHeight())); - votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(60, 60)); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(4); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(desiredSpecs.primaryRefreshRateRange.max) - .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primaryRefreshRateRange.min); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.physical.max) + .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primary.physical.min); votes.clear(); appRequestedMode = modes[1]; @@ -340,22 +392,28 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), appRequestedMode.getPhysicalHeight())); - votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(90, 90)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(90, 90)); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(2); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max) - .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primaryRefreshRateRange.min); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.max) + .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primary.physical.min); } @Test - public void testLPMHasHigherPriorityThanUser() { - assertTrue(Vote.PRIORITY_LOW_POWER_MODE > Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); - assertTrue(Vote.PRIORITY_LOW_POWER_MODE > Vote.PRIORITY_APP_REQUEST_SIZE); - - + @Parameters({ + "true", + "false" + }) + public void testLPMHasHigherPriorityThanUser(boolean frameRateIsRefreshRate) { + assertTrue(Vote.PRIORITY_LOW_POWER_MODE + > Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); + assertTrue(Vote.PRIORITY_LOW_POWER_MODE + > Vote.PRIORITY_APP_REQUEST_SIZE); + + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); Display.Mode[] modes = new Display.Mode[4]; modes[0] = new Display.Mode( /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60); @@ -375,12 +433,18 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), appRequestedMode.getPhysicalHeight())); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(2); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60); + } else { + assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity(); + } + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60); votes.clear(); appRequestedMode = modes[3]; @@ -388,12 +452,18 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), appRequestedMode.getPhysicalHeight())); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(90, 90)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(4); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90); + } else { + assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity(); + } + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90); votes.clear(); appRequestedMode = modes[3]; @@ -401,12 +471,18 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), appRequestedMode.getPhysicalHeight())); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(2); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60); + } else { + assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity(); + } + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60); votes.clear(); appRequestedMode = modes[1]; @@ -414,26 +490,37 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), appRequestedMode.getPhysicalHeight())); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(90, 90)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(4); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90); + } else { + assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity(); + } + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90); } @Test - public void testAppRequestRefreshRateRange() { + @Parameters({ + "true", + "false" + }) + public void testAppRequestRefreshRateRange(boolean frameRateIsRefreshRate) { // Confirm that the app request range doesn't include flicker or min refresh rate settings, // but does include everything else. assertTrue( Vote.PRIORITY_FLICKER_REFRESH_RATE < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); - assertTrue(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE + assertTrue(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); - assertTrue(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE + assertTrue(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); Display.Mode[] modes = new Display.Mode[3]; modes[0] = new Display.Mode( /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60); @@ -446,25 +533,25 @@ public class DisplayModeDirectorTest { SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); - votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(60, 60)); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(60); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); - assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f); + assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f); - votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, - Vote.forRefreshRates(90, Float.POSITIVE_INFINITY)); + votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(90, Float.POSITIVE_INFINITY)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(90); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f); - assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); - assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.physical.max).isAtLeast(90f); + assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f); + assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f); Display.Mode appRequestedMode = modes[1]; votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, @@ -474,25 +561,28 @@ public class DisplayModeDirectorTest { director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(75); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75); - assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); - assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(75); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(75); + assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f); + assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f); } void verifySpecsWithRefreshRateSettings(DisplayModeDirector director, float minFps, - float peakFps, float defaultFps, float primaryMin, float primaryMax, - float appRequestMin, float appRequestMax) { + float peakFps, float defaultFps, RefreshRateRanges primary, + RefreshRateRanges appRequest) { DesiredDisplayModeSpecs specs = director.getDesiredDisplayModeSpecsWithInjectedFpsSettings( minFps, peakFps, defaultFps); - assertThat(specs.primaryRefreshRateRange.min).isEqualTo(primaryMin); - assertThat(specs.primaryRefreshRateRange.max).isEqualTo(primaryMax); - assertThat(specs.appRequestRefreshRateRange.min).isEqualTo(appRequestMin); - assertThat(specs.appRequestRefreshRateRange.max).isEqualTo(appRequestMax); + assertThat(specs.primary).isEqualTo(primary); + assertThat(specs.appRequest).isEqualTo(appRequest); } @Test - public void testSpecsFromRefreshRateSettings() { + @Parameters({ + "true", + "false" + }) + public void testSpecsFromRefreshRateSettings(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); // Confirm that, with varying settings for min, peak, and default refresh rate, // DesiredDisplayModeSpecs is calculated correctly. float[] refreshRates = {30.f, 60.f, 90.f, 120.f, 150.f}; @@ -500,17 +590,56 @@ public class DisplayModeDirectorTest { createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/0); float inf = Float.POSITIVE_INFINITY; - verifySpecsWithRefreshRateSettings(director, 0, 0, 0, 0, inf, 0, inf); - verifySpecsWithRefreshRateSettings(director, 0, 0, 90, 0, 90, 0, inf); - verifySpecsWithRefreshRateSettings(director, 0, 90, 0, 0, 90, 0, 90); - verifySpecsWithRefreshRateSettings(director, 0, 90, 60, 0, 60, 0, 90); - verifySpecsWithRefreshRateSettings(director, 0, 90, 120, 0, 90, 0, 90); - verifySpecsWithRefreshRateSettings(director, 90, 0, 0, 90, inf, 0, inf); - verifySpecsWithRefreshRateSettings(director, 90, 0, 120, 90, 120, 0, inf); - verifySpecsWithRefreshRateSettings(director, 90, 0, 60, 90, inf, 0, inf); - verifySpecsWithRefreshRateSettings(director, 90, 120, 0, 90, 120, 0, 120); - verifySpecsWithRefreshRateSettings(director, 90, 60, 0, 90, 90, 0, 90); - verifySpecsWithRefreshRateSettings(director, 60, 120, 90, 60, 90, 0, 120); + RefreshRateRange rangeAll = new RefreshRateRange(0, inf); + RefreshRateRange range0to60 = new RefreshRateRange(0, 60); + RefreshRateRange range0to90 = new RefreshRateRange(0, 90); + RefreshRateRange range0to120 = new RefreshRateRange(0, 120); + RefreshRateRange range60to90 = new RefreshRateRange(60, 90); + RefreshRateRange range90to90 = new RefreshRateRange(90, 90); + RefreshRateRange range90to120 = new RefreshRateRange(90, 120); + RefreshRateRange range60toInf = new RefreshRateRange(60, inf); + RefreshRateRange range90toInf = new RefreshRateRange(90, inf); + + RefreshRateRanges frameRateAll = new RefreshRateRanges(rangeAll, rangeAll); + RefreshRateRanges frameRate90toInf = new RefreshRateRanges(range90toInf, range90toInf); + RefreshRateRanges frameRate0to60; + RefreshRateRanges frameRate0to90; + RefreshRateRanges frameRate0to120; + RefreshRateRanges frameRate60to90; + RefreshRateRanges frameRate90to90; + RefreshRateRanges frameRate90to120; + if (frameRateIsRefreshRate) { + frameRate0to60 = new RefreshRateRanges(range0to60, range0to60); + frameRate0to90 = new RefreshRateRanges(range0to90, range0to90); + frameRate0to120 = new RefreshRateRanges(range0to120, range0to120); + frameRate60to90 = new RefreshRateRanges(range60to90, range60to90); + frameRate90to90 = new RefreshRateRanges(range90to90, range90to90); + frameRate90to120 = new RefreshRateRanges(range90to120, range90to120); + } else { + frameRate0to60 = new RefreshRateRanges(rangeAll, range0to60); + frameRate0to90 = new RefreshRateRanges(rangeAll, range0to90); + frameRate0to120 = new RefreshRateRanges(rangeAll, range0to120); + frameRate60to90 = new RefreshRateRanges(range60toInf, range60to90); + frameRate90to90 = new RefreshRateRanges(range90toInf, range90to90); + frameRate90to120 = new RefreshRateRanges(range90toInf, range90to120); + } + + verifySpecsWithRefreshRateSettings(director, 0, 0, 0, frameRateAll, frameRateAll); + verifySpecsWithRefreshRateSettings(director, 0, 0, 90, frameRate0to90, frameRateAll); + verifySpecsWithRefreshRateSettings(director, 0, 90, 0, frameRate0to90, frameRate0to90); + verifySpecsWithRefreshRateSettings(director, 0, 90, 60, frameRate0to60, frameRate0to90); + verifySpecsWithRefreshRateSettings(director, 0, 90, 120, frameRate0to90, + frameRate0to90); + verifySpecsWithRefreshRateSettings(director, 90, 0, 0, frameRate90toInf, frameRateAll); + verifySpecsWithRefreshRateSettings(director, 90, 0, 120, frameRate90to120, + frameRateAll); + verifySpecsWithRefreshRateSettings(director, 90, 0, 60, frameRate90toInf, frameRateAll); + verifySpecsWithRefreshRateSettings(director, 90, 120, 0, frameRate90to120, + frameRate0to120); + verifySpecsWithRefreshRateSettings(director, 90, 60, 0, frameRate90to90, + frameRate0to90); + verifySpecsWithRefreshRateSettings(director, 60, 120, 90, frameRate60to90, + frameRate0to120); } void verifyBrightnessObserverCall(DisplayModeDirector director, float minFps, float peakFps, @@ -523,7 +652,12 @@ public class DisplayModeDirectorTest { } @Test - public void testBrightnessObserverCallWithRefreshRateSettings() { + @Parameters({ + "true", + "false" + }) + public void testBrightnessObserverCallWithRefreshRateSettings(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); // Confirm that, with varying settings for min, peak, and default refresh rate, we make the // correct call to the brightness observer. float[] refreshRates = {60.f, 90.f, 120.f}; @@ -538,7 +672,12 @@ public class DisplayModeDirectorTest { } @Test - public void testVotingWithAlwaysRespectAppRequest() { + @Parameters({ + "true", + "false" + }) + public void testVotingWithAlwaysRespectAppRequest(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); Display.Mode[] modes = new Display.Mode[3]; modes[0] = new Display.Mode( /*modeId=*/50, /*width=*/1000, /*height=*/1000, 50); @@ -549,61 +688,94 @@ public class DisplayModeDirectorTest { DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]); - SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); - votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(0, 60)); - votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(60, 90)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(0, 60)); + votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(60, 90)); Display.Mode appRequestedMode = modes[2]; votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); - votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(60, 60)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(60, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse(); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60); + } else { + assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity(); + } + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60); assertThat(desiredSpecs.baseModeId).isEqualTo(60); director.setShouldAlwaysRespectAppRequestedMode(true); assertThat(director.shouldAlwaysRespectAppRequestedMode()).isTrue(); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isAtMost(50); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90); + assertThat(desiredSpecs.primary.physical.min).isAtMost(50); + assertThat(desiredSpecs.primary.physical.max).isAtLeast(90); + assertThat(desiredSpecs.primary.render.min).isAtMost(50); + assertThat(desiredSpecs.primary.render.max).isAtLeast(90); assertThat(desiredSpecs.baseModeId).isEqualTo(90); director.setShouldAlwaysRespectAppRequestedMode(false); assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse(); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60); + } else { + assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity(); + } + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60); assertThat(desiredSpecs.baseModeId).isEqualTo(60); } @Test - public void testVotingWithSwitchingTypeNone() { + @Parameters({ + "true", + "false" + }) + public void testVotingWithSwitchingTypeNone(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); DisplayModeDirector director = createDirectorFromFpsRange(0, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); - votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(30, 90)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); - + votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(30, 90)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); assertThat(director.getModeSwitchingType()) .isNotEqualTo(DisplayManager.SWITCHING_TYPE_NONE); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.appRequestRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0); - assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60); + } else { + assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity(); + } + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of( + 60); + } else { + assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity(); + } + assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0); + assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60); assertThat(desiredSpecs.baseModeId).isEqualTo(30); director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_NONE); @@ -611,10 +783,14 @@ public class DisplayModeDirectorTest { .isEqualTo(DisplayManager.SWITCHING_TYPE_NONE); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(30); - assertThat(desiredSpecs.appRequestRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30); - assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30); assertThat(desiredSpecs.baseModeId).isEqualTo(30); } @@ -641,7 +817,12 @@ public class DisplayModeDirectorTest { } @Test - public void testDefaultDisplayModeIsSelectedIfAvailable() { + @Parameters({ + "true", + "false" + }) + public void testDefaultDisplayModeIsSelectedIfAvailable(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); final float[] refreshRates = new float[]{24f, 25f, 30f, 60f, 90f}; final int defaultModeId = 3; DisplayModeDirector director = createDirectorFromRefreshRateArray( @@ -778,7 +959,7 @@ public class DisplayModeDirectorTest { sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/)); Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); - assertVoteForRefreshRate(vote, 90 /*fps*/); + assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/); vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); assertThat(vote).isNotNull(); assertThat(vote.disableRefreshRateSwitching).isTrue(); @@ -847,7 +1028,7 @@ public class DisplayModeDirectorTest { sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000)); vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); - assertVoteForRefreshRate(vote, 60 /*fps*/); + assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/); vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); assertThat(vote).isNotNull(); assertThat(vote.disableRefreshRateSwitching).isTrue(); @@ -923,12 +1104,17 @@ public class DisplayModeDirectorTest { } @Test - public void testAppRequestMinRefreshRate() { + @Parameters({ + "true", + "false" + }) + public void testAppRequestMinRefreshRate(boolean frameRateIsRefreshRate) { // Confirm that the app min request range doesn't include flicker or min refresh rate // settings but does include everything else. - assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE + assertTrue(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); Display.Mode[] modes = new Display.Mode[3]; modes[0] = new Display.Mode( /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60); @@ -942,38 +1128,42 @@ public class DisplayModeDirectorTest { SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); - votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); - assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f); + assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f); - votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, - Vote.forRefreshRates(90, Float.POSITIVE_INFINITY)); + votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(90, Float.POSITIVE_INFINITY)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f); - assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); - assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.physical.max).isAtLeast(90f); + assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f); + assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f); - votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE, - Vote.forRefreshRates(75, Float.POSITIVE_INFINITY)); + votes.put(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE, + Vote.forRenderFrameRates(75, Float.POSITIVE_INFINITY)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(desiredSpecs.appRequestRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75); - assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(75); + assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f); } @Test - public void testAppRequestMaxRefreshRate() { + @Parameters({ + "true", + "false" + }) + public void testAppRequestMaxRefreshRate(boolean frameRateIsRefreshRate) { // Confirm that the app max request range doesn't include flicker or min refresh rate // settings but does include everything else. - assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE + assertTrue(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); Display.Mode[] modes = new Display.Mode[3]; @@ -984,47 +1174,85 @@ public class DisplayModeDirectorTest { modes[2] = new Display.Mode( /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90); + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); DisplayModeDirector director = createDirectorFromModeArray(modes, modes[1]); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); - votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); - assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60); + } else { + assertThat(desiredSpecs.primary.render.min).isZero(); + } + assertThat(desiredSpecs.primary.render.max).isAtMost(60); + assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f); + assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.appRequest.render.min).isAtMost(60f); + } else { + assertThat(desiredSpecs.appRequest.render.min).isZero(); + } + assertThat(desiredSpecs.appRequest.render.max).isAtLeast(90f); - votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, - Vote.forRefreshRates(90, Float.POSITIVE_INFINITY)); + votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(90, Float.POSITIVE_INFINITY)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f); - assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); - assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); - - votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE, Vote.forRefreshRates(0, 75)); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.render.max).isAtLeast(90f); + assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f); + assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f); + assertThat(desiredSpecs.appRequest.render.min).isAtMost(60f); + assertThat(desiredSpecs.appRequest.render.max).isAtLeast(90f); + + votes.put(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE, + Vote.forRenderFrameRates(0, 75)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75); - assertThat(desiredSpecs.appRequestRefreshRateRange.min).isZero(); - assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(75); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(75); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(75); + } else { + assertThat(desiredSpecs.primary.render.min).isZero(); + } + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(75); + assertThat(desiredSpecs.appRequest.physical.min).isZero(); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of( + 75); + } else { + assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f); + } + assertThat(desiredSpecs.appRequest.render.min).isZero(); + assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(75); } @Test - public void testAppRequestObserver_modeId() { + @Parameters({ + "true", + "false" + }) + public void testAppRequestObserver_modeId(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); DisplayModeDirector director = createDirectorFromFpsRange(60, 90); director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0, 0); Vote appRequestRefreshRate = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); assertNotNull(appRequestRefreshRate); - assertThat(appRequestRefreshRate.refreshRateRange.min).isZero(); - assertThat(appRequestRefreshRate.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestRefreshRate.refreshRateRanges.physical.min).isZero(); + assertThat(appRequestRefreshRate.refreshRateRanges.physical.max).isPositiveInfinity(); + assertThat(appRequestRefreshRate.refreshRateRanges.render.min).isZero(); + assertThat(appRequestRefreshRate.refreshRateRanges.render.max).isPositiveInfinity(); assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse(); assertThat(appRequestRefreshRate.baseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(60); assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE); @@ -1032,15 +1260,17 @@ public class DisplayModeDirectorTest { Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); assertNotNull(appRequestSize); - assertThat(appRequestSize.refreshRateRange.min).isZero(); - assertThat(appRequestSize.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestSize.refreshRateRanges.physical.min).isZero(); + assertThat(appRequestSize.refreshRateRanges.physical.max).isPositiveInfinity(); + assertThat(appRequestSize.refreshRateRanges.render.min).isZero(); + assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity(); assertThat(appRequestSize.disableRefreshRateSwitching).isFalse(); assertThat(appRequestSize.baseModeRefreshRate).isZero(); assertThat(appRequestSize.height).isEqualTo(1000); assertThat(appRequestSize.width).isEqualTo(1000); Vote appRequestRefreshRateRange = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE); assertNull(appRequestRefreshRateRange); director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 90, 0, 0); @@ -1048,8 +1278,10 @@ public class DisplayModeDirectorTest { appRequestRefreshRate = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); assertNotNull(appRequestRefreshRate); - assertThat(appRequestRefreshRate.refreshRateRange.min).isZero(); - assertThat(appRequestRefreshRate.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestRefreshRate.refreshRateRanges.physical.min).isZero(); + assertThat(appRequestRefreshRate.refreshRateRanges.physical.max).isPositiveInfinity(); + assertThat(appRequestRefreshRate.refreshRateRanges.render.min).isZero(); + assertThat(appRequestRefreshRate.refreshRateRanges.render.max).isPositiveInfinity(); assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse(); assertThat(appRequestRefreshRate.baseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(90); assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE); @@ -1057,18 +1289,25 @@ public class DisplayModeDirectorTest { appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); assertNotNull(appRequestSize); - assertThat(appRequestSize.refreshRateRange.min).isZero(); - assertThat(appRequestSize.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestSize.refreshRateRanges.physical.min).isZero(); + assertThat(appRequestSize.refreshRateRanges.physical.max).isPositiveInfinity(); + assertThat(appRequestSize.refreshRateRanges.render.min).isZero(); + assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity(); assertThat(appRequestSize.height).isEqualTo(1000); assertThat(appRequestSize.width).isEqualTo(1000); appRequestRefreshRateRange = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE); assertNull(appRequestRefreshRateRange); } @Test - public void testAppRequestObserver_minRefreshRate() { + @Parameters({ + "true", + "false" + }) + public void testAppRequestObserver_minRefreshRate(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); DisplayModeDirector director = createDirectorFromFpsRange(60, 90); director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 60, 0); Vote appRequestRefreshRate = @@ -1079,11 +1318,20 @@ public class DisplayModeDirectorTest { assertNull(appRequestSize); Vote appRequestRefreshRateRange = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE); assertNotNull(appRequestRefreshRateRange); - assertThat(appRequestRefreshRateRange.refreshRateRange.min) + if (frameRateIsRefreshRate) { + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min) + .isWithin(FLOAT_TOLERANCE).of(60); + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max).isAtLeast(90); + } else { + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero(); + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max) + .isPositiveInfinity(); + } + assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min) .isWithin(FLOAT_TOLERANCE).of(60); - assertThat(appRequestRefreshRateRange.refreshRateRange.max).isAtLeast(90); + assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max).isAtLeast(90); assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE); assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE); @@ -1096,17 +1344,32 @@ public class DisplayModeDirectorTest { assertNull(appRequestSize); appRequestRefreshRateRange = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE); assertNotNull(appRequestRefreshRateRange); - assertThat(appRequestRefreshRateRange.refreshRateRange.min) + if (frameRateIsRefreshRate) { + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isWithin( + FLOAT_TOLERANCE).of(90); + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max).isAtLeast(90); + } else { + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero(); + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max) + .isPositiveInfinity(); + } + + assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min) .isWithin(FLOAT_TOLERANCE).of(90); - assertThat(appRequestRefreshRateRange.refreshRateRange.max).isAtLeast(90); + assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max).isAtLeast(90); assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE); assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE); } @Test - public void testAppRequestObserver_maxRefreshRate() { + @Parameters({ + "true", + "false" + }) + public void testAppRequestObserver_maxRefreshRate(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); DisplayModeDirector director = createDirectorFromFpsRange(60, 90); director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 0, 90); Vote appRequestRefreshRate = @@ -1117,10 +1380,19 @@ public class DisplayModeDirectorTest { assertNull(appRequestSize); Vote appRequestRefreshRateRange = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE); assertNotNull(appRequestRefreshRateRange); - assertThat(appRequestRefreshRateRange.refreshRateRange.min).isZero(); - assertThat(appRequestRefreshRateRange.refreshRateRange.max) + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero(); + if (frameRateIsRefreshRate) { + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max) + .isWithin(FLOAT_TOLERANCE).of(90); + } else { + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max) + .isPositiveInfinity(); + } + + assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero(); + assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max) .isWithin(FLOAT_TOLERANCE).of(90); assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE); assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE); @@ -1134,10 +1406,19 @@ public class DisplayModeDirectorTest { assertNull(appRequestSize); appRequestRefreshRateRange = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE); assertNotNull(appRequestRefreshRateRange); - assertThat(appRequestRefreshRateRange.refreshRateRange.min).isZero(); - assertThat(appRequestRefreshRateRange.refreshRateRange.max) + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero(); + if (frameRateIsRefreshRate) { + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max) + .isWithin(FLOAT_TOLERANCE).of(60); + } else { + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max) + .isPositiveInfinity(); + } + + assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero(); + assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max) .isWithin(FLOAT_TOLERANCE).of(60); assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE); assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE); @@ -1155,20 +1436,27 @@ public class DisplayModeDirectorTest { assertNull(appRequestSize); Vote appRequestRefreshRateRange = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE); assertNull(appRequestRefreshRateRange); } @Test - public void testAppRequestObserver_modeIdAndRefreshRateRange() { + @Parameters({ + "true", + "false" + }) + public void testAppRequestObserver_modeIdAndRefreshRateRange(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); DisplayModeDirector director = createDirectorFromFpsRange(60, 90); director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90, 90); Vote appRequestRefreshRate = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); assertNotNull(appRequestRefreshRate); - assertThat(appRequestRefreshRate.refreshRateRange.min).isZero(); - assertThat(appRequestRefreshRate.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestRefreshRate.refreshRateRanges.physical.min).isZero(); + assertThat(appRequestRefreshRate.refreshRateRanges.physical.max).isPositiveInfinity(); + assertThat(appRequestRefreshRate.refreshRateRanges.render.min).isZero(); + assertThat(appRequestRefreshRate.refreshRateRanges.render.max).isPositiveInfinity(); assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse(); assertThat(appRequestRefreshRate.baseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(60); assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE); @@ -1177,24 +1465,41 @@ public class DisplayModeDirectorTest { Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); assertNotNull(appRequestSize); - assertThat(appRequestSize.refreshRateRange.min).isZero(); - assertThat(appRequestSize.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestSize.refreshRateRanges.physical.min).isZero(); + assertThat(appRequestSize.refreshRateRanges.physical.max).isPositiveInfinity(); + assertThat(appRequestSize.refreshRateRanges.render.min).isZero(); + assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity(); assertThat(appRequestSize.height).isEqualTo(1000); assertThat(appRequestSize.width).isEqualTo(1000); Vote appRequestRefreshRateRange = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE); assertNotNull(appRequestRefreshRateRange); - assertThat(appRequestRefreshRateRange.refreshRateRange.max) + if (frameRateIsRefreshRate) { + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min) + .isWithin(FLOAT_TOLERANCE).of(90); + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max) + .isWithin(FLOAT_TOLERANCE).of(90); + } else { + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero(); + assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max) + .isPositiveInfinity(); + } + assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min) .isWithin(FLOAT_TOLERANCE).of(90); - assertThat(appRequestRefreshRateRange.refreshRateRange.max) + assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max) .isWithin(FLOAT_TOLERANCE).of(90); assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE); assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE); } @Test - public void testAppRequestsIsTheDefaultMode() { + @Parameters({ + "true", + "false" + }) + public void testAppRequestsIsTheDefaultMode(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); Display.Mode[] modes = new Display.Mode[2]; modes[0] = new Display.Mode( /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60); @@ -1204,8 +1509,8 @@ public class DisplayModeDirectorTest { DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(1); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isAtMost(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90); + assertThat(desiredSpecs.primary.physical.min).isAtMost(60); + assertThat(desiredSpecs.primary.physical.max).isAtLeast(90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); @@ -1214,105 +1519,148 @@ public class DisplayModeDirectorTest { votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), - appRequestedMode.getPhysicalHeight())); + appRequestedMode.getPhysicalHeight())); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(2); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isAtMost(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90); + assertThat(desiredSpecs.primary.physical.min).isAtMost(60); + assertThat(desiredSpecs.primary.physical.max).isAtLeast(90); } @Test - public void testDisableRefreshRateSwitchingVote() { + @Parameters({ + "true", + "false" + }) + public void testDisableRefreshRateSwitchingVote(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); DisplayModeDirector director = createDirectorFromFpsRange(50, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); - votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, - Vote.forRefreshRates(90, Float.POSITIVE_INFINITY)); + votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(90, Float.POSITIVE_INFINITY)); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(50); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(50); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(50); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(50); assertThat(desiredSpecs.baseModeId).isEqualTo(50); votes.clear(); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, - Vote.forRefreshRates(70, Float.POSITIVE_INFINITY)); - votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, - Vote.forRefreshRates(80, Float.POSITIVE_INFINITY)); + Vote.forPhysicalRefreshRates(70, Float.POSITIVE_INFINITY)); + votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(80, Float.POSITIVE_INFINITY)); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 90)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 90)); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(80); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(80); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(80); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(80); assertThat(desiredSpecs.baseModeId).isEqualTo(80); votes.clear(); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, - Vote.forRefreshRates(90, Float.POSITIVE_INFINITY)); - votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, - Vote.forRefreshRates(80, Float.POSITIVE_INFINITY)); + Vote.forPhysicalRefreshRates(90, Float.POSITIVE_INFINITY)); + votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(80, Float.POSITIVE_INFINITY)); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 90)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 90)); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90); assertThat(desiredSpecs.baseModeId).isEqualTo(90); } @Test - public void testBaseModeIdInPrimaryRange() { + @Parameters({ + "true", + "false" + }) + public void testBaseModeIdInPrimaryRange(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); DisplayModeDirector director = createDirectorFromFpsRange(50, 90); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(70)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.baseModeId).isEqualTo(50); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.baseModeId).isEqualTo(50); + } else { + assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity(); + assertThat(desiredSpecs.baseModeId).isEqualTo(70); + } + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60); votes.clear(); votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(55)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60); + } else { + assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity(); + } + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60); assertThat(desiredSpecs.baseModeId).isEqualTo(55); votes.clear(); - votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE, Vote.forRefreshRates(0, 52)); + votes.put(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE, + Vote.forRenderFrameRates(0, 52)); votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(55)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60); + } else { + assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity(); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(52); + } + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0); assertThat(desiredSpecs.baseModeId).isEqualTo(55); votes.clear(); - votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE, Vote.forRefreshRates(0, 58)); + votes.put(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE, + Vote.forRenderFrameRates(0, 58)); votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(55)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(58); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(58); + } else { + assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity(); + } + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(58); assertThat(desiredSpecs.baseModeId).isEqualTo(55); } @Test - public void testStaleAppVote() { + @Parameters({ + "true", + "false" + }) + public void testStaleAppVote(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); Display.Mode[] modes = new Display.Mode[4]; modes[0] = new Display.Mode( /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60); @@ -1358,9 +1706,124 @@ public class DisplayModeDirectorTest { } @Test - public void testProximitySensorVoting() throws Exception { + @Parameters({ + "true", + "false" + }) + public void testRefreshRateIsSubsetOfFrameRate(boolean frameRateIsRefreshRate) { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate); + DisplayModeDirector director = createDirectorFromFpsRange(60, 120); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(DISPLAY_ID, votes); + + + votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(90, 120)); + director.injectVotesByDisplay(votesByDisplay); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90); + } else { + assertThat(desiredSpecs.appRequest.render.min).isZero(); + } + assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120); + + votes.clear(); + votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(90, 120)); + votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(0, 60)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of( + 120); + } else { + assertThat(desiredSpecs.appRequest.render.min).isZero(); + assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60); + } + + votes.clear(); + votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(90, 120)); + votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(60, 60)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of( + 120); + } else { + assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60); + } + + votes.clear(); + votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(90, 120)); + votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, + Vote.forRenderFrameRates(140, 140)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120); + if (frameRateIsRefreshRate) { + assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90); + } else { + assertThat(desiredSpecs.appRequest.render.min).isZero(); + } + assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120); + } + + @Test + public void testRenderFrameRateIsAchievableByPhysicalRefreshRate() { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(false); + DisplayModeDirector director = createDirectorFromFpsRange(60, 120); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(DISPLAY_ID, votes); + + + votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(120, 120)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90)); + director.injectVotesByDisplay(votesByDisplay); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(120); + assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120); + assertThat(desiredSpecs.appRequest.render.min).isZero(); + assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120); + } + + @Test + public void testRenderFrameRateIsDroppedIfLowerPriorityThenBaseModeRefreshRate() { + when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(false); + DisplayModeDirector director = createDirectorFromFpsRange(60, 120); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(DISPLAY_ID, votes); + votes.put(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE, + Vote.forRenderFrameRates(120, 120)); + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(90)); + votes.put(Vote.PRIORITY_PROXIMITY, Vote.forPhysicalRefreshRates(60, 120)); + director.injectVotesByDisplay(votesByDisplay); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(120); + assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0); + assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(120); + assertThat(desiredSpecs.baseModeId).isEqualTo(90); + } + + @Test + public void testProximitySensorVoting() { DisplayModeDirector director = - createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + createDirectorFromRefreshRateArray(new float[]{60.f, 90.f}, 0); director.start(createMockSensorManager()); ArgumentCaptor<ProximityActiveListener> ProximityCaptor = @@ -1389,7 +1852,7 @@ public class DisplayModeDirectorTest { // Set the proximity to active and verify that we added a vote. proximityListener.onProximityActive(true); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); - assertVoteForRefreshRate(vote, 60.f); + assertVoteForPhysicalRefreshRate(vote, 60.f); // Set the display state to doze and verify that the vote is gone when(mInjector.isDozeState(any(Display.class))).thenReturn(true); @@ -1401,7 +1864,7 @@ public class DisplayModeDirectorTest { when(mInjector.isDozeState(any(Display.class))).thenReturn(false); displayListener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); - assertVoteForRefreshRate(vote, 60.f); + assertVoteForPhysicalRefreshRate(vote, 60.f); // Set the display state to doze and verify that the vote is gone when(mInjector.isDozeState(any(Display.class))).thenReturn(true); @@ -1412,7 +1875,7 @@ public class DisplayModeDirectorTest { // Remove the display to cause the doze state to be removed displayListener.onDisplayRemoved(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); - assertVoteForRefreshRate(vote, 60.f); + assertVoteForPhysicalRefreshRate(vote, 60.f); // Turn prox off and verify vote is gone. proximityListener.onProximityActive(false); @@ -1456,7 +1919,7 @@ public class DisplayModeDirectorTest { BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); - assertVoteForRefreshRate(vote, hbmRefreshRate); + assertVoteForPhysicalRefreshRate(vote, hbmRefreshRate); // Turn on HBM, with brightness below the HBM range when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( @@ -1483,7 +1946,7 @@ public class DisplayModeDirectorTest { BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); - assertVoteForRefreshRate(vote, hbmRefreshRate); + assertVoteForPhysicalRefreshRate(vote, hbmRefreshRate); // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( @@ -1579,7 +2042,7 @@ public class DisplayModeDirectorTest { TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); - assertVoteForRefreshRate(vote, initialRefreshRate); + assertVoteForPhysicalRefreshRate(vote, initialRefreshRate); // Change refresh rate vote value through DeviceConfig, ensure it takes precedence final int updatedRefreshRate = 90; @@ -1589,7 +2052,7 @@ public class DisplayModeDirectorTest { assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight()) .isEqualTo(updatedRefreshRate); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); - assertVoteForRefreshRate(vote, updatedRefreshRate); + assertVoteForPhysicalRefreshRate(vote, updatedRefreshRate); // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( @@ -1605,7 +2068,7 @@ public class DisplayModeDirectorTest { TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); - assertVoteForRefreshRate(vote, updatedRefreshRate); + assertVoteForPhysicalRefreshRate(vote, updatedRefreshRate); // Reset DeviceConfig refresh rate, ensure vote falls back to the initial value mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(0); @@ -1613,7 +2076,7 @@ public class DisplayModeDirectorTest { waitForIdleSync(); assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight()).isEqualTo(0); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); - assertVoteForRefreshRate(vote, initialRefreshRate); + assertVoteForPhysicalRefreshRate(vote, initialRefreshRate); // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( @@ -1693,7 +2156,7 @@ public class DisplayModeDirectorTest { TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); - assertVoteForRefreshRate(vote, 60.0f); + assertVoteForPhysicalRefreshRate(vote, 60.0f); // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( @@ -1741,7 +2204,7 @@ public class DisplayModeDirectorTest { if (Float.isNaN(rr)) { assertNull(vote); } else { - assertVoteForRefreshRate(vote, rr); + assertVoteForPhysicalRefreshRate(vote, rr); } } @@ -1817,7 +2280,7 @@ public class DisplayModeDirectorTest { TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); - assertVoteForRefreshRate(vote, 60.f); + assertVoteForPhysicalRefreshRate(vote, 60.f); // Turn off HBM listener.onDisplayRemoved(DISPLAY_ID); @@ -1845,7 +2308,7 @@ public class DisplayModeDirectorTest { // Set the skin temperature to critical and verify that we added a vote. listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL)); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_SKIN_TEMPERATURE); - assertVoteForRefreshRateRange(vote, 0f, 60.f); + assertVoteForRenderFrameRateRange(vote, 0f, 60.f); // Set the skin temperature to severe and verify that the vote is gone. listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_SEVERE)); @@ -1871,18 +2334,18 @@ public class DisplayModeDirectorTest { return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status); } - private void assertVoteForRefreshRate(Vote vote, float refreshRate) { + private void assertVoteForPhysicalRefreshRate(Vote vote, float refreshRate) { assertThat(vote).isNotNull(); final RefreshRateRange expectedRange = new RefreshRateRange(refreshRate, refreshRate); - assertThat(vote.refreshRateRange).isEqualTo(expectedRange); + assertThat(vote.refreshRateRanges.physical).isEqualTo(expectedRange); } - private void assertVoteForRefreshRateRange( - Vote vote, float refreshRateLow, float refreshRateHigh) { + private void assertVoteForRenderFrameRateRange( + Vote vote, float frameRateLow, float frameRateHigh) { assertThat(vote).isNotNull(); final RefreshRateRange expectedRange = - new RefreshRateRange(refreshRateLow, refreshRateHigh); - assertThat(vote.refreshRateRange).isEqualTo(expectedRange); + new RefreshRateRange(frameRateLow, frameRateHigh); + assertThat(vote.refreshRateRanges.render).isEqualTo(expectedRange); } public static class FakeDeviceConfig extends FakeDeviceConfigInterface { @@ -2083,6 +2546,11 @@ public class DisplayModeDirectorTest { return null; } + @Override + public boolean renderFrameRateIsPhysicalRefreshRate() { + return true; + } + void notifyPeakRefreshRateChanged() { if (mPeakRefreshRateObserver != null) { mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/, |