Now that Windows 8 Consumer Preview is out we are eager to explore the new framework and the features it offers. While part of it is the good old XAML, there are plenty of other unique features, one of them being the DirectX integration at XAML level. While XAML is a great technology and offers literally unlimited features regarding layout and UI richness, sometimes there are cases where we need to push the framework beyond its limits in order to achieve highly-performing components with responsive and fluid user interaction. Although in most cases writing efficient algorithms and using the proper data structures will be enough to achieve the desired performance, there are scenarios where extensive UI scenes are rendered frequently and the density of the updated pixels is high. So I was most excited by the opportunity to present Direct2D content directly in XAM and started a research on how it actually works and how the performance is affected.

WinRT is a brand new framework and there is little to no information over the Internet about Direct2D and XAML integration, so I decided to summarize my efforts and to hopefully help other people who are doing the same tests. In this post I will show you a simple C++ demo that uses Direct2D and a SurfaceImageSource instance to render a Polyline with 5000 points. Microsoft has done a great job with introducing three different entry points where DirectX content may be directly embedded in a XAML application. The demo also compares the same scenario implemented with a XAML Polyline shape.

 

Download the Demo 

Disclaimer: I am not an experienced C++ developer (still :)), so probably there are areas in the code that might be written in a better way. In fact it is @Tim Heuer and a colleague of his that helped us with a flickering issue we had while performing this test – thanks a lot guys! 

Initializing the SurfaceImageSource

We will create a SurfaceImageSource that will be passed as a filling of a Rectangle XAML shape.

this->imageSource = ref new SurfaceImageSource(PixelWidth, PixelHeight);
IInspectable* inspectable = (IInspectable*) reinterpret_cast<IInspectable*>(this->imageSource);
inspectable->QueryInterface(__uuidof(ISurfaceImageSourceNative), (void **)&this->nativeImageSource);
 
ImageBrush^ imageBrush = ref new ImageBrush();
imageBrush->ImageSource = this->imageSource;
 
this->fillTarget->Fill = imageBrush;

Setting-up the DirectX resources

The sample code is taken directly from the MSDN article about DirectX and XAML Interop and modified to fit the demo:

UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
 
// This array defines the set of DirectX hardware feature levels this app will support.
// Note the ordering should be preserved.
// Don't forget to declare your application's minimum required feature level in its
// description.  All applications are assumed to support 9.1 unless otherwise stated.
D3D_FEATURE_LEVEL featureLevels[] =
{
    D3D_FEATURE_LEVEL_11_1,
    D3D_FEATURE_LEVEL_11_0,
    D3D_FEATURE_LEVEL_10_1,
    D3D_FEATURE_LEVEL_10_0,
    D3D_FEATURE_LEVEL_9_3,
    D3D_FEATURE_LEVEL_9_2,
    D3D_FEATURE_LEVEL_9_1
};
 
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> context;
D3D_FEATURE_LEVEL m_featureLevel;
 
D3D11CreateDevice(
            nullptr,                    // specify null to use the default adapter
            D3D_DRIVER_TYPE_HARDWARE,
            0,                          // leave as 0 unless software device
            creationFlags,              // optionally set debug and Direct2D compatibility flags
            featureLevels,              // list of feature levels this app can support
            ARRAYSIZE(featureLevels),   // number of entries in above list
            D3D11_SDK_VERSION,          // always set this to D3D11_SDK_VERSION for Metro style apps
            &device,                    // returns the Direct3D device created
            &m_featureLevel,            // returns feature level of device created
            &context                    // returns the device immediate context
            );
 
ComPtr<IDXGIDevice> dxgiDevice;
device.As(&dxgiDevice);
 
this->nativeImageSource->SetDevice(dxgiDevice.Get());
 
// create D2D device context
ComPtr<ID2D1Device> d2dDevice;
D2D1CreateDevice(dxgiDevice.Get(), NULL, &d2dDevice);
d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &this->d2dContext);
     
// create brush
d2dContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Bisque), &this->brush);

Performing the actual drawing

We will perform the drawing upon each CompositionTarget.Rendering callback so that we can measure the frames-per-second rate:

this->BeginDraw();
     
D2D_POINT_2F pt1, pt2;
 
float scale = PixelWidth / (float)(PointCount);
pt1.x = 0;
pt1.y = (float)(rand() % PixelHeight);
 
for(int i = 1; i < PointCount; i++)
{
    pt2.x = scale * i;
    pt2.y = (float)(rand() % PixelHeight);
 
    this->d2dContext->DrawLine(pt1, pt2, this->brush.Get(), 2);
 
    pt1 = pt2;
}
 
this->EndDraw();

The results

The dimensions of the updated surface are 1024 by 768 pixels. Point count is 5000. Here is a comparison table that summarizes the results:

Frames per second (FPS)
Direct2D  60
XAML Polyline  ~3

Test are performed on my desktop machine – quad core, 16 GB RAM. 

Pretty impressive - outputting a polyline using Direct2D is about 20 times faster. There are some things however that might need additional attention if you decide to output Direct2D content in your applications. Taking the rendering in our hands implies that we will also take care about the user interaction and hit-testing. We will need to facilitate the ID2D1Geometry interface, track input events and perform some manual hit-testing. Indeed, there will be some more code to be written but the most important thing is that it is doable and if you really need better performance you can achieve it with DirectX. 

Conclusion

I really love the option to mix XAML and DirectX content. There are many performance-critical areas in a XAML-based application where you can benefit a lot by using DirectX to present some content. Such areas for example are image rendering and image effects, text rendering and text measuring (you can even measure text on a background thread!) plus much more. The code from the demo application can be easily compiled as a WinMD component that might be accessed from C#. The Windows 8 Runtime (WinRT) is really a powerful framework that provides myriad of unique new features. There are so many new things I’ve learned so far that I can’t wait to share them with you - stay tuned for more posts on WinRT and Windows 8 to come :)

Download the Demo 


About the Author

Georgi Atanasov


Related Posts

Comments