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

This commit is contained in:
Ady Abraham
2022-10-24 17:46:26 +00:00
committed by Android (Google) Code Review
9 changed files with 1326 additions and 541 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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