From 912925ef2c2aa0a24e2dd64cea58f9955f1fcae2 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 18 Nov 2017 17:14:40 -0800 Subject: [PATCH] Disable performance optimizations when in multi-window --- app/src/main/java/com/limelight/Game.java | 12 ++ .../video/MediaCodecDecoderRenderer.java | 103 +++++++++++++----- 2 files changed, 88 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index 330c7b149..b5edf2669 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -29,6 +29,7 @@ import com.limelight.utils.UiHelper; import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.app.Activity; import android.app.PictureInPictureParams; import android.app.Service; @@ -517,16 +518,27 @@ private void hideSystemUi(int delay) { } @Override + @TargetApi(Build.VERSION_CODES.N) public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { super.onMultiWindowModeChanged(isInMultiWindowMode); // In multi-window, we don't want to use the full-screen layout // flag. It will cause us to collide with the system UI. + // This function will also be called for PiP so we can cover + // that case here too. if (isInMultiWindowMode) { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + + // Disable performance optimizations for foreground + getWindow().setSustainedPerformanceMode(false); + decoderRenderer.notifyVideoBackground(); } else { getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + + // Enable performance optimizations for foreground + getWindow().setSustainedPerformanceMode(true); + decoderRenderer.notifyVideoForeground(); } // Correct the system UI visibility flags diff --git a/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java b/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java index 03f08aca4..79d7fbaa9 100644 --- a/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java +++ b/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java @@ -1,6 +1,7 @@ package com.limelight.binding.video; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Locale; import org.jcodec.codecs.h264.H264Utils; @@ -123,8 +124,8 @@ public MediaCodecDecoderRenderer(PreferenceConfiguration prefs, this.crashListener = crashListener; this.consecutiveCrashCount = consecutiveCrashCount; - // Disable spinner threads in battery saver mode - if (prefs.batterySaver) { + // Disable spinner threads in battery saver mode or 4K + if (prefs.batterySaver || prefs.height >= 2160) { spinnerThreads = new Thread[0]; } else { @@ -193,6 +194,17 @@ public boolean isHevcMain10Hdr10Supported() { return false; } + public void notifyVideoForeground() { + startSpinnerThreads(); + } + + public void notifyVideoBackground() { + // Signal the spinner threads to stop but + // don't wait for them to terminate to avoid + // delaying the state transition + signalSpinnerStop(); + } + public int getActiveVideoFormat() { return this.videoFormat; } @@ -424,26 +436,70 @@ public void run() { rendererThread.start(); } + private void startSpinnerThread(final int i) { + spinnerThreads[i] = new Thread() { + @Override + public void run() { + // This thread exists to keep the CPU at a higher DVFS state on devices + // where the governor scales clock speed sporadically, causing dropped frames. + // + // Run until we notice our thread has been removed from the spinner threads + // array. Even if we don't notice immediately, we'll notice soon enough. + // This will also ensure we terminate even if someone has restarted spinning + // before we realized we should stop. + while (this == spinnerThreads[i]) { + try { + Thread.sleep(0, 1); + } catch (InterruptedException e) { + break; + } + } + } + }; + spinnerThreads[i].setName("Spinner-"+i); + spinnerThreads[i].setPriority(Thread.MIN_PRIORITY); + spinnerThreads[i].start(); + } + private void startSpinnerThreads() { LimeLog.info("Using "+spinnerThreads.length+" spinner threads"); for (int i = 0; i < spinnerThreads.length; i++) { - spinnerThreads[i] = new Thread() { - @Override - public void run() { - // This thread exists to keep the CPU at a higher DVFS state on devices - // where the governor scales clock speed sporadically, causing dropped frames. - while (!stopping) { - try { - Thread.sleep(0, 1); - } catch (InterruptedException e) { - break; - } - } - } - }; - spinnerThreads[i].setName("Spinner-"+i); - spinnerThreads[i].setPriority(Thread.MIN_PRIORITY); - spinnerThreads[i].start(); + if (spinnerThreads[i] != null) { + continue; + } + + startSpinnerThread(i); + } + } + + private Thread[] signalSpinnerStop() { + // Capture current running threads + Thread[] runningThreads = Arrays.copyOf(spinnerThreads, spinnerThreads.length); + + // Clear the spinner threads to signal their termination + for (int i = 0; i < spinnerThreads.length; i++) { + spinnerThreads[i] = null; + } + + // Interrupt the threads + for (int i = 0; i < runningThreads.length; i++) { + if (runningThreads[i] != null) { + runningThreads[i].interrupt(); + } + } + + return runningThreads; + } + + private void stopSpinnerThreads() { + // Signal and wait for the threads to stop + Thread[] runningThreads = signalSpinnerStop(); + for (int i = 0; i < runningThreads.length; i++) { + if (runningThreads[i] != null) { + try { + runningThreads[i].join(); + } catch (InterruptedException ignored) { } + } } } @@ -517,14 +573,7 @@ public void stop() { } // Halt the spinner threads - for (Thread t : spinnerThreads) { - t.interrupt(); - } - for (Thread t : spinnerThreads) { - try { - t.join(); - } catch (InterruptedException ignored) { } - } + stopSpinnerThreads(); } @Override