From 1e828a10b92c5d3e0f0f6d3bca900fa7883e9999 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 15 Dec 2022 22:53:25 -0600 Subject: [PATCH] Request our desired refresh rate rather than the actual frame rate This ensures we'll get the highest compatible refresh rate rather than the lowest. --- app/src/main/java/com/limelight/Game.java | 26 +++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index e299f55d..c8556875 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -125,6 +125,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, private int suppressPipRefCount = 0; private String pcName; private String appName; + private float desiredRefreshRate; private InputCaptureProvider inputCaptureProvider; private int modifierFlags = 0; @@ -976,6 +977,9 @@ public class Game extends Activity implements SurfaceHolder.Callback, streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height); } + // Set the desired refresh rate that will get passed into setFrameRate() later + desiredRefreshRate = displayRefreshRate; + if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION) || getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { // TVs may take a few moments to switch refresh rates, and we can probably assume @@ -2097,19 +2101,37 @@ public class Game extends Activity implements SurfaceHolder.Callback, @Override public void surfaceCreated(SurfaceHolder holder) { + float desiredFrameRate; + surfaceCreated = true; + // Android will pick the lowest matching refresh rate for a given frame rate value, so we want + // to report the true FPS value if refresh rate reduction is enabled. We also report the true + // FPS value if there's no suitable matching refresh rate. In that case, Android could try to + // select a lower refresh rate that avoids uneven pull-down (ex: 30 Hz for a 60 FPS stream on + // a display that maxes out at 50 Hz). + if (mayReduceRefreshRate() || desiredRefreshRate < prefConfig.fps) { + desiredFrameRate = prefConfig.fps; + } + else { + // Otherwise, we will pretend that our frame rate matches the refresh rate we picked in + // prepareDisplayForRendering(). This will usually be the highest refresh rate that our + // frame rate evenly divides into, which ensures the lowest possible display latency. + desiredFrameRate = desiredRefreshRate; + } + // 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, + holder.getSurface().setFrameRate(desiredFrameRate, 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(desiredFrameRate, + Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE); } }