Home

Direct2D

March 27, 2013

One of the first thing to have when developing a 3D game is actually to be able to output text on the screen. This is a requirement if you want to display debug messages and basically know what’s happening without using break points everywhere.

On my first attempt, I simply used WPF for that. On Windows 8 there is a fancy way of sharing a surface between the Xaml world and the DirectX world ( SwapChainBackgroundPanel ) but there are two drawbacks on that:

  • Only works on Windows 8. Yeah that’s fair, I should upgrade to Win8 …
  • Only works in Windows 8 Store Apps. That’s not fair. I’m planning to release something in the store but while I’m developing the game I surely don’t want to debug a game as a store app.

What I did first was to use a plain WPF UI, and draw it to a bitmap using RenderTargetBitmap.Draw. Once there’s a bitmap with the WPF content I had to copy it into a Texture2D I could then use in the rendering process.

  • Pro : Ability to use plain WPF
  • Pro : Visual Studio’s designer to design Game UI
  • Con : SLOW !

Also, this is quite awkward as there are two philosophies cohabiting together and this is not really easy to integrate them both.

I then went down another path, and I tried Direct2D (thank you SharpDX for having a binding on that) and it is incredibly easy to use ! Direct2D have RenderTarget things than can be used to draw stuff into that in the same way as GDI+:

public override void Rasterize(WindowContainer container, RenderTarget d2dTarget)
{
	if (container == null)
		throw new ArgumentNullException("container");
	if (d2dTarget == null)
		throw new ArgumentNullException("d2dTarget");

	if(brush != null)
	{
		d2dTarget.FillRectangle(TranslateRectangleToScreen(container, targetRect), brush);
	}
}

The beauty here is that you can create a Direct2D RenderTarget from a DirectX RenderTarget2D and bind it as a ShaderResource. With this architecture I am then able to have several UI projected into 2D panels in my 3D scene. Because it’s faster than a WPF render to bitmap with a copy afterward, I can have several UI running together at 60 fps.

One optimization here is to have only changed UI panels to redraw themselves. Having this will only trigger a redraw operation for frequently changing panels but other one will simply be cached and reused in the next draw operations without any Direct2D rendering.

To be funnier, I created a custom wrapper for render targets with implicit conversion to be able to use it indifferently when calling Direct2D functions, or binding it as a texture to Direct3D objects:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpDX.Direct2D1;
using SharpDX.Toolkit.Graphics;

namespace Kadma.Engine.UI
{
    ///
    /// Render target wrapped used by Direct2D and that
    /// can be passed down to Direct3D
    ///
    public sealed class RenderTargetWrapper : IDisposable
    {
        ///
        /// Direct2D Target
        ///
        public RenderTarget Direct2DTarget { get; private set; }

        ///
        /// Direct3D Target
        ///
        public RenderTarget2D Direct3DTarget { get; private set; }

        internal RenderTargetWrapper(RenderTarget direct2DTarget, RenderTarget2D direct3DTarget)
        {
            if (direct2DTarget == null)
                throw new ArgumentNullException("direct2DTarget");

            if (direct3DTarget == null)
                throw new ArgumentNullException("direct3DTarget");

            Direct2DTarget = direct2DTarget;
            Direct3DTarget = direct3DTarget;
        }

        public static implicit operator RenderTarget2D(RenderTargetWrapper wrapper)
        {
            return wrapper.Direct3DTarget;
        }

        public static implicit operator RenderTarget(RenderTargetWrapper wrapper)
        {
            return wrapper.Direct2DTarget;
        }

        #region IDisposable Members

        ~RenderTargetWrapper()
        {
            Dispose(false);
        }

        bool disposed = false;

        private void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    Direct2DTarget.Dispose();
                    Direct3DTarget.Dispose();

                    Direct2DTarget = null;
                    Direct3DTarget = null;
                }

                disposed = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        #endregion
    }
}

And voilà !

Direct2D

The fps is shown using a 2D Texture layer applied on top of the scene, and the “Direct2D !” text is a 2D panel in the 3D world rendering my UI 😀

Advertisements

Leave a Reply

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: