(flutter) better synchronization in ThermionTextureWidget with presentation deadline
This commit is contained in:
@@ -5,7 +5,6 @@ import 'package:thermion_flutter/src/widgets/src/resize_observer.dart';
|
|||||||
import 'package:thermion_flutter/thermion_flutter.dart' hide Texture;
|
import 'package:thermion_flutter/thermion_flutter.dart' hide Texture;
|
||||||
|
|
||||||
class ThermionTextureWidget extends StatefulWidget {
|
class ThermionTextureWidget extends StatefulWidget {
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
@@ -45,23 +44,21 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
|||||||
|
|
||||||
static final _views = <View>[];
|
static final _views = <View>[];
|
||||||
|
|
||||||
final _logger = Logger("_ThermionTextureWidgetState");
|
late final _logger = Logger(this.runtimeType.toString());
|
||||||
|
|
||||||
int _fps = 0;
|
int _fps = 0;
|
||||||
int _frameCount = 0;
|
int _frameCount = 0;
|
||||||
int _frameRequestCount = 0;
|
|
||||||
int _frameRequestPercentage = 0;
|
int _frameRequestPercentage = 0;
|
||||||
int _lastFpsUpdateTime = 0;
|
|
||||||
Timer? _fpsUpdateTimer;
|
Timer? _fpsUpdateTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_views.remove(widget.viewer.view);
|
||||||
final texture = _texture;
|
final texture = _texture;
|
||||||
_texture = null;
|
_texture = null;
|
||||||
super.dispose();
|
super.dispose();
|
||||||
_views.remove(widget.viewer.view);
|
|
||||||
if (texture != null) {
|
if (texture != null) {
|
||||||
ThermionFlutterPlatform.instance.destroyTextureDescriptor(texture!);
|
ThermionFlutterPlatform.instance.destroyTextureDescriptor(texture);
|
||||||
}
|
}
|
||||||
_fpsUpdateTimer?.cancel();
|
_fpsUpdateTimer?.cancel();
|
||||||
_states.remove(this);
|
_states.remove(this);
|
||||||
@@ -69,9 +66,9 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
if (_views.contains(widget.viewer.view)) {
|
// if (_views.contains(widget.viewer.view)) {
|
||||||
throw Exception("View already embedded in a widget");
|
// throw Exception("View already embedded in a widget");
|
||||||
}
|
// }
|
||||||
_views.add(widget.viewer.view);
|
_views.add(widget.viewer.view);
|
||||||
|
|
||||||
// Start FPS counter update timer if enabled
|
// Start FPS counter update timer if enabled
|
||||||
@@ -80,18 +77,13 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
|||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_fps = _frameCount;
|
_fps = _frameCount;
|
||||||
_frameRequestPercentage = _frameCount > 0
|
|
||||||
? (_frameCount / _frameRequestCount * 100).round()
|
|
||||||
: 0;
|
|
||||||
_frameCount = 0;
|
_frameCount = 0;
|
||||||
_frameRequestCount = 0;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
|
|
||||||
var dpr = MediaQuery.of(context).devicePixelRatio;
|
var dpr = MediaQuery.of(context).devicePixelRatio;
|
||||||
|
|
||||||
var size = ((context.findRenderObject()) as RenderBox).size;
|
var size = ((context.findRenderObject()) as RenderBox).size;
|
||||||
@@ -147,13 +139,8 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _rendering = false;
|
|
||||||
|
|
||||||
static final _states = <_ThermionTextureWidgetState>{};
|
static final _states = <_ThermionTextureWidgetState>{};
|
||||||
|
|
||||||
int lastRender = 0;
|
|
||||||
int _headroomInMs = 5;
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Each instance of ThermionTextureWidget in the widget hierarchy must
|
/// Each instance of ThermionTextureWidget in the widget hierarchy must
|
||||||
/// call [markFrameAvailable] on every frame to notify Flutter that the content
|
/// call [markFrameAvailable] on every frame to notify Flutter that the content
|
||||||
@@ -167,42 +154,48 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
|||||||
/// [_ThermionTextureWidgetState] in a static set, and allowing the first
|
/// [_ThermionTextureWidgetState] in a static set, and allowing the first
|
||||||
/// instance to call [requestFrame].
|
/// instance to call [requestFrame].
|
||||||
///
|
///
|
||||||
void _requestFrame() {
|
|
||||||
|
DateTime _deadline = DateTime.now();
|
||||||
|
Completer? _frameCompleter;
|
||||||
|
|
||||||
|
void _requestFrame() async {
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.showFpsCounter) {
|
await _frameCompleter?.future;
|
||||||
_frameRequestCount++;
|
|
||||||
|
_frameCompleter = Completer();
|
||||||
|
|
||||||
|
var headroom = _deadline.difference(DateTime.now());
|
||||||
|
|
||||||
|
if (headroom.inMilliseconds > 5) {
|
||||||
|
var waitForNs = headroom.inMicroseconds - 5000;
|
||||||
|
await Future.delayed(Duration(microseconds: waitForNs));
|
||||||
}
|
}
|
||||||
|
|
||||||
WidgetsBinding.instance.scheduleFrameCallback((d) async {
|
if (widget.viewer.rendering && _resizing.isEmpty) {
|
||||||
|
if (_states.isNotEmpty && this == _states.first && _texture != null) {
|
||||||
|
await FilamentApp.instance!.requestFrame();
|
||||||
|
|
||||||
if (!mounted) {
|
if (widget.showFpsCounter) {
|
||||||
return;
|
_frameCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (widget.viewer.rendering &&
|
}
|
||||||
!_rendering &&
|
|
||||||
_resizing.isEmpty &&
|
|
||||||
(d.inMilliseconds - lastRender >
|
|
||||||
widget.viewer.msPerFrame - _headroomInMs)) {
|
|
||||||
_rendering = true;
|
|
||||||
if (this == _states.first && _texture != null) {
|
|
||||||
await FilamentApp.instance!.requestFrame();
|
|
||||||
lastRender = d.inMilliseconds;
|
|
||||||
|
|
||||||
if (widget.showFpsCounter) {
|
WidgetsBinding.instance.scheduleFrameCallback((Duration d) async {
|
||||||
_frameCount++;
|
if (_texture != null) {
|
||||||
}
|
await ThermionFlutterPlatform.instance
|
||||||
}
|
.markTextureFrameAvailable(_texture!);
|
||||||
if (_texture != null) {
|
|
||||||
await ThermionFlutterPlatform.instance
|
|
||||||
.markTextureFrameAvailable(_texture!);
|
|
||||||
}
|
|
||||||
_rendering = false;
|
|
||||||
}
|
}
|
||||||
_requestFrame();
|
_frameCompleter?.complete();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var deadlineInMicros = (widget.viewer.msPerFrame * 1000).toInt();
|
||||||
|
_deadline = DateTime.now().add(Duration(microseconds: deadlineInMicros));
|
||||||
|
|
||||||
|
Timer.run(_requestFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
final _resizing = <Future>[];
|
final _resizing = <Future>[];
|
||||||
|
|||||||
Reference in New Issue
Block a user