namespace RealTimeGraphEx.DX2D { using SharpDX; using SharpDX.Direct2D1; using SharpDX.Direct3D10; using SharpDX.DXGI; using System; using System.ComponentModel; using System.Diagnostics; using System.Threading; using System.Windows; using Device = SharpDX.Direct3D10.Device1; using FeatureLevel = SharpDX.Direct3D10.FeatureLevel; public abstract class Direct2DControl : System.Windows.Controls.Image { private Device Device; private Texture2D RenderTarget; private DX10ImageSource D3DSurface; private readonly Stopwatch RenderTimer; private RenderTarget m_d2dRenderTarget; protected SharpDX.Direct2D1.Factory m_d2dFactory; private bool enableRendering; //private Stopwatch renderWatch; //public int FrameRate { get; set; } public Direct2DControl() { //renderWatch = new Stopwatch(); //FrameRate = 60; this.RenderTimer = new Stopwatch(); this.Loaded += this.Window_Loaded; this.Unloaded += this.Window_Closing; this.Stretch = System.Windows.Media.Stretch.Fill; } public void EnableRendering() { enableRendering = true; } public void DisableRendering() { enableRendering = false; } private void Window_Loaded(object sender, RoutedEventArgs e) { if (Direct2DControl.IsInDesignMode) { return; } this.StartD3D(); this.StartRendering(); } private void Window_Closing(object sender, RoutedEventArgs e) { if (Direct2DControl.IsInDesignMode) { return; } this.StopRendering(); this.EndD3D(); } private void StartD3D() { this.Device = new Device(DriverType.Hardware, DeviceCreationFlags.BgraSupport, FeatureLevel.Level_10_0); this.D3DSurface = new DX10ImageSource(); this.D3DSurface.IsFrontBufferAvailableChanged += OnIsFrontBufferAvailableChanged; this.CreateAndBindTargets(); this.Source = this.D3DSurface; } private void EndD3D() { this.D3DSurface.IsFrontBufferAvailableChanged -= OnIsFrontBufferAvailableChanged; this.Source = null; Disposer.SafeDispose(ref this.m_d2dRenderTarget); Disposer.SafeDispose(ref this.m_d2dFactory); Disposer.SafeDispose(ref this.D3DSurface); Disposer.SafeDispose(ref this.RenderTarget); Disposer.SafeDispose(ref this.Device); } private void CreateAndBindTargets() { if (this.D3DSurface == null) return; this.D3DSurface.SetRenderTargetDX10(null); Disposer.SafeDispose(ref this.m_d2dRenderTarget); Disposer.SafeDispose(ref this.m_d2dFactory); Disposer.SafeDispose(ref this.RenderTarget); var width = Math.Max((int)this.ActualWidth, 100); var height = Math.Max((int)this.ActualHeight, 100); var colordesc = new Texture2DDescription { BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource, Format = Format.B8G8R8A8_UNorm, Width = width, Height = height, MipLevels = 1, SampleDescription = new SampleDescription(1, 0), Usage = ResourceUsage.Default, OptionFlags = ResourceOptionFlags.Shared, CpuAccessFlags = CpuAccessFlags.None, ArraySize = 1 }; this.RenderTarget = new Texture2D(this.Device, colordesc); var surface = this.RenderTarget.QueryInterface(); m_d2dFactory = new SharpDX.Direct2D1.Factory(); var rtp = new RenderTargetProperties(new PixelFormat(Format.Unknown, AlphaMode.Premultiplied)); m_d2dRenderTarget = new RenderTarget(m_d2dFactory, surface, rtp); this.D3DSurface.SetRenderTargetDX10(this.RenderTarget); } private void StartRendering() { if (this.RenderTimer.IsRunning) { return; } System.Windows.Media.CompositionTarget.Rendering += OnRendering; this.RenderTimer.Start(); //renderWatch.Start(); } private void StopRendering() { if (!this.RenderTimer.IsRunning) { return; } System.Windows.Media.CompositionTarget.Rendering -= OnRendering; this.RenderTimer.Stop(); RenderTimer.Stop(); } private void OnRendering(object sender, EventArgs e) { if (!this.RenderTimer.IsRunning) { return; } //if (renderWatch.ElapsedMilliseconds >= (1000 / FrameRate)) //{ //renderWatch.Restart(); if (enableRendering) { this.PrepareAndCallRender(); this.D3DSurface.InvalidateD3DImage(); } //} } protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { this.CreateAndBindTargets(); base.OnRenderSizeChanged(sizeInfo); } private void PrepareAndCallRender() { var device = this.Device; if (device == null) { return; } var renderTarget = this.RenderTarget; if (renderTarget == null) { return; } var targetWidth = renderTarget.Description.Width; var targetHeight = renderTarget.Description.Height; device.Rasterizer.SetViewports(new Viewport(0, 0, targetWidth, targetHeight, 0.0f, 1.0f)); // Does the actual rendering m_d2dRenderTarget.BeginDraw(); this.Render(m_d2dRenderTarget); m_d2dRenderTarget.EndDraw(); //this.ShowFramesPerSecondsOnDebug(); device.Flush(); } /// /// Does the actual rendering. /// BeginDraw and EndDraw are already called by the caller. /// public abstract void Render(RenderTarget target); /// /// Shows the number of frames per seconds on the debug line /// private void ShowFramesPerSecondsOnDebug() { // Spits out the Frames per second f++; if ((DateTime.Now - last).TotalSeconds > 1) { System.Diagnostics.Debug.WriteLine(f); f = 0; last = DateTime.Now; } } private DateTime last; private int f; private void OnIsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e) { // this fires when the screensaver kicks in, the machine goes into sleep or hibernate // and any other catastrophic losses of the d3d device from WPF's point of view if (this.D3DSurface.IsFrontBufferAvailable) { this.StartRendering(); } else { this.StopRendering(); } } /// /// Gets f value indicating whether the control is in design mode /// (running in Blend or Visual Studio). /// public static bool IsInDesignMode { get { var prop = DesignerProperties.IsInDesignModeProperty; var isDesignMode = (bool)DependencyPropertyDescriptor.FromProperty(prop, typeof(FrameworkElement)).Metadata.DefaultValue; return isDesignMode; } } } }