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.");
}
}
}
}
SlimDX and WPF
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
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:
The client:
This will probably evolve more but that is just a matter of refactoring. As far as I can see everything gets released now.
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
Popular Topics
Advertisement