Home > DirectX > C# DirectX API Face-off: SlimDX vs SharpDX – Which should you choose?

C# DirectX API Face-off: SlimDX vs SharpDX – Which should you choose?


A question I often see asked by beginning game programmers on the internet is:

I want to code my game in C#. Which DirectX wrapper API should I use?

As with most such things, all choices have their pros and cons. Here I will look at the two frameworks most currently in use at the time of writing: SlimDX and SharpDX.

Why choose C# for games and graphical applications?

Actually, as a general rule, I strongly advise all users to write their games in C++ where possible. Almost all commercial game development is done in C++, using C# decreases portability between platforms and incurs a small (perhaps 1-2%) performance hit due to the extra abstraction layer you will be using over the standard C++ DirectX API.

There are, however, some compelling reasons to use C#, and if you are in one of the categories below, read on:

  • you are a hobbyist developer writing code for fun, you know C#, and feel that C++ is too complicated (which is perfectly reasonable)
  • you want to quickly prototype a game and are using C# to save time
  • your environment prevents you from using C++

Obviously, if you’re a hobbyist coder, there is no need to get embroiled in C++, and if you’re a commercial outfit, using C# to prototype will get you up and running with less boilerplate code and memory management woes than C++. In all other cases, C++ is the preferred choice.

Note that in this article, I shall not be assessing the relative performance of SlimDX and SharpDX against each other, or against the native C++ DirectX API.

English: The wordmark of Microsoft's DirectX-A...

(Photo credit: Wikipedia)

SlimDX and SharpDX on paper

So what are these APIs? Well, SlimDX is an OOP-ified version of the C++ DirectX API written in C#, with some tweaks. It is updated as and when Microsoft release updates to DirectX itself. SharpDX uses a different approach, instead generating C# wrapper calls directly from the C++ DirectX API header files, meaning that unlike SlimDX, every method, property etc. available in the C++ DirectX API is also available in SharpDX.

SlimDX was the first product to arrive, and as such SharpDX has tried to encourage users to switch by making it largely syntactically compatible with SlimDX, using the same object and field names in most places etc.

Both products are free for commercial use.

Feature comparison

The following table shows some important comparisons between the two APIs:

SlimDX SharpDX
Paradigm Custom wrapper API Thin wrapper over DirectX API
Auto-generated from DirectX SDK headers
Latest Release January 2012
(4.0.13.43)
August 2013
(2.5.0)
License MIT MIT
Platform Compatibility Windows XP
Windows Vista
Windows Server 2003
Windows Server 2008
Windows 7
Windows XP
Windows Vista
Windows 7
Windows 8
Windows Phone 8
Native Libraries .NET 2.0 (x86/x64)
.NET 4.0 (x86/x64)
.NET 2.0-4.5
.NET 4.5 Core (Windows 8 Metro)
Mono 2.10 and later
Any CPU Support Yes but requires GAC install Yes
Direct2D 1.1 / DXGI 1.2 available in Windows 7 No No
Higher level API No Yes
Documentation state Getting Started Guide
Programming Guide
Deployment Guide
Class Library Reference
Class Library Reference (incomplete)
No tutorials
Use of MSDN DirectX docs recommended
NuGet Packages available Yes Yes
Notable users/games Spiderman: Web of Shadows
Zipper Interactive (SOCOM, MAG etc.)
LizardTech
Star Wars: The Force Unleashed
Operation Flashpoint: Dragon Rising
Unknown
Compatible game engines Unknown MonoGame
ANX Framework
Axiom3D
Delta Engine

If you are a professional user, the first thing to note is that SlimDX has not been updated since the start of 2012. In fairness, neither has DirectX itself to any major degree, but it would appear that development has stopped on SlimDX and the web site no longer seems to be maintained as it contains a number of out-of-date pieces of information.

On the other hand, the documentation for SharpDX – if you could call it that – is awful, and as you will see below I had quite a headache preparing some example code for this article. Production of code for SlimDX was quite smooth thanks to reasonable-to-good documentation on the web site.

SlimDX would appear to have the larger user base at present, so it may be easier to find help online from other users for SlimDX, however there is plenty of knowledge available from users of both frameworks. Despite the lack of tutorials on the official sites (SlimDX only provides 3 or 4 very basic tutorials), note that tutorials for both products can of course be found on various independent web sites.

In terms of features, only SharpDX supports Metro apps, Windows Phone 8, builds for Mono and .NET Any CPU support without requiring assemblies to be installed into the GAC, which is a potential minor inconvenience for the end-users of your software. SharpDX is also the only library of the two to offer a higher-level API – the SharpDX Toolkit – which abstracts away some of the complexity and boilerplate code.

Note that neither library provides support for Xbox 360 development (use XNA for that, it is your only choice).

In terms of trust, SlimDX has a proven track record, having been used in a number of high-profile video game titles from recent years. SharpDX does not have this track record yet, however most of the upcoming breed of C# “game engines” are based on SharpDX, so we will likely see a rise in use soon.

One important difference between the two APIs is the way COM objects are managed. SlimDX uses an object table to track the lifetime of COM objects and map them to SlimDX objects. It deals with duplicate instances for you (reference counting) but you must dispose of any objects you create. SharpDX leaves it up to the programmer to manage object lifetime, providing no middle management layer. Both have their pros and cons, and really, both go against the grain of .NET programming where you shouldn’t normally need to care about object lifetimes; however it is worth noting that the SlimDX solution – while making life for the user more consistent – does impose a small performance penalty.

Now let’s have a look at what it’s like to write code using these APIs.

The example

Figure 1. A clone of the MSDN Direct2D QuickStart demo application using SharpDX

Figure 1. A clone of the MSDN Direct2D QuickStart demo application using SharpDX

Here I will show how to create an exact clone of the MSDN Direct2D QuickStart application (see MSDN: Creating A Simple Direct2D Application and MSDN: Direct2D QuickStart (Windows 7 versions linked, a Windows 8 tutorial is also available via the links but the code is incorrect at the time of writing). This is a hideously bloated mess which takes over 400 lines to implement in C++, producing the image shown in figure 1.

For this example I will assume you have a basic knowledge of the components of DirectX and some basic experience writing simple DirectX applications. I shall use the latest techniques from Direct2D 1.1 – that is to say, using DXGI to deal with creating the render target, rather than the out-dated HwndRenderTarget interface from Direct2D 1.0. More details can be found in my article Direct2D 1.1 Migration Guide for Windows 7 Developers.

Please note that the example is purely for educational purposes and should not be used as a comparison between the coding complexity of the two APIs, especially since most of it is initialization boilerplate code.

Let’s get cracking.

Installation

Both frameworks have NuGet packages available, so just choose Manage NuGet packages… in Visual Studio to install them the easy way.

Namespaces

SlimDX and SharpDX use their own namespaces, unsurprisingly named SlimDX and SharpDX respectively. The pile of using statements at the very top of the code looks like this:

SlimDX
using System;
using System.Drawing;
using System.Windows.Forms;

using SlimDX;
using SlimDX.DXGI;
using SlimDX.Direct3D11;
using SlimDX.Direct2D;
using SlimDX.Windows;
using Device = SlimDX.Direct3D11.Device;
using FactoryD2D = SlimDX.Direct2D.Factory;
using FactoryDXGI = SlimDX.DXGI.Factory;
SharpDX
using System;
using System.Windows.Forms;

using SharpDX;
using SharpDX.DXGI;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.Direct2D1;
using SharpDX.Windows;
using Device = SharpDX.Direct3D11.Device;
using FactoryD2D = SharpDX.Direct2D1.Factory;
using FactoryDXGI = SharpDX.DXGI.Factory1;

SlimDX requires System.Drawing because the System.Drawing.Color object is used to specify colours. SharpDX uses its own SharpDX.Color struct for this.

The last three definitions for both APIs resolve name conflicts with the imported namespaces for objects we want to use. Note that SharpDX uses DXGI.Factory1 rather than DXGI.Factory to indicate the use of DXGI 1.2 as released in conjunction with Direct2D 1.1.

Form, device, swap chain, back buffer and render target initialization

This code goes at the start of Main() and is the same for both APIs:

// Create render target window
var form = new RenderForm("MSDN Direct2D Demo clone in C# - written by Katy Coe");

// Create swap chain description
var swapChainDesc = new SwapChainDescription()
{
    BufferCount = 2,
    Usage = Usage.RenderTargetOutput,
    OutputHandle = form.Handle,
    IsWindowed = true,
    ModeDescription = new ModeDescription(0, 0, new Rational(60, 1), Format.R8G8B8A8_UNorm),
    SampleDescription = new SampleDescription(1, 0),
    Flags = SwapChainFlags.AllowModeSwitch,
    SwapEffect = SwapEffect.Discard
};

// Create swap chain and Direct3D device
// The BgraSupport flag is needed for Direct2D compatibility otherwise RenderTarget.FromDXGI will fail!
Device device;
SwapChain swapChain;
Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.BgraSupport, swapChainDesc, out device, out swapChain);

// Get back buffer in a Direct2D-compatible format (DXGI surface)
Surface backBuffer = Surface.FromSwapChain(swapChain, 0);

Both APIs provide a RenderForm object which derives from System.Windows.Forms.Form and modifies the message pump to be more amenable to the needs of a DirectX application.

Some minor differences in syntax crop up when we create the render target:

SlimDX
RenderTarget renderTarget;

// Create Direct2D factory
using (var factory = new FactoryD2D())
{
    // Get desktop DPI
    var dpi = factory.DesktopDpi;

    // Create bitmap render target from DXGI surface
    renderTarget = RenderTarget.FromDXGI(factory, backBuffer, new RenderTargetProperties()
    {
        HorizontalDpi = dpi.Width,
        VerticalDpi = dpi.Height,
        MinimumFeatureLevel = SlimDX.Direct2D.FeatureLevel.Default,
        PixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Ignore),
        Type = RenderTargetType.Default,
        Usage = RenderTargetUsage.None
    });
}
SharpDX
RenderTarget renderTarget;

// Create Direct2D factory
using (var factory = new FactoryD2D())
{
    // Get desktop DPI
    var dpi = factory.DesktopDpi;

    // Create bitmap render target from DXGI surface
    renderTarget = new RenderTarget(factory, backBuffer, new RenderTargetProperties()
    {
        DpiX = dpi.Width,
        DpiY = dpi.Height,
        MinLevel = SharpDX.Direct2D1.FeatureLevel.Level_DEFAULT,
        PixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Ignore),
        Type = RenderTargetType.Default,
        Usage = RenderTargetUsage.None
    });
}

While SlimDX uses the static member function RenderTarget.FromDXGI to create a render target from a DXGI surface, SharpDX provides an overloaded constructor for the purpose. The field names for DPI and DirectX feature level in RenderTargetProperties differ slightly between the two APIs; otherwise, the code is identical.

Specialized window behaviour

.NET’s Windows Forms architecture doesn’t play nice with Alt+Enter to switch to full-screen in DirectX applications, so we override its behaviour in both versions and tell Windows Forms to ignore Alt+Enter key presses. Such key presses will be intercepted and processed automatically by DirectX, so we don’t want Windows Forms to interfere with this.

SlimDX
// Disable automatic ALT+Enter processing because it doesn't work properly with WinForms
using (var factory = swapChain.GetParent<FactoryDXGI>())
    factory.SetWindowAssociation(form.Handle, WindowAssociationFlags.IgnoreAltEnter);
SharpDX
// Disable automatic ALT+Enter processing because it doesn't work properly with WinForms
using (var factory = swapChain.GetParent<FactoryDXGI>())
    factory.MakeWindowAssociation(form.Handle, WindowAssociationFlags.IgnoreAltEnter);

Note the difference in method names here: SlimDX uses SetWindowAssociation while SharpDX uses MakeWindowAssociation.

Now we replace the default Alt+Enter handling function with our own, set the window’s starting size and prevent it from being re-sized (I have deliberately left out window re-sizing code here for simplicity):

// Add event handler for ALT+Enter
form.KeyDown += (o, e) =>
{
    if (e.Alt && e.KeyCode == Keys.Enter)
        swapChain.IsFullScreen = !swapChain.IsFullScreen;
};

// Set window size
form.Size = new System.Drawing.Size(640, 480);

// Prevent window from being re-sized
form.AutoSizeMode = AutoSizeMode.GrowAndShrink;

The code is the same for both APIs.

The main rendering loop

Both APIs provide a mechanism to allow you to inject custom rendering code into a RenderForm. A static method in each API simply takes the desired RenderForm and rendering function as arguments, then runs it indefinitely until the window is closed. In SlimDX this method is called MessagePump.Run; in SharpDX it is called RenderLoop.Run.

SlimDX
// Rendering function
MessagePump.Run(form, () =>
{
    renderTarget.BeginDraw();
    renderTarget.Transform = Matrix3x2.Identity;
    renderTarget.Clear(Color.White);

    // put drawing code here (see below)

    renderTarget.EndDraw();

    swapChain.Present(0, PresentFlags.None);
});
SharpDX
// Rendering function
RenderLoop.Run(form, () =>
{
    renderTarget.BeginDraw();
    renderTarget.Transform = Matrix3x2.Identity;
    renderTarget.Clear(Color.White);

    // put drawing code here (see below)

    renderTarget.EndDraw();

    swapChain.Present(0, PresentFlags.None);
});

Here we have just supplied a lambda function which clears the render target (window) to a white background and calls SwapChain.Present to flip the back buffers.

Drawing the example image

The following code draws the image in figure 1. It should replace the commented line in the section above.

A number of differences start to creep in now:

  • while SlimDX uses System.Drawing.Color as an argument to its Color4 object constructor to reference colours, SharpDX uses SharpDX.Color directly
  • in SlimDX, methods such as DrawLineDrawRectangle and FillRectangle have overloads which accept the brush as the first argument, then a series of co-ordinates, followed by any other parameters such as stroke thickness. In SharpDX, the co-ordinate arguments come first and the brush argument comes after the co-ordinates, followed by any other parameters
  • in SharpDX, point co-ordinates must be specified with a Vector2 object, whereas in SlimDX they can be plain integers or floats

Let’s have a look at the code:

SlimDX
using (var brush = new SolidColorBrush(renderTarget, new Color4(Color.LightSlateGray)))
{
    for (int x = 0; x < renderTarget.Size.Width; x += 10)
        renderTarget.DrawLine(brush, x, 0, x, renderTarget.Size.Height, 0.5f);

    for (int y = 0; y < renderTarget.Size.Height; y += 10)
        renderTarget.DrawLine(brush, 0, y, renderTarget.Size.Width, y, 0.5f);

    renderTarget.FillRectangle(brush, new RectangleF(renderTarget.Size.Width / 2 - 50, renderTarget.Size.Height / 2 - 50, 100, 100));
}

renderTarget.DrawRectangle(new SolidColorBrush(renderTarget, new Color4(Color.CornflowerBlue)),
    new RectangleF(renderTarget.Size.Width / 2 - 100, renderTarget.Size.Height / 2 - 100, 200, 200));
SharpDX
using (var brush = new SolidColorBrush(renderTarget, Color.LightSlateGray))
{
    for (int x = 0; x < renderTarget.Size.Width; x += 10)
        renderTarget.DrawLine(new Vector2(x, 0), new Vector2(x, renderTarget.Size.Height), brush, 0.5f);

    for (int y = 0; y < renderTarget.Size.Height; y += 10)
        renderTarget.DrawLine(new Vector2(0, y), new Vector2(renderTarget.Size.Width, y), brush, 0.5f);

    renderTarget.FillRectangle(new RectangleF(renderTarget.Size.Width / 2 - 50, renderTarget.Size.Height / 2 - 50, 100, 100), brush);
}

renderTarget.DrawRectangle(
    new RectangleF(renderTarget.Size.Width / 2 - 100, renderTarget.Size.Height / 2 - 100, 200, 200),
    new SolidColorBrush(renderTarget, Color.CornflowerBlue));

As you can see, the arguments are re-ordered as appropriate for what each API expects, and wrapped in Color4 and Vector2 objects where applicable.

Cleaning up

Both APIs expect you to dispose of objects wrapping COM interfaces properly, and the code for both is the same although the inner workings are substantially different under the hood between implementations (see the notes about SlimDX object tracking in the Feature Comparison section above):

renderTarget.Dispose();
swapChain.Dispose();
device.Dispose();

Source code

The complete source code for both versions follows.

SlimDX
using System;
using System.Drawing;
using System.Windows.Forms;

using SlimDX;
using SlimDX.DXGI;
using SlimDX.Direct3D11;
using SlimDX.Direct2D;
using SlimDX.Windows;
using Device = SlimDX.Direct3D11.Device;
using FactoryD2D = SlimDX.Direct2D.Factory;
using FactoryDXGI = SlimDX.DXGI.Factory;

namespace BasicWindow
{
    static class Program
    {
        static void Main()
        {
            // Create render target window
            var form = new RenderForm("MSDN Direct2D Demo clone using SlimDX - written by Katy Coe");

            // Create swap chain description
            var swapChainDesc = new SwapChainDescription()
            {
                BufferCount = 2,
                Usage = Usage.RenderTargetOutput,
                OutputHandle = form.Handle,
                IsWindowed = true,
                ModeDescription = new ModeDescription(0, 0, new Rational(60, 1), Format.R8G8B8A8_UNorm),
                SampleDescription = new SampleDescription(1, 0),
                Flags = SwapChainFlags.AllowModeSwitch,
                SwapEffect = SwapEffect.Discard
            };

            // Create swap chain and Direct3D device
            // The BgraSupport flag is needed for Direct2D compatibility otherwise RenderTarget.FromDXGI will fail!
            Device device;
            SwapChain swapChain;
            Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.BgraSupport, swapChainDesc, out device, out swapChain);

            // Get back buffer in a Direct2D-compatible format (DXGI surface)
            Surface backBuffer = Surface.FromSwapChain(swapChain, 0);
            RenderTarget renderTarget;

            // Create Direct2D factory
            using (var factory = new FactoryD2D())
            {
                // Get desktop DPI
                var dpi = factory.DesktopDpi;

                // Create bitmap render target from DXGI surface
                renderTarget = RenderTarget.FromDXGI(factory, backBuffer, new RenderTargetProperties()
                {
                    HorizontalDpi = dpi.Width,
                    VerticalDpi = dpi.Height,
                    MinimumFeatureLevel = SlimDX.Direct2D.FeatureLevel.Default,
                    PixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Ignore),
                    Type = RenderTargetType.Default,
                    Usage = RenderTargetUsage.None
                });
            }

            // Disable automatic ALT+Enter processing because it doesn't work properly with WinForms
            using (var factory = swapChain.GetParent<FactoryDXGI>())
                factory.SetWindowAssociation(form.Handle, WindowAssociationFlags.IgnoreAltEnter);

            // Add event handler for ALT+Enter
            form.KeyDown += (o, e) =>
                {
                    if (e.Alt && e.KeyCode == Keys.Enter)
                        swapChain.IsFullScreen = !swapChain.IsFullScreen;
                };

            // Set window size
            form.Size = new System.Drawing.Size(640, 480);

            // Prevent window from being re-sized
            form.AutoSizeMode = AutoSizeMode.GrowAndShrink;

            // Rendering function
            MessagePump.Run(form, () =>
            {
                renderTarget.BeginDraw();
                renderTarget.Transform = Matrix3x2.Identity;
                renderTarget.Clear(Color.White);

                using (var brush = new SolidColorBrush(renderTarget, new Color4(Color.LightSlateGray)))
                {
                    for (int x = 0; x < renderTarget.Size.Width; x += 10)
                        renderTarget.DrawLine(brush, x, 0, x, renderTarget.Size.Height, 0.5f);

                    for (int y = 0; y < renderTarget.Size.Height; y += 10)
                        renderTarget.DrawLine(brush, 0, y, renderTarget.Size.Width, y, 0.5f);

                    renderTarget.FillRectangle(brush, new RectangleF(renderTarget.Size.Width / 2 - 50, renderTarget.Size.Height / 2 - 50, 100, 100));
                }

                renderTarget.DrawRectangle(new SolidColorBrush(renderTarget, new Color4(Color.CornflowerBlue)),
                    new RectangleF(renderTarget.Size.Width / 2 - 100, renderTarget.Size.Height / 2 - 100, 200, 200));

                renderTarget.EndDraw();

                swapChain.Present(0, PresentFlags.None);
            });

            renderTarget.Dispose();
            swapChain.Dispose();
            device.Dispose();
        }
    }
}
SharpDX
using System;
using System.Windows.Forms;

using SharpDX;
using SharpDX.DXGI;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.Direct2D1;
using SharpDX.Windows;
using Device = SharpDX.Direct3D11.Device;
using FactoryD2D = SharpDX.Direct2D1.Factory;
using FactoryDXGI = SharpDX.DXGI.Factory1;

namespace BasicWindow
{
    static class Program
    {
        static void Main()
        {
            // Create render target window
            var form = new RenderForm("MSDN Direct2D Demo clone using SharpDX - written by Katy Coe");

            // Create swap chain description
            var swapChainDesc = new SwapChainDescription()
            {
                BufferCount = 2,
                Usage = Usage.RenderTargetOutput,
                OutputHandle = form.Handle,
                IsWindowed = true,
                ModeDescription = new ModeDescription(0, 0, new Rational(60, 1), Format.R8G8B8A8_UNorm),
                SampleDescription = new SampleDescription(1, 0),
                Flags = SwapChainFlags.AllowModeSwitch,
                SwapEffect = SwapEffect.Discard
            };

            // Create swap chain and Direct3D device
            // The BgraSupport flag is needed for Direct2D compatibility otherwise new RenderTarget() will fail!
            Device device;
            SwapChain swapChain;
            Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.BgraSupport, swapChainDesc, out device, out swapChain);

            // Get back buffer in a Direct2D-compatible format (DXGI surface)
            Surface backBuffer = Surface.FromSwapChain(swapChain, 0);
            RenderTarget renderTarget;

            // Create Direct2D factory
            using (var factory = new FactoryD2D())
            {
                // Get desktop DPI
                var dpi = factory.DesktopDpi;

                // Create bitmap render target from DXGI surface
                renderTarget = new RenderTarget(factory, backBuffer, new RenderTargetProperties()
                {
                    DpiX = dpi.Width,
                    DpiY = dpi.Height,
                    MinLevel = SharpDX.Direct2D1.FeatureLevel.Level_DEFAULT,
                    PixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Ignore),
                    Type = RenderTargetType.Default,
                    Usage = RenderTargetUsage.None
                });
            }

            // Disable automatic ALT+Enter processing because it doesn't work properly with WinForms
            using (var factory = swapChain.GetParent<FactoryDXGI>())   // Factory or Factory1?
                factory.MakeWindowAssociation(form.Handle, WindowAssociationFlags.IgnoreAltEnter);

            // Add event handler for ALT+Enter
            form.KeyDown += (o, e) =>
            {
                if (e.Alt && e.KeyCode == Keys.Enter)
                    swapChain.IsFullScreen = !swapChain.IsFullScreen;
            };

            // Set window size
            form.Size = new System.Drawing.Size(640, 480);

            // Prevent window from being re-sized
            form.AutoSizeMode = AutoSizeMode.GrowAndShrink;

            // Rendering function
            RenderLoop.Run(form, () =>
            {
                renderTarget.BeginDraw();
                renderTarget.Transform = Matrix3x2.Identity;
                renderTarget.Clear(Color.White);

                using (var brush = new SolidColorBrush(renderTarget, Color.LightSlateGray))
                {
                    for (int x = 0; x < renderTarget.Size.Width; x += 10)
                        renderTarget.DrawLine(new Vector2(x, 0), new Vector2(x, renderTarget.Size.Height), brush, 0.5f);

                    for (int y = 0; y < renderTarget.Size.Height; y += 10)
                        renderTarget.DrawLine(new Vector2(0, y), new Vector2(renderTarget.Size.Width, y), brush, 0.5f);

                    renderTarget.FillRectangle(new RectangleF(renderTarget.Size.Width / 2 - 50, renderTarget.Size.Height / 2 - 50, 100, 100), brush);
                }

                renderTarget.DrawRectangle(
                    new RectangleF(renderTarget.Size.Width / 2 - 100, renderTarget.Size.Height / 2 - 100, 200, 200),
                    new SolidColorBrush(renderTarget, Color.CornflowerBlue));

                renderTarget.EndDraw();

                swapChain.Present(0, PresentFlags.None);
            });

            renderTarget.Dispose();
            swapChain.Dispose();
            device.Dispose();
        }
    }
}

I would like to thank Roberto for his SharpDX tutorial repository at GitHub which saved me a lot of hassle while writing the SharpDX version of the example. Check out his repository for tons of good tutorials!

Conclusions

As you can see, the code for both versions of the example is very similar, although as noted earlier, this should not be taken as representative of how all applications written using these APIs will look. The availability of documentation and examples, higher-level APIs, support, updates and which API best suits your target platform are essentially the most important deciding factors, since – as shown – both have a fairly clean class-based API which closely resembles that of the COM interfaces used in the C++ DirectX SDK.

If I was to speculate, I would predict that SharpDX is the better choice for new projects, with the caveat that the learning curve is steep due to a lack of proper documentation. Since it is just a thin wrapper over the C++ headers, the MSDN documentation can be used and C++ code from other sources can be ported without too much trauma to C#; however, using the un-documented SharpDX Toolkit and other tools which come with the API is another matter entirely. Of crucial importance to professional developers is that SharpDX is still maintained, while SlimDX is not.

I hope you found this overview helpful. Please add your own feedback and experiences below!

I’m a software developer with very limited work capacity due to having the debilitating illness M.E. – please read my article Dying with M.E. as a Software Developer and donate to the crowdfund to help me with my bucket list if you found this article useful. Thank you so much!

Further Reading

Some useful links I found while writing this article:

Tutorials on SharpDX by Roberto/RobyDX (direct GitHub link)

SharpDX and Game Engines – Back to Zero? An interesting discussion of C# game engines on GameDevSE.

Richards Software Ramblings – Hills Demo with SlimDX and C# (gamedev.net)

Advertisements
Categories: DirectX Tags: , , , , ,
  1. January 6, 2014 at 23:40

    Hi, thanks for linking to my article! (Richards Software Ramblings – Hills Demo with SlimDX and C#)

    • January 7, 2014 at 03:15

      No problem 🙂 I own the book in question and re-wrote the Hills demo in C++ to use my own library (elsewhere on the site), was good to see it in C# 🙂

  2. zyko
    April 15, 2014 at 16:04

    SharpDx seems dead – forum is closed
    http://sharpdx.org/community/

    • Wraith
      June 15, 2014 at 15:23

      The forum was shut down, but SharpDx is still being supported and actively developed.

  3. August 25, 2014 at 21:49

    Hmm, how does choosing c# make your code less portable? Imo it makes it more portable.

    If you build an abstraction layer in c# to wrap SlimDx/SharpDx and OpenTK and build all of your code against mono, every platform is supported except the xbox 360 with a 1 button build… No need to port anything.

  4. umut
    December 2, 2014 at 12:44

    great article, it helped me a lot to figure out directx. thanks a lot for sharing

  5. umut
    December 4, 2014 at 12:39

    Hi, I will really appreciate if you can direct me on how to fill a polygon ? or fill a triangle? in sharpdx or slimdx. Regards

  6. January 5, 2015 at 22:47

    Thanks for making me aware of these two. I’m going to try ’em out right away!

  7. April 7, 2015 at 19:00

    PixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Ignore), —– this does not wok for me, how can I fix?

  8. April 7, 2015 at 21:31

    PixelFormat = new PixelFormat() { AlphaMode = SharpDX.Direct2D1.AlphaMode.Ignore, Format = Format.R8G8B8A8_UNorm },

    This worked from another example – great work Katy!!!!

  9. rimsey
    April 12, 2015 at 16:43

    Hi Katy – love your site! I am using SharpDX and I want to load a bitmap (.jpg or .png) from an embedded resource rather than from the disk. So the .exe is standalone. I have added the .png to the c# project and changed it’s build action to “Embedded Resource! but now I cannot find a way to load and then show it using your SharpDX 2D example. Any help would be greatly appreciated

  10. rimsey
    December 18, 2015 at 12:19

    Hi Katy, as per your example on this page (Sharpdx) I am drawing a simple pimal shape (private static SharpDX.RectangleF _ball;) and rendering it using Fillrectangle and a solidbrush (_whiteBrush = new SolidColorBrush(_renderTarget, SharpDX.Color.White); However I would like to give it a slight blur or glow to the edges to mimic an old crt based game (like pong). Any idea how to do this?

    thanks

  11. James
    March 13, 2016 at 14:46

    Hi Katy, on the topic of the performance hit taken by using SharpDX or SlimDX (or another graphical API wrapper) in C#. I’d say that’s actually debatable.

    While C++ is faster when used correctly, C# removes the human-error element from things like memory-management and makes it reasonably faster to create clean and structured code, which may run faster as a result of being able to apply optimizations easier.

    I’ve no doubt that big AAA studios with their 10s of programmers can build a game engine that outperforms even the best C# engine, but for smaller studios, That 1-2% performance trade-off is a bargin in terms of the amount of work that C# helps to reduce.

    Also, If you would like a “AAA quality” game to check out, try Space Engineers. It’s still currently in alpha (bordering on beta), but the graphical fidelity and sheer scale of the game blow many AAA games out of the water. And its written entirely in C# and SharpDX.

    They’re still using placeholder textures and models in many places too. 🙂

    • James
      March 13, 2016 at 14:48

      I forgot to mention that the team that created Space Engineers also have a second game built on the same engine called Medieval Engineers. But that one isn’t as far along.

  12. April 20, 2016 at 01:40

    If someone is looging for a graphics and game engine with C# I think Unity3D deserves a big mention.

    • rimsey
      April 20, 2016 at 14:05

      Why? Please elaborate.

  13. June 6, 2016 at 20:13

    I’m having a big problem with learning from this sample.
    I’m always getting Direct2DException while initializing the renderTarget. Can someone help me?

  14. svanpo
    August 26, 2016 at 03:03

    Hi Katy, thank you for this page very helpfull.

    I saw there are some memory leaks with the SolidColorBrush in the loop and with the backBuffer.

    For the loop, i fixed it with:

    using (SolidColorBrush brush = new SolidColorBrush(renderTarget, Color.CornflowerBlue))
    {
        renderTarget.DrawRectangle(
        new RectangleF(renderTarget.Size.Width / 2 - 100, renderTarget.Size.Height / 2 - 100, 200, 200),
        brush);
    }

    For the backBuffer:

    renderTarget.Dispose();
    backBuffer.Dispose();
    swapChain.Dispose();
    device3d.Dispose();

    Regards,
    Stef

  1. October 14, 2013 at 10:41
  2. July 1, 2014 at 20:15

Share your thoughts! Note: to post source code, enclose it in [code lang=...] [/code] tags. Valid values for 'lang' are cpp, csharp, xml, javascript, php etc. To post compiler errors or other text that is best read monospaced, use 'text' as the value for lang.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: