Improve display mode selection algorithm

- Allow the refresh rate to drop if it results in a better match for the stream frame rate
- Allow the resolution to drop for > 60 FPS streams to allow matching a higher refresh rate
This commit is contained in:
Cameron Gutman 2020-08-06 20:14:56 -07:00
parent 8f55517236
commit 17c47a15da

View File

@ -616,6 +616,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
} }
} }
private boolean isRefreshRateGoodMatch(float refreshRate) {
return refreshRate >= prefConfig.fps &&
Math.round(refreshRate) % prefConfig.fps <= 3;
}
private float prepareDisplayForRendering() { private float prepareDisplayForRendering() {
Display display = getWindowManager().getDefaultDisplay(); Display display = getWindowManager().getDefaultDisplay();
WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes(); WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes();
@ -624,41 +629,59 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// On M, we can explicitly set the optimal display mode // On M, we can explicitly set the optimal display mode
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Display.Mode bestMode = display.getMode(); Display.Mode bestMode = display.getMode();
boolean refreshRateIsGood = isRefreshRateGoodMatch(bestMode.getRefreshRate());
for (Display.Mode candidate : display.getSupportedModes()) { for (Display.Mode candidate : display.getSupportedModes()) {
boolean refreshRateOk = candidate.getRefreshRate() >= bestMode.getRefreshRate(); boolean refreshRateReduced = candidate.getRefreshRate() < bestMode.getRefreshRate();
boolean resolutionOk = candidate.getPhysicalWidth() >= bestMode.getPhysicalWidth() && boolean resolutionReduced = candidate.getPhysicalWidth() < bestMode.getPhysicalWidth() ||
candidate.getPhysicalHeight() >= bestMode.getPhysicalHeight() && candidate.getPhysicalHeight() < bestMode.getPhysicalHeight();
candidate.getPhysicalWidth() <= 4096; boolean resolutionFitsStream = candidate.getPhysicalWidth() >= prefConfig.width &&
candidate.getPhysicalHeight() >= prefConfig.height;
LimeLog.info("Examining display mode: "+candidate.getPhysicalWidth()+"x"+ LimeLog.info("Examining display mode: "+candidate.getPhysicalWidth()+"x"+
candidate.getPhysicalHeight()+"x"+candidate.getRefreshRate()); candidate.getPhysicalHeight()+"x"+candidate.getRefreshRate());
// On non-4K streams, we force the resolution to never change if (candidate.getPhysicalWidth() > 4096) {
if (prefConfig.width < 3840) { // Avoid resolutions options above 4K to be safe
continue;
}
// On non-4K streams, we force the resolution to never change unless it's above
// 60 FPS, which may require a resolution reduction due to HDMI bandwidth limitations.
if (prefConfig.width < 3840 && prefConfig.fps <= 60) {
if (display.getMode().getPhysicalWidth() != candidate.getPhysicalWidth() || if (display.getMode().getPhysicalWidth() != candidate.getPhysicalWidth() ||
display.getMode().getPhysicalHeight() != candidate.getPhysicalHeight()) { display.getMode().getPhysicalHeight() != candidate.getPhysicalHeight()) {
continue; continue;
} }
} }
// Ensure the frame rate stays around 60 Hz for <= 60 FPS streams // Make sure the resolution doesn't regress unless if it's over 60 FPS
if (prefConfig.fps <= 60) { // where we may need to reduce resolution to achieve the desired refresh rate.
if (candidate.getRefreshRate() >= 63) { if (resolutionReduced && !(prefConfig.fps > 60 && resolutionFitsStream)) {
continue;
}
if (refreshRateIsGood) {
// We have a good matching refresh rate, so we're looking for equal or greater
// that is also a good matching refresh rate for our stream frame rate.
if (refreshRateReduced || !isRefreshRateGoodMatch(candidate.getRefreshRate())) {
continue; continue;
} }
} }
else if (!isRefreshRateGoodMatch(candidate.getRefreshRate())) {
// Make sure the refresh rate doesn't regress // We didn't have a good match and this match isn't good either, so just don't
if (!refreshRateOk) { // reduce the refresh rate.
continue; if (refreshRateReduced) {
} continue;
}
// Make sure the resolution doesn't regress } else {
if (!resolutionOk) { // We didn't have a good match and this match is good. Prefer this refresh rate
continue; // even if it reduces the refresh rate. Lowering the refresh rate can be beneficial
// when streaming a 60 FPS stream on a 90 Hz device. We want to select 60 Hz to
// match the frame rate even if the active display mode is 90 Hz.
} }
bestMode = candidate; bestMode = candidate;
refreshRateIsGood = isRefreshRateGoodMatch(candidate.getRefreshRate());
} }
LimeLog.info("Selected display mode: "+bestMode.getPhysicalWidth()+"x"+ LimeLog.info("Selected display mode: "+bestMode.getPhysicalWidth()+"x"+
bestMode.getPhysicalHeight()+"x"+bestMode.getRefreshRate()); bestMode.getPhysicalHeight()+"x"+bestMode.getRefreshRate());
@ -669,9 +692,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
float bestRefreshRate = display.getRefreshRate(); float bestRefreshRate = display.getRefreshRate();
for (float candidate : display.getSupportedRefreshRates()) { for (float candidate : display.getSupportedRefreshRates()) {
if (candidate > bestRefreshRate) { LimeLog.info("Examining refresh rate: "+candidate);
LimeLog.info("Examining refresh rate: "+candidate);
if (candidate > bestRefreshRate) {
// Ensure the frame rate stays around 60 Hz for <= 60 FPS streams // Ensure the frame rate stays around 60 Hz for <= 60 FPS streams
if (prefConfig.fps <= 60) { if (prefConfig.fps <= 60) {
if (candidate >= 63) { if (candidate >= 63) {