kopia lustrzana https://github.com/ryukoposting/Signal-Android
102 wiersze
3.3 KiB
Java
102 wiersze
3.3 KiB
Java
package org.thoughtcrime.securesms.util;
|
|
|
|
import android.app.Application;
|
|
import android.content.Context;
|
|
import android.view.Choreographer;
|
|
import android.view.Display;
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import org.signal.core.util.logging.Log;
|
|
|
|
import java.util.Locale;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
/**
|
|
* Tracks the frame rate of the app and logs when things are bad.
|
|
*
|
|
* In general, whenever alterations are made here, the author should be very cautious to do as
|
|
* little work as possible, because we don't want the tracker itself to impact the frame rate.
|
|
*/
|
|
public class FrameRateTracker {
|
|
|
|
private static final String TAG = Log.tag(FrameRateTracker.class);
|
|
|
|
private static final int MAX_CONSECUTIVE_FRAME_LOGS = 10;
|
|
|
|
private final Application context;
|
|
|
|
private double refreshRate;
|
|
private long idealTimePerFrameNanos;
|
|
private long badFrameThresholdNanos;
|
|
|
|
private long lastFrameTimeNanos;
|
|
|
|
private long consecutiveFrameWarnings;
|
|
|
|
public FrameRateTracker(@NonNull Application application) {
|
|
this.context = application;
|
|
|
|
updateRefreshRate();
|
|
}
|
|
|
|
public void start() {
|
|
Log.d(TAG, String.format(Locale.ENGLISH, "Beginning frame rate tracking. Screen refresh rate: %.2f hz, or %.2f ms per frame.", refreshRate, idealTimePerFrameNanos / (float) 1_000_000));
|
|
|
|
lastFrameTimeNanos = System.nanoTime();
|
|
|
|
Choreographer.getInstance().postFrameCallback(calculator);
|
|
}
|
|
|
|
public void stop() {
|
|
Choreographer.getInstance().removeFrameCallback(calculator);
|
|
}
|
|
|
|
/**
|
|
* The natural screen refresh rate, in hertz. May not always return the same value if a display
|
|
* has a dynamic refresh rate.
|
|
*/
|
|
public static float getDisplayRefreshRate(@NonNull Context context) {
|
|
Display display = ServiceUtil.getWindowManager(context).getDefaultDisplay();
|
|
return display.getRefreshRate();
|
|
}
|
|
|
|
/**
|
|
* Displays with dynamic refresh rates may change their reported refresh rate over time.
|
|
*/
|
|
private void updateRefreshRate() {
|
|
double newRefreshRate = getDisplayRefreshRate(context);
|
|
|
|
if (this.refreshRate != newRefreshRate) {
|
|
if (this.refreshRate > 0) {
|
|
Log.d(TAG, String.format(Locale.ENGLISH, "Refresh rate changed from %.2f hz to %.2f hz", refreshRate, newRefreshRate));
|
|
}
|
|
|
|
this.refreshRate = getDisplayRefreshRate(context);
|
|
this.idealTimePerFrameNanos = (long) (TimeUnit.SECONDS.toNanos(1) / refreshRate);
|
|
this.badFrameThresholdNanos = idealTimePerFrameNanos * (int) (refreshRate / 4);
|
|
}
|
|
}
|
|
|
|
private final Choreographer.FrameCallback calculator = new Choreographer.FrameCallback() {
|
|
@Override
|
|
public void doFrame(long frameTimeNanos) {
|
|
long elapsedNanos = frameTimeNanos - lastFrameTimeNanos;
|
|
double fps = TimeUnit.SECONDS.toNanos(1) / (double) elapsedNanos;
|
|
|
|
if (elapsedNanos > badFrameThresholdNanos) {
|
|
if (consecutiveFrameWarnings < MAX_CONSECUTIVE_FRAME_LOGS) {
|
|
long droppedFrames = elapsedNanos / idealTimePerFrameNanos;
|
|
Log.w(TAG, String.format(Locale.ENGLISH, "Bad frame! Took %d ms (%d dropped frames, or %.2f FPS)", TimeUnit.NANOSECONDS.toMillis(elapsedNanos), droppedFrames, fps));
|
|
consecutiveFrameWarnings++;
|
|
}
|
|
} else {
|
|
consecutiveFrameWarnings = 0;
|
|
}
|
|
|
|
lastFrameTimeNanos = frameTimeNanos;
|
|
Choreographer.getInstance().postFrameCallback(this);
|
|
}
|
|
};
|
|
}
|