import 'package:flutter/material.dart'; import 'package:flutter_gpu_texture_renderer/flutter_gpu_texture_renderer.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/models/model.dart'; import 'package:get/get.dart'; import '../../common.dart'; import './platform_model.dart'; import 'package:texture_rgba_renderer/texture_rgba_renderer.dart' if (dart.library.html) 'package:flutter_hbb/web/texture_rgba_renderer.dart'; class _PixelbufferTexture { int _textureKey = -1; int _display = 0; SessionID? _sessionId; bool _destroying = false; int? _id; final textureRenderer = TextureRgbaRenderer(); int get display => _display; create(int d, SessionID sessionId, FFI ffi) { _display = d; _textureKey = bind.getNextTextureKey(); _sessionId = sessionId; textureRenderer.createTexture(_textureKey).then((id) async { _id = id; if (id != -1) { ffi.textureModel.setRgbaTextureId(display: d, id: id); final ptr = await textureRenderer.getTexturePtr(_textureKey); platformFFI.registerPixelbufferTexture(sessionId, display, ptr); debugPrint( "create pixelbuffer texture: peerId: ${ffi.id} display:$_display, textureId:$id, texturePtr:$ptr"); } }); } destroy(bool unregisterTexture, FFI ffi) async { if (!_destroying && _textureKey != -1 && _sessionId != null) { _destroying = true; if (unregisterTexture) { platformFFI.registerPixelbufferTexture(_sessionId!, display, 0); // sleep for a while to avoid the texture is used after it's unregistered. await Future.delayed(Duration(milliseconds: 100)); } await textureRenderer.closeTexture(_textureKey); _textureKey = -1; _destroying = false; debugPrint( "destroy pixelbuffer texture: peerId: ${ffi.id} display:$_display, textureId:$_id"); } } } class _GpuTexture { int _textureId = -1; SessionID? _sessionId; final support = bind.mainHasGpuTextureRender(); bool _destroying = false; int _display = 0; int? _id; int? _output; int get display => _display; final gpuTextureRenderer = FlutterGpuTextureRenderer(); _GpuTexture(); create(int d, SessionID sessionId, FFI ffi) { if (support) { _sessionId = sessionId; _display = d; gpuTextureRenderer.registerTexture().then((id) async { _id = id; if (id != null) { _textureId = id; ffi.textureModel.setGpuTextureId(display: d, id: id); final output = await gpuTextureRenderer.output(id); _output = output; if (output != null) { platformFFI.registerGpuTexture(sessionId, d, output); } debugPrint( "create gpu texture: peerId: ${ffi.id} display:$_display, textureId:$id, output:$output"); } }, onError: (err) { debugPrint("Failed to register gpu texture:$err"); }); } } destroy(bool unregisterTexture, FFI ffi) async { // must stop texture render, render unregistered texture cause crash if (!_destroying && support && _sessionId != null && _textureId != -1) { _destroying = true; if (unregisterTexture) { platformFFI.registerGpuTexture(_sessionId!, _display, 0); // sleep for a while to avoid the texture is used after it's unregistered. await Future.delayed(Duration(milliseconds: 100)); } await gpuTextureRenderer.unregisterTexture(_textureId); _textureId = -1; _destroying = false; debugPrint( "destroy gpu texture: peerId: ${ffi.id} display:$_display, textureId:$_id, output:$_output"); } } } class _Control { RxInt textureID = (-1).obs; int _rgbaTextureId = -1; int get rgbaTextureId => _rgbaTextureId; int _gpuTextureId = -1; int get gpuTextureId => _gpuTextureId; bool _isGpuTexture = false; bool get isGpuTexture => _isGpuTexture; setTextureType({bool gpuTexture = false}) { _isGpuTexture = gpuTexture; textureID.value = _isGpuTexture ? gpuTextureId : rgbaTextureId; } setRgbaTextureId(int id) { _rgbaTextureId = id; textureID.value = _isGpuTexture ? gpuTextureId : rgbaTextureId; } setGpuTextureId(int id) { _gpuTextureId = id; textureID.value = _isGpuTexture ? gpuTextureId : rgbaTextureId; } } class TextureModel { final WeakReference parent; final Map _control = {}; final Map _pixelbufferRenderTextures = {}; final Map _gpuRenderTextures = {}; TextureModel(this.parent); setTextureType({required int display, required bool gpuTexture}) { debugPrint("setTextureType: display=$display, isGpuTexture=$gpuTexture"); ensureControl(display); _control[display]?.setTextureType(gpuTexture: gpuTexture); // For versions that do not support multiple displays, the display parameter is always 0, need set type of current display final ffi = parent.target; if (ffi == null) return; if (!ffi.ffiModel.pi.isSupportMultiDisplay) { final currentDisplay = CurrentDisplayState.find(ffi.id).value; if (currentDisplay != display) { debugPrint( "setTextureType: currentDisplay=$currentDisplay, isGpuTexture=$gpuTexture"); ensureControl(currentDisplay); _control[currentDisplay]?.setTextureType(gpuTexture: gpuTexture); } } } setRgbaTextureId({required int display, required int id}) { ensureControl(display); _control[display]?.setRgbaTextureId(id); } setGpuTextureId({required int display, required int id}) { ensureControl(display); _control[display]?.setGpuTextureId(id); } RxInt getTextureId(int display) { ensureControl(display); return _control[display]!.textureID; } updateCurrentDisplay(int curDisplay) { final ffi = parent.target; if (ffi == null) return; tryCreateTexture(int idx) { if (!_pixelbufferRenderTextures.containsKey(idx)) { final renderTexture = _PixelbufferTexture(); _pixelbufferRenderTextures[idx] = renderTexture; renderTexture.create(idx, ffi.sessionId, ffi); } if (!_gpuRenderTextures.containsKey(idx)) { final renderTexture = _GpuTexture(); _gpuRenderTextures[idx] = renderTexture; renderTexture.create(idx, ffi.sessionId, ffi); } } tryRemoveTexture(int idx) { _control.remove(idx); if (_pixelbufferRenderTextures.containsKey(idx)) { _pixelbufferRenderTextures[idx]!.destroy(true, ffi); _pixelbufferRenderTextures.remove(idx); } if (_gpuRenderTextures.containsKey(idx)) { _gpuRenderTextures[idx]!.destroy(true, ffi); _gpuRenderTextures.remove(idx); } } if (curDisplay == kAllDisplayValue) { final displays = ffi.ffiModel.pi.getCurDisplays(); for (var i = 0; i < displays.length; i++) { tryCreateTexture(i); } } else { tryCreateTexture(curDisplay); for (var i = 0; i < ffi.ffiModel.pi.displays.length; i++) { if (i != curDisplay) { tryRemoveTexture(i); } } } } onRemotePageDispose(bool closeSession) async { final ffi = parent.target; if (ffi == null) return; for (final texture in _pixelbufferRenderTextures.values) { await texture.destroy(closeSession, ffi); } for (final texture in _gpuRenderTextures.values) { await texture.destroy(closeSession, ffi); } } ensureControl(int display) { var ctl = _control[display]; if (ctl == null) { ctl = _Control(); _control[display] = ctl; } } }