【发布时间】:2020-01-05 07:53:12
【问题描述】:
有什么方法可以在 Flutter 中向用户显示 FPS (即每秒帧数)?是否有任何小部件或包?
注意:
我不是在测试期间要求 FPS。
【问题讨论】:
-
这是一个很好的演示:medium.com/@matanlurey/…
有什么方法可以在 Flutter 中向用户显示 FPS (即每秒帧数)?是否有任何小部件或包?
注意:
我不是在测试期间要求 FPS。
【问题讨论】:
截图:
代码:
我不知道是否有一个包可以帮助你不断地向用户显示 FPS,但是如果你想手动进行,你可以尝试这种方法:
class _FpsPageState extends State<FpsPage> {
bool _shouldCount = false;
var _count = 0;
@override
Widget build(BuildContext context) {
if (_shouldCount) _count++;
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
_shouldCount = true;
_count = 0;
Timer.periodic(Duration(milliseconds: 1),(timer) {
setState(() {
if (timer.tick >= 1000) {
timer.cancel();
_shouldCount = false;
}
});
});
},
child: Text(_count != 0 ? 'FPS: $_count' : 'START'),
),
),
);
}
}
【讨论】:
作为一个如何在 Dart 中构建 FRP(功能响应程序[s])的示例,我决定尝试构建一个 FPS 计数器——即一个用于确定正在运行的应用程序的帧速率的实用程序。
import 'dart:async';
/// Returns a [Stream] that fires every [animationFrame].
///
/// May provide a function that returns a future completing in the next
/// available frame. For example in a browser environment this may be delegated
/// to `window.animationFrame`:
///
/// ```
/// eachFrame(animationFrame: () => window.animationFrame)
/// ```
Stream<num> eachFrame({Future<num> animationFrame(): nextFrame});
确定每秒帧数意味着我想在我的应用程序的零星时刻检查自上次能够检查以来已经过去了多长时间。假设如下: 渲染通常以秒为单位,或多少帧适合 1000 毫秒 (ms) 是一秒 渲染一帧所需的时间是两个时间戳之间的差异 我们可以从每“帧”发出一个时间戳开始
在 Dart 中,它是单线程的并且基于事件循环,我们假设如果我们能够在每个(空闲时间)至少一次控制事件循环,那么之前的所有工作实际上都是帧。
我尝试相当天真地实现它——我创建了一个新的 StreamController,并使用 Future.delayed(它在所有平台上委托给一个 Timer)来通知下一帧何时是:
import 'dart:async';
/// A cross-platform implementation for requesting the next animation frame.
///
/// Returns a [Future<num>] that completes as close as it can to the next
/// frame, given that it will attempt to be called 60 times per second (60 FPS)
/// by default - customize by setting the [target].
Future<num> nextFrame([num target = 60]) {
final current = new DateTime.now().millisecondsSinceEpoch;
final call = math.max(0, (1000 ~/ target) - (current - _previous));
return new Future.delayed(
new Duration(milliseconds: call),
() => _previous = new DateTime.now().millisecondsSinceEpoch,
);
}
/// Returns a [Stream] that fires every [animationFrame].
///
/// May provide a function that returns a future completing in the next
/// available frame. For example in a browser environment this may be delegated
/// to `window.animationFrame`:
///
/// ```
/// eachFrame(animationFrame: () => window.animationFrame)
/// ```
Stream<num> eachFrame({Future<num> animationFrame(): nextFrame}) {
StreamController<num> controller;
var cancelled = false;
void onNext(num timestamp) {
if (cancelled) return;
controller.add(timestamp);
animationFrame().then(onNext);
}
controller = new StreamController<num>(
sync: true,
onListen: () {
animationFrame().then(onNext);
},
onCancel: () {
cancelled = true;
},
);
return controller.stream;
}
import 'package:fps/fps.dart';
/// Prints 10 timestamps, each printed ~every 13.33ms.
void main() {
eachFrame().take(10).listen(print);
}
注意:这并不总是准确的——特别是在 web 上,我们可以使用 window.animationFrame 做得更好,但同样,我想要开箱即用的跨平台运行的东西。
好的,所以我需要计算并输出每秒帧数,而不是添加时间戳。我本可以内联完成,但为了使其更具可扩展性,我通过一个简单的 StreamTransformer 实现了它,这是我可以通过转换在任何 Stream 之上使用的代码:
/// Computes frames-per-second given a [Stream<num>] of timestamps.
///
/// The resulting [Stream] is capped at reporting a maximum of 60 FPS.
///
/// ```
/// // Listens to FPS for 10 frames, and reports at FPS, printing to console.
/// eachFrame()
/// .take(10)
/// .transform(const ComputeFps())
/// .listen(print);
/// ```
class ComputeFps implements StreamTransformer<num, num> {
final num _filterStrength;
/// Create a transformer.
///
/// Optionally specify a `filterStrength`, or how little to reflect temporary
/// variations in FPS. A value of `1` will only keep the last value.
const ComputeFps([this._filterStrength = 20]);
@override
Stream<num> bind(Stream<num> stream) {
StreamController<num> controller;
StreamSubscription<num> subscription;
num frameTime = 0;
num lastLoop;
controller = new StreamController<num>(
sync: true,
onListen: () {
subscription = stream.listen((thisLoop) {
if (lastLoop != null) {
var thisFrameTime = thisLoop - lastLoop;
frameTime += (thisFrameTime - frameTime) / _filterStrength;
controller.add(math.min(1000 / frameTime, 60));
}
lastLoop = thisLoop;
});
},
onCancel: () => subscription.cancel(),
);
return controller.stream;
}
}
import 'package:fps/fps.dart';
void main() {
eachFrame()
.take(10)
.transform(const ComputeFps())
.listen(print);
}
我现在得到类似这样的输出:
60 60 60 60 60 60 60 60 60
看起来不错!我有included the source code and an example repo on github.
【讨论】: