DisplayModeDirector: simplify base mode selection

Add some documentation and mode some code to better explain
how the APP_REQUEST_BASE_MODE_REFRESH_RATE is used.

Bug: 241460058
Test: atest DisplayModeDirectorTest
Change-Id: Ic015270a48d55e37d6da17628f528398af12cd6d
This commit is contained in:
Ady Abraham
2022-10-21 16:12:11 -07:00
parent b878a206e8
commit 3d7b3ff206
2 changed files with 70 additions and 54 deletions

View File

@@ -263,7 +263,7 @@ public class DisplayModeDirector {
public int width;
public int height;
public boolean disableRefreshRateSwitching;
public float baseModeRefreshRate;
public float appRequestBaseModeRefreshRate;
VoteSummary() {
reset();
@@ -277,7 +277,7 @@ public class DisplayModeDirector {
width = Vote.INVALID_SIZE;
height = Vote.INVALID_SIZE;
disableRefreshRateSwitching = false;
baseModeRefreshRate = 0f;
appRequestBaseModeRefreshRate = 0f;
}
}
@@ -326,8 +326,9 @@ public class DisplayModeDirector {
if (!summary.disableRefreshRateSwitching && vote.disableRefreshRateSwitching) {
summary.disableRefreshRateSwitching = true;
}
if (summary.baseModeRefreshRate == 0f && vote.baseModeRefreshRate > 0f) {
summary.baseModeRefreshRate = vote.baseModeRefreshRate;
if (summary.appRequestBaseModeRefreshRate == 0f
&& vote.appRequestBaseModeRefreshRate > 0f) {
summary.appRequestBaseModeRefreshRate = vote.appRequestBaseModeRefreshRate;
}
if (mLoggingEnabled) {
@@ -341,11 +342,41 @@ public class DisplayModeDirector {
+ ", maxRenderFrameRate=" + summary.maxRenderFrameRate
+ ", disableRefreshRateSwitching="
+ summary.disableRefreshRateSwitching
+ ", baseModeRefreshRate=" + summary.baseModeRefreshRate);
+ ", appRequestBaseModeRefreshRate="
+ summary.appRequestBaseModeRefreshRate);
}
}
}
private boolean equalsWithinFloatTolerance(float a, float b) {
return a >= b - FLOAT_TOLERANCE && a <= b + FLOAT_TOLERANCE;
}
private Display.Mode selectBaseMode(VoteSummary summary,
ArrayList<Display.Mode> availableModes, Display.Mode defaultMode) {
// The base mode should be as close as possible to the app requested mode. Since all the
// available modes already have the same size, we just need to look for a matching refresh
// rate. If the summary doesn't include an app requested refresh rate, we'll use the default
// mode refresh rate. This is important because SurfaceFlinger can do only seamless switches
// by default. Some devices (e.g. TV) don't support seamless switching so the mode we select
// here won't be changed.
float preferredRefreshRate =
summary.appRequestBaseModeRefreshRate > 0
? summary.appRequestBaseModeRefreshRate : defaultMode.getRefreshRate();
for (Display.Mode availableMode : availableModes) {
if (equalsWithinFloatTolerance(preferredRefreshRate, availableMode.getRefreshRate())) {
return availableMode;
}
}
// If we couldn't find a mode id based on the refresh rate, it means that the available
// modes were filtered by the app requested size, which is different that the default mode
// size, and the requested app refresh rate was dropped from the summary due to a higher
// priority vote. Since we don't have any other hint about the refresh rate,
// we just pick the first.
return !availableModes.isEmpty() ? availableModes.get(0) : null;
}
/**
* Calculates the refresh rate ranges and display modes that the system is allowed to freely
* switch between based on global and display-specific constraints.
@@ -410,7 +441,8 @@ public class DisplayModeDirector {
+ ", maxRenderFrameRate=" + primarySummary.maxRenderFrameRate
+ ", disableRefreshRateSwitching="
+ primarySummary.disableRefreshRateSwitching
+ ", baseModeRefreshRate=" + primarySummary.baseModeRefreshRate);
+ ", appRequestBaseModeRefreshRate="
+ primarySummary.appRequestBaseModeRefreshRate);
}
break;
}
@@ -427,7 +459,8 @@ public class DisplayModeDirector {
+ ", maxRenderFrameRate=" + primarySummary.maxRenderFrameRate
+ ", disableRefreshRateSwitching="
+ primarySummary.disableRefreshRateSwitching
+ ", baseModeRefreshRate=" + primarySummary.baseModeRefreshRate);
+ ", appRequestBaseModeRefreshRate="
+ primarySummary.appRequestBaseModeRefreshRate);
}
// If we haven't found anything with the current set of votes, drop the
@@ -479,38 +512,7 @@ public class DisplayModeDirector {
+ "]");
}
// 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
>= availableMode.getRefreshRate() - FLOAT_TOLERANCE
&& primarySummary.baseModeRefreshRate
<= availableMode.getRefreshRate() + FLOAT_TOLERANCE) {
baseMode = availableMode;
}
}
// Select the default mode if available. This is important because SurfaceFlinger
// can do only seamless switches by default. Some devices (e.g. TV) don't support
// seamless switching so the mode we select here won't be changed.
if (baseMode == null) {
for (Display.Mode availableMode : availableModes) {
if (availableMode.getModeId() == defaultMode.getModeId()) {
baseMode = defaultMode;
break;
}
}
}
// If the application requests a display mode by setting
// LayoutParams.preferredDisplayModeId, it will be the only available mode and it'll
// be stored as baseModeId.
if (baseMode == null && !availableModes.isEmpty()) {
baseMode = availableModes.get(0);
}
Display.Mode baseMode = selectBaseMode(primarySummary, availableModes, defaultMode);
if (baseMode == null) {
Slog.w(TAG, "Can't find a set of allowed modes which satisfies the votes. Falling"
+ " back to the default mode. Display = " + displayId + ", votes = " + votes
@@ -574,7 +576,7 @@ public class DisplayModeDirector {
}
ArrayList<Display.Mode> availableModes = new ArrayList<>();
boolean missingBaseModeRefreshRate = summary.baseModeRefreshRate > 0f;
boolean missingBaseModeRefreshRate = summary.appRequestBaseModeRefreshRate > 0f;
for (Display.Mode mode : supportedModes) {
if (mode.getPhysicalWidth() != summary.width
|| mode.getPhysicalHeight() != summary.height) {
@@ -626,8 +628,8 @@ public class DisplayModeDirector {
}
availableModes.add(mode);
if (mode.getRefreshRate() >= summary.baseModeRefreshRate - FLOAT_TOLERANCE
&& mode.getRefreshRate() <= summary.baseModeRefreshRate + FLOAT_TOLERANCE) {
if (equalsWithinFloatTolerance(mode.getRefreshRate(),
summary.appRequestBaseModeRefreshRate)) {
missingBaseModeRefreshRate = false;
}
}
@@ -1118,10 +1120,21 @@ public class DisplayModeDirector {
// Application can specify preferred refresh rate with below attrs.
// @see android.view.WindowManager.LayoutParams#preferredRefreshRate
// @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
// These translates into votes for the base mode refresh rate and resolution to be
// used by SurfaceFlinger as the policy of choosing the display mode. The system also
// forces some apps like denylisted app to run at a lower refresh rate.
//
// When the app specifies a LayoutParams#preferredDisplayModeId, in addition to the
// refresh rate, it also chooses a preferred size (resolution) as part of the selected
// mode id. The app preference is then translated to APP_REQUEST_BASE_MODE_REFRESH_RATE and
// optionally to APP_REQUEST_SIZE as well, if a mode id was selected.
// The system also forces some apps like denylisted app to run at a lower refresh rate.
// @see android.R.array#config_highRefreshRateBlacklist
//
// When summarizing the votes and filtering the allowed display modes, these votes determine
// which mode id should be the base mode id to be sent to SurfaceFlinger:
// - APP_REQUEST_BASE_MODE_REFRESH_RATE is used to validate the vote summary. If a summary
// includes a base mode refresh rate, but it is not in the refresh rate range, then the
// summary is considered invalid so we could drop a lower priority vote and try again.
// - APP_REQUEST_SIZE is used to filter out display modes of a different size.
//
// The preferred refresh rate is set on the main surface of the app outside of
// DisplayModeDirector.
// @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
@@ -1189,10 +1202,10 @@ public class DisplayModeDirector {
public final boolean disableRefreshRateSwitching;
/**
* The base mode refresh rate to be used for this display. This would be used when deciding
* the base mode id.
* The preferred refresh rate selected by the app. It is used to validate that the summary
* refresh rate ranges include this value, and are not restricted by a lower priority vote.
*/
public final float baseModeRefreshRate;
public final float appRequestBaseModeRefreshRate;
public static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) {
return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate, 0,
@@ -1237,7 +1250,7 @@ public class DisplayModeDirector {
new RefreshRateRange(minPhysicalRefreshRate, maxPhysicalRefreshRate),
new RefreshRateRange(minRenderFrameRate, maxRenderFrameRate));
this.disableRefreshRateSwitching = disableRefreshRateSwitching;
this.baseModeRefreshRate = baseModeRefreshRate;
this.appRequestBaseModeRefreshRate = baseModeRefreshRate;
}
public static String priorityToString(int priority) {
@@ -1279,7 +1292,7 @@ public class DisplayModeDirector {
+ "width=" + width + ", height=" + height
+ ", refreshRateRanges=" + refreshRateRanges
+ ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
+ ", baseModeRefreshRate=" + baseModeRefreshRate + "}";
+ ", appRequestBaseModeRefreshRate=" + appRequestBaseModeRefreshRate + "}";
}
}

View File

@@ -1254,7 +1254,8 @@ public class DisplayModeDirectorTest {
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.appRequestBaseModeRefreshRate)
.isWithin(FLOAT_TOLERANCE).of(60);
assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
@@ -1265,7 +1266,7 @@ public class DisplayModeDirectorTest {
assertThat(appRequestSize.refreshRateRanges.render.min).isZero();
assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity();
assertThat(appRequestSize.disableRefreshRateSwitching).isFalse();
assertThat(appRequestSize.baseModeRefreshRate).isZero();
assertThat(appRequestSize.appRequestBaseModeRefreshRate).isZero();
assertThat(appRequestSize.height).isEqualTo(1000);
assertThat(appRequestSize.width).isEqualTo(1000);
@@ -1283,7 +1284,8 @@ public class DisplayModeDirectorTest {
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.appRequestBaseModeRefreshRate)
.isWithin(FLOAT_TOLERANCE).of(90);
assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
@@ -1458,7 +1460,8 @@ public class DisplayModeDirectorTest {
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.appRequestBaseModeRefreshRate)
.isWithin(FLOAT_TOLERANCE).of(60);
assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);