Click here to Skip to main content
Click here to Skip to main content

Using Direct2D with WPF

By , 3 Nov 2010
 

Introduction

With Windows 7, Microsoft introduced a new technology called Direct2D (which is also supported on Windows Vista SP2 with the Platform Update installed). Looking through all its documentation, you'll notice it's aimed at Win32 developers; however, the Windows API Code Pack allows .NET developers to use the features of Windows 7 easily, with Direct2D being one of the features supported. Unfortunately, all the WPF examples included with the Code Pack require hosting the control in a HwndHost, which is a problem as it has airspace issues. This basically means that the Direct2D control needs to be separated from the rest of the WPF controls, which means no overlapping controls with transparency.

The attached code allows Direct2D to be treated as a normal WPF control and, thanks to some COM interfaces, doesn't require you to download the DirectX SDK or even play around with any C++ - the only dependency is the aforementioned Code Pack (the binaries of which are included in the attached file). This article is more about the problems found along the way the challenges involved in creating the control, so feel free to skip to the Using the code section if you want to jump right in.

Background

WPF architecture

WPF is built on top of DirectX 9, and uses a retained rendering system. What this means is that you don't draw anything to the screen, but instead create a tree of visual objects; their drawing instructions are cached and later rendered automatically by the framework. This, coupled with using DirectX to do the graphics processing, enables WPF applications not only to remain responsive when they have to be redrawn, but also allows WPF to use a "painter's algorithm" painting model. In this model, each component (starting at the back of the display, going towards the front) is asked to draw itself, allowing them to paint over the previous component's display. This is the reason it's so easy to have complex and/or partially transparent shapes with WPF - because it was designed taking this scenario into account. For more information, check out the MSDN article.

Direct2D architecture

In contrast to the managed WPF model, Direct2D is immediate-mode where the developer is responsible for everything. This means you are responsible for creating your resources, refreshing the screen, and cleaning up after yourself. It's built on top of Direct3D 10.1, which gives it high-performance rendering, but provides several of the advantages of WPF (such as device independent units, ClearType text rendering, per primitive anti-aliasing, and solid/linear/radial/bitmap brushes). MSDN has a more in-depth introduction; however, it's more aimed at native developers.

Interoperability

Direct2D has been designed to be easily integrated into existing projects that use GDI, GDI+, or Direct3D, with multiple options available for incorporating Direct2D content with Direct3D 10.1 or above. The Direct2D SDK even includes a nice sample called DXGI Interop to show how to do this.

To host Direct3D content inside WPF, the D3DImage class was introduced in .NET 3.5 SP1. This allows you to host Direct3D 9 content as an ImageSource, enabling it to be used inside an Image control, or as an ImageBrush etc. There's a great article here on CodeProject with more information and examples.

The astute would have noticed that whilst both technologies can work with Direct3D, Direct2D requires version 10.1 or later, whilst the D3DImage in WPF only supports version 9. A quick internet search resulted in this blog post by Jeremiah Morrill. He explains that an IDirect3DDevice9Ex (which is supported by D3DImage) supports sharing resources between devices. A shared render target created in Direct3D 10.1 can therefore be pulled into a D3DImage via an intermediate IDirect3DDevice9Ex device. He also includes example source code which does exactly this, and the attached code is derived from his work.

So, we now have a way of getting Direct2D working with Direct3D 10.1, and we can get WPF working with Direct3D 10.1; the only problem is the dependency of both of the examples on unmanaged C++ code and the DirectX SDK. To get around this problem, we'll access DirectX through its COM interface.

Component Object Model

I'll admit I know nothing about COM, apart from to avoid it! However, there's an article here on CodeProject that helped to make it a bit less scary. To use COM, we have to use low level techniques, and I was surprised (and relieved!) to find that the Marshal class has methods which could mimic anything that would normally have to be done in unmanaged code.

Since there are only a few objects we need from Direct3D 9, and there are only one or two functions in each object that are of interest to us, instead of trying to convert all the interfaces and their functions to their C# equivalent, we'll manually map the V-table as discussed in the linked article. To do this, we'll create a helper function that will extract a method from the specified slot in the V-table:

public static bool GetComMethod<T, U>(T comObj, int slot, out U method) where U : class
{
    IntPtr objectAddress = Marshal.GetComInterfaceForObject(comObj, typeof(T));
    if (objectAddress == IntPtr.Zero)
    {
        method = null;
        return false;
    }

    try
    {
        IntPtr vTable = Marshal.ReadIntPtr(objectAddress, 0);
        IntPtr methodAddress = Marshal.ReadIntPtr(vTable, slot * IntPtr.Size);

        // We can't have a Delegate constraint, so we have to cast to
        // object then to our desired delegate
        method = (U)((object)Marshal.GetDelegateForFunctionPointer(
                             methodAddress, typeof(U)));
        return true;
    }
    finally
    {
        Marshal.Release(objectAddress); // Prevent memory leak
    }
}

This code first gets the address of the COM object (using Marshal.GetComInterfaceForObject), then gets the location of the V-table stored at the start of the COM object (using Marshal.ReadIntPtr), then gets the address of the method at the specified slot from the V-table (multiplying by the system size of a pointer, as Marshal.ReadIntPtr specifies the offset in bytes), then finally creates a callable delegate to the returned function pointer (Marshal.GetDelegateForFunctionPointer). Simple!

An important thing to note is that the IntPtr returned by the call to Marshal.GetComInterfaceForObject must be released; I wasn't aware of this, and found my program leaking memory when the resources were being re-created. Also, the function uses an out parameter for the delegate so we get all the nice benefits of type inference and, therefore, reduces the amount of typing required for the caller. Finally, you'll notice there's some nasty casting to object and then to the delegate type. This is unfortunate but necessary, as there's no way to specify a delegate generic constraint in C# (the CLI does actually allow this constraint, as mentioned by Jon Skeet in his blog). Since this is an internal class, we'll assume that the caller of the function knows this constraint.

With this helper function, it becomes a lot easier to create a wrapper around the COM interfaces, so let's take a look at how to provide a wrapper around the IDirect3DTexture9 interface. First, we'll create an internal interface with the ComImport, Guid, and InterfaceType attributes attached so that the Marshal class knows how to use the object. For guid, we'll need to look inside the DirectX SDK header files, in particular d3d9.h:

interface DECLSPEC_UUID("85C31227-3DE5-4f00-9B3A-F11AC38C18B5") IDirect3DTexture9;

With the same header open, we can also look for the interface's declaration, which looks like this after running it through the pre-processor and removing the __declspec and __stdcall attributes:

struct IDirect3DTexture9 : public IDirect3DBaseTexture9
{
    virtual HRESULT QueryInterface( const IID & riid, void** ppvObj) = 0;
    virtual ULONG AddRef(void) = 0;
    virtual ULONG Release(void) = 0;
    
    virtual HRESULT GetDevice( IDirect3DDevice9** ppDevice) = 0;
    virtual HRESULT SetPrivateData( const GUID & refguid, 
            const void* pData,DWORD SizeOfData,DWORD Flags) = 0;
    virtual HRESULT GetPrivateData( const GUID & refguid, 
            void* pData,DWORD* pSizeOfData) = 0;
    virtual HRESULT FreePrivateData( const GUID & refguid) = 0;
    virtual DWORD SetPriority( DWORD PriorityNew) = 0;
    virtual DWORD GetPriority(void) = 0;
    virtual void PreLoad(void) = 0;
    virtual D3DRESOURCETYPE GetType(void) = 0;
    virtual DWORD SetLOD( DWORD LODNew) = 0;
    virtual DWORD GetLOD(void) = 0;
    virtual DWORD GetLevelCount(void) = 0;
    virtual HRESULT SetAutoGenFilterType( D3DTEXTUREFILTERTYPE FilterType) = 0;
    virtual D3DTEXTUREFILTERTYPE GetAutoGenFilterType(void) = 0;
    virtual void GenerateMipSubLevels(void) = 0;
    virtual HRESULT GetLevelDesc( UINT Level,D3DSURFACE_DESC *pDesc) = 0;
    virtual HRESULT GetSurfaceLevel( UINT Level,IDirect3DSurface9** ppSurfaceLevel) = 0;
    virtual HRESULT LockRect( UINT Level,D3DLOCKED_RECT* pLockedRect, 
            const RECT* pRect,DWORD Flags) = 0;
    virtual HRESULT UnlockRect( UINT Level) = 0;
    virtual HRESULT AddDirtyRect( const RECT* pDirtyRect) = 0;
};

We only need one of these methods for our code, which is the GetSurfaceLevel method. Starting from the top and counting down, we can see that this is the 19th method, so will therefore be at slot 18 in the V-table. We can now create a wrapper class around this interface.

internal sealed class Direct3DTexture9 : IDisposable
{
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int GetSurfaceLevelSignature(IDirect3DTexture9 texture, 
                         uint Level, out IntPtr ppSurfaceLevel);

    [ComImport, Guid("85C31227-3DE5-4f00-9B3A-F11AC38C18B5"), 
                InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IDirect3DTexture9
    {
    }

    private IDirect3DTexture9 comObject;
    private GetSurfaceLevelSignature getSurfaceLevel;

    internal Direct3DTexture9(IDirect3DTexture9 obj)
    {
        this.comObject = obj;
        HelperMethods.GetComMethod(this.comObject, 18, 
                                   out this.getSurfaceLevel);
    }

    ~Direct3DTexture9()
    {
        this.Release();
    }

    public void Dispose()
    {
        this.Release();
        GC.SuppressFinalize(this);
    }

    public IntPtr GetSurfaceLevel(uint Level)
    {
        IntPtr surface;
        Marshal.ThrowExceptionForHR(this.getSurfaceLevel(
                              this.comObject, Level, out surface));
        return surface;
    }

    private void Release()
    {
        if (this.comObject != null)
        {
            Marshal.ReleaseComObject(this.comObject);
            this.comObject = null;
            this.getSurfaceLevel = null;
        }
    }
}

In the code, I've used Marshal.ThrowExceptionForHR to make sure that the call succeeds - if there's an error, then it will throw the relevant .NET type (e.g., a result of E_NOTIMPL will result in a NotImplementedException being thrown).

Using the code

To use the attached code, you can either include the compiled binary into your project, or include the code as there's not a lot of it (despite the time spent on creating it!). Either way, you'll need to make sure you reference the Windows API Code Pack DirectX library in your project.

In the code, there are three classes of interest: D3D10Image, Direct2DControl, and Scene.

The D3D10Image class inherits from D3DImage, and adds an override of the SetBackBuffer method that accepts a Direct3D 10 texture (in the form of a Microsoft.WindowsAPICodePack.DirectX.Direct3D10.Texture2D object). As the code is written, the texture must be in the DXGI_FORMAT_B8G8R8A8_UNORM format; however, feel free to edit the code inside the GetSharedSurface function to whatever format you want (in fact, the original code by Jeremiah Morrill did allow for different formats, so take a look at that for inspiration).

Direct2DControl is a wrapper around the D3D10Image control, and provides an easy way to display a Scene. The control takes care of redrawing the Scene and D3D10Image when it's invalidated, and also resizes their contents. To help improve performance, the control uses a timer to resize the contents 100ms after the resize event has been received. If another request to be resized occurs during this time, the timer is reset to 100ms again. This might sound like it could cause problems when resizing, but internally, the control uses an Image control, which will stretch its contents when it's resized so the contents will always be visible; they just might get temporarily blurry. Once resizing has finished, the control will redraw its contents at the correct resolution. Sometimes, for reasons unknown to me, there will be a flicker when this happens, but by using the timer, this will occur infrequently.

The Scene class is an abstract class containing three main functions for you to override: OnCreateResources, OnFreeResources, and OnRender. The reason for the first two functions is that a DirectX device can get destroyed (for example, if you switch users), and afterwards, you will need to create a new device. These methods allow you to create/free device dependent resources, such as brushes for example. The OnRender method, as the name implies, is where you do the actual drawing.

Putting this together gives us this code to create a simple rectangle on a semi-transparent blue background:

<!-- Inside your main window XAML code -->
<!-- Make sure you put a reference to this at the top of the file:
        xmlns:d2d="clr-namespace:Direct2D;assembly=Direct2D"
 -->

<d2d:Direct2DControl x:Name="d2DControl" />
using D2D = Microsoft.WindowsAPICodePack.DirectX.Direct2D1;

internal sealed class MyScene : Direct2D.Scene
{
    private D2D.SolidColorBrush redBrush;

    protected override void OnCreateResources()
    {
        // We'll fill our rectangle with this brush
        this.redBrush = this.RenderTarget.CreateSolidColorBrush(
                             new D2D.ColorF(1, 0, 0));
    }

    protected override void OnFreeResources()
    {
        if (this.redBrush != null)
        {
            this.redBrush.Dispose();
            this.redBrush = null;
        }
    }

    protected override void OnRender()
    {
        // This is what we're going to draw
        var size = this.RenderTarget.Size;
        var rect = new D2D.Rect
            (
                5,
                5,
                (int)size.Width - 10,
                (int)size.Height - 10
            );

        // This actually draws the rectangle
        this.RenderTarget.BeginDraw();
        this.RenderTarget.Clear(new D2D.ColorF(0, 0, 1, 0.5f));
        this.RenderTarget.FillRectangle(rect, this.redBrush);
        this.RenderTarget.EndDraw();
    }
}

// This is the code behind class for the XAML
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // Add this after the call to InitializeComponent. Really you should
        // store this object as a member so you can dispose of it, but in our
        // example it will get disposed when the window is closed.
        this.d2DControl.Scene = new MyScene();
    }
}

Updating the Scene

In the original code to update the Scene, you needed to call Direct2DControl.InvalidateVisual. This has now been changed so that calling the Render method on Scene will cause the new Updated event to be fired, which the Direct2DControl subscribes to and invalidates its area accordingly.

Also discovered was that the Scene would sometimes flicker when redrawn. This seems to be an issue with the D3DImage control, and the solution (whilst not 100%) is to synchronize the AddDirtyRect call with when WPF is rendering (by subscribing to the CompositionTarget.Rendering event). This is all handled by the Direct2DControl for you.

To make things easier still, there's a new class deriving from Scene called AnimatableScene. After releasing the first version, there was some confusion with how to do continuous scene updates, so hopefully this class should make it easier - you use it the same as the Scene class, but your OnRender code will be called, when required, by setting the desired frames per second in the constructor (though see the Limitations section). Also note that if you override the OnCreateResources method, you need to make sure to call the base's version at the end of your code to start the animation, and when you override the OnFreeResources method, you need to call the base's version first to stop the animation (see the example in the attached code).

Mixed mode assembly is built against version 'v2.0.50727'

The attached code is compiled against .NET 4.0 (though it could probably be retargeted to work under .NET 2.0), but the Code Pack is compiled against .NET 2.0. When I first referenced the Code Pack and tried running the application, the above exception kept getting raised. The solution, found here, is to include an app.config file in the project with the following startup information:

<?xml version="1.0"?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0"/>
  </startup>
</configuration>

Limitations

Direct2D will work over remote desktop; however (as far as I can tell), the D3DImage control is not rendered. Unfortunately, I only have a Home Premium version of Windows 7, so cannot test any workarounds, but would welcome feedback in the comments.

The code written will work with targeting either x86 or x64 platforms (or even using the Any CPU setting); however, you'll need to use the correct version of Microsoft.WindowsAPICodePack.DirectX.dll; I couldn't find a way of making this automatic, and I don't think the Code Pack can be compiled to use Any CPU as it uses unmanaged code.

The timer used in the AnimatableScene is a DispatchTimer. MSDN states:

[The DispatcherTimer is] not guaranteed to execute exactly when the time interval occurs [...]. This is because DispatcherTimer operations are placed on the Dispatcher queue like other operations. When the DispatcherTimer operation executes is dependent on the other jobs in the queue and their priorities.

History

  • 02/11/10 - Direct2DControl has been changed to use a DispatchTimer so that it doesn't contain any controls needing to be disposed of (makes FxCop a little happier), and the control is now synchronized with WPF's CompositionTarget.Rendering event to reduce flickering. Scene has been changed to include an Updated event and to allow access to its D2DFactory to derived classes. Also, the AnimatedScene class has been added.
  • 21/09/10 - Initial version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Sam Cragg
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionDifferent PC = different behavior?memberRené Greiner25 Mar '13 - 10:15 
I ran the app on 2 PCs with different CPUs but the same graphics card. On one PC the ellipse flickers while it's fine on the other. But on both PCs the FPS output flickers. I really wonder about that because if I understood it correctly, the complete scene is rendered on back surface. It looks like GDI where everything is drawn one by one on the monitor. But maybe I'm wrong and the backsurface rendering sometimes works and sometimes not?
Where could the problem be located?
 
Anyway nice article, thanks!
QuestionHow to capture an Resize() eventmemberTony Teveris13 Dec '12 - 9:35 
How can I capture the Resize() event in the myscene.cs code. Newbie to C#, WPF and DirectX but from your example I'm learning a bunch and really appriciate it.
 
Thanks in advance.
 
Tony Teveris
Gerber Scientific Products
Senior Software Engineer
Phone: 860 648 8151
Fax: 860 648 8214
83 Gerber Road West
South Windsor, CT   06074
QuestionHigh Speed ChartsmemberDavid Roh18 Sep '12 - 2:26 
Awesome article - you got my five.
 
I am interested in using your library for high speed line and bar charts - what kind of performance improvement could I expect over standard WPF.
 
My typical line chart has 10,000 point, is real time, updates every second, and scrolls as it updates.
AnswerRe: High Speed ChartsmemberSam Cragg18 Sep '12 - 13:54 
It should be able to help in real time situations as Direct2D draws immediately to the screen.
 
That said, I don't have any hard figures to show you; you'll just have to try it and see, unfortunately. However, I have used it for drawing thousands of lines I noticed an improvement.
Question"Unable to create a Direct2D and/or Direct3D device." [modified]memberName taken26 Jul '11 - 1:14 
What does this error message at application startup mean?
 
"Unable to create a Direct2D and/or Direct3D device."
 
The Exception being thrown in this moment is a System.Exception with _COMPlusExceptionCode = -532462766 (0xE0434352). I have not changed anything at the code after unpacking the archive. (I.e. it's set to x86.) I'm on Windows 7 x64 and I have installed VS2010 SP1 and the latest Win7 SDK.
 
Does this kind of code require special hardware?
 
When switching to x64 and replacing the DLLs, it only gets worse. (I can't remember the details, I've deleted it all again.)
 
Update on Win7 x86: Doesn't work, too. Seems this doesn't work at all! Frown | :-(

modified on Tuesday, July 26, 2011 12:03 PM

AnswerRe: "Unable to create a Direct2D and/or Direct3D device."memberSam Cragg26 Jul '11 - 8:17 
The code uses the Windows API Code Pack to interface with Direct2D, which seems to be where the error is coming from.
 
Have you managed to get any of the examples from the Code Pack working, as they should be able to provide more detail to where the problem is (look through the source\Samples\DirectX\CS\Direct2D directory of the code pack archive).
GeneralProblem with small resolutionmembernout13 Apr '11 - 9:21 
hello,
 
first I have to say great work!!! I searched long time for such an example!
 
I want to use d2d with a very small resolution (width = 40 and height = 20). So I changed the folowing values in your example:
 
file: scenes.cs
function: private void CreateTexture(int width, int height)
changed values: description.Width = 40;
description.Height = 20;
 
This works so far. But now the d2DControl renders all graphics blurry. Is there any possibility that d2DControl renders without antialiasing? I searched a little bit and found something with the property SnapsToDevicePixels="True" but it seams that it doesn't work???
 
Thank you very much
Florian
GeneralRe: Problem with small resolutionmemberSam Cragg13 Apr '11 - 9:43 
The size of the texture will be the same size as the control, so if you try setting the Direct2DControl's Width/Height to 40/20 then it should create the right size texture for you.
 
In XAML this would be:
 
<d2d:Direct2DControl x:Name="d2DControl" Width="40" Height="20" />
 
Or in the code behaind (assuming the control is declared as above):
 
d2DControl.Width = 40;
d2DControl.Height = 20;

GeneralRe: Problem with small resolutionmembernout13 Apr '11 - 11:49 
Thank you for your fast answer!
 
OK, this works, but it would be nice when I can display the Direct2DControl bigger. This means I want "draw" into the Direct2DControl with a resolution of 40x20 but I want display the Direct2DControl for example with a resolution of 320x240. I hope you know what I mean??? So when I enter that code:
<d2d:Direct2DControl x:Name="d2DControl" Width="320" Height="240" />
the Direct2DControl is shown blurry.
 
Thanks so much
Florian
GeneralRe: Problem with small resolutionmemberSam Cragg13 Apr '11 - 13:16 
I'll be honest I'm struggling a little to understand what you want.
 
The Direct2DControl is similar to any bitmap image - if you draw a 40x20 image and scale it to 320x240 then it will be blurry by default (uses linear scaling). If you want to change a bitmap to be blocky then you can set the RenderOptions.BitmapScalingMode to NearestNeighbor.
 
So you could use this (which will also work for any image control):
<Viewbox Width="320" Height="240">
  <d2d:Direct2DControl x:Name="d2DControl" Width="40" Height="20"
    RenderOptions.BitmapScalingMode="NearestNeighbor" />
</Viewbox>
Also (again, not sure what you're aiming for) you might want to disable the anti-aliasing in Direct2D, in which case add the following to your derived Scene class:
protected override void OnCreateResources()
{
    // Create your resources here. e.g.
    // this.redBrush = this.RenderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0));

    // Add this line before calling the base's function
    this.RenderTarget.AntiAliasMode = D2D.AntiAliasMode.Aliased;
 
    base.OnCreateResources();
}
If this doesn't help then could you explain what you're trying to achieve and may be there's another solution.
GeneralRe: Problem with small resolutionmembernout14 Apr '11 - 6:11 
Hi,
thank you, your first solution is the solution I searched for! Now it works as I have imagined!!
 
Florian
GeneralMy vote of 5memberJohn Schroedl9 Nov '10 - 2:52 
Excellent blend of technologies.
GeneralProblem with bitmapsmemberTombah29 Oct '10 - 9:36 
Hi,
 
I've been using the D2D-WPF code from http://windowsclient.net/blogs/rob_relyea/archive/2010/04/30/gizmodo-posts-wpf-direct2d-sample-wow.aspx[^] which has been working fine for me.
 
After seeing your code, I decided to try it also (to get rid of the managed C++). There seems to be some kind of problem with bitmaps. Using your example code and changing the MyScene class to http://pastebin.com/gd45MaV3[^] and using CompositionTarget.Rendering event to render the scene, you'll see mighty flickering (you'll see the same with resize, but CompositionTarget.Rendering makes it very visible). But removing the DrawBitmap call stops it.
 
I'm total newbie with D2D/D3D, so I really don't have any idea what this is about, but looks like some kind of synchronization/flushing problem...
 
Any ideas?
 
Tomi
GeneralRe: Problem with bitmapsmemberSam Cragg29 Oct '10 - 10:21 
The code you're using to draw the bitmap look ok so I think the problem is elsewhere. How are you updating the drawing in the CompositionTarget.Rendering event?
 
I think it was mentioned in a previous thread (Performance and Device Lost handling) that it's a bad idea to call SetBackBuffer multiple times but instead to invalidate the D3DImage using the AddDirtyRect method.
GeneralRe: Problem with bitmapsmemberTombah29 Oct '10 - 19:52 
I'm calling d2DControl.InvalidateVisual(). And the same problem can be seen without using CompositionTarget.Rendering by just resizing the window, so the only change to the example is the MyScene class. The problem doesn't appear on every frame, but making the window very large increases the chances of it happening.
 
Further experimenting showed that the bitmaps are not to blame, as the same happens if I draw lots of rectangles also.
 
It looks to me as if the buffer was drawn to the screen in the middle of rendering. Here's a screen-shot:
http://www.taika.org/~tomba/test/d2d.png[^]
 
It looks like the background has been cleared (green), and a bit less than half of the screen has been drawn ok (black), and for one tile the red rectangle has been drawn, but not the black bitmap on top of it.
GeneralRe: Problem with bitmapsmemberSam Cragg30 Oct '10 - 10:24 
Hmmm... The resizing issue I assumed was because we're changing the back buffer but the AddDirtyRect function should just switch the front and back buffers, so there should be no flicker.
 
I'm a bit busy at the moment so I'll take a look at it on Tuesday and I'll update the article + code with an example of per frame updating (also want to include the code by xyzzer).
GeneralRe: Problem with bitmapsmemberTomi Valkeinen3 Nov '10 - 21:36 
I tried this again with your latest version, and the problem is still there =(. Have you been able to reproduce it, or is it just my machine?
GeneralRe: Problem with bitmapsmemberSam Cragg4 Nov '10 - 3:28 
When I first created the new example I noticed the text would flicker when the window was maximised so I investigated it and the problem was due to the call to AddDirtyRect not being synchronised with the WPF rendering system. I updated the code in the Direct2DControl to use a flag when the control needs updating and to look for this flag during the CompositionTarget.Rendering event to make sure the call to AddDirtyRect is finished during the WPF update process. On my machine this stopped the flickering.
 
Have you tried the new example with the FPS being drawn in the top left corner? (BTW, there was a small problem yesterday with the article that the article had been updated but not the code - this has been fixed now and the file you should have downloaded should be called Direct2DExample.zip)
GeneralRe: Problem with bitmapsmemberTomi Valkeinen4 Nov '10 - 3:42 
Yes, it was the new example with AnimatableScene.
 
I tested this with my laptop also, and I see the same problem. As if the front/back buffer swapping happens too early, or that the drawing actually happens to front buffer...
 
Have you tried making the scene very heavy? I see it clearly in my tests that the more I draw, the more visible is the effect. Both my desktop and laptop have quite slow gfx cards, so that may also affect why I see it so clearly.
GeneralPerformance and Device Lost handlingmemberRhox26 Oct '10 - 1:43 
First of all, thanks for your code. Looks very cool. I have 2 questions about performance and Device Lost Handling.
1. How do you handle Device Lost event (for example, if user will press Windows+L or Ctrl+Alt+Delete while running your app) ?
2. I've looked throw your code and figured out that you are recreating DX9 and DX9Device, which you are use to pass backbuffer to D3DImage, for each SetBackBuffer. Whay about performance issues if you need to call SetBackBuffer often (for example, if you need per frame rendering?) ?
 
Regards,
Nikita A. Skoblov
GeneralRe: Performance and Device Lost handlingmemberSam Cragg26 Oct '10 - 6:59 
The Direct2DControl subscribes to the IsFrontBufferAvailableChanged event and creates or frees the resources as appropriate.
 
Regarding the rendering you shouldn't need to call SetBackBuffer, because as you said this would hurt performance. To get a D3DImage to refresh it's content you call the AddDirtyRect method. The Direct2DControl does this for you when it's invalidated (e.g. by calling InvalidateVisual) and will also call the Scene.Render, which in turn calls the OnRender method of a derived class.
 
You might also want to look at this CodeProject article on the D3DImage control, as that control is used to display the Direct2D content.
 
Hope it helps!
QuestionHow to make use of the binaries?memberbimbambumbum15 Oct '10 - 13:19 
Hi
 
I'm a newbie trying to get started with WPF. I can run the submitted project but when I try to make a new WPF project in VS2010 I can't make the project run.
 
I've added the two DLLs as references (Direct2D.dll + Microsoft.WindowsAPICOdePack.DirectX.dll) which I guess is what you refer to as binaries? I have also added the XAML lines that references the Direct2D assembly but it still doesn't work out for me.
 
I get the error caught by the exception:
 
"Unable to create a Direct2D and/or Direct3D device!"
 

Can someone please tell me how to proceed?
 
Sorry if this one is a no-brainer
 
Thanks
Tom
AnswerRe: How to make use of the binaries?memberSam Cragg15 Oct '10 - 14:40 
In the example code I used a catch (Exception) block, which unfortunately hides any information of what error actually happened. Any chance you let me know the actual exception?
 
Just a guess, but you said the original project works but when you created yours it didn't work - did you also create the app.config file and include it in your project to enable the mixed mode assemblies?
GeneralRe: How to make use of the binaries?memberbimbambumbum15 Oct '10 - 15:02 
Hi Sam
 
I owe you an apology - I was indeed missing the app.config file in my project. Thanks a lot for taking time to help out here.
 
May I just ask two more questions.
 
I want to plot audio data from the soundcard (drawing 1024 lines 25 times/sec) and that is the reason why I'm trying to work with your project. I tried to do this using the traditional line drawing method in WPF but that wasn't fast enough - do you think that I'm doing the right thing exploiting Direct2D for this task?
 
I have tried to instantiate a timer in your project which should update the scene at regular intervals. The timer doesn't make the scene graphics update by itself (calling this.myScene.renderer() ) but I have to resize the window for the rendering to be updated. Can you please point me in the right direction on how to do this?
 
Sorry for stealing your time here with all these questions.
 
Best Regards
Tom
GeneralRe: How to make use of the binaries?memberSam Cragg15 Oct '10 - 15:52 
Not a problem, glad I could help.
 
Direct2D should be able to handle that. I'm running this on a two year old laptop with integrated graphics and can render 25,000 lines in 900ms with anti-alaising - this drops down to only 70ms (~13x faster) with alaising but won't look as nice. To disable anti-alaising you can do this in your custom Scene class:
 
protected override void OnRender()
{
    this.RenderTarget.AntiAliasMode = D2D.AntiAliasMode.Aliased;
    this.RenderTarget.BeginDraw();
 
    // Your drawing code

    this.RenderTarget.EndDraw();
}
 
As for updating the display, all you need to do is invalidate the Direct2DControl (i.e. this.d2DControl.InvalidateVisual()).
 
Let me know how you get on, sounds an interesting project!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 3 Nov 2010
Article Copyright 2010 by Sam Cragg
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid