From 17c47a15daa0eedce4f2923525566864d7474a2e Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 6 Aug 2020 20:14:56 -0700 Subject: [PATCH] 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 --- app/src/main/java/com/limelight/Game.java | 63 ++++++++++++++++------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index 9a39b590..28de16e3 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -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() { Display display = getWindowManager().getDefaultDisplay(); 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 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Display.Mode bestMode = display.getMode(); + boolean refreshRateIsGood = isRefreshRateGoodMatch(bestMode.getRefreshRate()); for (Display.Mode candidate : display.getSupportedModes()) { - boolean refreshRateOk = candidate.getRefreshRate() >= bestMode.getRefreshRate(); - boolean resolutionOk = candidate.getPhysicalWidth() >= bestMode.getPhysicalWidth() && - candidate.getPhysicalHeight() >= bestMode.getPhysicalHeight() && - candidate.getPhysicalWidth() <= 4096; + boolean refreshRateReduced = candidate.getRefreshRate() < bestMode.getRefreshRate(); + boolean resolutionReduced = candidate.getPhysicalWidth() < bestMode.getPhysicalWidth() || + candidate.getPhysicalHeight() < bestMode.getPhysicalHeight(); + boolean resolutionFitsStream = candidate.getPhysicalWidth() >= prefConfig.width && + candidate.getPhysicalHeight() >= prefConfig.height; LimeLog.info("Examining display mode: "+candidate.getPhysicalWidth()+"x"+ candidate.getPhysicalHeight()+"x"+candidate.getRefreshRate()); - // On non-4K streams, we force the resolution to never change - if (prefConfig.width < 3840) { + if (candidate.getPhysicalWidth() > 4096) { + // 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() || display.getMode().getPhysicalHeight() != candidate.getPhysicalHeight()) { continue; } } - // Ensure the frame rate stays around 60 Hz for <= 60 FPS streams - if (prefConfig.fps <= 60) { - if (candidate.getRefreshRate() >= 63) { + // Make sure the resolution doesn't regress unless if it's over 60 FPS + // where we may need to reduce resolution to achieve the desired refresh rate. + 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; } } - - // Make sure the refresh rate doesn't regress - if (!refreshRateOk) { - continue; - } - - // Make sure the resolution doesn't regress - if (!resolutionOk) { - continue; + else if (!isRefreshRateGoodMatch(candidate.getRefreshRate())) { + // We didn't have a good match and this match isn't good either, so just don't + // reduce the refresh rate. + if (refreshRateReduced) { + continue; + } + } else { + // We didn't have a good match and this match is good. Prefer this refresh rate + // 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; + refreshRateIsGood = isRefreshRateGoodMatch(candidate.getRefreshRate()); } LimeLog.info("Selected display mode: "+bestMode.getPhysicalWidth()+"x"+ 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) { float bestRefreshRate = display.getRefreshRate(); 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 if (prefConfig.fps <= 60) { if (candidate >= 63) {