fix: address texture watchdog/probe review findings

- watchdog: compare the plugin's cumulative consumed counter against a
  snapshot taken when arming (re-arm was a no-op before), and judge on
  cumulative pushes + elapsed time so sparse damage-driven streams are
  still detected
- failure handling is idempotent (one record per breakage) and updates
  every render session like main_set_local_option does, not only the
  failing one; the fallback toast is not claimed for multi-display
  windows the soft path cannot rescue
- probe: skip when the consumed API is missing (old plugin) or a
  raster-stall is recorded (compositing could hang the main window);
  a fail verdict requires fresh frame timings and a non-minimized
  window; a pass never clears failed-raster-stall; toast only when
  texture rendering was effectively on; probe pixel is transparent
- raster-stall monitor: judge on frame-timing staleness (a mid-episode
  hang was undetectable before), gate on lifecycle/minimized/idle with
  a moving quiet anchor, threshold 30s, shared with the camera page;
  clear stateGlobal minimized flag on plain window restore
- adopt mismatched frame sizes only in multi-ui-session mode (legacy
  mode pairs frames loosely and could ping-pong between displays)
- drop the now-dead closeSession parameter from texture destroy();
  trim comments to repo style

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
rustdesk
2026-08-13 11:22:13 +08:00
parent c5adac828b
commit 7da2bbe6ac
10 changed files with 201 additions and 111 deletions

View File

@@ -52,15 +52,14 @@ class _PixelbufferTexture {
});
}
destroy(bool closeSession, FFI ffi) async {
destroy(FFI ffi) async {
_closed = true;
if (!_destroying && _textureKey != -1 && _sessionId != null) {
_destroying = true;
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.
// Compare-and-clear: only clears if Rust still holds this pointer
// (#8016-safe); returning from this synchronous call also means no
// push through the old pointer is still in flight.
platformFFI.unregisterPixelbufferTexture(_sessionId!, display, _ptr);
_ptr = 0;
}
@@ -122,7 +121,7 @@ class _GpuTexture {
}
}
destroy(bool closeSession, FFI ffi) async {
destroy(FFI ffi) async {
// must stop texture render, render unregistered texture cause crash
_closed = true;
if (!_destroying && support && _sessionId != null && _textureId != -1) {
@@ -229,11 +228,11 @@ class TextureModel {
tryRemoveTexture(int idx) {
_control.remove(idx);
if (_pixelbufferRenderTextures.containsKey(idx)) {
_pixelbufferRenderTextures[idx]!.destroy(true, ffi);
_pixelbufferRenderTextures[idx]!.destroy(ffi);
_pixelbufferRenderTextures.remove(idx);
}
if (_gpuRenderTextures.containsKey(idx)) {
_gpuRenderTextures[idx]!.destroy(true, ffi);
_gpuRenderTextures[idx]!.destroy(ffi);
_gpuRenderTextures.remove(idx);
}
}
@@ -253,25 +252,25 @@ class TextureModel {
}
}
onRemotePageDispose(bool closeSession) async {
onRemotePageDispose() async {
final ffi = parent.target;
if (ffi == null) return;
for (final texture in _pixelbufferRenderTextures.values) {
await texture.destroy(closeSession, ffi);
await texture.destroy(ffi);
}
for (final texture in _gpuRenderTextures.values) {
await texture.destroy(closeSession, ffi);
await texture.destroy(ffi);
}
}
onViewCameraPageDispose(bool closeSession) async {
onViewCameraPageDispose() async {
final ffi = parent.target;
if (ffi == null) return;
for (final texture in _pixelbufferRenderTextures.values) {
await texture.destroy(closeSession, ffi);
await texture.destroy(ffi);
}
for (final texture in _gpuRenderTextures.values) {
await texture.destroy(closeSession, ffi);
await texture.destroy(ffi);
}
}