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).
KSurfaceImageSourceManager _pKSurfaceImageSourceManager = new KSurfaceImageSourceManager();
SurfaceImageSource _pSurfaceImageSource = _pKSurfaceImageSourceManager.NewSurfaceImageSource(512, 512);
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
{
_hrenderTarget = _pKSurfaceImageSourceManager.BeginDraw2D(_pSurfaceImageSource, out _offsetx,
out _offsety, out _surfacewidth, out _surfaceheight);
RenderTarget _pRenderTarget = new RenderTarget(new IntPtr(_hrenderTarget));
{
_pRenderTarget.BeginDraw();
SharpDX.Direct2D1.SolidColorBrush _brush = null;
int _left = _offsetx;
int _top = _offsety;
int _right = _surfacewidth;
int _bottom = _surfaceheight;
_brush = new SharpDX.Direct2D1.SolidColorBrush(_pRenderTarget, new SharpDX.Color4(1, 0, 0, 1));
_pRenderTarget.FillRectangle(new SharpDX.RectangleF(_left, _top, _right, _bottom), _brush);
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
{
_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:
{
int _hdevice = 0;
int _pID3D11Texture2D = 0;
int _offsetx = 0;
int _offsety = 0;
int _surfacewidth = 0;
int _surfaceheight = 0;
try
{
_hdevice = _pKSurfaceImageSourceManager.Get_ID3D11Device();
SharpDX.Direct3D11.Device1 _device = new SharpDX.Direct3D11.Device1(new IntPtr(_hdevice));
_pID3D11Texture2D = _pKSurfaceImageSourceManager.BeginDraw3D(_pSurfaceImageSource,
out _offsetx, out _offsety, out _surfacewidth, out _surfaceheight);
Texture2D _pTexture2D = new Texture2D(new IntPtr(_pID3D11Texture2D));
SharpDX.Direct3D11.DeviceContext _context = _device.ImmediateContext;
var _renderView = new RenderTargetView(_device, _pTexture2D);
_context.OutputMerger.SetTargets(_renderView);
_context.ClearRenderTargetView(_renderView, new Color4(0, 0, 1, 1f));
{
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));
}
render(_device, _context);
}
catch (Exception E)
{
}
finally
{
_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 - ...