9
votes

I want to draw lines as fast as possible. For that reason I implemented a method using InteropBitmap. This works quite good. Next step was to compare with ShardDX. Basically what I want to do is: Running the following code in a BackgroundWorker. This does inform the WPF about an update of WIC. I found out that this code (creating all needed for ShapeDX and draw line) takes about 10ms longer than doing the same using InteropBitmap.

My question now is simply, how to speed this up? Can I change the code somehow that I only have to call BeginDraw, create lines and EndDraw, not always doing all of this Image Encoding/Decoding stuff? Or is there a better approach?

var wicFactory = new ImagingFactory();
var d2dFactory = new SharpDX.Direct2D1.Factory();

const int width = 800;
const int height = 200;

var wicBitmap = new Bitmap(wicFactory, width, height, SharpDX.WIC.PixelFormat.Format32bppBGR, BitmapCreateCacheOption.CacheOnLoad);

var renderTargetProperties = new RenderTargetProperties(RenderTargetType.Default, new PixelFormat(Format.Unknown, AlphaMode.Unknown), 0, 0, RenderTargetUsage.None, FeatureLevel.Level_DEFAULT);

var d2dRenderTarget = new WicRenderTarget(d2dFactory, wicBitmap, renderTargetProperties);

var solidColorBrush = new SharpDX.Direct2D1.SolidColorBrush(d2dRenderTarget, SharpDX.Color.White);

d2dRenderTarget.BeginDraw();
//draw whatever you want
d2dRenderTarget.EndDraw();

// Memorystream.
MemoryStream ms = new MemoryStream();
var stream = new WICStream(wicFactory, ms);
// JPEG encoder
var encoder = new SharpDX.WIC.JpegBitmapEncoder(wicFactory);
encoder.Initialize(stream);

// Frame encoder
var bitmapFrameEncode = new BitmapFrameEncode(encoder);
bitmapFrameEncode.Initialize();
bitmapFrameEncode.SetSize(width, height);
var pixelFormatGuid = SharpDX.WIC.PixelFormat.FormatDontCare;
bitmapFrameEncode.SetPixelFormat(ref pixelFormatGuid);
bitmapFrameEncode.WriteSource(wicBitmap);

bitmapFrameEncode.Commit();
encoder.Commit();

ms.Seek(0, SeekOrigin.Begin);

// JPEG decoder
var decoder = new System.Windows.Media.Imaging.JpegBitmapDecoder(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
// Write to wpf image
_WIC = decoder.Frames[0];
// Tell WPF to update
RaisePropertyChanged("WIC");

bitmapFrameEncode.Dispose();
encoder.Dispose();
stream.Dispose();

With:

System.Windows.Media.Imaging.BitmapFrame _WIC;
    public System.Windows.Media.Imaging.BitmapSource WIC
    {
        get
        {
            return (System.Windows.Media.Imaging.BitmapSource)_WIC.GetAsFrozen();
        }
    }

And:

<StackPanel>
   <Image Name="huhu1" Source="{Binding WIC}" />  
</StackPanel>
3
Cool. Same Problem here. The newest idea is to use C++, C#'s DirectX support seems to be not really good. Then I'd need to host the C++ renderer in a C# appanhoppe

3 Answers

6
votes

SharpDX Toolkit has support for WPF via a Direct3D11-to-Direct3D9 shared texture. It is implemented in the SharpDXElement class.

You may not be able to reuse this as is because Direct2D (which you are using to draw) can interop either with Direct3D11.1 or Direct3D10 and SharpDX uses Direct3D11 for WPF support, so you will need to tweak the solution a little bit.

Basically you need to do the following:

  • Initialize Direct3D (10 or 11.1).
  • Initialize Direct2D.
  • Create the D3D render target with Direct2D support and the Shared flag (here is how it is done in SharpDX).
  • Initialize Direct3D9.
  • Create the shared texture.
  • Bind the texture to an D3DImage.

Do not forget to call D3DImage.AddDirtyRect when the contents of the render target are updated.

From the code you provided it is not clear if you are doing all initializations only once or not, so try to call any initialization code only once and reuse the render target - just clear it at the beginning of every frame. This is mandatory to get a decent performance.

Update: SharpDX.Toolkit has been deprecated and it is not maintained anymore. It is moved to a separate repository.

5
votes

If you want to share a directx surface with WPF, the best option is using a WPF D3DImage. It promises to work without copying if you use the right color format.

I have only used it with Directx9, but it is possible that its compatible with Direct2D too, but if it isn't D3d9 can draw lines too.

There's a great codeproject article with examples. From managed code using slimdx or sharpdx the only nonobvious caveat is that D3DImage retains a reference count to your DirectX surface, so you need to null the backbuffer expicitly when you want to reset your d3d device.

0
votes

You can use Smartrak/WpfSharpDxControl: Provides WPF control to host SharpDx content.

It uses sharpDx in WPF and the wpf can add Win32HwndControl to HwndHost.