From 7c830e76c6ca2327dd352e235128029d3be92d2d Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 24 Aug 2026 19:11:22 +0800 Subject: [PATCH] fix: reuse the dxgi staging texture instead of one per frame ohgodwhat() created a full screen D3D11_USAGE_STAGING texture for every captured frame and pinned each one with SetEvictionPriority(MAXIMUM). Because D3D11 resource destruction may be deferred, that per-frame churn can accumulate a large amount of graphics kernel paged pool on affected drivers. Keep a single staging texture and rebuild it only when the desktop image changes shape. Also check the IDXGISurface QueryInterface result, so a failure can no longer leave surface null while readable holds a valid texture. Reported in #15945. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011sw75MSAz7PTqrSALdStXe --- libs/scrap/src/dxgi/mod.rs | 49 +++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/libs/scrap/src/dxgi/mod.rs b/libs/scrap/src/dxgi/mod.rs index 1f5296954..ad731c57c 100644 --- a/libs/scrap/src/dxgi/mod.rs +++ b/libs/scrap/src/dxgi/mod.rs @@ -48,6 +48,7 @@ pub struct Capturer { duplication: ComPtr, fastlane: bool, surface: ComPtr, + readable: ComPtr, texture: ComPtr, width: usize, height: usize, @@ -163,6 +164,7 @@ impl Capturer { duplication: ComPtr(duplication), fastlane: desc.DesktopImageInSystemMemory == TRUE, surface: ComPtr(ptr::null_mut()), + readable: ComPtr(ptr::null_mut()), texture: ComPtr(ptr::null_mut()), width: display.width() as usize, height: display.height() as usize, @@ -346,14 +348,14 @@ impl Capturer { if self.fastlane { wrap_hresult((*self.duplication.0).MapDesktopSurface(&mut rect))?; } else { - self.surface = ComPtr(self.ohgodwhat(frame.0)?); + self.ohgodwhat(frame.0)?; wrap_hresult((*self.surface.0).Map(&mut rect, DXGI_MAP_READ))?; } Ok((rect.pBits, rect.Pitch)) } // copy from GPU memory to system memory - unsafe fn ohgodwhat(&mut self, frame: *mut IDXGIResource) -> io::Result<*mut IDXGISurface> { + unsafe fn ohgodwhat(&mut self, frame: *mut IDXGIResource) -> io::Result<()> { let mut texture: *mut ID3D11Texture2D = ptr::null_mut(); (*frame).QueryInterface( &IID_ID3D11Texture2D, @@ -370,24 +372,37 @@ impl Capturer { texture_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; texture_desc.MiscFlags = 0; - let mut readable = ptr::null_mut(); - wrap_hresult((*self.device.0).CreateTexture2D( - &mut texture_desc, - ptr::null(), - &mut readable, - ))?; - (*readable).SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM); - let readable = ComPtr(readable); + // Avoid per-frame staging texture allocation and the kernel allocation churn it causes. + let mut current: D3D11_TEXTURE2D_DESC = mem::zeroed(); + if !self.surface.is_null() { + (*self.readable.0).GetDesc(&mut current); + } + if current.Width != texture_desc.Width + || current.Height != texture_desc.Height + || current.Format != texture_desc.Format + { + let mut readable = ptr::null_mut(); + wrap_hresult((*self.device.0).CreateTexture2D( + &mut texture_desc, + ptr::null(), + &mut readable, + ))?; + (*readable).SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM); + let readable = ComPtr(readable); - let mut surface = ptr::null_mut(); - (*readable.0).QueryInterface( - &IID_IDXGISurface, - &mut surface as *mut *mut _ as *mut *mut _, - ); + let mut surface = ptr::null_mut(); + wrap_hresult((*readable.0).QueryInterface( + &IID_IDXGISurface, + &mut surface as *mut *mut _ as *mut *mut _, + ))?; - (*self.context.0).CopyResource(readable.0 as *mut _, texture.0 as *mut _); + self.readable = readable; + self.surface = ComPtr(surface); + } - Ok(surface) + (*self.context.0).CopyResource(self.readable.0 as *mut _, texture.0 as *mut _); + + Ok(()) } pub fn frame<'a>(&'a mut self, timeout: UINT) -> io::Result> {