Click here to Skip to main content
15,886,067 members
Articles / Multimedia / DirectX

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

Rate me:
Please Sign up or sign in to vote.
4.88/5 (6 votes)
21 Mar 2012CPOL2 min read 44.1K   1.3K   14   7
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)


Written By
France France
I am a freelance software engineer living in Paris, France.

Comments and Discussions

 
QuestionDoes this work on Windows Phone 8 Pin
jarnomo6-Nov-12 4:51
jarnomo6-Nov-12 4:51 
Hey, very nice!

I'd be interested to know whether this works on Windows Phone 8? Does SurfaceImageSource exist on WP8 at all? If not is there another way to render animated 3D models on TOP of xaml content. From what I can tell, SharpDX can only render content below of any xaml content on the phone.

Thanks!
AnswerRe: Does this work on Windows Phone 8 Pin
Toni Petrina13-Nov-12 23:38
Toni Petrina13-Nov-12 23:38 
AnswerRe: Does this work on Windows Phone 8 Pin
Philippe Monteil13-Nov-12 23:43
Philippe Monteil13-Nov-12 23:43 
GeneralRe: Does this work on Windows Phone 8 Pin
Vanlalhriata5-Jun-13 19:53
Vanlalhriata5-Jun-13 19:53 
GeneralMy vote of 5 Pin
Farhan Ghumra28-Jun-12 21:29
professionalFarhan Ghumra28-Jun-12 21:29 
QuestionNice I give it 5 Pin
Sacha Barber21-Mar-12 22:34
Sacha Barber21-Mar-12 22:34 
GeneralMy vote of 5 Pin
Sacha Barber21-Mar-12 22:33
Sacha Barber21-Mar-12 22:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.