mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-18 02:10:59 +03:00
fix: recover DXGI capture after access loss (#16024)
* fix: recover DXGI capture after access loss * fix: track DXGI frame ownership * fix: only recover DXGI access loss * fix: stabilize DXGI recovery after mode switches * fix: preserve DXGI recovery budget * refactor: model DXGI frame lifecycle as enum * docs: clarify DXGI frame cleanup order * fix: scope DXGI frame grace to access loss recovery * fix: preserve DXGI recovery across display changes * chore: log DXGI recovery attempts at debug level
This commit is contained in:
@@ -41,6 +41,13 @@ impl<T> Drop for ComPtr<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum FrameState {
|
||||
Idle,
|
||||
Acquired,
|
||||
Mapped,
|
||||
}
|
||||
|
||||
pub struct Capturer {
|
||||
device: ComPtr<ID3D11Device>,
|
||||
display: Display,
|
||||
@@ -58,6 +65,7 @@ pub struct Capturer {
|
||||
output_texture: bool,
|
||||
adapter_desc1: DXGI_ADAPTER_DESC1,
|
||||
rotate: Rotate,
|
||||
frame_state: FrameState,
|
||||
}
|
||||
|
||||
impl Capturer {
|
||||
@@ -174,6 +182,7 @@ impl Capturer {
|
||||
output_texture: false,
|
||||
adapter_desc1,
|
||||
rotate,
|
||||
frame_state: FrameState::Idle,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -335,6 +344,7 @@ impl Capturer {
|
||||
let mut info = mem::MaybeUninit::uninit().assume_init();
|
||||
|
||||
wrap_hresult((*self.duplication.0).AcquireNextFrame(timeout, &mut info, &mut frame))?;
|
||||
self.frame_state = FrameState::Acquired;
|
||||
let frame = ComPtr(frame);
|
||||
|
||||
if *info.LastPresentTime.QuadPart() == 0 {
|
||||
@@ -345,9 +355,11 @@ impl Capturer {
|
||||
let mut rect = mem::MaybeUninit::uninit().assume_init();
|
||||
if self.fastlane {
|
||||
wrap_hresult((*self.duplication.0).MapDesktopSurface(&mut rect))?;
|
||||
self.frame_state = FrameState::Mapped;
|
||||
} else {
|
||||
self.surface = ComPtr(self.ohgodwhat(frame.0)?);
|
||||
wrap_hresult((*self.surface.0).Map(&mut rect, DXGI_MAP_READ))?;
|
||||
self.frame_state = FrameState::Mapped;
|
||||
}
|
||||
Ok((rect.pBits, rect.Pitch))
|
||||
}
|
||||
@@ -424,7 +436,7 @@ impl Capturer {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.unmap();
|
||||
self.release_frame()?;
|
||||
let r = self.load_frame(timeout)?;
|
||||
let rotate = match self.display.rotation() {
|
||||
DXGI_MODE_ROTATION_IDENTITY | DXGI_MODE_ROTATION_UNSPECIFIED => kRotate0,
|
||||
@@ -472,12 +484,13 @@ impl Capturer {
|
||||
if self.duplication.0.is_null() {
|
||||
return Err(std::io::ErrorKind::AddrNotAvailable.into());
|
||||
}
|
||||
(*self.duplication.0).ReleaseFrame();
|
||||
self.release_frame()?;
|
||||
let mut frame = ptr::null_mut();
|
||||
#[allow(invalid_value)]
|
||||
let mut info = mem::MaybeUninit::uninit().assume_init();
|
||||
|
||||
wrap_hresult((*self.duplication.0).AcquireNextFrame(timeout, &mut info, &mut frame))?;
|
||||
self.frame_state = FrameState::Acquired;
|
||||
let frame = ComPtr(frame);
|
||||
|
||||
if info.AccumulatedFrames == 0 || *info.LastPresentTime.QuadPart() == 0 {
|
||||
@@ -574,16 +587,42 @@ impl Capturer {
|
||||
}
|
||||
}
|
||||
|
||||
fn unmap(&self) {
|
||||
fn release_frame(&mut self) -> io::Result<()> {
|
||||
if self.duplication.is_null() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut first_error = None;
|
||||
// Unmap before ReleaseFrame invalidates the desktop surface; use the same
|
||||
// order for staging surfaces. Cleanup advances Mapped -> Acquired -> Idle,
|
||||
// while Idle is a no-op. Advance state even on errors to avoid retrying
|
||||
// cleanup, but still attempt ReleaseFrame if unmapping fails.
|
||||
unsafe {
|
||||
(*self.duplication.0).ReleaseFrame();
|
||||
if self.fastlane {
|
||||
(*self.duplication.0).UnMapDesktopSurface();
|
||||
} else {
|
||||
if !self.surface.is_null() {
|
||||
(*self.surface.0).Unmap();
|
||||
if self.frame_state == FrameState::Mapped {
|
||||
let result = if self.fastlane {
|
||||
wrap_hresult((*self.duplication.0).UnMapDesktopSurface())
|
||||
} else if !self.surface.is_null() {
|
||||
wrap_hresult((*self.surface.0).Unmap())
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
self.frame_state = FrameState::Acquired;
|
||||
if let Err(err) = result {
|
||||
first_error = Some(err);
|
||||
}
|
||||
}
|
||||
if self.frame_state == FrameState::Acquired {
|
||||
let result = wrap_hresult((*self.duplication.0).ReleaseFrame());
|
||||
self.frame_state = FrameState::Idle;
|
||||
if first_error.is_none() {
|
||||
if let Err(err) = result {
|
||||
first_error = Some(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match first_error {
|
||||
Some(err) => Err(err),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,8 +638,8 @@ impl Capturer {
|
||||
|
||||
impl Drop for Capturer {
|
||||
fn drop(&mut self) {
|
||||
if !self.duplication.is_null() {
|
||||
self.unmap();
|
||||
if let Err(err) = self.release_frame() {
|
||||
eprintln!("DXGI frame cleanup failed: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user