Use setFrameRate() instead of preferredDisplayModeId if the modes differ only by refresh rate

This seems to avoid a bug on the Chromecast 4K where it can decide to switch to 4K24 instead of 4K60

Fixes #1151
This commit is contained in:
Cameron Gutman 2022-12-14 23:05:22 -06:00
parent 2d7493fd1e
commit 970423f873

View File

@ -809,6 +809,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
boolean refreshRateIsGood = isRefreshRateGoodMatch(bestMode.getRefreshRate()); boolean refreshRateIsGood = isRefreshRateGoodMatch(bestMode.getRefreshRate());
boolean refreshRateIsEqual = isRefreshRateEqualMatch(bestMode.getRefreshRate()); boolean refreshRateIsEqual = isRefreshRateEqualMatch(bestMode.getRefreshRate());
LimeLog.info("Current display mode: "+bestMode.getPhysicalWidth()+"x"+
bestMode.getPhysicalHeight()+"x"+bestMode.getRefreshRate());
for (Display.Mode candidate : display.getSupportedModes()) { for (Display.Mode candidate : display.getSupportedModes()) {
boolean refreshRateReduced = candidate.getRefreshRate() < bestMode.getRefreshRate(); boolean refreshRateReduced = candidate.getRefreshRate() < bestMode.getRefreshRate();
boolean resolutionReduced = candidate.getPhysicalWidth() < bestMode.getPhysicalWidth() || boolean resolutionReduced = candidate.getPhysicalWidth() < bestMode.getPhysicalWidth() ||
@ -885,9 +888,30 @@ public class Game extends Activity implements SurfaceHolder.Callback,
refreshRateIsGood = isRefreshRateGoodMatch(candidate.getRefreshRate()); refreshRateIsGood = isRefreshRateGoodMatch(candidate.getRefreshRate());
refreshRateIsEqual = isRefreshRateEqualMatch(candidate.getRefreshRate()); refreshRateIsEqual = isRefreshRateEqualMatch(candidate.getRefreshRate());
} }
LimeLog.info("Selected display mode: "+bestMode.getPhysicalWidth()+"x"+
LimeLog.info("Best display mode: "+bestMode.getPhysicalWidth()+"x"+
bestMode.getPhysicalHeight()+"x"+bestMode.getRefreshRate()); bestMode.getPhysicalHeight()+"x"+bestMode.getRefreshRate());
// Only apply new window layout parameters if we've actually changed the display mode
if (display.getMode().getModeId() != bestMode.getModeId()) {
// If we only changed refresh rate and we're on an OS that supports Surface.setFrameRate()
// use that instead of using preferredDisplayModeId to avoid the possibility of triggering
// bugs that can cause the system to switch from 4K60 to 4K24 on Chromecast 4K.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S ||
display.getMode().getPhysicalWidth() != bestMode.getPhysicalWidth() ||
display.getMode().getPhysicalHeight() != bestMode.getPhysicalHeight()) {
// Apply the display mode change
windowLayoutParams.preferredDisplayModeId = bestMode.getModeId(); windowLayoutParams.preferredDisplayModeId = bestMode.getModeId();
getWindow().setAttributes(windowLayoutParams);
}
else {
LimeLog.info("Using setFrameRate() instead of preferredDisplayModeId due to matching resolution");
}
}
else {
LimeLog.info("Current display mode is already the best display mode");
}
displayRefreshRate = bestMode.getRefreshRate(); displayRefreshRate = bestMode.getRefreshRate();
} }
// On L, we can at least tell the OS that we want a refresh rate // On L, we can at least tell the OS that we want a refresh rate
@ -907,9 +931,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
bestRefreshRate = candidate; bestRefreshRate = candidate;
} }
} }
LimeLog.info("Selected refresh rate: "+bestRefreshRate); LimeLog.info("Selected refresh rate: "+bestRefreshRate);
windowLayoutParams.preferredRefreshRate = bestRefreshRate; windowLayoutParams.preferredRefreshRate = bestRefreshRate;
displayRefreshRate = bestRefreshRate; displayRefreshRate = bestRefreshRate;
// Apply the refresh rate change
getWindow().setAttributes(windowLayoutParams);
} }
else { else {
// Otherwise, the active display refresh rate is just // Otherwise, the active display refresh rate is just
@ -917,14 +945,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
displayRefreshRate = display.getRefreshRate(); displayRefreshRate = display.getRefreshRate();
} }
// Enable HDMI ALLM (game mode) on Android R
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
windowLayoutParams.preferMinimalPostProcessing = true;
}
// Apply the display mode change
getWindow().setAttributes(windowLayoutParams);
// From 4.4 to 5.1 we can't ask for a 4K display mode, so we'll // From 4.4 to 5.1 we can't ask for a 4K display mode, so we'll
// need to hint the OS to provide one. // need to hint the OS to provide one.
boolean aspectRatioMatch = false; boolean aspectRatioMatch = false;
@ -2079,8 +2099,16 @@ public class Game extends Activity implements SurfaceHolder.Callback,
public void surfaceCreated(SurfaceHolder holder) { public void surfaceCreated(SurfaceHolder holder) {
surfaceCreated = true; surfaceCreated = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// Tell the OS about our frame rate to allow it to adapt the display refresh rate appropriately // Tell the OS about our frame rate to allow it to adapt the display refresh rate appropriately
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// We want to change frame rate even if it's not seamless, since prepareDisplayForRendering()
// will not set the display mode on S+ if it only differs by the refresh rate. It depends
// on us to trigger the frame rate switch here.
holder.getSurface().setFrameRate(prefConfig.fps,
Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
Surface.CHANGE_FRAME_RATE_ALWAYS);
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
holder.getSurface().setFrameRate(prefConfig.fps, Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE); holder.getSurface().setFrameRate(prefConfig.fps, Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
} }
} }