mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-08 21:41: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:
@@ -16,6 +16,8 @@ class _PixelbufferTexture {
|
||||
int _display = 0;
|
||||
SessionID? _sessionId;
|
||||
bool _destroying = false;
|
||||
bool _closed = false;
|
||||
int _ptr = 0;
|
||||
int? _id;
|
||||
|
||||
final textureRenderer = TextureRgbaRenderer();
|
||||
@@ -27,11 +29,22 @@ class _PixelbufferTexture {
|
||||
_textureKey = bind.getNextTextureKey();
|
||||
_sessionId = sessionId;
|
||||
|
||||
textureRenderer.createTexture(_textureKey).then((id) async {
|
||||
final textureKey = _textureKey;
|
||||
textureRenderer.createTexture(textureKey).then((id) async {
|
||||
_id = id;
|
||||
if (id != -1) {
|
||||
if (_closed) {
|
||||
// Destroyed while creation was still in flight (rapid
|
||||
// connect/disconnect); nobody else will close this texture.
|
||||
await textureRenderer.closeTexture(textureKey);
|
||||
return;
|
||||
}
|
||||
ffi.textureModel.setRgbaTextureId(display: d, id: id);
|
||||
final ptr = await textureRenderer.getTexturePtr(_textureKey);
|
||||
final ptr = await textureRenderer.getTexturePtr(textureKey);
|
||||
if (_closed) {
|
||||
return;
|
||||
}
|
||||
_ptr = ptr;
|
||||
platformFFI.registerPixelbufferTexture(sessionId, display, ptr);
|
||||
debugPrint(
|
||||
"create pixelbuffer texture: peerId: ${ffi.id} display:$_display, textureId:$id, texturePtr:$ptr");
|
||||
@@ -39,13 +52,17 @@ class _PixelbufferTexture {
|
||||
});
|
||||
}
|
||||
|
||||
destroy(bool unregisterTexture, FFI ffi) async {
|
||||
destroy(bool closeSession, FFI ffi) async {
|
||||
_closed = true;
|
||||
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));
|
||||
if (_ptr != 0) {
|
||||
// Compare-and-clear: clears only if Rust still holds this pointer, so
|
||||
// a registration a new window has already made stays intact (#8016).
|
||||
// Returning from this synchronous call also guarantees no push
|
||||
// through the old pointer is still in flight.
|
||||
platformFFI.unregisterPixelbufferTexture(_sessionId!, display, _ptr);
|
||||
_ptr = 0;
|
||||
}
|
||||
await textureRenderer.closeTexture(_textureKey);
|
||||
_textureKey = -1;
|
||||
@@ -61,6 +78,7 @@ class _GpuTexture {
|
||||
SessionID? _sessionId;
|
||||
final support = bind.mainHasGpuTextureRender();
|
||||
bool _destroying = false;
|
||||
bool _closed = false;
|
||||
int _display = 0;
|
||||
int? _id;
|
||||
int? _output;
|
||||
@@ -79,9 +97,18 @@ class _GpuTexture {
|
||||
gpuTextureRenderer.registerTexture().then((id) async {
|
||||
_id = id;
|
||||
if (id != null) {
|
||||
if (_closed) {
|
||||
// Destroyed while creation was still in flight (rapid
|
||||
// connect/disconnect); nobody else will unregister this texture.
|
||||
await gpuTextureRenderer.unregisterTexture(id);
|
||||
return;
|
||||
}
|
||||
_textureId = id;
|
||||
ffi.textureModel.setGpuTextureId(display: d, id: id);
|
||||
final output = await gpuTextureRenderer.output(id);
|
||||
if (_closed) {
|
||||
return;
|
||||
}
|
||||
_output = output;
|
||||
if (output != null) {
|
||||
platformFFI.registerGpuTexture(sessionId, d, output);
|
||||
@@ -95,20 +122,22 @@ class _GpuTexture {
|
||||
}
|
||||
}
|
||||
|
||||
destroy(bool unregisterTexture, FFI ffi) async {
|
||||
destroy(bool closeSession, FFI ffi) async {
|
||||
// must stop texture render, render unregistered texture cause crash
|
||||
_closed = true;
|
||||
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));
|
||||
final output = _output;
|
||||
if (output != null) {
|
||||
// Compare-and-clear, see _PixelbufferTexture.destroy.
|
||||
platformFFI.unregisterGpuTexture(_sessionId!, _display, output);
|
||||
_output = null;
|
||||
}
|
||||
await gpuTextureRenderer.unregisterTexture(_textureId);
|
||||
_textureId = -1;
|
||||
_destroying = false;
|
||||
debugPrint(
|
||||
"destroy gpu texture: peerId: ${ffi.id} display:$_display, textureId:$_id, output:$_output");
|
||||
"destroy gpu texture: peerId: ${ffi.id} display:$_display, textureId:$_id, output:$output");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user