@@ -133,100 +133,119 @@ public async Task UpdateWriteableBitmap(PixelSize currentSize,
133133 {
134134 var target = _controller . GetRootVisualTarget ( ) ;
135135
136- if ( target is null || currentSize . Height == 0 || currentSize . Width == 0 )
136+ if ( target is null )
137137 return ;
138138
139- // ReSharper disable once SuspiciousTypeConversion.Global
140- var compositor = ( ( ICompositionObject ) target ) . Compositor ( ) ;
141- // ReSharper disable once SuspiciousTypeConversion.Global
142- var compositorCapture = ( ICompositionCaptureTest ) compositor ;
143-
144- // https://github.com/ocalvo/WorkTests/blob/a2f18151be579addc60d4464b1e2a9f54f8e3314/MediaTestManaged/DCompHelpers.cs#L162
145-
146- // Render Visual:
147- // * This function is basically async and returns immediately after putting
148- // a MILCMD onto the batch for the application channel, and marks the device dirty.
149- // * It returns two handles by reference.
150- // * The first handle (hMap) is to a map of bits.
151- // * The second handle (hEvent) is to an event.
152- // * The event is signaled when a commit has happened
153- // and the after actual renderpass has been rendered and presented.
154- // * Once signaled, the bits are ready for us to grab.
155- // * Any changes to the tree before the implicit commit sends the batch to the
156- // Compositor will be reflected in the capture, even if made after the initial
157- // RenderVisual function call.
158- // * Any changes to the tree after the implicit commit may safely modify the tree,
159- // as they will be processed in a separate batch
160- var hMap = IntPtr . Zero ;
161- var hEvent = IntPtr . Zero ;
162-
163- var hr = compositorCapture . RenderVisual (
164- target ,
165- 0 , // offset X
166- 0 , // offset y
167- ( uint ) currentSize . Width ,
168- ( uint ) currentSize . Height ,
169- CompositionCaptureTestBitmapPixelFormat . Bgra8 ,
170- ref hMap ,
171- ref hEvent ,
172- out var cbMap ) ;
173- if ( hr != 0 )
174- {
175- throw new COMException ( "Render Visual Failed" , new Win32Exception ( hr ) ) ;
176- }
177-
178139 try
179140 {
180- using var wh = new ManualResetEvent ( false ) ;
181- wh . SafeWaitHandle = new SafeWaitHandle ( hEvent , ownsHandle : false ) ;
182-
183- var tcs = new TaskCompletionSource < bool > ( ) ;
184- ThreadPool . RegisterWaitForSingleObject ( wh , static ( state , timedOut ) =>
185- {
186- var tcs = ( TaskCompletionSource < bool > ) state ! ;
187- tcs . SetResult ( ! timedOut ) ;
188- } , tcs , TimeSpan . FromMilliseconds ( 100 ) , true ) ;
141+ if ( currentSize . Height == 0 || currentSize . Width == 0 )
142+ return ;
189143
190- if ( ! await tcs . Task )
144+ // ReSharper disable once SuspiciousTypeConversion.Global
145+ var compositor = ( ( ICompositionObject ) target ) . Compositor ( ) ;
146+ // ReSharper disable once SuspiciousTypeConversion.Global
147+ var compositorCapture = ( ICompositionCaptureTest ) compositor ;
148+
149+ // https://github.com/ocalvo/WorkTests/blob/a2f18151be579addc60d4464b1e2a9f54f8e3314/MediaTestManaged/DCompHelpers.cs#L162
150+
151+ // Render Visual:
152+ // * This function is basically async and returns immediately after putting
153+ // a MILCMD onto the batch for the application channel, and marks the device dirty.
154+ // * It returns two handles by reference.
155+ // * The first handle (hMap) is to a map of bits.
156+ // * The second handle (hEvent) is to an event.
157+ // * The event is signaled when a commit has happened
158+ // and the after actual renderpass has been rendered and presented.
159+ // * Once signaled, the bits are ready for us to grab.
160+ // * Any changes to the tree before the implicit commit sends the batch to the
161+ // Compositor will be reflected in the capture, even if made after the initial
162+ // RenderVisual function call.
163+ // * Any changes to the tree after the implicit commit may safely modify the tree,
164+ // as they will be processed in a separate batch
165+ var hMap = IntPtr . Zero ;
166+ var hEvent = IntPtr . Zero ;
167+
168+ var hr = compositorCapture . RenderVisual (
169+ target ,
170+ 0 , // offset X
171+ 0 , // offset y
172+ ( uint ) currentSize . Width ,
173+ ( uint ) currentSize . Height ,
174+ CompositionCaptureTestBitmapPixelFormat . Bgra8 ,
175+ ref hMap ,
176+ ref hEvent ,
177+ out var cbMap ) ;
178+ if ( hr != 0 )
191179 {
192- // Timeout, ignore
193- return ;
180+ throw new COMException ( "Render Visual Failed" , new Win32Exception ( hr ) ) ;
194181 }
195182
196- using ( producer . GetNextFrame ( currentSize , out var frame ) )
183+ try
197184 {
198- var pbMap = PInvoke . MapViewOfFile (
199- new HANDLE ( hMap ) , FILE_MAP . FILE_MAP_WRITE , 0 , 0 , UIntPtr . Zero ) ;
185+ using var wh = new ManualResetEvent ( false ) ;
186+ wh . SafeWaitHandle = new SafeWaitHandle ( hEvent , ownsHandle : false ) ;
200187
201- if ( pbMap != IntPtr . Zero )
188+ var tcs = new TaskCompletionSource < bool > ( ) ;
189+ RegisteredWaitHandle ? registeredWaitHandle = null ;
190+ try
202191 {
203- try
192+ registeredWaitHandle = ThreadPool . RegisterWaitForSingleObject ( wh , static ( state , timedOut ) =>
204193 {
205- using var buf = frame . Lock ( ) ;
206- unsafe
207- {
208- Buffer . MemoryCopy (
209- source : pbMap ,
210- destination : ( void * ) buf . Address ,
211- destinationSizeInBytes : buf . RowBytes * currentSize . Height ,
212- sourceBytesToCopy : cbMap
213- ) ;
214- }
194+ var tcs = ( TaskCompletionSource < bool > ) state ! ;
195+ tcs . SetResult ( ! timedOut ) ;
196+ } , tcs , TimeSpan . FromMilliseconds ( 100 ) , true ) ;
197+
198+ if ( ! await tcs . Task )
199+ {
200+ // Timeout, ignore this frame but keep the capture loop alive.
201+ return ;
215202 }
216- finally
203+ }
204+ finally
205+ {
206+ registeredWaitHandle ? . Unregister ( null ) ;
207+ }
208+
209+ using ( producer . GetNextFrame ( currentSize , out var frame ) )
210+ {
211+ var pbMap = PInvoke . MapViewOfFile (
212+ new HANDLE ( hMap ) , FILE_MAP . FILE_MAP_WRITE , 0 , 0 , UIntPtr . Zero ) ;
213+
214+ if ( pbMap != IntPtr . Zero )
217215 {
218- PInvoke . UnmapViewOfFile ( pbMap ) ;
216+ try
217+ {
218+ using var buf = frame . Lock ( ) ;
219+ unsafe
220+ {
221+ Buffer . MemoryCopy (
222+ source : pbMap ,
223+ destination : ( void * ) buf . Address ,
224+ destinationSizeInBytes : buf . RowBytes * currentSize . Height ,
225+ sourceBytesToCopy : cbMap
226+ ) ;
227+ }
228+ }
229+ finally
230+ {
231+ PInvoke . UnmapViewOfFile ( pbMap ) ;
232+ }
219233 }
220234 }
221235 }
236+ finally
237+ {
238+ PInvoke . CloseHandle ( new HANDLE ( hMap ) ) ;
239+ PInvoke . CloseHandle ( new HANDLE ( hEvent ) ) ;
240+ }
222241 }
223242 finally
224243 {
225- PInvoke . CloseHandle ( new HANDLE ( hMap ) ) ;
226- PInvoke . CloseHandle ( new HANDLE ( hEvent ) ) ;
244+ if ( ! Disposed )
245+ {
246+ _commitAsyncLoopHandler . RegisterNext ( ) ;
247+ }
227248 }
228-
229- _commitAsyncLoopHandler . RegisterNext ( ) ;
230249 }
231250
232251 internal EventHandler ? GetCursorChanged ( ) => _cursorChangedHandler ;
0 commit comments