1+ using System ;
2+ using System . Runtime . CompilerServices ;
3+ using System . Runtime . InteropServices ;
4+ using System . Threading ;
5+ using ScreenCapture . NET . Downscale ;
6+ using SharpGen . Runtime ;
7+ using Vortice . Direct3D9 ;
8+
9+ namespace ScreenCapture . NET ;
10+
11+ /// <summary>
12+ /// Represents a ScreenCapture using DirectX 11 desktop duplicaton.
13+ /// https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/desktop-dup-api
14+ /// </summary>
15+ // ReSharper disable once InconsistentNaming
16+ public sealed class DX9ScreenCapture : AbstractScreenCapture < ColorBGRA >
17+ {
18+ #region Properties & Fields
19+
20+ private readonly object _captureLock = new ( ) ;
21+
22+ private readonly IDirect3D9 _direct3D9 ;
23+ private IDirect3DDevice9 ? _device ;
24+ private IDirect3DSurface9 ? _surface ;
25+ private byte [ ] ? _buffer ;
26+ private int _stride ;
27+
28+ #endregion
29+
30+ #region Constructors
31+
32+ /// <summary>
33+ /// Initializes a new instance of the <see cref="DX9ScreenCapture"/> class.
34+ /// </summary>
35+ /// <param name="display">The <see cref="Display"/> to duplicate.</param>
36+ public DX9ScreenCapture ( IDirect3D9 direct3D9 , Display display )
37+ : base ( display )
38+ {
39+ this . _direct3D9 = direct3D9 ;
40+
41+ Restart ( ) ;
42+ }
43+
44+ #endregion
45+
46+ #region Methods
47+
48+ protected override bool PerformScreenCapture ( )
49+ {
50+ bool result = false ;
51+ lock ( _captureLock )
52+ {
53+ if ( ( _device == null ) || ( _surface == null ) || ( _buffer == null ) )
54+ {
55+ Restart ( ) ;
56+ return false ;
57+ }
58+
59+ try
60+ {
61+ _device . GetFrontBufferData ( 0 , _surface ) ;
62+
63+ LockedRectangle dr = _surface . LockRect ( LockFlags . NoSystemLock | LockFlags . ReadOnly ) ;
64+
65+ nint ptr = dr . DataPointer ;
66+ for ( int y = 0 ; y < Display . Height ; y ++ )
67+ {
68+ Marshal . Copy ( ptr , _buffer , y * _stride , _stride ) ;
69+ ptr += dr . Pitch ;
70+ }
71+
72+ _surface . UnlockRect ( ) ;
73+
74+ result = true ;
75+ }
76+ catch ( SharpGenException dxException )
77+ {
78+ if ( dxException . ResultCode == Result . AccessDenied )
79+ {
80+ try
81+ {
82+ Restart ( ) ;
83+ }
84+ catch { Thread . Sleep ( 100 ) ; }
85+ }
86+ }
87+ }
88+
89+ return result ;
90+ }
91+
92+ protected override void PerformCaptureZoneUpdate ( CaptureZone < ColorBGRA > captureZone , in Span < byte > buffer )
93+ {
94+ if ( _buffer == null ) return ;
95+
96+ using IDisposable @lock = captureZone . Lock ( ) ;
97+ {
98+ if ( captureZone . DownscaleLevel == 0 )
99+ CopyZone ( captureZone , buffer ) ;
100+ else
101+ DownscaleZone ( captureZone , buffer ) ;
102+ }
103+ }
104+
105+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
106+ private void CopyZone ( CaptureZone < ColorBGRA > captureZone , in Span < byte > buffer )
107+ {
108+ ReadOnlySpan < ColorBGRA > source = MemoryMarshal . Cast < byte , ColorBGRA > ( _buffer ) ;
109+ Span < ColorBGRA > target = MemoryMarshal . Cast < byte , ColorBGRA > ( buffer ) ;
110+
111+ int offsetX = captureZone . X ;
112+ int offsetY = captureZone . Y ;
113+ int width = captureZone . Width ;
114+ int height = captureZone . Height ;
115+
116+ for ( int y = 0 ; y < height ; y ++ )
117+ {
118+ int sourceOffset = ( ( y + offsetY ) * Display . Width ) + offsetX ;
119+ int targetOffset = y * width ;
120+
121+ source . Slice ( sourceOffset , width ) . CopyTo ( target . Slice ( targetOffset , width ) ) ;
122+ }
123+ }
124+
125+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
126+ private void DownscaleZone ( CaptureZone < ColorBGRA > captureZone , in Span < byte > buffer )
127+ {
128+ ReadOnlySpan < byte > source = _buffer ;
129+ Span < byte > target = buffer ;
130+
131+ int blockSize = captureZone . DownscaleLevel switch
132+ {
133+ 1 => 2 ,
134+ 2 => 4 ,
135+ 3 => 8 ,
136+ 4 => 16 ,
137+ 5 => 32 ,
138+ 6 => 64 ,
139+ 7 => 128 ,
140+ 8 => 256 ,
141+ _ => ( int ) Math . Pow ( 2 , captureZone . DownscaleLevel ) ,
142+ } ;
143+
144+ int offsetX = captureZone . X ;
145+ int offsetY = captureZone . Y ;
146+ int width = captureZone . Width ;
147+ int height = captureZone . Height ;
148+ int stride = captureZone . Stride ;
149+ int bpp = captureZone . ColorFormat . BytesPerPixel ;
150+ int unscaledWith = captureZone . UnscaledWidth ;
151+
152+ Span < byte > scaleBuffer = stackalloc byte [ bpp ] ;
153+ for ( int y = 0 ; y < height ; y ++ )
154+ for ( int x = 0 ; x < width ; x ++ )
155+ {
156+ AverageByteSampler . Sample ( new SamplerInfo < byte > ( ( x + offsetX ) * blockSize , ( y + offsetY ) * blockSize , blockSize , blockSize , unscaledWith , bpp , source ) , scaleBuffer ) ;
157+
158+ int targetOffset = ( y * stride ) + ( x * bpp ) ;
159+
160+ // DarthAffe 07.09.2023: Unroll as optimization since we know it's always 4 bpp - not ideal but it does quite a lot
161+ target [ targetOffset ] = scaleBuffer [ 0 ] ;
162+ target [ targetOffset + 1 ] = scaleBuffer [ 1 ] ;
163+ target [ targetOffset + 2 ] = scaleBuffer [ 2 ] ;
164+ target [ targetOffset + 3 ] = scaleBuffer [ 3 ] ;
165+ }
166+ }
167+
168+ /// <inheritdoc />
169+ public override void Restart ( )
170+ {
171+ base . Restart ( ) ;
172+
173+ lock ( _captureLock )
174+ {
175+ DisposeDX ( ) ;
176+
177+ try
178+ {
179+ PresentParameters presentParameters = new ( )
180+ {
181+ BackBufferWidth = Display . Width ,
182+ BackBufferHeight = Display . Height ,
183+ Windowed = true ,
184+ SwapEffect = SwapEffect . Discard
185+ } ;
186+ _device = _direct3D9 . CreateDevice ( Display . Index , DeviceType . Hardware , IntPtr . Zero , CreateFlags . SoftwareVertexProcessing , presentParameters ) ;
187+ _surface = _device . CreateOffscreenPlainSurface ( Display . Width , Display . Height , Format . A8R8G8B8 , Pool . Scratch ) ;
188+ _stride = Display . Width * ColorBGRA . ColorFormat . BytesPerPixel ;
189+ _buffer = new byte [ Display . Height * _stride ] ;
190+ }
191+ catch
192+ {
193+ DisposeDX ( ) ;
194+ }
195+ }
196+ }
197+
198+ protected override void Dispose ( bool disposing )
199+ {
200+ base . Dispose ( disposing ) ;
201+ DisposeDX ( ) ;
202+ }
203+
204+ private void DisposeDX ( )
205+ {
206+ try
207+ {
208+ try { _surface ? . Dispose ( ) ; } catch { /**/ }
209+ try { _device ? . Dispose ( ) ; } catch { /**/ }
210+ _buffer = null ;
211+ _device = null ;
212+ _surface = null ;
213+ }
214+ catch { /**/ }
215+ }
216+
217+ #endregion
218+ }
0 commit comments