Home » Community » Forums » DirectX and XNA » WPF D3DImage with SlimDx
Intel sponsors gamedev.net search:
Control Panel Register Bookmarks Who's Online Active Topics Stats FAQ Search

Add Forum to Favorites |  Send Topic To a Friend | View Forum FAQ | Track this topic


 Last Thread Next Thread 
 WPF D3DImage with SlimDx
Post New Topic  Post Reply 
Hi all,
is there a well working sample using SlimDx with .Net Framwork 3.5 (SP1) available?
I checked the blog from SteveKr. The sample works very well till I open the Task-Manager in Windows Vista. The Device is lost. Sometims I can recreate it. After the second or third time the program crashes with an exception in SlimDX in "Result.cpp".

So I'm searching for a better sample.

Best regards,
Juergen

 User Rating: 1027   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Using SlimDX in Framework 3.5 isn't any different than using it anywhere else. Using it with WPF is another issue altogether though, as you have airspace issues to worry about. What's this sample you're using / what kind of code are we talking about here?

Losing the device is expected when you do things like that, it sounds like the code is not handling the reset correctly. But you're going to have to provide more information.


Josh Petrie | DirectX/XNA MVP | Scientific Ninja | Twitter | SlimDX

 User Rating: 1966   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Ok, here is my little sample program. If I try to reset the device (device.Reset) it fails with an exception.
If I get an Exception in RenderD3D() I try to create it again. Sometimes it works sometimes not. I want to use the new control D3DImage because it's implemented in WPF and the hole application is written in WPF. At the moment I use a 3D-control written in WinForms inside the WPF-application.

Ok, here is the code:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Interop;

using System.Drawing;

using SlimDX;
using SlimDX.Direct3D9;

namespace D3DIMageTest
{
	/// <summary>
	/// Interaction logic for Window1.xaml
	/// </summary>
	public partial class Window1 : Window
	{
		SlimDX.Direct3D9.Direct3D direct3D = new SlimDX.Direct3D9.Direct3D();
		Device device;  
		Sprite sprite;  
		Texture texture;  

		public Window1()
		{
			InitializeComponent();
			Loaded += new RoutedEventHandler(Window_Loaded);
		}
		
		private void Window_Loaded(object sender, RoutedEventArgs e)  
		{  
			InitializeD3D();  
			CompositionTarget.Rendering += OnRendering;  
			d3dimage.IsFrontBufferAvailableChanged += new DependencyPropertyChangedEventHandler(OnIsFrontBufferAvailableChanged);
			
			d3dimage.Lock();  
			d3dimage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, device.GetBackBuffer(0, 0).ComPointer);  
			d3dimage.Unlock();  
		}

		
		public void InitializeD3D()  
		{  
			HwndSource hwnd = new HwndSource(0, 0, 0, 0, 0, "test", IntPtr.Zero);  
			
			PresentParameters pp = new PresentParameters();  
			pp.SwapEffect = SwapEffect.Discard;  
			pp.DeviceWindowHandle = hwnd.Handle;  
			pp.Windowed = true;  
			pp.BackBufferWidth = 400;  
			pp.BackBufferHeight = 400;  
			pp.BackBufferFormat = Format.X8R8G8B8;  
			
			device = new Device(direct3D, 0, 
			                    DeviceType.Hardware, 
			                    hwnd.Handle, 
			                    CreateFlags.HardwareVertexProcessing, 
			                    pp);
			
		}
		
		
		private void OnRendering(object sender, EventArgs e)
		{
			try {
				d3dimage.Lock(); 
				d3dimage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, device.GetBackBuffer(0, 0).ComPointer);  
				RenderD3D();
				d3dimage.AddDirtyRect(new Int32Rect(0, 0, d3dimage.PixelWidth, d3dimage.PixelHeight));
				d3dimage.Unlock();
			}
			catch (Direct3D9Exception ex) {
				string msg = ex.Message;
			}
		}
		
		void OnIsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
		{
			if (d3dimage.IsFrontBufferAvailable) {
				InitializeD3D();  
				CompositionTarget.Rendering += OnRendering;  
				d3dimage.IsFrontBufferAvailableChanged += new DependencyPropertyChangedEventHandler(OnIsFrontBufferAvailableChanged);
				
				d3dimage.Lock();  
				d3dimage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, device.GetBackBuffer(0, 0).ComPointer);  
				d3dimage.Unlock();  
			} 
			else {
				CompositionTarget.Rendering -= OnRendering; 
			}
				
		}

		public void RenderD3D()
		{
            Result result;

			try {
                if (d3dimage.IsFrontBufferAvailable)
                {
                    result = device.TestCooperativeLevel();
                    if (result.IsFailure) {
                        throw new Direct3D9Exception();
                    }
                    device.Clear(ClearFlags.Target, new Color4(System.Drawing.Color.Yellow), 0, 0);
                    device.BeginScene();

                    device.EndScene();
                    device.Present();
                }
			}
			catch (Exception ex) {
				string msg = ex.Message;

				InitializeD3D();
				CompositionTarget.Rendering += OnRendering;  
				d3dimage.IsFrontBufferAvailableChanged += new DependencyPropertyChangedEventHandler(OnIsFrontBufferAvailableChanged);
				
				d3dimage.Lock();  
				d3dimage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, device.GetBackBuffer(0, 0).ComPointer);  
				d3dimage.Unlock();  
			}
		}
	}
}

[Mod edit -- added source tags]

[Edited by - Promit on September 10, 2008 5:40:16 PM]

 User Rating: 1027   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

This might be one of those times you have to create an Ex device. Lucky for you, we added support for just this occasion. Switch to using Direct3DEx and DeviceEx, and see if that works out better.

 User Rating: 1898   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Quote:
Original post by jpetrie
Using it with WPF is another issue altogether though, as you have airspace issues to worry about.


.Net 3.5 SP1 adds the ability to use the D3DImage object. This object allows you to work around the airspace problems that previously made using DirectX with WPF very ugly. The D3DImage object is an ImageSource. Thefore, you can apply an IDirect3DSurface9 as if it was any other WPF brush.

I'd recommend checking out the following if you have not done so already.
Video outlining .NET 3.5 SP1 Pixel Shader Support and D3DImage
Dr WPF's D3DImage article

I have had good sucess using the D3DImage object with XNA and a small bit of reflection hackery. :-)

 User Rating: 1222   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
Original post by Billr17
Quote:
Original post by jpetrie
Using it with WPF is another issue altogether though, as you have airspace issues to worry about.


.Net 3.5 SP1 adds the ability to use the D3DImage object. This object allows you to work around the airspace problems that previously made using DirectX with WPF very ugly. The D3DImage object is an ImageSource. Thefore, you can apply an IDirect3DSurface9 as if it was any other WPF brush.

Right, but that doesn't change how one would use SlimDX itself (which is what I meant by 'another issue'). As for using D3DImage, I don't have much input on that -- looks like it's designed to deal with unmanaged D3D, and SlimDX exposes the unmanaged COM interfaces as IntPtrs, so that's likely what you'd want to use -- as the OP is doing. Beyond that I can't really provide a whole lot of insight...

Promit's suggestion of the Ex interface is worth a look, and there may also be issues with the D3DImage doing something to the D3D interface that SlimDX doesn't expect (or vise-versa), but it's hard to tell without more in-depth analysis.

Making sure unmanaged debugging is enabled so you can see the native D3D debug spew is also worth doing, if there's just a resource that isn't being released before the reset (a pretty common issue), that'll be easy to see with the output.


Josh Petrie | DirectX/XNA MVP | Scientific Ninja | Twitter | SlimDX

 User Rating: 1966   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

I tried the same code with Direct3DEx and DeviceEx and everything works fine.

Thank you very much,
Jürgen

 User Rating: 1027   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Now I have a problem with my textures. I get them with Texture.FromFile().
Each time I want to load a texture I get a "D3DERR_INVALIDCALL: Invalid call (-2005530516)".
I use now the Direct3D9.DeviceEx. It works with the normal Device.

HwndSource hwnd = new HwndSource(0, 0, 0, 0, 0, "test", IntPtr.Zero);  
			
PresentParameters pp = new PresentParameters();  
pp.SwapEffect = SwapEffect.Discard;  
pp.DeviceWindowHandle = hwnd.Handle;  
pp.Windowed = true;  
pp.BackBufferWidth = 400;  
pp.BackBufferHeight = 400;  
pp.BackBufferFormat = Format.X8R8G8B8;
           
device = new DeviceEx(direct3D, 0, 
                    DeviceType.Hardware, 
                    hwnd.Handle, 
                    CreateFlags.HardwareVertexProcessing, 
                    pp);
			
sprite = new Sprite(device); 
texture = Texture.FromFile(device, "pic.jpg");




 User Rating: 1027   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

What do the debug runtimes give you?


Mike Popoloski | Journal | SlimDX

 User Rating: 1828   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

FromFile probably defaults to managed pool, which isn't legal on an Ex device. Use the version that specifies the pool explicitly, and pass default.

 User Rating: 1898   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Promit, I tried this and it works.
Thank you.

But the problem with the DeviceEx is, that it's not running on WinXP.
What do you think?
Is it possible to make a SampleFramework depending on a WPF D3DImage working with normal D3D9 Device?



 User Rating: 1027   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
Original post by juergen1969
But the problem with the DeviceEx is, that it's not running on WinXP.
What do you think?


Yeah, what about Windows XP? I need the WPF applications to run on WinXP as well, otherwise I could use DirectX 10 instead of DirectX 9.

What does the DeviceEx do anyway, I mean what is the difference to the regular Device?

Thanks,
Christian



 User Rating: 1028   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Sorry, I have no idea what affects how the normal device works on Vista.

 User Rating: 1898   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Christian,
don't know if it's too late:
The only thing you have to do is to try to create a DeviceEx device. If it fails you don't have a Vista system. Then you have to create a normal Device.

Here is the code and hope it's not to large:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Interop;

using System.Drawing;

using SlimDX;
using SlimDX.Direct3D9;

namespace TriVista.Controls.D3D
{
	/// <summary>
	/// Description of ControlWPF.
	/// </summary>
	public class ControlWPF		:	DockPanel
	{
		// we use it for 3D
		Direct3D direct3D;
		Direct3DEx direct3DEx;
		Device device;  
		DeviceEx deviceEx;
		PresentParameters pp;
		
		// this one is our only child
		System.Windows.Controls.Image image;
		D3DImage d3dimage;
		bool StartThread = false;
		bool sizeChanged = false;
		
		// some public properties
		public bool useDeviceEx
		{
			get;
			private set;
		}
		
		public Direct3D Direct3D
		{
			get {
				if (useDeviceEx)
					return direct3DEx;
				else
					return direct3D;
			}
		}

		public Device Device
		{
			get {
				if (useDeviceEx)
					return deviceEx;
				else
					return device;
			}
		}
		
        #region Events

        /// <summary>
        /// Occurs once per iteration of the main loop.
        /// </summary>
        public event EventHandler MainLoop;

        /// <summary>
        /// Occurs when the device is created.
        /// </summary>
        public event EventHandler DeviceCreated;

        /// <summary>
        /// Occurs when the device is destroyed.
        /// </summary>
        public event EventHandler DeviceDestroyed;

        /// <summary>
        /// Occurs when the device is lost.
        /// </summary>
        public event EventHandler DeviceLost;

        /// <summary>
        /// Occurs when the device is reset.
        /// </summary>
        public event EventHandler DeviceReset;

        /// <summary>
        /// Raises the OnInitialize event.
        /// </summary>
        protected virtual void OnInitialize()
        {
        }
        
        /// <summary>
        /// Raises the <see cref="E:MainLoop"/> event.
        /// </summary>
        protected virtual void OnMainLoop(EventArgs e)
        {
            if (MainLoop != null)
                MainLoop(this, e);
        }

        /// <summary>
        /// Raises the DeviceCreated event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnDeviceCreated(EventArgs e)
        {
            if (DeviceCreated != null)
                DeviceCreated(this, e);
        }

        /// <summary>
        /// Raises the DeviceDestroyed event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnDeviceDestroyed(EventArgs e)
        {
            if (DeviceDestroyed != null)
                DeviceDestroyed(this, e);
        }

        /// <summary>
        /// Raises the DeviceLost event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnDeviceLost(EventArgs e)
        {
            if (DeviceLost != null)
                DeviceLost(this, e);
        }

        /// <summary>
        /// Raises the DeviceReset event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnDeviceReset(EventArgs e)
        {
            if (DeviceReset != null)
                DeviceReset(this, e);
        }
        
        #endregion
        
		public ControlWPF()
		{
			image = new System.Windows.Controls.Image();
			d3dimage = new D3DImage();
			image.Source = d3dimage;
			
			Children.Clear();
			Children.Add(image);
		}
		
		protected override void OnInitialized(EventArgs e)
		{
			base.OnInitialized(e);
			InitializeDirect3D();
		}
		
		protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
		{
			base.OnRenderSizeChanged(sizeInfo);
			sizeChanged = true;
		}
		
		void InitializeDirect3D()
		{
			try {
	            direct3DEx = new Direct3DEx();
	            useDeviceEx = true;
			} catch {
	            direct3D = new Direct3D();
	            useDeviceEx = false;
			}
		}
		
        /// <summary>
        /// Initializes the various Direct3D objects we'll be using.
        /// </summary>
		public bool Initialize(bool startThread)
		{  
			try {
				StartThread = startThread;
				
				ReleaseD3D();
				HwndSource hwnd = new HwndSource(0, 0, 0, 0, 0, "test", IntPtr.Zero);  
				
				pp = new PresentParameters();  
				pp.SwapEffect = SwapEffect.Discard;  
				pp.DeviceWindowHandle = hwnd.Handle;  
				pp.Windowed = true;  
				pp.BackBufferWidth = (int)ActualWidth;
				pp.BackBufferHeight = (int)ActualHeight;
				pp.BackBufferFormat = Format.X8R8G8B8;
	            
				if (useDeviceEx)
				{
					deviceEx = new DeviceEx((Direct3DEx)Direct3D, 0,
					                    DeviceType.Hardware, 
					                    hwnd.Handle, 
					                    CreateFlags.HardwareVertexProcessing, 
					                    pp);
				}
				else {
					device = new Device(Direct3D, 0, 
					                    DeviceType.Hardware, 
					                    hwnd.Handle, 
					                    CreateFlags.HardwareVertexProcessing, 
					                    pp);
				}
				
				// call the users one
				OnDeviceCreated(EventArgs.Empty);
				OnDeviceReset(EventArgs.Empty);
				
				// only if startThread is true
				if (StartThread) {
					CompositionTarget.Rendering += OnRendering;  
					d3dimage.IsFrontBufferAvailableChanged += new DependencyPropertyChangedEventHandler(OnIsFrontBufferAvailableChanged);
				}
				d3dimage.Lock();  
				d3dimage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, Device.GetBackBuffer(0, 0).ComPointer);  
				d3dimage.Unlock();  

				return true;
			}
			catch {
				return false;
			}
		}

		public void ReleaseD3D()
		{
			if (device != null) {
                if (!device.Disposed)
                {
                    device.Dispose();
                    device = null;
                }
			}
            d3dimage.Lock();
            d3dimage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero);
            d3dimage.Unlock();
		}
		
		private void OnRendering(object sender, EventArgs e)
		{
            Result result;
	
			try {
                if (Device == null)
                    Initialize(StartThread);
                
                if (sizeChanged) 
                {
					pp.BackBufferWidth = (int)ActualWidth;
					pp.BackBufferHeight = (int)ActualHeight;
					Device.Reset(pp);
					OnDeviceReset(EventArgs.Empty);
                }
				
                if (d3dimage.IsFrontBufferAvailable)
                {
                    result = Device.TestCooperativeLevel();
                    if (result.IsFailure) {
                        throw new Direct3D9Exception();
                    }
					d3dimage.Lock(); 

					Device.Clear(ClearFlags.Target, new Color4(System.Drawing.Color.Yellow), 0, 0);
                    Device.BeginScene();
					// call the users method
					OnMainLoop(EventArgs.Empty);
					
                    Device.EndScene();
                    Device.Present();

                    d3dimage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, Device.GetBackBuffer(0, 0).ComPointer);
	                d3dimage.AddDirtyRect(new Int32Rect(0, 0, d3dimage.PixelWidth, d3dimage.PixelHeight));
					d3dimage.Unlock();
                }
			}
			catch (Direct3D9Exception ex) {
				string msg = ex.Message;
				Initialize(StartThread);
			}
        	sizeChanged = false;
		}

		void OnIsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
		{
			if (d3dimage.IsFrontBufferAvailable) {
				Initialize(StartThread);  
			} 
			else {
				CompositionTarget.Rendering -= OnRendering; 
			}
		}

	}
}






If someone is interested in a complete working sample, feel free to write.


Juergen

 User Rating: 1027   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

All times are ET (US)

Post Reply
 Last Thread Next Thread 
Forum Rules:
You may not post new threads
You may post replies
You may not edit your posts
You may not use HTML in your posts
Jump To:
Administrative Options: