Basic Direct2D drawing with SharpDX

If you are an experienced DirectX programmer chances are you used the DirectDraw API for 2D drawing. If not, here’s a quick summary: it was used for quickly copying parts of one image to another using binary operations (“bit blitting”). This was a quick and good enough method for paletted or low BPP images, but if you wanted to do more advanced operations like rotation or alpha blending you had to implement your own software solution. With the advent of 3D graphics cards, hardware 2D support was phased out in favor of 3D acceleration, and this set the demise of DirectDraw.

Many years later, Direct2D was born to provide true 2D graphics to the DirectX API, instead of drawing 3D shapes using an orthographic projection. It features GPU accelerated raster and vector graphics support, and an immediate mode with a syntax similar to XNA’s SpriteBatch.

In this sample we will add Direct2D drawing to the basic Direct3D app created in the previous tutorial, so get the source code from the repository in case you don’t have it. First we must add the DeviceCreationFlags.BgraSupport parameter to the creation of our Direct3D11.Device, since it’s the color format that Direct2D works with. The next step is to create the default Direct2D device from the existing DXGI.Device2 we initialized previously and use it to generate a Direct2D1.DeviceContext instance:

Direct2D context creation
  1. SharpDX.Direct2D1.Device d2dDevice = new SharpDX.Direct2D1.Device(dxgiDevice2);
  2. d2dContext = new SharpDX.Direct2D1.DeviceContext(d2dDevice, SharpDX.Direct2D1.DeviceContextOptions.None);

Next up we must create a Bitmap1 object that will hold a reference to the backbuffer so Direct2D has a surface to draw on; to do this we have to specify its properties using a BitmapProperties1 declaration:

BitmapProperties1 declaration
  1. BitmapProperties1 properties = newBitmapProperties1(newPixelFormat(SharpDX.DXGI.Format.B8G8R8A8_UNorm, SharpDX.Direct2D1.AlphaMode.Premultiplied),
  2.     DisplayProperties.LogicalDpi, DisplayProperties.LogicalDpi, BitmapOptions.Target | BitmapOptions.CannotDraw);

This way we are going to create a bitmap in 32-bit BGRA format (that’s why we previously changed the DeviceCreationFlags!) with premultiplied alpha; BitmapOptions.Target allows the bitmap to be used as a device context target and BitmapOptions.CannotDraw forgives from using it when creating a brush or other drawing resource.

Next up we get the surface of the swap chain that hold the backbuffer and create the Bitmap1 using it as its source:

Bitmap1 creation
  1. Surface backBuffer = swapChain.GetBackBuffer<Surface>(0);
  2. d2dTarget = newBitmap1(d2dContext, backBuffer, properties);

And it’s ready to use it as our drawing target. Simply assign it to the Target property of the Direct2D1.DeviceContext you created previously and start drawing whatever your want to it.

To draw anything, we have to specify a brush that holds the properties of the stroke used to fill the figure. In this tutorial we use the three basic brushes:

  • SolidColorBrush: just a solid, plain color.
  • LinearGradientBrush: a linear gradient that interpolates between the GradientStop specified in a GradientStopCollection. We use a 2-stop gradient.
  • RadialGradientBrush: another gradient brush, but this time in a radial distribution. You can specify its RadiusX and RadiusY properties independently to get a circle or an ellipse.

Below is the code for drawing, where we set the drawing target and tell the context to start drawing. After clearing it with a well-known color, we fill different geometric shapes with the brushes previously created. EndDraw tells Direct2D to flush the buffer and raster as needed.

Geometry drawing
  1. d2dContext.Target = d2dTarget;
  2. d2dContext.BeginDraw();
  3. d2dContext.Clear(Color.CornflowerBlue);
  4. d2dContext.FillRectangle(new Vector2(50, 50, 450, 150), solidBrush);
  5. d2dContext.FillRoundedRectangle(new RoundedRectangle()
  6. {
  7.     Rect = new Vector2(50, 250, 450, 150),
  8.     RadiusX = 10,
  9.     RadiusY = 10
  10. }, linearGradientBrush);
  11. d2dContext.FillEllipse(new Ellipse(new Vector2(250, 525), 100, 100), radialGradientBrush);
  12. d2dContext.EndDraw();

And this is the colorful screen we will get:

Basic Direct2D drawing final result

As always, get the source code from the GitHub repo.

Your first DirectX 11 Metro application using SharpDX

UPDATE: you can learn how to initialize Direct3D in XAML applications in this new article.

Since Microsoft has decided that “XNA is mature enough” and Shawn Hargreaves has moved to Windows Phone, it looks like we won’t see a version 5 that supports Metro-style applications. The only alternative for graphics programming is using pure DirectX 11 now that it has been merged in the Windows core (no more separate DirectX SDK downloads, yay!) and Microsoft is going to actively promote it (Visual Studio 2012 includes a nice node-based shader editor).

So, if you want to target Metro apps with DirectX, the only initial option is to use C++/CX, which in my humble opinion is a total overkill and a huge step back. Sealed classes, no member functions for structures… this is the 21st century, please let me use a modern programming language and not one that has been patched through 30 years of existence. Well, now you can do this, thanks to the SharpDX project. It’s a series of wrappers that let you access the DirectX libraries from your C# code, and since XNA looks like a dead end, it’s what I’ll be writing about from now on.

Begin creating a new Windows Store > Blank App (XAML) (remember that this template is only available if you are running Visual Studio 2012 in Windows 8!)

Create a new Windows Metro Style Blank Application.

Now you can safely delete all the files except the Assets folder (contains the Windows Store icons for your program), the Package.appxmanifest file (has information about your application deployment settings) and the *_TemporaryKey.pfx file (the temporary certificate for signing the app). We will make everything from scratch.

Get yourself a copy of SharpDX’s libraries. You can download the latest stable version (recommended, since DirectX SDK is required for compiling) or get a copy of the source code and build it by yourself. I will be using the stable versions (2.5.0 as of the writing of this tutorial) and updating when necessary. Now, add references to SharpDX.dll, SharpDX.DXGI.dll and SharpDX.Direct3D11.dll.

Window creation for Windows Store applications is a bit different from everything so far. You can’t instance them directly, as they must be created via a proxy class that implements the IFrameworkViewSource interface. The only method that is enforced by this interface is CreateView, that returns a IFrameworkView object. And yes, you guessed it correct, your window class must implement it.

MyViewProviderFactory class
  1. internal class MyViewProviderFactory : IFrameworkViewSource
  2. {
  3.     public IFrameworkView CreateView()
  4.     {
  5.         return new MyViewProvider();
  6.     }
  7. }

This factory must be instantiated in your Main function and passed to the Run method of CoreApplication:

Main function
  1. private static void Main()
  2. {
  3.     // Tell CoreApplication to create a view instance through the factory.
  4.     MyViewProviderFactory factory = new MyViewProviderFactory();
  5.     CoreApplication.Run(factory);
  6. }

The methods enforced by IFrameworkView are the following:

  • Initialize(CoreApplicationView applicationView): this is the first function called when running the application. The applicationView variable has a CoreWindow property, but it’s null for some reason, so we can’t do anything at the moment.
  • SetWindow(CoreWindow window): called immediately after Initialize, with the current window as a parameter. Now we can save it in a member variable for future use.
  • Load(string entryPoint): create all the necessary Direct3D resources. Explained in more detail below.
  • Run(): we must implement here our update and render loop. Explained in more detail below.
  • Uninitialize(): called when the app is closing. We should release here all our non-disposable resources.

Now comes the fun part: creating all the Direct3D/DXGI objects to get a valid backbuffer. Start by instantiating a new Direct3D11.Device and specify DriverType.Hardware in the constructor. If this fails it means that you don’t have any hardware devices capable of using DirectX so you will have to fall back to software or upgrade your GPU!

Default device creation
  1. SharpDX.Direct3D11.Device defaultDevice = new SharpDX.Direct3D11.Device(DriverType.Hardware, DeviceCreationFlags.Debug);

We have to execute a series of queries to retrieve the final object that we will need: a DXGI.Factory2 that will allow us to create a swap chain for our CoreWindow based on the settings we specify in a SwapChainDescription1 instance. Finally, we create a RenderTargetView from said swap chain and save it for future use.

Backbuffer creation
  1. SharpDX.DXGI.Adapter dxgiAdapter = dxgiDevice2.Adapter;
  2. SharpDX.DXGI.Factory2 dxgiFactory2 = dxgiAdapter.GetParent<SharpDX.DXGI.Factory2>();
  3. // Description for our swap chain settings.
  4. SwapChainDescription1 description = new SwapChainDescription1()
  5. {
  6.     // 0 means to use automatic buffer sizing.
  7.     Width = 0,
  8.     Height = 0,
  9.     // 32 bit RGBA color.
  10.     Format = Format.B8G8R8A8_UNorm,
  11.     // No stereo (3D) display.
  12.     Stereo = false,
  13.     // No multisampling.
  14.     SampleDescription = new SampleDescription(1, 0),
  15.     // Use the swap chain as a render target.
  16.     Usage = Usage.RenderTargetOutput,
  17.     // Enable double buffering to prevent flickering.
  18.     BufferCount = 2,
  19.     // No scaling.
  20.     Scaling = Scaling.None,
  21.     // Flip between both buffers.
  22.     SwapEffect = SwapEffect.FlipSequential,
  23. };
  24. // Generate a swap chain for our window based on the specified description.
  25. swapChain = dxgiFactory2.CreateSwapChainForCoreWindow(device, new ComObject(window), ref description, null);
  26. // Create the texture and render target that will hold our backbuffer.
  27. Texture2D backBufferTexture = Texture2D.FromSwapChain<Texture2D>(swapChain, 0);
  28. backBuffer = new RenderTargetView(device, backBufferTexture);

Let’s finish this up by creating our main loop. Since CoreWindow doesn’t do it by default, you have to create your own loop and check when to exit the application (key pressed, focus lost, etc.). So just slap a while (true) in the Run() function and exit it with a return when you want (in this tutorial we check for the Escape key being pressed with window.GetAsyncKeyState(VirtualKey.Escape) == CoreVirtualKeyStates.Down). For now, the only Direct3D operations that we will be performing is to set the backbuffer and clear it with the color red, and then presenting it:

Clear screen/present
  1. context.OutputMerger.SetTargets(backBuffer);
  2. context.ClearRenderTargetView(backBuffer, Color.Red);
  3. // Present the current buffer to the screen.
  4. swapChain.Present(1, PresentFlags.None);

That’s it. You can grab the entire source code from the GitHub repo. Stay tuned for more articles!