Avoid changing resolution by display mode when animating
There was a logic to use default refresh rate when animating (e.g. default is high refresh rate, but app requested low refresh rate). But since display mode can also affect display size, if the mode triggers the size change when animating, the animation may be interrupted or canceled. So it is better to keep the current mode, and the mode will be updated on the window traversal after the animation is finished. Also do not set display properties when display is seamless rotating, otherwise there will be obvious flickering because there won't be screenshot to cover the resolution change. Fixes: 238771010 Bug: 255850221 Bug: 267365057 Test: RefreshRatePolicyTest#testAnimatingAppOverridePreferredModeId Change-Id: I5b3ddbc8278a95b4c5239812b2c4a293775c740a
This commit is contained in:
@@ -2163,6 +2163,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
|
w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
|
||||||
}, true /* traverseTopToBottom */);
|
}, true /* traverseTopToBottom */);
|
||||||
mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation);
|
mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation);
|
||||||
|
if (!mDisplayRotation.hasSeamlessRotatingWindow()) {
|
||||||
|
// Make sure DisplayRotation#isRotatingSeamlessly() will return false.
|
||||||
|
mDisplayRotation.cancelSeamlessRotation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mWmService.mDisplayManagerInternal.performTraversal(transaction);
|
mWmService.mDisplayManagerInternal.performTraversal(transaction);
|
||||||
@@ -4933,7 +4937,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
|
mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
|
||||||
|
|
||||||
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
|
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
|
||||||
if (!mWmService.mDisplayFrozen) {
|
if (!mWmService.mDisplayFrozen && !mDisplayRotation.isRotatingSeamlessly()) {
|
||||||
mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
|
mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
|
||||||
mLastHasContent,
|
mLastHasContent,
|
||||||
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
|
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ class RefreshRatePolicy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final DisplayInfo mDisplayInfo;
|
||||||
|
private final Mode mDefaultMode;
|
||||||
private final Mode mLowRefreshRateMode;
|
private final Mode mLowRefreshRateMode;
|
||||||
private final PackageRefreshRate mNonHighRefreshRatePackages = new PackageRefreshRate();
|
private final PackageRefreshRate mNonHighRefreshRatePackages = new PackageRefreshRate();
|
||||||
private final HighRefreshRateDenylist mHighRefreshRateDenylist;
|
private final HighRefreshRateDenylist mHighRefreshRateDenylist;
|
||||||
@@ -89,7 +91,9 @@ class RefreshRatePolicy {
|
|||||||
|
|
||||||
RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
|
RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
|
||||||
HighRefreshRateDenylist denylist) {
|
HighRefreshRateDenylist denylist) {
|
||||||
mLowRefreshRateMode = findLowRefreshRateMode(displayInfo);
|
mDisplayInfo = displayInfo;
|
||||||
|
mDefaultMode = displayInfo.getDefaultMode();
|
||||||
|
mLowRefreshRateMode = findLowRefreshRateMode(displayInfo, mDefaultMode);
|
||||||
mHighRefreshRateDenylist = denylist;
|
mHighRefreshRateDenylist = denylist;
|
||||||
mWmService = wmService;
|
mWmService = wmService;
|
||||||
}
|
}
|
||||||
@@ -98,10 +102,9 @@ class RefreshRatePolicy {
|
|||||||
* Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the
|
* Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the
|
||||||
* default mode.
|
* default mode.
|
||||||
*/
|
*/
|
||||||
private Mode findLowRefreshRateMode(DisplayInfo displayInfo) {
|
private Mode findLowRefreshRateMode(DisplayInfo displayInfo, Mode defaultMode) {
|
||||||
Mode mode = displayInfo.getDefaultMode();
|
|
||||||
float[] refreshRates = displayInfo.getDefaultRefreshRates();
|
float[] refreshRates = displayInfo.getDefaultRefreshRates();
|
||||||
float bestRefreshRate = mode.getRefreshRate();
|
float bestRefreshRate = defaultMode.getRefreshRate();
|
||||||
mMinSupportedRefreshRate = bestRefreshRate;
|
mMinSupportedRefreshRate = bestRefreshRate;
|
||||||
mMaxSupportedRefreshRate = bestRefreshRate;
|
mMaxSupportedRefreshRate = bestRefreshRate;
|
||||||
for (int i = refreshRates.length - 1; i >= 0; i--) {
|
for (int i = refreshRates.length - 1; i >= 0; i--) {
|
||||||
@@ -127,13 +130,39 @@ class RefreshRatePolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int getPreferredModeId(WindowState w) {
|
int getPreferredModeId(WindowState w) {
|
||||||
// If app is animating, it's not able to control refresh rate because we want the animation
|
final int preferredDisplayModeId = w.mAttrs.preferredDisplayModeId;
|
||||||
// to run in default refresh rate.
|
if (preferredDisplayModeId <= 0) {
|
||||||
if (w.isAnimating(TRANSITION | PARENTS)) {
|
// Unspecified, use default mode.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return w.mAttrs.preferredDisplayModeId;
|
// If app is animating, it's not able to control refresh rate because we want the animation
|
||||||
|
// to run in default refresh rate. But if the display size of default mode is different
|
||||||
|
// from the using preferred mode, then still keep the preferred mode to avoid disturbing
|
||||||
|
// the animation.
|
||||||
|
if (w.isAnimating(TRANSITION | PARENTS)) {
|
||||||
|
Display.Mode preferredMode = null;
|
||||||
|
for (Display.Mode mode : mDisplayInfo.supportedModes) {
|
||||||
|
if (preferredDisplayModeId == mode.getModeId()) {
|
||||||
|
preferredMode = mode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (preferredMode != null) {
|
||||||
|
final int pW = preferredMode.getPhysicalWidth();
|
||||||
|
final int pH = preferredMode.getPhysicalHeight();
|
||||||
|
if ((pW != mDefaultMode.getPhysicalWidth()
|
||||||
|
|| pH != mDefaultMode.getPhysicalHeight())
|
||||||
|
&& pW == mDisplayInfo.getNaturalWidth()
|
||||||
|
&& pH == mDisplayInfo.getNaturalHeight()) {
|
||||||
|
// Prefer not to change display size when animating.
|
||||||
|
return preferredDisplayModeId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return preferredDisplayModeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -234,14 +263,10 @@ class RefreshRatePolicy {
|
|||||||
if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) {
|
if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) {
|
||||||
final int preferredModeId = w.mAttrs.preferredDisplayModeId;
|
final int preferredModeId = w.mAttrs.preferredDisplayModeId;
|
||||||
if (preferredModeId > 0) {
|
if (preferredModeId > 0) {
|
||||||
DisplayInfo info = w.getDisplayInfo();
|
for (Display.Mode mode : mDisplayInfo.supportedModes) {
|
||||||
if (info != null) {
|
|
||||||
for (Display.Mode mode : info.supportedModes) {
|
|
||||||
if (preferredModeId == mode.getModeId()) {
|
if (preferredModeId == mode.getModeId()) {
|
||||||
return w.mFrameRateVote.update(mode.getRefreshRate(),
|
return w.mFrameRateVote.update(mode.getRefreshRate(),
|
||||||
Surface.FRAME_RATE_COMPATIBILITY_EXACT);
|
Surface.FRAME_RATE_COMPATIBILITY_EXACT);
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITIO
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.hardware.display.DisplayManager;
|
import android.hardware.display.DisplayManager;
|
||||||
@@ -289,6 +291,14 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
|
|||||||
assertEquals(FRAME_RATE_VOTE_NONE, overrideWindow.mFrameRateVote);
|
assertEquals(FRAME_RATE_VOTE_NONE, overrideWindow.mFrameRateVote);
|
||||||
assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
|
assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
|
||||||
assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
|
assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
|
||||||
|
|
||||||
|
// If there will be display size change when switching from preferred mode to default mode,
|
||||||
|
// then keep the current preferred mode during animating.
|
||||||
|
mDisplayInfo = spy(mDisplayInfo);
|
||||||
|
final Mode defaultMode = new Mode(4321 /* width */, 1234 /* height */, LOW_REFRESH_RATE);
|
||||||
|
doReturn(defaultMode).when(mDisplayInfo).getDefaultMode();
|
||||||
|
mPolicy = new RefreshRatePolicy(mWm, mDisplayInfo, mDenylist);
|
||||||
|
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user