WPF Hosting for XNA Game Studio 4.0

A while back I wrote up a method for rendering XNA Game Studio 4.0 content inside of a WPF window. My method involved using a render target and doing a CPU transfer of those bits into a WriteableBitmap. This method had some pros and cons:

Pros:

  1. Extremely simple in concept and in actual implementation.
  2. Enabled high level of composition with native WPF elements.
  3. Allowed app to leverage WPF input events.

Cons:

  1. Not the most efficient process, given a full CPU swap of the pixels each frame. (The R/B swap I did on the CPU could be moved to a pixel shader prior to getting the data which would reduce a good bit of the work, but you're still moving a lot of data from the GPU into the WriteableBitmap every frame).
  2. Limits the app from using render targets without more plumbing work as switching away from the main render target prevents the content from making it to the screen.

These limits may be acceptable for some people. I built a real-time shader editing tool using this system and it worked great for my needs. However since some people have asked for more ways to do this integration that provide more performance and don't require the render targets, I went back to the drawing board a bit. After talking with a coworker for a bit (i.e. taking his core idea and running with it ;-) ), I have come up a new solution for this integration using WPF's HwndHost base type.

The idea of the new solution is simple: create a control subclasses WPF's HwndHost to create a child window to which the XNA Framework will render graphics. I leveraged the GraphicsDeviceService from the WinForms sample (with some modifications) to handle GraphicsDevice management and used the CompositionTarget.Rendering event to drive my draw loop. I also used some concepts from the GraphicsDeviceControl in the WinForms sample so that each of my controls will present to the correct window handle, thus enabling apps to have multiple controls running at the same time and sharing the same GraphicsDevice.

This solution, however, is not without its own set of trade offs. Essentially we're flipping the pro/con list from above:

Pros:

  1. Rendering requires no CPU copies as we Present directly to the child window where we want our content. This means we get some great performance even with large windows.
  2. Since we're presenting directly to the window, we no longer use render targets which allows the app itself to use them for any purpose.

Cons:

  1. Due to the nature of hosting a child window, you cannot composite WPF controls on top of the GraphicsDeviceControl (the exception being other child window content such as menus or dialogs).
  2. Because the control is really a child window, mouse input is directed to that window's WndProc which means input handling requires a bit more work and causes some duplication of events. My sample does implement enough of this work to be useful, however there may be some things you want that I didn't tackle.
  3. This solution requires a few native method calls and an implementation of a WndProc which means the implementation is quite a bit more complex than my previous solution. The usage is still relatively straight forward, however.

What it comes down to is that these two solutions are both useful in different ways and therefore I recommend you look at them both to see which will meet your needs.

At a high level, use of this new sample is very simple. Add a GraphicsDeviceControl into your XAML layout where you want to render XNA graphics. The GraphicsDeviceControl has a LoadContent event that will fire when the GraphicsDevice has been created and a RenderXna event that fires each frame for you to draw your content. Additionally there are a number of "Hwnd*" events for various aspects of mouse input since I couldn't route it through the normal UIElement mouse events. The control also has methods to capture/release the mouse which makes the cursor invisible and forces the cursor position to reset, which is great for tools where you want to rotate an object and not worry about the mouse leaving the view.

The sample application shows how to leverage these various actions in an app with two GraphicsDeviceControls. The top control draws a constantly spinning cube whose color is based on the sliders in the left panel. The bottom control draws a cube in one of three colors than can be changed with the buttons next to the control (or using the 'R', 'G', or 'B' keyboard keys which are set up as hotkeys for those buttons). Additionally you can use the right mouse button and drag to rotate the bottom cube without capturing the mouse or the left mouse button and drag to rotate the cube with capturing the mouse.

Hopefully this proves useful to some of you. Feel free to leave questions or comments below, but to make this point clear: the implementation of this solution is pretty complex and I cannot provide much support as I simply don't have the time. I encourage everyone to play with it, but if you run into issues please start by checking out MSDN documentation or ask on the App Hub forums so that the community can work with you to solve the issue.

Disclaimer: This code was written by a colleague and myself and is not an official solution for mixing XNA Game Studio and WPF. Use of this sample is at your own risk and I make no guarantees as to the quality or usability of the code. I do not guarantee any level of support for users of this sample. The code in the downloadable sample is licensed under the Microsoft Public License, the terms of which can be found here:  https://www.microsoft.com/opensource/licenses.mspx#Ms-PL

WpfHostedXna.zip