Jump to content
  • Advertisement
Sign in to follow this  
ernow

SlimDX and WPF

This topic is 3447 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.");
            }
        }
    }
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!