Premiere Post! WPF w/ Direct3D Shader Interop and simple databinding

Based on some questions I’ve seen on the forums, with regards to WPF on the issue of Shader use and technologies, I have decided to prepare a sample which illustrates one approach by which DirectX content can be embedded in a WPF app. In particular, I render a single Managed DirectX surface containing a simple mesh rendered with shaders,  in a control embedded in a WPF app, and databound to a set of sliders to manipulate a color shift implemented via a pixel shader. This form can be helpful for teams that want to incorporate WPF as the overall UI technology(in place of GDI, MFC, winforms, etc) of their 3D heavy applications, for which a fair bit of DirectX code is often present.

In this example, I used Managed DirectX API for primary language consistency in this sample, but the HwndHost technology for interop can similarly be used to embed unmanaged DX 9/10. For this overview, I assume an working knowledge of Direct3D, in conjunction with some familiarity with WPF.

Screenshot.jpg 

1-The application references a custom control containing the Manged DX Device in the XAML layout(For non XAML junkies, keep in mind, this can just as easily be done in code):

Custom Namespaces:
  xmlns:local=“clr-namespace:WPF_MDX_Interop_App“
  xmlns:MDXControl=“clr-namespace:WpfNuggets.ManagedDirectX;assembly=WpfNuggets.ManagedDirectX“

… 

< MDXControl:ManagedDirectXControl>
< MDXControl:ManagedDirectXControl.RenderContent >
< local:AdjustableShader x:Name=“AdjustableShader“ TextureFilename=“Content\gradient1.bmp“/>
  MDXControl:ManagedDirectXControl.RenderContent>
MDXControl:ManagedDirectXControl>

2-Here’s the code for the custom control - this is a WindowsFormsHost which allows us to contain Winforms content in WPF. The Xaml visible RenderContent field is used here for encapsulating the rendering behavior we want to execute in DirectX:

public class ManagedDirectXControl : WindowsFormsHost
{
  public ManagedDirectXControl()
{
Child = new ManagedDirectXDeviceWrapper();
}
  public Renderable RenderContent
  {
    get { return ((ManagedDirectXDeviceWrapper)Child).RenderContent; }
    set { ((ManagedDirectXDeviceWrapper)Child).RenderContent = value; }
}
}

3-The ManagedDirectXDeviceWrapper code which is contained in the project attached below, is pretty routine DirectX code for initialization and a rendering loop tied to repaint events on the container control. There are many fine tutorials on that subject, so I won't reinvent that wheel of guidance. The wrapper will check for HW PS & VS shaders V1.1, and will silently fall back to software mode. If you would like active notification of the fallback, look at the “_alreadyNagged” field in that class. This class contains hooks which depend on the user supplied Renderable object.

4-Finally, the implementation of “ShaderRender” supplies a generic Quad mesh which gets textured and processed by user supplied shaders. The derived class “AdjustableShader” pre-defines a reference to a particular shader file, and exposes R,G,B properties for data binding use as targets, which provide parameters which are passed on to the pixel shader. Again, the mechanics of this is familiar territory in the DirectX world, so I'm not delving into that aspect here. You can get the VS Solution with projects for the Managed DX wrapper project, and a WPF sample app containing simple data binding to drive the pixel shader inputs for shifting the texture color. That code is available from here.

To work on the project you will need the following installed(Combined pre-req's for DX and WPF):

  1. Visual Studio 2005 
  2. DirectX SDK
  3. WPF Runtime or be running on Vista
  4. Orcas CTP for Visual Studio 2005(to support WPF VS project)

I would be glad to hear any questions, suggestions or other feedback...

Comments

  • Anonymous
    March 29, 2007
    Awesome, Awesome, Awesome! Is there any hope of getting simple WPF controls to appear on top of the MDX control without z-order issues?

  • Anonymous
    April 01, 2007
    Thanks Adam- That's a fair question which I'm sure some other folks were wondering about also. Overlaying of such content is not currently an option in a naive implementation, as the control's rendering surface and WPF are not exposed to each other. As such, at this point, I would not suggest overlaying WPF and DX content on the same regions of screen space. At present, folks with a critical burning urge to overlay control content in the same regions as DirectX may want to consider rendering a WPF control to an intermediate image to use as background/foreground content rendered in DX. Alternately, rendering DX content and applying to an imagebrush for display in WPF app.  Either way will involve a noticeable perf hit, due to the software(ie- non GPU) level communication of the content. To preempt a related question, WPF Brushes/Materials and other types are not  exposed in a native DX format.

  • Anonymous
    July 30, 2007
    Follow up Note: This version does not use the DirectX Device constructor option "CreateFlags.FpuPreserve", which creates compatability problems with other CLR dependencies including in the Visual Studio designer. Take a look at details here: http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_m_Apr_2005/directx/ref/ns/microsoft.directx.direct3d/c/device/m/ctor.asp

  • Anonymous
    February 08, 2008
    if this sample use WindowsFormsHost as wrapper to deliver directx content to the screen, this actually will be using GDI..wich is a kind of deprecated (for old pré-vista windows apps), or am i wrong?? and if it´s using GDI, do we have another way of doing it?

  • Anonymous
    February 19, 2008
    The Winforms Host wrapper is just a hook to contain an hWnd and service paint messages. GDI is not being used for rendering. The alternative approach for hosting externally rendered content in WPF is via image interop, wherein you feed your surface as an ImageSource or related type.