SlimDX and WPF

Started by
0 comments, last by ernow 15 years, 3 months ago
I grabbed the last code from this thread and made some changes: 1. I decided to make it inherit from Border - No Docking on the child elements - Free border ;) 2. I decided to override the Child property: - Preventing designers from adding a child to the Border and thereby overriding the DirectX content 3. I changed the coding conventions - Just a personal thing
public class SlimDXControl : Border
{
    // we use it for 3D
    private Direct3D _direct3D;
    private Direct3DEx _direct3DEx;
    private Device _device;
    private DeviceEx _deviceEx;
    private PresentParameters _pp;

    // this one is our only child
    private System.Windows.Controls.Image _image;
    private D3DImage _d3dimage;
    private bool _startThread = false;
    private 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 SlimDXControl()
    {
        _image = new System.Windows.Controls.Image();
        _d3dimage = new D3DImage();
        _image.Source = _d3dimage;

        Child = _image;

        //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;
        }
    }

    public override UIElement Child
    {
        get
        {
            return base.Child;
        }
        set
        {
            if (base.Child == null)
            {
                base.Child = value;
            }
            else
            {
                throw new ApplicationException("Setting the Child property is not allowed.");
            }
        }
    }
}
Advertisement
I noticed that with the code above a lot of COM objects did not get released.
So I added:
1. A method to release Direct3D
2. A method to release the back buffer surface

I called these methods where appropriate but you still need to call a method in the client code as well.

The control:
/// <summary>/// Description of SlimDXControl./// </summary>public class SlimDXControl : Border{    // we use it for 3D    private Direct3D _direct3D;    private Direct3DEx _direct3DEx;    private Device _device;    private DeviceEx _deviceEx;    private Surface _backBufferSurface;    private PresentParameters _pp;    // this one is our only child    private System.Windows.Controls.Image _image;    private D3DImage _d3dimage;    private bool _startThread = false;    private 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 SlimDXControl()    {        _image = new System.Windows.Controls.Image();        _d3dimage = new D3DImage();        _image.Source = _d3dimage;        Child = _image;    }    public override UIElement Child    {        get        {            return base.Child;        }        set        {            if (base.Child == null)            {                base.Child = value;            }            else            {                throw new ApplicationException("Setting the Child property is not allowed.");            }        }    }    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;            ReleaseDevice();            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();            _backBufferSurface = Device.GetBackBuffer(0, 0);            _d3dimage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, _backBufferSurface.ComPointer);            _d3dimage.Unlock();            return true;        }        catch        {            return false;        }    }    public void ReleaseDevice()    {        if (_device != null)        {            if (!_device.Disposed)            {                _device.Dispose();                _device = null;                OnDeviceDestroyed(EventArgs.Empty);            }        }        if (_deviceEx != null)        {            if (!_deviceEx.Disposed)            {                _deviceEx.Dispose();                _device = null;                OnDeviceDestroyed(EventArgs.Empty);            }        }                _d3dimage.Lock();        _d3dimage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero);        _d3dimage.Unlock();        ReleaseBackBuffer();    }    public void ReleaseDirect3D()    {        if (_direct3D != null && !_direct3D.Disposed)        {            _direct3D.Dispose();            _direct3D = null;        }        if (_direct3DEx != null && !_direct3DEx.Disposed)        {            _direct3DEx.Dispose();            _direct3DEx = null;        }    }    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;                ReleaseBackBuffer();                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();                _backBufferSurface = Device.GetBackBuffer(0, 0);                _d3dimage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, _backBufferSurface.ComPointer);                _d3dimage.AddDirtyRect(new Int32Rect(0, 0, _d3dimage.PixelWidth, _d3dimage.PixelHeight));                _d3dimage.Unlock();            }        }        catch (Direct3D9Exception ex)        {            string msg = ex.Message;            Initialize(_startThread);        }        _sizeChanged = false;    }    private void ReleaseBackBuffer()    {        if (_backBufferSurface != null && !_backBufferSurface.Disposed)        {            _backBufferSurface.Dispose();            _backBufferSurface = null;        }    }    void OnIsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)    {        if (_d3dimage.IsFrontBufferAvailable)        {            Initialize(_startThread);        }        else        {            CompositionTarget.Rendering -= OnRendering;        }    }}


The client:
/// <summary>/// Interaction logic for Window1.xaml/// </summary>public partial class Window1 : Window{    public Window1()    {        InitializeComponent();    }    private Sprite _sprite;    private Texture _texture;    private void Window_Loaded(object sender, RoutedEventArgs e)    {        _slimDXControl.Initialize(true);    }    private void _slimDXControl_DeviceCreated(object sender, EventArgs e)    {    }    private void _slimDXControl_DeviceDestroyed(object sender, EventArgs e)    {        if (_sprite != null && !_sprite.Disposed)        {            _sprite.Dispose();        }        if (_texture != null && !_texture.Disposed)        {            _texture.Dispose();        }    }    private void _slimDXControl_DeviceLost(object sender, EventArgs e)    {    }    private void _slimDXControl_DeviceReset(object sender, EventArgs e)    {        SlimDXControl control = sender as SlimDXControl;        if (control != null)        {            if (_sprite != null)            {                _sprite.Dispose();            }            _sprite = new Sprite(control.Device);            if (_texture != null)            {                _texture.Dispose();            }                _texture = Texture.FromFile(control.Device, "test.png", Usage.None, Pool.Default);        }    }    private void _slimDXControl_MainLoop(object sender, EventArgs e)    {        _sprite.Begin(SpriteFlags.AlphaBlend);        _sprite.Draw(_texture, Vector3.Zero, Vector3.Zero, new Color4(System.Drawing.Color.White));        _sprite.End();    }    private void Window_Closed(object sender, EventArgs e)    {        _slimDXControl.ReleaseDevice();        _slimDXControl.ReleaseDirect3D();    }}


This will probably evolve more but that is just a matter of refactoring. As far as I can see everything gets released now.

This topic is closed to new replies.

Advertisement