From b878a206e8fb976f7ca8b2969a5c2ddb4fc24cc3 Mon Sep 17 00:00:00 2001 From: Ady Abraham Date: Mon, 12 Sep 2022 16:29:24 -0700 Subject: [PATCH] DisplayModeDirector: separate the display refresh rate and the render frame rate Add an additional dimension to DisplayModeDirector for render frame rate in addition to the display physical refresh rate. DisplayModeDirector aggregates the requests for both render frame rates and physical refresh rates, and sets the policy to SurfaceFlinger. SurfaceFlinger will select the display refresh rate based on the physical refresh rate range, and schedule applications based on the render frame rate range. This is a change over the previous logic where the render frame rate and physical refresh rate were always the same for SurfaceFlinger. Test: atest LocalDisplayAdapterTest DisplayModeDirectorTest Bug: 241460058 Change-Id: I9563257c7268a731a3919ffa368f7b8ae777eb99 --- .../display/DisplayManagerInternal.java | 69 +- core/java/android/view/SurfaceControl.java | 218 +++- core/jni/android_view_SurfaceControl.cpp | 156 ++- .../server/display/DisplayManagerService.java | 1 + .../server/display/DisplayModeDirector.java | 396 +++++-- .../server/display/LocalDisplayAdapter.java | 16 +- .../android/server/wm/RefreshRatePolicy.java | 2 +- .../display/LocalDisplayAdapterTest.java | 31 +- .../display/DisplayModeDirectorTest.java | 978 +++++++++++++----- 9 files changed, 1326 insertions(+), 541 deletions(-) diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 00bccc686919c..6b3e67309f220 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; /** @@ -650,72 +649,6 @@ public abstract class DisplayManagerInternal { void onDisplayGroupChanged(int groupId); } - /** - * Information about the min and max refresh rate DM would like to set the display to. - */ - public static final class RefreshRateRange { - public static final String TAG = "RefreshRateRange"; - - // The tolerance within which we consider something approximately equals. - public static final float FLOAT_TOLERANCE = 0.01f; - - /** - * The lowest desired refresh rate. - */ - public float min; - - /** - * The highest desired refresh rate. - */ - public float max; - - public RefreshRateRange() {} - - public RefreshRateRange(float min, float max) { - if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) { - Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : " - + min + " " + max); - this.min = this.max = 0; - return; - } - if (min > max) { - // Min and max are within epsilon of each other, but in the wrong order. - float t = min; - min = max; - max = t; - } - this.min = min; - this.max = max; - } - - /** - * Checks whether the two objects have the same values. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof RefreshRateRange)) { - return false; - } - - RefreshRateRange refreshRateRange = (RefreshRateRange) other; - return (min == refreshRateRange.min && max == refreshRateRange.max); - } - - @Override - public int hashCode() { - return Objects.hash(min, max); - } - - @Override - public String toString() { - return "(" + min + " " + max + ")"; - } - } - /** * Describes a limitation on a display's refresh rate. Includes the allowed refresh rate * range as well as information about when it applies, such as high-brightness-mode. diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index c46f33a4a786c..06851de95f82f 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. @@ -1681,45 +1822,50 @@ public final class SurfaceControl implements Parcelable { */ public static final class DesiredDisplayModeSpecs { public int defaultMode; - /** - * The primary refresh rate range represents display manager's general guidance on the - * display configs surface flinger will consider when switching refresh rates. Unless - * surface flinger has a specific reason to do otherwise, it will stay within this range. - */ - public float primaryRefreshRateMin; - public float primaryRefreshRateMax; - /** - * The app request refresh rate range allows surface flinger to consider more display - * configs when switching refresh rates. Although surface flinger will generally stay within - * the primary range, specific considerations, such as layer frame rate settings specified - * via the setFrameRate() api, may cause surface flinger to go outside the primary - * range. Surface flinger never goes outside the app request range. The app request range - * will be greater than or equal to the primary refresh rate range, never smaller. - */ - public float appRequestRefreshRateMin; - public float appRequestRefreshRateMax; - /** * If true this will allow switching between modes in different display configuration * groups. This way the user may see visual interruptions when the display mode changes. */ public boolean allowGroupSwitching; - public DesiredDisplayModeSpecs() {} + /** + * The primary physical and render refresh rate ranges represent display manager's general + * guidance on the display configs surface flinger will consider when switching refresh + * rates and scheduling the frame rate. Unless surface flinger has a specific reason to do + * otherwise, it will stay within this range. + */ + public final RefreshRateRanges primaryRanges; + + /** + * The app request physical and render refresh rate ranges allow surface flinger to consider + * more display configs when switching refresh rates. Although surface flinger will + * generally stay within the primary range, specific considerations, such as layer frame + * rate settings specified via the setFrameRate() api, may cause surface flinger to go + * outside the primary range. Surface flinger never goes outside the app request range. + * The app request range will be greater than or equal to the primary refresh rate range, + * never smaller. + */ + public final RefreshRateRanges appRequestRanges; + + public DesiredDisplayModeSpecs() { + this.primaryRanges = new RefreshRateRanges(); + this.appRequestRanges = new RefreshRateRanges(); + } public DesiredDisplayModeSpecs(DesiredDisplayModeSpecs other) { + this.primaryRanges = new RefreshRateRanges(); + this.appRequestRanges = new RefreshRateRanges(); copyFrom(other); } public DesiredDisplayModeSpecs(int defaultMode, boolean allowGroupSwitching, - float primaryRefreshRateMin, float primaryRefreshRateMax, - float appRequestRefreshRateMin, float appRequestRefreshRateMax) { + RefreshRateRanges primaryRanges, RefreshRateRanges appRequestRanges) { this.defaultMode = defaultMode; this.allowGroupSwitching = allowGroupSwitching; - this.primaryRefreshRateMin = primaryRefreshRateMin; - this.primaryRefreshRateMax = primaryRefreshRateMax; - this.appRequestRefreshRateMin = appRequestRefreshRateMin; - this.appRequestRefreshRateMax = appRequestRefreshRateMax; + this.primaryRanges = + new RefreshRateRanges(primaryRanges.physical, primaryRanges.render); + this.appRequestRanges = + new RefreshRateRanges(appRequestRanges.physical, appRequestRanges.render); } @Override @@ -1732,10 +1878,9 @@ public final class SurfaceControl implements Parcelable { */ public boolean equals(DesiredDisplayModeSpecs other) { return other != null && defaultMode == other.defaultMode - && primaryRefreshRateMin == other.primaryRefreshRateMin - && primaryRefreshRateMax == other.primaryRefreshRateMax - && appRequestRefreshRateMin == other.appRequestRefreshRateMin - && appRequestRefreshRateMax == other.appRequestRefreshRateMax; + && allowGroupSwitching == other.allowGroupSwitching + && primaryRanges.equals(other.primaryRanges) + && appRequestRanges.equals(other.appRequestRanges); } @Override @@ -1748,18 +1893,17 @@ public final class SurfaceControl implements Parcelable { */ public void copyFrom(DesiredDisplayModeSpecs other) { defaultMode = other.defaultMode; - primaryRefreshRateMin = other.primaryRefreshRateMin; - primaryRefreshRateMax = other.primaryRefreshRateMax; - appRequestRefreshRateMin = other.appRequestRefreshRateMin; - appRequestRefreshRateMax = other.appRequestRefreshRateMax; + allowGroupSwitching = other.allowGroupSwitching; + primaryRanges.copyFrom(other.primaryRanges); + appRequestRanges.copyFrom(other.appRequestRanges); } @Override public String toString() { - return String.format("defaultConfig=%d primaryRefreshRateRange=[%.0f %.0f]" - + " appRequestRefreshRateRange=[%.0f %.0f]", - defaultMode, primaryRefreshRateMin, primaryRefreshRateMax, - appRequestRefreshRateMin, appRequestRefreshRateMax); + return "defaultMode=" + defaultMode + + " allowGroupSwitching=" + allowGroupSwitching + + " primaryRanges=" + primaryRanges + + " appRequestRanges=" + appRequestRanges; } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index e36815c4beb9d..8fd040189f8be 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -187,15 +187,27 @@ static struct { jfieldID white; } gDisplayPrimariesClassInfo; +static struct { + jclass clazz; + jmethodID ctor; + jfieldID min; + jfieldID max; +} gRefreshRateRangeClassInfo; + +static struct { + jclass clazz; + jmethodID ctor; + jfieldID physical; + jfieldID render; +} gRefreshRateRangesClassInfo; + static struct { jclass clazz; jmethodID ctor; jfieldID defaultMode; jfieldID allowGroupSwitching; - jfieldID primaryRefreshRateMin; - jfieldID primaryRefreshRateMax; - jfieldID appRequestRefreshRateMin; - jfieldID appRequestRefreshRateMax; + jfieldID primaryRanges; + jfieldID appRequestRanges; } gDesiredDisplayModeSpecsClassInfo; static struct { @@ -1190,6 +1202,39 @@ static jobject nativeGetDynamicDisplayInfo(JNIEnv* env, jclass clazz, jobject to return object; } +struct RefreshRateRange { + const float min; + const float max; + + RefreshRateRange(float min, float max) : min(min), max(max) {} + + RefreshRateRange(JNIEnv* env, jobject obj) + : min(env->GetFloatField(obj, gRefreshRateRangeClassInfo.min)), + max(env->GetFloatField(obj, gRefreshRateRangeClassInfo.max)) {} + + jobject toJava(JNIEnv* env) const { + return env->NewObject(gRefreshRateRangeClassInfo.clazz, gRefreshRateRangeClassInfo.ctor, + min, max); + } +}; + +struct RefreshRateRanges { + const RefreshRateRange physical; + const RefreshRateRange render; + + RefreshRateRanges(RefreshRateRange physical, RefreshRateRange render) + : physical(physical), render(render) {} + + RefreshRateRanges(JNIEnv* env, jobject obj) + : physical(env, env->GetObjectField(obj, gRefreshRateRangesClassInfo.physical)), + render(env, env->GetObjectField(obj, gRefreshRateRangesClassInfo.render)) {} + + jobject toJava(JNIEnv* env) const { + return env->NewObject(gRefreshRateRangesClassInfo.clazz, gRefreshRateRangesClassInfo.ctor, + physical.toJava(env), render.toJava(env)); + } +}; + static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj, jobject DesiredDisplayModeSpecs) { sp 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, "", "(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, "", + "(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, "", "(IZFFFF)V"); + GetMethodIDOrDie(env, gDesiredDisplayModeSpecsClassInfo.clazz, "", + "(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 dc2dc2abaeeb0..7326d5435c303 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -117,6 +117,7 @@ import android.view.DisplayEventReceiver; import android.view.DisplayInfo; import android.view.Surface; import android.view.SurfaceControl; +import android.view.SurfaceControl.RefreshRateRange; import android.window.DisplayWindowPolicyController; import android.window.ScreenCapture; diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 912b1b2e8da23..22a47393b3d6d 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)); + "App request range: [" + + appRequestSummary.minPhysicalRefreshRate + + " " + + appRequestSummary.maxPhysicalRefreshRate + + "] Frame rate range: [" + + appRequestSummary.minRenderFrameRate + + " " + + appRequestSummary.maxRenderFrameRate + + "]"); } - // Select the base mode id based on the base mode refresh rate, if available, since this - // will be the mode id the app voted for. + // Select the base mode id based on the base mode physical refresh rate, + // if available, since this will be the mode id the + // app voted for. Display.Mode baseMode = null; for (Display.Mode availableMode : availableModes) { if (primarySummary.baseModeRefreshRate @@ -427,18 +517,26 @@ public class DisplayModeDirector { + ", supported modes = " + Arrays.toString(modes)); float fps = defaultMode.getRefreshRate(); + final RefreshRateRange range = new RefreshRateRange(fps, fps); + final RefreshRateRanges ranges = new RefreshRateRanges(range, range); return new DesiredDisplayModeSpecs(defaultMode.getModeId(), /*allowGroupSwitching */ false, - new RefreshRateRange(fps, fps), - new RefreshRateRange(fps, fps)); + ranges, ranges); } if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE || primarySummary.disableRefreshRateSwitching) { float fps = baseMode.getRefreshRate(); - primarySummary.minRefreshRate = primarySummary.maxRefreshRate = fps; + primarySummary.minPhysicalRefreshRate = primarySummary.maxPhysicalRefreshRate = fps; + if (mRenderFrameRateIsPhysicalRefreshRate) { + primarySummary.minRenderFrameRate = primarySummary.maxRenderFrameRate = fps; + } if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) { - appRequestSummary.minRefreshRate = appRequestSummary.maxRefreshRate = fps; + primarySummary.minRenderFrameRate = primarySummary.maxRenderFrameRate = fps; + appRequestSummary.minPhysicalRefreshRate = + appRequestSummary.maxPhysicalRefreshRate = fps; + appRequestSummary.minRenderFrameRate = + appRequestSummary.maxRenderFrameRate = fps; } } @@ -447,15 +545,34 @@ public class DisplayModeDirector { return new DesiredDisplayModeSpecs(baseMode.getModeId(), allowGroupSwitching, - new RefreshRateRange( - primarySummary.minRefreshRate, primarySummary.maxRefreshRate), - new RefreshRateRange( - appRequestSummary.minRefreshRate, appRequestSummary.maxRefreshRate)); + new RefreshRateRanges( + new RefreshRateRange( + primarySummary.minPhysicalRefreshRate, + primarySummary.maxPhysicalRefreshRate), + new RefreshRateRange( + primarySummary.minRenderFrameRate, + primarySummary.maxRenderFrameRate)), + new RefreshRateRanges( + new RefreshRateRange( + appRequestSummary.minPhysicalRefreshRate, + appRequestSummary.maxPhysicalRefreshRate), + new RefreshRateRange( + appRequestSummary.minRenderFrameRate, + appRequestSummary.maxRenderFrameRate))); } } private ArrayList 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 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 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 002209e32304a..2c2075d33bb91 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 f3713eb7f474f..ccc71bb8c537b 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 3866da33a24e6..d41ac7021077d 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 18dd264558ac4..f8dd4436fac04 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 votes = new SparseArray<>(); SparseArray> 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 votes = new SparseArray<>(); SparseArray> 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 votes = new SparseArray<>(); SparseArray> 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 votes = new SparseArray<>(); SparseArray> 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 votes = new SparseArray<>(); SparseArray> 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 votes = new SparseArray<>(); SparseArray> 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> 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 votes = new SparseArray<>(); SparseArray> 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); + 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_REFRESH_RATE_RANGE, Vote.forRefreshRates(0, 75)); + 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 votes = new SparseArray<>(); SparseArray> 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 votes = new SparseArray<>(); SparseArray> 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 votes = new SparseArray<>(); SparseArray> 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 votes = new SparseArray<>(); + SparseArray> 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 votes = new SparseArray<>(); + SparseArray> 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 votes = new SparseArray<>(); + SparseArray> 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 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*/,