From 31e68df1c5a010d2cd23db4ca086f888f4805335 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 12:57:16 +0800 Subject: [PATCH] make ThermionFlutterPlugin static, remove dispose() and add internal listener for ThermionViewer.onDispose --- .../lib/thermion/thermion_flutter_plugin.dart | 91 +++++++++---------- .../lib/thermion_flutter_ffi.dart | 88 +++++++++--------- 2 files changed, 90 insertions(+), 89 deletions(-) diff --git a/thermion_flutter/thermion_flutter/lib/thermion/thermion_flutter_plugin.dart b/thermion_flutter/thermion_flutter/lib/thermion/thermion_flutter_plugin.dart index 9ff1e327..3c784cc9 100644 --- a/thermion_flutter/thermion_flutter/lib/thermion/thermion_flutter_plugin.dart +++ b/thermion_flutter/thermion_flutter/lib/thermion/thermion_flutter_plugin.dart @@ -1,103 +1,100 @@ import 'dart:async'; -import 'dart:ui'; import 'package:thermion_dart/thermion_dart.dart'; import 'package:flutter/widgets.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; /// -/// Handles all platform-specific initialization work necessary to create a -/// backing rendering surface in a Flutter application. -/// Instantiates/wraps a [ThermionViewer], +/// Handles all platform-specific initialization to create a backing rendering +/// surface in a Flutter application and lifecycle listeners to pause rendering +/// when the app is inactive or in the background. +/// Call [createViewer] to create an instance of [ThermionViewer]. +/// This is a lightweight singleton that /// class ThermionFlutterPlugin { - ThermionViewer get _viewer => ThermionFlutterPlatform.instance.viewer; + + ThermionFlutterPlugin._(); - bool _wasRenderingOnInactive = false; + static AppLifecycleListener? _appLifecycleListener; - void _handleStateChange(AppLifecycleState state) async { - await initialized; + static bool _initializing = false; + + static ThermionViewer? _viewer; + + static bool _wasRenderingOnInactive = false; + + static void _handleStateChange(AppLifecycleState state) async { + if (_viewer == null) { + return; + } + + await _viewer!.initialized; switch (state) { case AppLifecycleState.detached: - print("Detached"); if (!_wasRenderingOnInactive) { - _wasRenderingOnInactive = _viewer.rendering; + _wasRenderingOnInactive = _viewer!.rendering; } - await _viewer.setRendering(false); + await _viewer!.setRendering(false); break; case AppLifecycleState.hidden: - print("Hidden"); if (!_wasRenderingOnInactive) { - _wasRenderingOnInactive = _viewer.rendering; + _wasRenderingOnInactive = _viewer!.rendering; } - await _viewer.setRendering(false); + await _viewer!.setRendering(false); break; case AppLifecycleState.inactive: - print("Inactive"); if (!_wasRenderingOnInactive) { - _wasRenderingOnInactive = _viewer.rendering; + _wasRenderingOnInactive = _viewer!.rendering; } // on Windows in particular, restoring a window after minimizing stalls the renderer (and the whole application) for a considerable length of time. // disabling rendering on minimize seems to fix the issue (so I wonder if there's some kind of command buffer that's filling up while the window is minimized). - await _viewer.setRendering(false); + await _viewer!.setRendering(false); break; case AppLifecycleState.paused: - print("Paused"); if (!_wasRenderingOnInactive) { - _wasRenderingOnInactive = _viewer.rendering; + _wasRenderingOnInactive = _viewer!.rendering; } - await _viewer.setRendering(false); + await _viewer!.setRendering(false); break; case AppLifecycleState.resumed: - print("Resumed"); - await _viewer.setRendering(_wasRenderingOnInactive); + await _viewer!.setRendering(_wasRenderingOnInactive); break; } } - AppLifecycleListener? _appLifecycleListener; - - final _initialized = Completer(); - Future get initialized => _initialized.future; - - bool _initializing = false; - - Future initialize({String? uberArchivePath}) async { - _initializing = true; - if (_initialized.isCompleted) { - return ThermionFlutterPlatform.instance.viewer; + static Future createViewer({String? uberArchivePath}) async { + if (_initializing) { + throw Exception("Existing call to createViewer has not completed."); } - await ThermionFlutterPlatform.instance - .initialize(uberArchivePath: uberArchivePath); - + _initializing = true; + _viewer = await ThermionFlutterPlatform.instance + .createViewer(uberArchivePath: uberArchivePath); _appLifecycleListener = AppLifecycleListener( onStateChange: _handleStateChange, ); - _viewer.initialized; - _initialized.complete(true); + _viewer!.onDispose(() async { + _viewer = null; + _appLifecycleListener?.dispose(); + _appLifecycleListener = null; + }); _initializing = false; - return ThermionFlutterPlatform.instance.viewer; + return _viewer!; } - Future createTexture( + static Future createTexture( int width, int height, int offsetLeft, int offsetRight) async { return ThermionFlutterPlatform.instance .createTexture(width, height, offsetLeft, offsetRight); } - Future destroyTexture(ThermionFlutterTexture texture) async { + static Future destroyTexture(ThermionFlutterTexture texture) async { return ThermionFlutterPlatform.instance.destroyTexture(texture); } @override - Future resizeTexture(ThermionFlutterTexture texture, + static Future resizeTexture(ThermionFlutterTexture texture, int width, int height, int offsetLeft, int offsetRight) async { return ThermionFlutterPlatform.instance .resizeTexture(texture, width, height, offsetLeft, offsetRight); } - - void dispose() { - ThermionFlutterPlatform.instance.dispose(); - _appLifecycleListener?.dispose(); - } } diff --git a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart index 07786138..77cdcea8 100644 --- a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart +++ b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart @@ -6,21 +6,24 @@ import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_in import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; /// -/// A subclass of [ThermionViewerFFI] that uses Flutter platform channels -/// to create rendering contexts, callbacks and surfaces (either backing texture(s). +/// An implementation of [ThermionFlutterPlatform] that uses a Flutter platform +/// channel to create a rendering context, resource loaders, and +/// render target(s). /// class ThermionFlutterFFI extends ThermionFlutterPlatform { final _channel = const MethodChannel("dev.thermion.flutter/event"); - late final ThermionViewerFFI viewer; + ThermionViewerFFI? _viewer; + + ThermionFlutterFFI._() {} static void registerWith() { - ThermionFlutterPlatform.instance = ThermionFlutterFFI(); + ThermionFlutterPlatform.instance = ThermionFlutterFFI._(); } final _textures = {}; - Future initialize({String? uberArchivePath}) async { + Future createViewer({String? uberArchivePath}) async { var resourceLoader = Pointer.fromAddress( await _channel.invokeMethod("getResourceLoaderWrapper")); @@ -46,22 +49,24 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform { ? nullptr : Pointer.fromAddress(sharedContext); - viewer = ThermionViewerFFI( + _viewer = ThermionViewerFFI( resourceLoader: resourceLoader, renderCallback: renderCallback, renderCallbackOwner: renderCallbackOwner, driver: driverPtr, sharedContext: sharedContextPtr, uberArchivePath: uberArchivePath); - await viewer.initialized; + await _viewer!.initialized; + return _viewer!; } bool _creatingTexture = false; + bool _destroyingTexture = false; Future _waitForTextureCreationToComplete() async { var iter = 0; - while (_creatingTexture) { + while (_creatingTexture || _destroyingTexture) { await Future.delayed(Duration(milliseconds: 50)); iter++; if (iter > 10) { @@ -125,39 +130,43 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform { print( "Created texture with flutter texture id ${flutterTextureId}, hardwareTextureId $hardwareTextureId and surfaceAddress $surfaceAddress"); - viewer.viewportDimensions = (width.toDouble(), height.toDouble()); + _viewer?.viewportDimensions = (width.toDouble(), height.toDouble()); final texture = ThermionFlutterTexture( - flutterTextureId, hardwareTextureId, width, height, surfaceAddress); + flutterTextureId, hardwareTextureId, width, height, surfaceAddress); - await viewer.createSwapChain(width.toDouble(), height.toDouble(), - surface: texture.surfaceAddress == null - ? nullptr - : Pointer.fromAddress(texture.surfaceAddress!)); + await _viewer?.createSwapChain(width.toDouble(), height.toDouble(), + surface: texture.surfaceAddress == null + ? nullptr + : Pointer.fromAddress(texture.surfaceAddress!)); if (texture.hardwareTextureId != null) { - print("Creating render target"); // ignore: unused_local_variable - var renderTarget = await viewer.createRenderTarget( - width.toDouble(), height.toDouble(), texture.hardwareTextureId!); + var renderTarget = await _viewer?.createRenderTarget( + width.toDouble(), height.toDouble(), texture.hardwareTextureId!); } - - await viewer.updateViewportAndCameraProjection( - width.toDouble(), height.toDouble()); - viewer.render(); + + await _viewer?.updateViewportAndCameraProjection(width.toDouble(), height.toDouble()); + _viewer?.render(); _creatingTexture = false; - + _textures.add(texture); - + return texture; } + /// - /// Called by [ThermionWidget] to destroy a texture. Don't call this yourself. + /// Destroy a texture and clean up the texture cache (if applicable). /// Future destroyTexture(ThermionFlutterTexture texture) async { - await _channel.invokeMethod("destroyTexture", texture.flutterTextureId); + if (_creatingTexture || _destroyingTexture) { + throw Exception("Cannot destroy texture while concurrent call to createTexture/destroyTexture has not completed"); + } + _destroyingTexture = true; _textures.remove(texture); + await _channel.invokeMethod("destroyTexture", texture.flutterTextureId); + _destroyingTexture = false; } bool _resizing = false; @@ -172,14 +181,14 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform { throw Exception("Resize underway"); } - if ((width - viewer.viewportDimensions.$1).abs() < 0.001 || - (height - viewer.viewportDimensions.$2).abs() < 0.001) { + if ((width - _viewer!.viewportDimensions.$1).abs() < 0.001 || + (height - _viewer!.viewportDimensions.$2).abs() < 0.001) { return texture; } _resizing = true; - bool wasRendering = viewer.rendering; - await viewer.setRendering(false); - await viewer.destroySwapChain(); + bool wasRendering = _viewer!.rendering; + await _viewer!.setRendering(false); + await _viewer!.destroySwapChain(); await destroyTexture(texture); var result = await _channel @@ -188,34 +197,29 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform { if (result == null || result[0] == -1) { throw Exception("Failed to create texture"); } - viewer.viewportDimensions = (width.toDouble(), height.toDouble()); + _viewer!.viewportDimensions = (width.toDouble(), height.toDouble()); var newTexture = ThermionFlutterTexture(result[0], result[1], width, height, result[2]); - await viewer.createSwapChain(width.toDouble(), height.toDouble(), + await _viewer!.createSwapChain(width.toDouble(), height.toDouble(), surface: newTexture.surfaceAddress == null ? nullptr : Pointer.fromAddress(newTexture.surfaceAddress!)); if (newTexture.hardwareTextureId != null) { // ignore: unused_local_variable - var renderTarget = await viewer.createRenderTarget( + var renderTarget = await _viewer!.createRenderTarget( width.toDouble(), height.toDouble(), newTexture.hardwareTextureId!); } - await viewer.updateViewportAndCameraProjection( - width.toDouble(), height.toDouble()); + await _viewer! + .updateViewportAndCameraProjection(width.toDouble(), height.toDouble()); - viewer.viewportDimensions = (width.toDouble(), height.toDouble()); + _viewer!.viewportDimensions = (width.toDouble(), height.toDouble()); if (wasRendering) { - await viewer.setRendering(true); + await _viewer!.setRendering(true); } _textures.add(newTexture); _resizing = false; return newTexture; } - - @override - void dispose() { - // TODO: implement dispose - } }