2929use std:: path:: Path ;
3030use std:: { fs, io, slice} ;
3131
32+ use rayon:: iter:: { IntoParallelIterator , ParallelIterator } ;
3233use windows:: Win32 :: Graphics :: Direct3D11 :: {
3334 D3D11_BOX , D3D11_CPU_ACCESS_READ , D3D11_CPU_ACCESS_WRITE , D3D11_MAP_READ_WRITE , D3D11_MAPPED_SUBRESOURCE ,
3435 D3D11_TEXTURE2D_DESC , D3D11_USAGE_STAGING , ID3D11Device , ID3D11DeviceContext , ID3D11Texture2D ,
@@ -100,6 +101,8 @@ pub struct DxgiDuplicationApi {
100101 duplication : IDXGIOutputDuplication ,
101102 /// Description of the duplication, including format and dimensions.
102103 duplication_desc : DXGI_OUTDUPL_DESC ,
104+ /// Whether the internal staging texture is currently holding a frame.
105+ is_holding_frame : bool ,
103106}
104107
105108impl DxgiDuplicationApi {
@@ -141,7 +144,7 @@ impl DxgiDuplicationApi {
141144 // Get the duplication description to determine the format for our internal texture.
142145 let duplication_desc = unsafe { duplication. GetDesc ( ) } ;
143146
144- Ok ( Self { d3d_device, d3d_device_context, duplication, duplication_desc } )
147+ Ok ( Self { d3d_device, d3d_device_context, duplication, duplication_desc, is_holding_frame : false } )
145148 }
146149
147150 /// Gets the underlying [`windows::Win32::Graphics::Direct3D11::ID3D11Device`] associated with
@@ -199,21 +202,26 @@ impl DxgiDuplicationApi {
199202 let mut frame_info = DXGI_OUTDUPL_FRAME_INFO :: default ( ) ;
200203 let mut resource = None ;
201204
205+ // Release the previous frame if we were holding one
206+ if self . is_holding_frame {
207+ unsafe { self . duplication . ReleaseFrame ( ) } ?;
208+ }
209+
202210 // Acquire frame
203- unsafe {
204- match self . duplication . AcquireNextFrame ( timeout_ms, & mut frame_info, & mut resource) {
205- Ok ( ( ) ) => ( ) ,
206- Err ( e) => {
207- if e. code ( ) == DXGI_ERROR_WAIT_TIMEOUT {
208- return Err ( Error :: Timeout ) ;
209- } else if e. code ( ) == DXGI_ERROR_ACCESS_LOST {
210- return Err ( Error :: AccessLost ) ;
211- } else {
212- return Err ( Error :: WindowsError ( e) ) ;
213- }
211+ match unsafe { self . duplication . AcquireNextFrame ( timeout_ms, & mut frame_info, & mut resource) } {
212+ Ok ( ( ) ) => ( ) ,
213+ Err ( e) => {
214+ if e. code ( ) == DXGI_ERROR_WAIT_TIMEOUT {
215+ return Err ( Error :: Timeout ) ;
216+ } else if e. code ( ) == DXGI_ERROR_ACCESS_LOST {
217+ return Err ( Error :: AccessLost ) ;
218+ } else {
219+ return Err ( Error :: WindowsError ( e) ) ;
214220 }
215221 }
216222 }
223+ self . is_holding_frame = true ;
224+
217225 let resource = resource. unwrap ( ) ;
218226
219227 // Convert the resource to an ID3D11Texture2D.
@@ -458,6 +466,12 @@ impl<'a> DxgiDuplicationFrame<'a> {
458466 return Err ( Error :: InvalidStagingTexture ( "format must match the frame" ) ) ;
459467 }
460468
469+ // Unmap if was previously mapped
470+ if staging. is_mapped ( ) {
471+ unsafe { self . d3d_device_context . Unmap ( staging. texture ( ) , 0 ) } ;
472+ staging. set_mapped ( false ) ;
473+ }
474+
461475 // Copy the acquired duplication texture into the provided staging texture
462476 unsafe {
463477 self . d3d_device_context . CopyResource ( staging. texture ( ) , & self . texture ) ;
@@ -468,6 +482,7 @@ impl<'a> DxgiDuplicationFrame<'a> {
468482 unsafe {
469483 self . d3d_device_context . Map ( staging. texture ( ) , 0 , D3D11_MAP_READ_WRITE , 0 , Some ( & mut mapped) ) ?;
470484 }
485+ staging. set_mapped ( true ) ;
471486
472487 // SAFETY: staging lives for 's and remains alive while the FrameBuffer is borrowed.
473488 let mapped_frame_data = unsafe {
@@ -521,6 +536,12 @@ impl<'a> DxgiDuplicationFrame<'a> {
521536 return Err ( Error :: InvalidStagingTexture ( "staging texture too small for crop region" ) ) ;
522537 }
523538
539+ // Unmap if was previously mapped
540+ if staging. is_mapped ( ) {
541+ unsafe { self . d3d_device_context . Unmap ( staging. texture ( ) , 0 ) } ;
542+ staging. set_mapped ( false ) ;
543+ }
544+
524545 // Define the source region to copy
525546 let src_box = D3D11_BOX { left : start_x, top : start_y, front : 0 , right : end_x, bottom : end_y, back : 1 } ;
526547
@@ -543,6 +564,7 @@ impl<'a> DxgiDuplicationFrame<'a> {
543564 unsafe {
544565 self . d3d_device_context . Map ( staging. texture ( ) , 0 , D3D11_MAP_READ_WRITE , 0 , Some ( & mut mapped) ) ?;
545566 }
567+ staging. set_mapped ( true ) ;
546568
547569 // SAFETY: staging lives for 's and remains alive while the FrameBuffer is borrowed.
548570 let mapped_frame_data =
@@ -585,15 +607,6 @@ impl<'a> DxgiDuplicationFrame<'a> {
585607 }
586608}
587609
588- impl Drop for DxgiDuplicationFrame < ' _ > {
589- fn drop ( & mut self ) {
590- // Release the frame back to the duplication interface.
591- unsafe {
592- let _ = self . duplication . ReleaseFrame ( ) ;
593- }
594- }
595- }
596-
597610/// Represents a frame buffer containing pixel data.
598611///
599612/// # Example
@@ -668,6 +681,43 @@ impl<'a> DxgiDuplicationFrameBuffer<'a> {
668681 self . width * 4 != self . row_pitch
669682 }
670683
684+ /// Gets the pixel data without padding.
685+ #[ inline]
686+ #[ must_use]
687+ pub fn as_nopadding_buffer < ' b > ( & ' b self , buffer : & ' b mut Vec < u8 > ) -> & ' b [ u8 ] {
688+ if !self . has_padding ( ) {
689+ return self . raw_buffer ;
690+ }
691+
692+ let multiplier = match self . color_format {
693+ ColorFormat :: Rgba16F => 8 ,
694+ ColorFormat :: Rgba8 => 4 ,
695+ ColorFormat :: Bgra8 => 4 ,
696+ } ;
697+
698+ let frame_size = ( self . width * self . height * multiplier) as usize ;
699+ if buffer. capacity ( ) < frame_size {
700+ buffer. resize ( frame_size, 0 ) ;
701+ }
702+
703+ let width_size = ( self . width * multiplier) as usize ;
704+ let buffer_address = buffer. as_mut_ptr ( ) as isize ;
705+ ( 0 ..self . height ) . into_par_iter ( ) . for_each ( |y| {
706+ let index = ( y * self . row_pitch ) as usize ;
707+ let ptr = buffer_address as * mut u8 ;
708+
709+ unsafe {
710+ std:: ptr:: copy_nonoverlapping (
711+ self . raw_buffer . as_ptr ( ) . add ( index) ,
712+ ptr. add ( y as usize * width_size) ,
713+ width_size,
714+ ) ;
715+ }
716+ } ) ;
717+
718+ & buffer[ 0 ..frame_size]
719+ }
720+
671721 /// Gets the raw pixel data, which may include padding.
672722 #[ inline]
673723 #[ must_use]
@@ -681,7 +731,12 @@ impl<'a> DxgiDuplicationFrameBuffer<'a> {
681731 let width = self . width ;
682732 let height = self . height ;
683733
684- let bytes = ImageEncoder :: new ( format, self . color_format ) ?. encode ( self . as_raw_buffer ( ) , width, height) ?;
734+ let mut buffer = Vec :: new ( ) ;
735+ let bytes = ImageEncoder :: new ( format, self . color_format ) ?. encode (
736+ self . as_nopadding_buffer ( & mut buffer) ,
737+ width,
738+ height,
739+ ) ?;
685740
686741 fs:: write ( path, bytes) ?;
687742
0 commit comments