Initial commit
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
import '../../../shared/utils/app_logger.dart';
|
||||
|
||||
const _tag = 'FrameJank';
|
||||
|
||||
|
||||
@immutable
|
||||
class FrameDiag {
|
||||
|
||||
final double fps;
|
||||
|
||||
|
||||
final double lastBuildMs;
|
||||
|
||||
|
||||
final double lastRasterMs;
|
||||
|
||||
|
||||
final int jankCount;
|
||||
|
||||
const FrameDiag({
|
||||
required this.fps,
|
||||
required this.lastBuildMs,
|
||||
required this.lastRasterMs,
|
||||
required this.jankCount,
|
||||
});
|
||||
|
||||
static const zero = FrameDiag(
|
||||
fps: 0,
|
||||
lastBuildMs: 0,
|
||||
lastRasterMs: 0,
|
||||
jankCount: 0,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
class FrameJankMonitor {
|
||||
FrameJankMonitor({this.jankThreshold = const Duration(milliseconds: 32)});
|
||||
|
||||
|
||||
final Duration jankThreshold;
|
||||
|
||||
final ValueNotifier<FrameDiag> diag = ValueNotifier<FrameDiag>(
|
||||
FrameDiag.zero,
|
||||
);
|
||||
|
||||
TimingsCallback? _callback;
|
||||
int _jankCount = 0;
|
||||
int _windowFrames = 0;
|
||||
final Stopwatch _windowClock = Stopwatch()..start();
|
||||
double _fps = 0;
|
||||
|
||||
void start() {
|
||||
if (kReleaseMode || _callback != null) return;
|
||||
final cb = _onTimings;
|
||||
_callback = cb;
|
||||
SchedulerBinding.instance.addTimingsCallback(cb);
|
||||
}
|
||||
|
||||
void _onTimings(List<FrameTiming> timings) {
|
||||
if (timings.isEmpty) return;
|
||||
for (final t in timings) {
|
||||
if (t.buildDuration >= jankThreshold ||
|
||||
t.rasterDuration >= jankThreshold) {
|
||||
_jankCount++;
|
||||
AppLogger.warn(
|
||||
_tag,
|
||||
'janky frame build=${_ms(t.buildDuration)} '
|
||||
'raster=${_ms(t.rasterDuration)} total=${_ms(t.totalSpan)}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_windowFrames += timings.length;
|
||||
final elapsedMs = _windowClock.elapsedMilliseconds;
|
||||
if (elapsedMs >= 500) {
|
||||
_fps = _windowFrames * 1000 / elapsedMs;
|
||||
_windowFrames = 0;
|
||||
_windowClock.reset();
|
||||
}
|
||||
|
||||
final last = timings.last;
|
||||
diag.value = FrameDiag(
|
||||
fps: _fps,
|
||||
lastBuildMs: last.buildDuration.inMicroseconds / 1000,
|
||||
lastRasterMs: last.rasterDuration.inMicroseconds / 1000,
|
||||
jankCount: _jankCount,
|
||||
);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
final cb = _callback;
|
||||
if (cb != null) {
|
||||
SchedulerBinding.instance.removeTimingsCallback(cb);
|
||||
_callback = null;
|
||||
}
|
||||
diag.dispose();
|
||||
}
|
||||
|
||||
static String _ms(Duration d) =>
|
||||
'${(d.inMicroseconds / 1000).toStringAsFixed(1)}ms';
|
||||
}
|
||||
Reference in New Issue
Block a user