I have a WPF application that is constantly animating. One interaction (mouseup) and animation in particular seems to freeze the UI thread for long enough to be very noticable.
Concurrency Visualizer shows me my UI thread is blocked for 300 – 500ms. It also shows the stack while it’s blocked and the unblocking stack:
Main thread stack while frozen:
Category = Synchronization
Delay = 495.2472 ms
ntoskrnl.exe!SwapContext_PatchXRstor
ntoskrnl.exe!KiSwapContext
ntoskrnl.exe!KiCommitThreadWait
ntoskrnl.exe!KeWaitForSingleObject
ntoskrnl.exe!NtWaitForSingleObject
ntoskrnl.exe!KiSystemServiceCopyEnd
ntdll.dll!NtWaitForSingleObject
kernelbase.dll!WaitForSingleObjectEx
wpfgfx_v0400.dll!CMilConnection::SynchronizeChannel
wpfgfx_v0400.dll!CMilChannel::SyncFlush
wpfgfx_v0400.dll!MilComposition_SyncFlush
clr.dll!DoNDirectCallWorker
presentationcore.dll!System.Windows.Media.Composition.DUCE+Channel.**SyncFlush**
presentationcore.dll!System.Windows.Media.MediaContext.NotifyChannelMessage
presentationcore.dll!System.Windows.Media.MediaContextNotificationWindow.MessageFilter
windowsbase.dll!MS.Win32.HwndWrapper.WndProc
windowsbase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation
windowsbase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall
windowsbase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen
windowsbase.dll!System.Windows.Threading.Dispatcher.WrappedInvoke
windowsbase.dll!System.Windows.Threading.Dispatcher.InvokeImpl
windowsbase.dll!MS.Win32.HwndSubclass.SubclassWndProc
windowsbase.dll!dynamicClass.IL_STUB_ReversePInvoke
clr.dll!UMThunkStubAMD64
user32.dll!UserCallWinProcCheckWow
user32.dll!DispatchMessageWorker
clr.dll!DoNDirectCall__PatchGetThreadCall
windowsbase.dll!dynamicClass.IL_STUB_PInvoke
windowsbase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl
presentationframework.dll!System.Windows.Application.RunInternal
presentationframework.dll!System.Windows.Application.Run
xqstream.windows.exe!IQ.IR.Stream.WPF.App.Main
Unblocking Stack:
ntoskrnl.exe! ?? ?? ::FNODOBFM::`string'
ntoskrnl.exe!NtSetEvent
ntoskrnl.exe!KiSystemServiceCopyEnd
ntdll.dll!ZwSetEvent
kernelbase.dll!SetEvent
wpfgfx_v0400.dll!CMilConnection::PostMessageToClient
wpfgfx_v0400.dll!CMilServerChannel::SignalFinishedFlush
wpfgfx_v0400.dll!CComposition::FlushChannels
wpfgfx_v0400.dll!CPartitionThread::RenderPartition
wpfgfx_v0400.dll!CPartitionThread::Run
wpfgfx_v0400.dll!CPartitionThread::ThreadMain
The main thread is waiting on a background thread. That background thread is doing a number of things, but this stack takes up ~3/4 of the time:
Blocking thread’s largest task:
Category = I/O
Delay = 347.5122 ms
ntoskrnl.exe!SwapContext_PatchXRstor
ntoskrnl.exe!KiSwapContext
ntoskrnl.exe!KiCommitThreadWait
ntoskrnl.exe!KeWaitForSingleObject
dxgmms1.sys!VIDMM_GLOBAL::CloseOneAllocation
dxgmms1.sys!VidMmCloseAllocation
dxgkrnl.sys!DXGDEVICE::DestroyAllocations
dxgkrnl.sys!DXGDEVICE::ProcessTerminationList
dxgkrnl.sys!DXGDEVICE::TerminateAllocations
dxgkrnl.sys!DXGDEVICE::DestroyAllocation
dxgkrnl.sys!DxgkDestroyAllocation
win32k.sys!NtGdiDdDDIDestroyAllocation
ntoskrnl.exe!KiSystemServiceCopyEnd
gdi32.dll!ZwGdiDdDDIDestroyAllocation
d3d9.dll!DeallocateCB
dlumd64.dll![dlumd64.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
dlumd64.dll![dlumd64.dll]
dlumd64.dll![dlumd64.dll]
d3d9.dll!DdBltLH
d3d9.dll!CSwapChain::PresentMain
d3d9.dll!CSwapChain::Present
wpfgfx_v0400.dll!CD3DDeviceLevel1::PresentWithD3D
wpfgfx_v0400.dll!CD3DDeviceLevel1::Present
wpfgfx_v0400.dll!CHwDisplayRenderTarget::PresentInternal
wpfgfx_v0400.dll!CHwDisplayRenderTarget::Present
wpfgfx_v0400.dll!CHwHWNDRenderTarget::Present
wpfgfx_v0400.dll!CDesktopRenderTarget::Present
wpfgfx_v0400.dll!CDesktopHWNDRenderTarget::Present
wpfgfx_v0400.dll!CSlaveHWndRenderTarget::Present
wpfgfx_v0400.dll!CRenderTargetManager::Present
wpfgfx_v0400.dll!CComposition::Present
wpfgfx_v0400.dll!CPartitionThread::PresentPartition
wpfgfx_v0400.dll!CPartitionThread::Run
wpfgfx_v0400.dll!CPartitionThread::ThreadMain
I’ve read SyncFlush() is called after several MilCore functions and appears to cause the changes that have been sent to be processed immediately (credit to Ray Burns).
I’d like to get rid of this freeze, but I’m also hoping to develop a better understanding of what WPF is doing in this situation so that I can build a better mental model of how WPF works.
From the back and forth stack traces it’s obvious that the main thread is waiting on something to fully flush. I have no idea though what a
SyncFlushis or how you’d work around it.Looking at the long IO stack trace the issue is possibly the Nvidia driver. WPF (
wpfgfx_v0400) is calling into DirectX 9 (d3d9) which is calling into the Nvidia driver (nvd3dumx).Now it’s possible that all of the platforms are behaving appropriately and are doing what your code is telling them to do. It’s also possible that there could be a bug in WPF, DirectX 9, or the Nvidia driver. Most of the time though the issue is with the calling code.
I’m assuming that this application as a mouse-up event handler. If so, looking at what that’s doing (or how it interferes with the animation) will be the most likely source of the performance issue.