WPF起步(上) --- WPF是如何把图像画到屏幕上
在做下面的研究前,我是一个WPF白痴。只会花一个简单的button。我的目的很简单,弄清楚WPF如何工作的。
我要解决的第一个问题是WPF如何Render的。据说是用DX,但是一定要亲自证实下才可以相信。怎么做呢,先找一个UI复杂的程序,这里选择了MSDN的sample:
http://msdn2.microsoft.com/en-us/library/ms771766.aspx
运行起来后,选择一个复杂的背景,我选择了用Video当背景。然后用Windbg断下来,接下来用~命令检查哪些线程比较繁忙,看到繁忙的线程是:
0:006> kL
ChildEBP RetAddr
0363fdf8 77c40d8f ntdll!KiFastSystemCallRet
0363fe18 4e040b1c GDI32!NtGdiDdDestroySurface+0xc
0363fe30 4e055ed7 d3d9!DdDestroySurface+0x1c
0363fe54 4e05624d d3d9!CMipMap::~CMipMap+0x67
0363fe60 4e04c442 d3d9!CMipMap::`scalar deleting destructor'+0xd
0363fe70 4e060b7d d3d9!CBaseObject::ReleaseImpl+0x42
0363fe80 531bf894 d3d9!CCubeSurface::Release+0x3d
0363fe8c 531b5aa9 MilCore!CD3DTextureSurface::ReleaseD3DResources+0x12
0363fe98 531b5b0e MilCore!CD3DResourceManager::DestroyResource+0xd
0363feb4 531bf2bb MilCore!CD3DResourceManager::DestroyResources+0x4c
0363fec4 531bf2e4 MilCore!CD3DDeviceLevel1::AdvanceFrame+0x30
0363fed4 531a73c3 MilCore!CHwDisplayRenderTarget::AdvanceFrame+0x27
0363fef0 531a7172 MilCore!CDesktopRenderTarget::AdvanceFrame+0x24
0363ff00 531a7144 MilCore!CSlaveHWndRenderTarget::AdvanceFrame+0x26
0363ff10 531a6f7b MilCore!CRenderTargetManager::AdvanceFrame+0x2d
0363ff24 531a70a6 MilCore!CRenderTargetManager::Present+0xca
0363ff74 531a6ffe MilCore!CComposition::Present+0x146
0363ff8c 5319c071 MilCore!CPartitionThread::PresentPartition+0x15
0363ffa0 53208d55 MilCore!CPartitionThread::Run+0x3c
0363ffb8 77e64829 MilCore!CPartitionThread::ThreadMain+0x1e
0:015> kL
ChildEBP RetAddr
04fbf794 7c827d0b ntdll!KiFastSystemCallRet
04fbf798 77e61d1e ntdll!NtWaitForSingleObject+0xc
04fbf808 77e61c8d KERNEL32!WaitForSingleObjectEx+0xac
04fbf81c 7560349d KERNEL32!WaitForSingleObject+0x12
04fbf838 4f9871b1 quartz!CBaseAllocator::GetBuffer+0x52
04fbf854 4f9ace41 wmp!CBaseOutputPin::GetDeliveryBuffer+0x24
04fbf89c 4f9ad410 wmp!CWMNodeFilter::SuckOutOutput+0xf8
04fbf8bc 4f9ae03d wmp!CWMNodeFilter::HandleSample+0x161
04fbf8dc 4f9871e3 wmp!CWMNodeFilterInputPin::Receive+0x117
04fbf8ec 4f9aba75 wmp!CBaseOutputPin::Deliver+0x22
04fbf91c 4f9ad526 wmp!CWMNodeFilter::ProcessInPlaceSample+0xb6
04fbf944 4f9ae03d wmp!CWMNodeFilter::HandleInPlaceSample+0xe3
04fbf964 4f9871e3 wmp!CWMNodeFilterInputPin::Receive+0x117
04fbf974 4f9ad116 wmp!CBaseOutputPin::Deliver+0x22
04fbf9b0 4f9ad410 wmp!CWMNodeFilter::SuckOutOutput+0x3cd
04fbf9d0 4f9ae03d wmp!CWMNodeFilter::HandleSample+0x161
04fbf9f0 4f9871e3 wmp!CWMNodeFilterInputPin::Receive+0x117
04fbfa00 4f9ad116 wmp!CBaseOutputPin::Deliver+0x22
04fbfa3c 4f9ad410 wmp!CWMNodeFilter::SuckOutOutput+0x3cd
04fbfa5c 4f9ae03d wmp!CWMNodeFilter::HandleSample+0x161
很明显了,线程6应该就是Render线程,负责画图的,这里有一个module叫d3d9,应该是Direct 3D version9的意思。如果Google以下关键子MilCore,就会知道这个是WPF的Render关键模块了。
看看主线程:
0:000> !clrstack
OS Thread Id: 0x1240 (0)
ESP EIP
0012f354 7c8285ec [ComPlusMethodFrameGeneric: 0012f354] MS.Win32.UnsafeNativeMethods+ITfMessagePump.GetMessageW(System.Windows.Interop.MSG ByRef, Int32, Int32, Int32, Boolean ByRef)
0012f374 56d61937 System.Windows.Threading.Dispatcher.GetMessage(System.Windows.Interop.MSG ByRef, IntPtr, Int32, Int32)
0012f3c0 56d617e3 System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)
0012f410 56d616c7 System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame)
0012f42c 56d6162d System.Windows.Threading.Dispatcher.Run()
0012f438 5533dce0 System.Windows.Application.RunInternal(System.Windows.Window)
0012f464 5533db15 System.Windows.Application.Run(System.Windows.Window)
0012f474 5533d766 System.Windows.Application.Run()
0012f47c 00f200ad PuzzleProject.app.Main()
0012f69c 79e7c74b [GCFrame: 0012f69c]
很简单,在等待消息。
从上面的例子,我得到的信息是:
1. WPF通过worker thread进行Render。这个thread的入口是MilCore!CPartitionThread里面的相关函数
2. WPF的确用了Direct X
接下来的问题是,WPF如何通知这个worker thread什么时候应该Render,这个thread跟main thread是如何通信的。 我们留在后面一节来介绍。
Lad.li