Click here to Skip to main content
Click here to Skip to main content

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

By , 21 Mar 2012
Rate this:
Please Sign up or sign in to vote.

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:

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).

// 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:

{
    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:

// 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)

About the Author

No Biography provided

Comments and Discussions

 
QuestionDoes this work on Windows Phone 8 Pinmemberjarnomo6-Nov-12 4:51 
AnswerRe: Does this work on Windows Phone 8 PinmemberToni Petrina13-Nov-12 23:38 
AnswerRe: Does this work on Windows Phone 8 PinmemberPhilippe Monteil13-Nov-12 23:43 
GeneralRe: Does this work on Windows Phone 8 PinmemberVanlalhriata5-Jun-13 19:53 
GeneralMy vote of 5 PinmemberFarhan Ghumra28-Jun-12 21:29 
QuestionNice I give it 5 PinmvpSacha Barber21-Mar-12 22:34 
GeneralMy vote of 5 PinmvpSacha Barber21-Mar-12 22:33 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 21 Mar 2012
Article Copyright 2012 by Philippe Monteil
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid