The new D3DImage in WPF opens up worlds for WPF. Direct3D content rendered with WPF...it doesn't get any better! The downside is it seems (correct me if I'm wrong) that Microsoft was only targeting unmanaged Direct3D. Yes there is Managed DirectX, but it seems as if MDX is no longer being maintained and it's not so obvious how to get a D3D surface pointer from the API. It really seems MDX has been left in the dust in favor for XNA, which is a very intuitive D3D wrapper. XNA further exacerbates the issue with XNA and WPF.
A couple of the issues are:
1.) XNA hides all the nasty D3D COM pointers, so you can't get the surface pointer via the XNA API.
2.) XNA creates a IDirect3DDevice9 internally. WPF D3DImage needs a IDirect3DDevice9Ex for maximum performance on Vista.
Initially, my plan was create a 9Ex device, inject it into the XNA GraphicsDevice class via reflection, which *worked* until you tried to load a texture. The XNA libraries are hard coded for a D3DPOOL_MANAGED pool, which is incompatible with the 9Ex device (Thanks Doc!). Ok, so we can live without break-neck performance of 9Ex...for now. So I just focused on getting the surface pointer and setting the render target. Here is a quick snippet for the Microsoft.Xna.Framework.Game subclass. You will need to add reference to the MDX assembly Microsoft.DirectX.Direct3D. Ignore all "LoaderLock" exceptions or just turn them off in Visual Studio. You will also need to throw an event on the Draw override to notify your D3DImage that there is a dirty rect. You should probably also add an event for when a new surface is available also, for when the D3D device is lost.
private Surface m_surface;
private int WIDTH = 300;
private int HEIGHT = 300;
unsafe protected override void BeginRun()
{
Type deviceType = typeof(GraphicsDevice);
FieldInfo fi = deviceType.GetField("pComPtr",
BindingFlags.NonPublic | BindingFlags.Instance);
object ptr = fi.GetValue(graphics.GraphicsDevice);
IntPtr pComPtr = new IntPtr(Pointer.Unbox(ptr));
Microsoft.DirectX.Direct3D.Device dev = new Microsoft.DirectX.Direct3D.Device(pComPtr);
m_surface = dev.CreateRenderTarget(WIDTH,
HEIGHT,
Format.X8R8G8B8,
Microsoft.DirectX.Direct3D.MultiSampleType.None,
0,
System.Environment.OSVersion.Version.Major >= 6);
dev.SetRenderTarget(0, m_surface);
base.BeginRun();
}
unsafe public IntPtr GetSurfacePointer()
{
/* TODO: Figure out how to convert Surface.UnmanagedComPointer to IntPtr */
return m_surface.GetObjectByValue(unchecked((int)0xd2b543af));
}