Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / DirectX

SurfaceImageSource Manager: Connecting C# and DirectX/Direct2D using the WinRT/Metro SurfaceImageSource class, through a small C++ component

4.88/5 (6 votes)
21 Mar 2012CPOL2 min read 45.2K   1.3K  
SurfaceImageSource Manager is a C++ WinRT component making it easy to produce ready-to-use SurfaceImageSource instances, and then use DirectX or Direct2D to define their aspect in C# Metro applications.

Sample Image

Introduction

WinRT includes a SurfaceImageSource class deriving from ImageSource making it easy to inject DirectX or Direct2D drawings in a Metro visual tree, as described in this article: "Combining XAML and DirectX". SurfaceImageSource is fully available to C++, but not to C#.

Background

The project requires the Windows 8 Customer Preview, the VS.NET 2011 Beta, and the SharpDX for Win8 Preview assemblies. The "Combining XAML and DirectX" article is a recommended reading.

Using the code

The SurfaceImageSource Manager solution provides the code for a KSurfaceImageSourceManager WinRT C++ Component along with a Test_SurfaceImageSourceManager C# Metro application showing how to use it. Test_SurfaceImageSourceManager relies on DXSharp to exploit the DirectX and Direct2D objects produced and returned by the KSurfaceImageSourceManager component.

Describing the KSurfaceImageSourceManager WinRT C++ Component

The role of the KSurfaceImageSourceManager component is to:

  • produce ready-to-use, fully connected, instances of the SurfaceImageSource WinRT class, given a (pixelWidth, pixelHeight) size (NewSurfaceImageSource), and later unregister the instance (DeleteSurfaceImageSource).
  • initialize and terminate Direct2D drawing sessions on a given SurfaceImageSource (BeginDraw2D / EndDraw2D).
  • initialize and terminate DirectX drawing sessions on a given SurfaceImageSource (BeginDraw3D / EndDraw3D).
  • provide the handles to the various Direct2D/DirectX devices, context handles owned by a given KSurfaceImageSourceManager (Get_ID3D11Device, Get_ID3D11DeviceContext, ...).

The public signature of the KSurfaceImageSourceManager class looks like:

C++
public ref class KSurfaceImageSourceManager sealed
{ 
public: 
	KSurfaceImageSourceManager(); 

	int Get_ID3D11Device();
	int Get_ID3D11DeviceContext();
	int Get_IDXGIDevice();
	int Get_ID2D1Factory1();
	int Get_ID2D1Device();
	int Get_ID2D1DeviceContext();

	SurfaceImageSource^ NewSurfaceImageSource(int pixelWidth, int pixelHeight);
	void DeleteSurfaceImageSource(SurfaceImageSource^ pSurfaceImageSource);
	
	int GetSurfaceImageSourceSize(SurfaceImageSource^ pSurfaceImageSource, int* pWidth, int* pHeight);

	int ClearSurfaceImageSource(SurfaceImageSource^ pSurfaceImageSource, float R, float G, float B, float A);

	int BeginDraw2D(SurfaceImageSource^ pSurfaceImageSource, int* pOffsetX, int* pOffsetY, int* pSurfaceWidth, int* pSurfaceHeight);
	void EndDraw2D(SurfaceImageSource^ pSurfaceImageSource);

	int BeginDraw3D(SurfaceImageSource^ pSurfaceImageSource, int* pOffsetX, int* pOffsetY, int* pSurfaceWidth, int* pSurfaceHeight);
	void EndDraw3D(SurfaceImageSource^ pSurfaceImageSource); };
};

A SurfaceImageSource produced by a KSurfaceImageSourceManager can be connected to an Image component as its Source (Image.Source property).

C++
// Creating a KSurfaceImageSourceManager instance
KSurfaceImageSourceManager _pKSurfaceImageSourceManager = new KSurfaceImageSourceManager();

// Using the KSurfaceImageSourceManager to create a (512 * 512) SurfaceImageSource 
SurfaceImageSource _pSurfaceImageSource = _pKSurfaceImageSourceManager.NewSurfaceImageSource(512, 512);

// Connecting the SurfaceImageSource to a 'im2D' Image component.
im2D.Source = _pSurfaceImageSource; 

Using KSurfaceImageSourceManager to manage Direct2D drawing sessions targeting a SurfaceImageSource

KSurfaceImageSourceManager makes it possible to perform Direct2D drawing sessions, opened by a call to BeginDraw2D and later closed by a call to EndDraw2D, on a SurfaceImageSource.

Each call to the KSurfaceImageSourceManager BeginDraw2D method returns a handle used to produce an instance of the SharpDX.Direct2D1.RenderTarget class and perform any Direct2D painting operation targeting the DXGI surface owned by a given SurfaceImageSource, as illustrated below:

C#
{
    int _hrenderTarget = 0;
    int _offsetx = 0;
    int _offsety = 0;
    int _surfacewidth = 0;
    int _surfaceheight = 0;
    try
    {
        // initiating a Direct2D drawing session: BeginDraw2D 
        _hrenderTarget = _pKSurfaceImageSourceManager.BeginDraw2D(_pSurfaceImageSource, out _offsetx, 
                         out _offsety, out _surfacewidth, out _surfaceheight);

        // connecting the _hrenderTarget handle returned by BeginDraw2D to a RenderTarget instance
        RenderTarget _pRenderTarget = new RenderTarget(new IntPtr(_hrenderTarget));

        // Direct2D drawing session targetting _pRenderTarget
        {
            _pRenderTarget.BeginDraw();

            SharpDX.Direct2D1.SolidColorBrush _brush = null;

            int _left = _offsetx;
            int _top = _offsety;
            int _right = _surfacewidth;
            int _bottom = _surfaceheight;

            // border
            _brush = new SharpDX.Direct2D1.SolidColorBrush(_pRenderTarget, new SharpDX.Color4(1, 0, 0, 1));
            _pRenderTarget.FillRectangle(new SharpDX.RectangleF(_left, _top, _right, _bottom), _brush);

            // fill
            int _border = 2;
            float _shade = 0.25f;
            _brush = new SharpDX.Direct2D1.SolidColorBrush(_pRenderTarget, new SharpDX.Color4(_shade, _shade, _shade, 1));
            _pRenderTarget.FillRectangle(new SharpDX.RectangleF(_left + _border, 
                    _top + _border, _right - 2 * _border, _bottom - 2 * _border), _brush);

            _pRenderTarget.EndDraw();
        }
    }
    catch (Exception E)
    {
    }
    finally
    {
        // terminating the Direct2D drawing session
        _pKSurfaceImageSourceManager.EndDraw2D(_pSurfaceImageSource);
    }
}

Using KSurfaceImageSourceManager to manage DirectX11 drawing sessions targeting a SurfaceImageSource

The KSurfaceImageSourceManager class exposes a Get_ID3D11Device method which returns a handle to be used to create a wrapping instance of the SharpDX.Direct3D11.Device1 class.

KSurfaceImageSourceManager makes possible to perform DirectX11 drawing sessions, opened by a call to BeginDraw3D and later closed by a call to EndDraw3D on a SurfaceImageSource.

Each call to the KSurfaceImageSourceManager BeginDraw3D method returns a handle used to produce an instance of the SharpDX.Direct3D11.Texture2D wrapping class, as illustrated in the code below:

C#
// a complete Direct3D11 drawing session targetting a _pSurfaceImageSource SurfaceImageSource
// produced by a _pKSurfaceImageSourceManager KSurfaceImageSourceManager
{
    int _hdevice = 0;
    int _pID3D11Texture2D = 0;
    int _offsetx = 0;
    int _offsety = 0;
    int _surfacewidth = 0;
    int _surfaceheight = 0;
    try
    {
        // retrieving the handle of the ID3D11Device used by _pKSurfaceImageSourceManager
        // and wrapping it with a SharpDX.Direct3D11.Device1 instance
        _hdevice = _pKSurfaceImageSourceManager.Get_ID3D11Device();
        SharpDX.Direct3D11.Device1 _device = new SharpDX.Direct3D11.Device1(new IntPtr(_hdevice));

        // initating a DirectX drawing session on _pSurfaceImageSource,
        // and wrapping the returned _pID3D11Texture2D ID3D11Texture2D handle
        // in a SharpDX.Direct3D11.Texture2D instance
        _pID3D11Texture2D = _pKSurfaceImageSourceManager.BeginDraw3D(_pSurfaceImageSource, 
             out _offsetx, out _offsety, out _surfacewidth, out _surfaceheight);
        Texture2D _pTexture2D = new Texture2D(new IntPtr(_pID3D11Texture2D));

        // preparing the _device.ImmediateContext context    
        SharpDX.Direct3D11.DeviceContext _context = _device.ImmediateContext;

        // making _pTexture2D, and thefore the _pSurfaceImageSource
        // DXGI surface, the _context current RenderTarget
        var _renderView = new RenderTargetView(_device, _pTexture2D);
        _context.OutputMerger.SetTargets(_renderView);
        // clearing the _context context
        _context.ClearRenderTargetView(_renderView, new Color4(0, 0, 1, 1f));

        // IMPORTANT: adapting the context ViewPort to the effective
        // position (_offsetx, _offsety, _surfacewidth, _surfaceheight)
        // of the drawing session retruned by BeginDraw3D
        {
            int _x = _offsetx;
            int _y = _offsety;
            int _dx = (_surfacewidth - _offsetx);
            int _dy = (_surfaceheight - _offsety);
            _context.Rasterizer.SetViewports(new Viewport(_x, _y, _dx, _dy, 0.0f, 1.0f));
        }

        // Perform some DirectX drawing operations in the SharpDX.Direct3D11.Device1 _device
        render(_device, _context);

    }
    catch (Exception E)
    {
    }
    finally
    {
        // Completing the DirectX drawing session
        _pKSurfaceImageSourceManager.EndDraw3D(_pSurfaceImageSource);
    }
}

Extra goodies

The Test_SurfaceImageSourceManager project C# source code provides a few extra goodies:

  • it gives the 2D_Color.fx source of a simple pair of Vertex / Pixel shaders, along with the Compile_FX.bat batch used to produce 2D_Color_vs.fxo / 2D_Color_ps.fxo
  • files embedded as resources in the project
  • it shows how to extract those .fxo resources and then turn them into SharpDX.Direct3D11.VertexShader/PixelShader instances assigned to a SharpDX.Direct3D11.DeviceContext context
  • ...

License

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