mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-14 08:21:02 +03:00
fix: texture render lifetime protocol, watchdog fallback, startup probe
Root cause of #15848 (and the long-standing macOS #6296 / Linux #3343 class): raw native texture pointers are shared across the platform thread, the engine raster thread and the video thread, with teardown ordered by a 100 ms sleep - or, when moving a tab to a new window, by nothing at all. A lost race frees the texture while it is still in use: the raster thread parks on a destroyed lock (frozen/black view, a never-presented 'transparent' hole, every later session black) and the video thread hangs while holding session locks (app half-dead until restart, still reported as Responding). - unregister textures with compare-and-clear (new session_unregister_* FFI): a late clear can no longer wipe a new window's registration (#8016) and Rust never keeps pushing into a freed texture; the 100 ms sleeps are gone (the plugins now drain in-flight pushes and defer object deletion until the raster thread is done) - guard the async texture create path against destroy racing it (#13596) - per-display locks: the per-frame plugin call no longer holds session-level locks, so a stalled plugin or driver call cannot freeze every window's UI thread - adopt the frame size after 30 consecutive mismatches instead of dropping frames forever (silent black screen on a live connection) - watchdog: frames pushed but never consumed by the engine fall the session back to software rendering live, record texture-render-health, and flip the effective default off; toggling the option clears the record and re-arms validation - Dart raster-stall monitor records a hung raster thread for the next launch (rendering cannot be rescued in-process in that state) - startup probe: render one frame through a 1x1 texture in the main window each launch; failure disables texture rendering before the first session goes black, a pass self-heals a stale failure record Platform defaults are unchanged (macOS off, Win10+ on, Linux on). Requires flutter_texture_rgba_renderer ad4c37e and flutter_gpu_texture_renderer 767bb9f (pinned in pubspec). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,7 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:window_size/window_size.dart' as window_size;
|
||||
import '../widgets/button.dart';
|
||||
import '../widgets/texture_render_probe.dart';
|
||||
|
||||
class DesktopHomePage extends StatefulWidget {
|
||||
const DesktopHomePage({Key? key}) : super(key: key);
|
||||
@@ -60,15 +61,20 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final isIncomingOnly = bind.isIncomingOnly();
|
||||
return _buildBlock(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
return Stack(
|
||||
children: [
|
||||
buildLeftPane(context),
|
||||
if (!isIncomingOnly) const VerticalDivider(width: 1),
|
||||
if (!isIncomingOnly) Expanded(child: buildRightPane(context)),
|
||||
_buildBlock(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildLeftPane(context),
|
||||
if (!isIncomingOnly) const VerticalDivider(width: 1),
|
||||
if (!isIncomingOnly) Expanded(child: buildRightPane(context)),
|
||||
],
|
||||
)),
|
||||
const Positioned(left: 0, top: 0, child: TextureRenderProbe()),
|
||||
],
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBlock({required Widget child}) {
|
||||
|
||||
@@ -156,6 +156,7 @@ class _RemotePageState extends State<RemotePage>
|
||||
widget.tabController?.state.listen(_onMacOSTabStateChanged);
|
||||
}
|
||||
Get.put<FFI>(_ffi, tag: widget.id);
|
||||
_RasterStallMonitor.start();
|
||||
_ffi.imageModel.addCallbackOnFirstImage((String peerId) {
|
||||
_ffi.canvasModel.activateLocalCursor();
|
||||
showKBLayoutTypeChooserIfNeeded(
|
||||
@@ -1402,3 +1403,45 @@ class CursorPaint extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Detects a hung raster thread: the UI keeps scheduling frames but the
|
||||
/// engine never reports completed frame timings. Rendering cannot be rescued
|
||||
/// in-process (software rendering needs the same raster thread), so this only
|
||||
/// records the breakage — the next launch then defaults texture rendering to
|
||||
/// off and the startup probe re-validates the environment.
|
||||
class _RasterStallMonitor {
|
||||
static bool _started = false;
|
||||
static bool _reported = false;
|
||||
static DateTime? _lastTimings;
|
||||
static DateTime? _scheduledSince;
|
||||
|
||||
static void start() {
|
||||
if (_started || isWeb) return;
|
||||
_started = true;
|
||||
SchedulerBinding.instance.addTimingsCallback((_) {
|
||||
_lastTimings = DateTime.now();
|
||||
});
|
||||
Timer.periodic(const Duration(seconds: 2), (_) {
|
||||
if (_reported) return;
|
||||
// A minimized window legitimately stops producing frame timings.
|
||||
if (stateGlobal.isMinimized ||
|
||||
!SchedulerBinding.instance.hasScheduledFrame) {
|
||||
_scheduledSince = null;
|
||||
return;
|
||||
}
|
||||
final now = DateTime.now();
|
||||
_scheduledSince ??= now;
|
||||
final lastTimings = _lastTimings;
|
||||
final stalled =
|
||||
now.difference(_scheduledSince!) > const Duration(seconds: 10) &&
|
||||
(lastTimings == null || lastTimings.isBefore(_scheduledSince!));
|
||||
if (stalled) {
|
||||
_reported = true;
|
||||
bind.mainSetLocalOption(
|
||||
key: kOptionTextureRenderHealth, value: 'failed-raster-stall');
|
||||
debugPrint(
|
||||
'raster thread stall detected, texture rendering disabled for next launch');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user