Tag Archives: C++/CX

Debug Layer in Direct3D 11.1 Metro Style Applications

DirectX developers might (should) be familiar with the Debug Layer. This is a piece of code that you can inject into Direct3D for debugging instrumentation. When you create a D3D device, by default – Direct3D is a very thin layer that allows you to achieve maximum performance from your API calls through the drivers to the hardware. In fact it is so thin, that when an error occurs – you only get one of few error codes. This might make you wonder why that API call you are making is failing. Did you forget to pad that constant buffer struct to a multiple of 16 bytes size? Or perhaps you passed an invalid combination of flags to that other method. Well, for years now – you could get all that information by enabling the Debug Layer, but how do you do that in the WinRT world?

Continue reading

Advertisements
Tagged , , , , ,

Creating DirectX Interop Libraries for XAML Metro Style Apps – Part 1: Direct2D

The XAML-based UI stack for Metro Style Apps is quite rich for the youngest member of the XAML UI frameworks family, but sometimes the basic controls even with their rich APIs are not enough. That is where the most powerful point of extensibility of the XAML UI comes in – the DirectX integration.

With DirectX you can create the highest quality real time Direct3D graphics, high performance Direct2D drawings, apply stunning looking pixel shader effects, read and write images of many different formats with WIC, play back video with the Media Framework and high quality audio with XAudio2. You can do all these things in a XAML-based app, so you can use all the rich UI APIs of the XAML framework to quickly create a beautiful looking application and add some special DirectX touch that will make it unique.

In this article I will show you how to create a libraries for drawing Direct2D or Direct3D scenes in the background of an otherwise XAML-rendered UI.

Continue reading on http://labs.vectorform.com

Where to start

The DirectX and XAML interop article on MSDN talks about 3 different ways to mix XAML and DirectX in WinRT:

  1. SurfaceImageSource – which is good for mostly static scenes that get drawn into an Image control
  2. VirtualSurfaceImageSource – which is similar to SurfaceImageSource, but allows to work with surfaces larger than the size of the screen – perhaps for working with high resolution photos or maps
  3. SwapChainBackgroundPanel – which can be used for high performance graphics with almost the same level of control you would otherwise get if you created a solely DirectX-driven app or game

The last option is what we’ll use to create a DirectX-based background for a XAML app. A lot of the things we will use are already available as part of the Metro style app samples available from MSDN. There is no sample however, with the DirectX stuff packaged as a separate library so that you could use it in any C++, C# or VB application and have some reusable code that you would not have to write repeatedly.

Anatomy of a Direct2D Application

To learn how to create a reusable library it helps to know what the basics are of a DirectX application. When you create a basic Direct2D Application from the template that ships with Visual Studio 11 for Windows 8 Consumer Preview you get this:

The pch files are for the precompiled header with references to DirectX headers and WRL (Windows Runtime C++ Template Library), which is an ATL-inspired library that helps to work with COM in a WinRT app – quite useful since DirectX is built on COM.

DirectXHelper.h contains a simple inline function that throws WinRT platform exceptions for failed HRESULTs. This is useful since these exceptions get projected to CLR Exceptions, so if you are planning on consuming the code with .NET you should get regular .NET exceptions on failed HRESULTs.

DirectXBase is an abstract base class that helps to build basic DirectX applications that use D2D, D3D, WIC or DirectWrite by providing basic access to the global class factories and shared resources. Its main entry point is the Initialize() method that takes an instance of the CoreWindow class, which is the core UI piece of every WinRT app and makes that window into a DirectX-managed one. This is also the thing we’ll need to change when we use the SwapChainBackgroundPanel since we want to only associate that panel with DirectX and not the entire UI. Initialization code is divided into 3 methods:

  1. CreateDeviceIndependentResources that in the case of a Direct2D app creates D2D and DWrite factories,
  2. CreateDeviceResources that creates so called D3D 11 device, its context, D2D device and sets the DPI
  3. CreateWindowSizeDependentResources creates
    1. The swap chain, which is a collection of bitmaps or textures that get presented on screen in quick succession to create an illusion of motion,
    2. The render target view, which tells the GPU what to render to (the back buffer of the swap chain),
    3. The depth stencil texture, which is used to control layering/z-ordering of pixels in the scene,
    4. The view port, which controls the view of the swap chain that is shown on screen,
    5. Finally – it associates the D2D device with the back buffer and sets the text anti-aliasing mode

UpdateForWindowSizeChange() is what you need to call whenever the screen resolution changes (e.g. when the app gets snapped, goes into portrait mode or back or when display monitor configuration changes). It basically clears out old window size dependent resources and calls CreateWindowSizeDependentResources to recreate these in different resolution.
The Render() method is where you put all your scene rendering code calls and is what you call when you want to update the view.
Present() should be called after Render() and it basically flips the buffers in the swap chain or reinitializes everything if something failed.
BasicDirect2DApplication.cpp/.h is named after my application name. It is the main entry point of the application and inherits from DirectXBase, overriding its methods to actually render some content on the screen, so you end up with a simple text displayed in the middle of the screen when the app is run. It also has the code that wires up DirectX to the CoreWindow and controls the rendering events. If you are interested in how to do it for a purely DirectX-based application – you can read the How to set up your Metro style DirectX app to display a view on MSDN. For DirectX-XAML interop – we will do things differently.

Anatomy of a Direct3D Application

The Direct3D app is a bit similar, where Direct3DBase is analogue to the DirectXBase class of the Direct2D app, but has a few more files worth noting:

My BasicDirect3DApplication class in this case does not include any DirectX code except for calling out to CubeRenderer which is the subclass of the Direct3DBase class in this case. The Direct3DBase class in the template D3D app is a bit different from its D2D version. Obviously it does not do anything with D2D or DWrite, so it does not deal with these class factories.
CubeRenderer defines the vertices of a cube and allows to render it animated based on system time.
SimplePixel/VertexShader.hlsl are the pixel and vertex shader files that are used to process the cube vertices and turn them into a rasterized output on your screen.

Creating a DirectX WinRT Component Library

I could just share a complete library, which I will at the end, but I think it helps to know how it is done in the first place, so in case you disagree with something I have done or if something changes in the future versions of the tools which currently are in flux you will be able to do it again yourself.

If you want to create the library you start with an empty C++ WinRT Component Library project. You can remove the stub WinRTComponent.cpp/.h files. Next you should to copy the list of referenced project linker input files (static libraries) from the template DX application to the library. The screenshot below shows where to do it. Note – you need to do it once per each solution configuration, so if you switch between Win32, x64 or ARM you will need to reenter these. The right thing to do then would be to switch between all configurations and set these for all of them now to save some trouble later once you start building the solution for other platforms. Here is the list:

d2d1.lib; d3d11.lib; dxgi.lib; ole32.lib; windowscodecs.lib; dwrite.lib; %(AdditionalDependencies)

The second step is to copy the header file includes from the template DX app pch into your project’s pch file.
Finally, copy the Direct3DBase and DirectXHelper files into the library and we are ready to do some coding. Note that copying and pasting between C++ projects in the Solution Explorer works differently than with say C#. If you do it for files in a C# project – they get copied between projects. If you do it for C++ they only get referenced in another project, so make sure you do not do it and delete a temporary project! You can instead just copy the files in Windows Explorer and add them to the project after making the copy outside.

Creating Direct2DBaseForComposition

Add a new class to the project called Direct2DBaseForComposition. You can do it by pressing Alt+Shift+C in the Solution Explorer. The class will derive from DirectXBase and will hook up to the SwapChainBackgroundPanel instead of the CoreWindow in the way described in the MSDN article, so we’ll need to hide the version of the Initialize method that only takes a CoreWindow reference and add one that takes a SwapChainBackgroundPanel, save the native interface to the panel, modify the swap chain initialization to set its dimensions based on panel size and set scaling to DXGI_SCALING_STRETCH, replace the CreateSwapChainForCoreWindow call with one for CreateSwapChainForComposition, hook up the swap chain to the swap chain panel and finally override the Present() method to call the version of the Initialize() method that takes the swap chain panel when the device gets reset.

#pragma once
#include "DirectXBase.h"
#include "windows.ui.xaml.media.dxinterop.h"

ref class Direct2DBaseForComposition abstract
    : public DirectXBase
{
public:
    Direct2DBaseForComposition(void);
    ~Direct2DBaseForComposition(void);

public:
	virtual void CreateWindowSizeDependentResources() override;
	virtual void Initialize(Windows::UI::Core::CoreWindow^ window) override;
	void Initialize(
		_In_ Windows::UI::Core::CoreWindow^ window,
		_In_ Windows::UI::Xaml::Controls::SwapChainBackgroundPanel^ swapChainPanel);
	virtual void Present() override;

private:
	Microsoft::WRL::ComPtr<ISwapChainBackgroundPanelNative>   m_swapChainNative;
	Windows::UI::Xaml::Controls::SwapChainBackgroundPanel^    m_swapChainPanel;
};

Direct2DBaseForComposition.cpp

#include "pch.h"
#include "Direct2DBaseForComposition.h"

using namespace Microsoft::WRL;
using namespace Windows::ApplicationModel;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::ApplicationModel::Activation;
using namespace Windows::UI::Core;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::Graphics::Display;
using namespace D2D1;

Direct2DBaseForComposition::Direct2DBaseForComposition(void)
{
}


Direct2DBaseForComposition::~Direct2DBaseForComposition(void)
{
}

void Direct2DBaseForComposition::Initialize(
	_In_ Windows::UI::Core::CoreWindow^ window)
{
    // Can't reduce visibility of a ref class method in a subclass
	throw Platform::Exception::CreateException(
		1 << 31 | FACILITY_ITF << 16 | 1);
		//ref new Platform::String(L"This method is not supported. Use the other overload."));
}

void Direct2DBaseForComposition::Initialize(
	_In_ Windows::UI::Core::CoreWindow^ window,
	_In_ Windows::UI::Xaml::Controls::SwapChainBackgroundPanel^ swapChainPanel)
{
	m_swapChainPanel = swapChainPanel;

	IInspectable* panelInspectable =
		(IInspectable*) reinterpret_cast<IInspectable*>(
			swapChainPanel);
	panelInspectable->QueryInterface(
		__uuidof(ISwapChainBackgroundPanelNative),
		(void **)&m_swapChainNative);

	DirectXBase::Initialize(window);
}

// Allocate all memory resources that change on a window SizeChanged event.
void Direct2DBaseForComposition::CreateWindowSizeDependentResources()
{ 
    // Store the window bounds so the next time we get a SizeChanged event we can
    // avoid rebuilding everything if the size is identical.
    m_windowBounds = m_window->Bounds;

    // If the swap chain already exists, resize it.
    if(m_swapChain != nullptr)
    {
        DX::ThrowIfFailed(
            m_swapChain->ResizeBuffers(2, 0, 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0)
            );
    }
    // Otherwise, create a new one.
    else
    {
        // Create a descriptor for the swap chain.
        DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
		swapChainDesc.Width = (UINT)m_windowBounds.Width;
		swapChainDesc.Height = (UINT)m_windowBounds.Height;
        swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;           // this is the most common swapchain format
        swapChainDesc.Stereo = false; 
        swapChainDesc.SampleDesc.Count = 1;                          // don't use multi-sampling
        swapChainDesc.SampleDesc.Quality = 0;
        swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        swapChainDesc.BufferCount = 2;                               // use double buffering to enable flip
        swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
        swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // we recommend using this swap effect for all applications
        swapChainDesc.Flags = 0;

        // Once the desired swap chain description is configured, it must be created on the same adapter as our D3D Device

        // First, retrieve the underlying DXGI Device from the D3D Device.
        ComPtr<IDXGIDevice1> dxgiDevice;
        DX::ThrowIfFailed(
            m_d3dDevice.As(&dxgiDevice)
            );

        // Identify the physical adapter (GPU or card) this device is running on.
        ComPtr<IDXGIAdapter> dxgiAdapter;
        DX::ThrowIfFailed(
            dxgiDevice->GetAdapter(&dxgiAdapter)
            );

        // And obtain the factory object that created it.
        ComPtr<IDXGIFactory2> dxgiFactory;
        DX::ThrowIfFailed(
            dxgiAdapter->GetParent(
                __uuidof(IDXGIFactory2), 
                &dxgiFactory
                )
            );

		// Create a swap chain for this window from the DXGI factory.
		DX::ThrowIfFailed(
			dxgiFactory->CreateSwapChainForComposition(
				m_d3dDevice.Get(),
				&swapChainDesc,
				nullptr,    // allow on all displays
				&m_swapChain
				)
			);

        m_swapChainNative->SetSwapChain(m_swapChain.Get());

        // Ensure that DXGI does not queue more than one frame at a time. This both reduces 
        // latency and ensures that the application will only render after each VSync, minimizing 
        // power consumption.
        DX::ThrowIfFailed(
            dxgiDevice->SetMaximumFrameLatency(1)
            );

    }

    // Obtain the backbuffer for this window which will be the final 3D rendertarget.
    ComPtr<ID3D11Texture2D> backBuffer;
    DX::ThrowIfFailed(
        m_swapChain->GetBuffer(
            0,
            __uuidof(ID3D11Texture2D),
            &backBuffer
            )
        );

    // Create a view interface on the rendertarget to use on bind.
    DX::ThrowIfFailed(
        m_d3dDevice->CreateRenderTargetView(
            backBuffer.Get(),
            nullptr,
            &m_renderTargetView
            )
        );

    // Cache the rendertarget dimensions in our helper class for convenient use.
    D3D11_TEXTURE2D_DESC backBufferDesc = {0};
    backBuffer->GetDesc(&backBufferDesc);
    m_renderTargetSize.Width  = static_cast<float>(backBufferDesc.Width);
    m_renderTargetSize.Height = static_cast<float>(backBufferDesc.Height);

    // Create a descriptor for the depth/stencil buffer.
    CD3D11_TEXTURE2D_DESC depthStencilDesc(
        DXGI_FORMAT_D24_UNORM_S8_UINT, 
        backBufferDesc.Width,
        backBufferDesc.Height,
        1,
        1,
        D3D11_BIND_DEPTH_STENCIL
        );

    // Allocate a 2-D surface as the depth/stencil buffer.
    ComPtr<ID3D11Texture2D> depthStencil;
    DX::ThrowIfFailed(
        m_d3dDevice->CreateTexture2D(
            &depthStencilDesc,
            nullptr,
            &depthStencil
            )
        );

    // Create a DepthStencil view on this surface to use on bind.
    DX::ThrowIfFailed(
        m_d3dDevice->CreateDepthStencilView(
            depthStencil.Get(),
            &CD3D11_DEPTH_STENCIL_VIEW_DESC(D3D11_DSV_DIMENSION_TEXTURE2D),
            &m_depthStencilView
            )
        );

    // Create a viewport descriptor of the full window size.
    CD3D11_VIEWPORT viewport(
        0.0f,
        0.0f,
        static_cast<float>(backBufferDesc.Width),
        static_cast<float>(backBufferDesc.Height)
        );

    // Set the current viewport using the descriptor.
    m_d3dContext->RSSetViewports(1, &viewport);

    // Now we set up the Direct2D render target bitmap linked to the swapchain. 
    // Whenever we render to this bitmap, it will be directly rendered to the 
    // swapchain associated with the window.
    D2D1_BITMAP_PROPERTIES1 bitmapProperties = 
        BitmapProperties1(
            D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
            PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
            m_dpi,
            m_dpi
            );

    // Direct2D needs the dxgi version of the backbuffer surface pointer.
    ComPtr<IDXGISurface> dxgiBackBuffer;
    DX::ThrowIfFailed(
        m_swapChain->GetBuffer(
            0,
            __uuidof(IDXGISurface),
            &dxgiBackBuffer
            )
        );

    // Get a D2D surface from the DXGI back buffer to use as the D2D render target.
    DX::ThrowIfFailed(
        m_d2dContext->CreateBitmapFromDxgiSurface(
            dxgiBackBuffer.Get(),
            &bitmapProperties,
            &m_d2dTargetBitmap
            )
        );

    // So now we can set the Direct2D render target.
    m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());

    // Set D2D text anti-alias mode to Grayscale to ensure proper rendering of text on intermediate surfaces.
    m_d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
}

// Method to deliver the final image to the display.
void Direct2DBaseForComposition::Present()
{
    // The first argument instructs DXGI to block until VSync, putting the application
    // to sleep until the next VSync. This ensures we don't waste any cycles rendering
    // frames that will never be displayed to the screen.
    HRESULT hr = m_swapChain->Present(1, 0);

    // If the device was removed either by a disconnect or a driver upgrade, we 
    // must completely reinitialize the renderer.
    if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
    {
        Initialize(m_window, m_swapChainPanel);
    }
    else
    {
        DX::ThrowIfFailed(hr);
    }
}

Adding the Rendering Code

The BasicDirect2DApplication I created earlier has four overrides to DirectXBase class that call back to the base class and then create resources for rendering or do the rendering. We will create another class derived from Direct2DBaseForComposition that will do the same, but call back to Direct2DBaseForComposition in the overrides instead of DirectXBase.

SampleD2DRenderer.h

#pragma once
#include "Direct2DBaseForComposition.h"

ref class SampleD2DRenderer
    : public Direct2DBaseForComposition
{
public:
    SampleD2DRenderer(void);
    ~SampleD2DRenderer(void);

    // DirectXBase Methods
    virtual void CreateDeviceIndependentResources() override;
    virtual void CreateDeviceResources() override;
    virtual void CreateWindowSizeDependentResources() override;
    virtual void Render() override;

private:
    Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> m_solidBrush;
    Microsoft::WRL::ComPtr<IDWriteTextFormat> m_textFormat;
    Microsoft::WRL::ComPtr<IDWriteTextLayout> m_textLayout;
};

SampleD2DRenderer.cpp

#include "pch.h"
#include "SampleD2DRenderer.h"


SampleD2DRenderer::SampleD2DRenderer(void)
{
}


SampleD2DRenderer::~SampleD2DRenderer(void)
{
}

void SampleD2DRenderer::CreateDeviceIndependentResources()
{
    Direct2DBaseForComposition::CreateDeviceIndependentResources();

    // Create a DirectWrite text format object.
    DX::ThrowIfFailed(
        m_dwriteFactory->CreateTextFormat(
            L"Gabriola",
            NULL,
            DWRITE_FONT_WEIGHT_REGULAR,
            DWRITE_FONT_STYLE_NORMAL,
            DWRITE_FONT_STRETCH_NORMAL,
            64.0f,
            L"en-US", // Locale
            &m_textFormat
            )
        );

    // Center the text horizontally and vertically.
    m_textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
    m_textFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
}

void SampleD2DRenderer::CreateDeviceResources()
{
    Direct2DBaseForComposition::CreateDeviceResources();

    DX::ThrowIfFailed(
        m_d2dContext->CreateSolidColorBrush(
            D2D1::ColorF(D2D1::ColorF::MidnightBlue),
            &m_solidBrush
            )
        );
}

void SampleD2DRenderer::CreateWindowSizeDependentResources()
{
    Direct2DBaseForComposition::CreateWindowSizeDependentResources();

    const wchar_t* text = L"Hello, Direct2D!";

    // Create a DirectWrite Text Layout object.
    DX::ThrowIfFailed(
        m_dwriteFactory->CreateTextLayout(
            text,                           // Text to be displayed
            wcslen(text),                   // Length of the text
            m_textFormat.Get(),             // DirectWrite Text Format object
            m_renderTargetSize.Width,       // Width of the Text Layout
            m_renderTargetSize.Height,      // Height of the Text Layout
            &m_textLayout
            )
        );

    // Create a text range corresponding to the entire string.
    DWRITE_TEXT_RANGE textRange = {0};
    textRange.length = wcslen(text);
    textRange.startPosition = 0;

    // Set the font size and weight on the text range.
    m_textLayout->SetFontSize(100.f, textRange);
    m_textLayout->SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, textRange);
}

void SampleD2DRenderer::Render()
{
    m_d2dContext->BeginDraw();

    m_d2dContext->Clear(D2D1::ColorF(D2D1::ColorF::CornflowerBlue));
    m_d2dContext->SetTransform(D2D1::Matrix3x2F::Identity());

    m_d2dContext->DrawTextLayout(
        D2D1::Point2F(0.0f, 0.0f),
        m_textLayout.Get(),
        m_solidBrush.Get()
        );

    HRESULT hr = m_d2dContext->EndDraw();

    if (hr == D2DERR_RECREATE_TARGET)
    {
        m_d2dContext->SetTarget(nullptr);
        m_d2dTargetBitmap = nullptr;
        CreateWindowSizeDependentResources();
    }
    else
    {
        DX::ThrowIfFailed(hr);
    }
}

Interface For a Xaml App

I had problems exposing a SwapChainBackgroundPanel class implementation to a C#-based XAML app, so I leave that to be implemented in the app itself. WinRT components cannot expose derived ref classes across ABI boundaries, so a C++ library cannot have public derived classes for use in a C# application directly. To work around that we can create a sort of proxy class that will simply relay the calls to our SampleD2DRenderer. The class needs to be a public ref sealed class defined in a namespace – like this:

SampleD2DRendererProxy.h

#pragma once
#include "SampleD2DRenderer.h"

namespace D2DRenderer
{
    public ref class SampleD2DRendererProxy sealed
    {
    public:
        SampleD2DRendererProxy(void);
        ~SampleD2DRendererProxy(void);
 
		void Initialize(
			_In_ Windows::UI::Core::CoreWindow^ window,
			_In_ Windows::UI::Xaml::Controls::SwapChainBackgroundPanel^ swapChainPanel);
		void Render();
		void Present();
		void UpdateForWindowSizeChange();

	private:
		SampleD2DRenderer^ m_renderer;
    };
}

SampleD2DRendererProxy.cpp

#include "pch.h"
#include "SampleD2DRendererProxy.h"

namespace D2DRenderer
{
    SampleD2DRendererProxy::SampleD2DRendererProxy(void)
    {
    }


    SampleD2DRendererProxy::~SampleD2DRendererProxy(void)
    {
    }

	void SampleD2DRendererProxy::Initialize(
		_In_ Windows::UI::Core::CoreWindow^ window,
		_In_ Windows::UI::Xaml::Controls::SwapChainBackgroundPanel^ swapChainPanel)
	{
        this->m_renderer = ref new SampleD2DRenderer();
		this->m_renderer->Initialize(window, swapChainPanel);
	}

	void SampleD2DRendererProxy::Render()
	{
		this->m_renderer->Render();
	}

	void SampleD2DRendererProxy::Present()
	{
		this->m_renderer->Present();
	}

	void SampleD2DRendererProxy::UpdateForWindowSizeChange()
	{
		this->m_renderer->UpdateForWindowSizeChange();
	}
}

Hooking It Up To The App

The last step is to just use all this in your XAML app. Start by creating any type of C# XAML App. You need to add a class that will be your SwapChainBackgroundPanel implementation like this:

using D2DRenderer;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

namespace SampleD2DXamlAppCSharp
{
    public class MyD2DSwapChainPanel
        : SwapChainBackgroundPanel
    {
        bool _isInitialized;
        SampleD2DRendererProxy _renderer;

        public MyD2DSwapChainPanel()
        {
            CompositionTarget.Rendering += CompositionTarget_Rendering;
            this.SizeChanged += OnSizeChanged;
        }

        private void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (this.ActualWidth == 0 ||
                this.ActualHeight == 0)
            {
                return;
            }

            if (!_isInitialized)
            {
                _renderer = new SampleD2DRendererProxy();
                _renderer.Initialize(
                    Window.Current.CoreWindow,
                    this);

                _isInitialized = true;
            }
            else
            {
                _renderer.UpdateForWindowSizeChange();
            }
        }

        void CompositionTarget_Rendering(object sender, object e)
        {
            if (_isInitialized)
            {
                _renderer.Render();
                _renderer.Present();
            }
        }
    }
}

The panel needs to be at the root of the visual tree of your application, and it is a subclass of the Grid class, so you can modify the OnLaunched() initialization code in your App.xaml.cs to make the navigation Frame class hosted in the panel:

var rootFrame = new Frame();
rootFrame.Navigate(typeof(GroupedItemsPage), sampleData.ItemGroups);

var dxPanel = new MyD2DSwapChainPanel();
dxPanel.Children.Add(rootFrame);
Window.Current.Content = dxPanel;
Window.Current.Activate();

That’s about it, but if I run the app now…

I Can’t See Anything!

Well, by default the layout root grid of each page has a background set to {StaticResource ApplicationPageBackgroundBrush} which is opaque black – you need to change it to Transparent or remove it altogether and that will let you see what Direct2D renders underneath. It also helps to modify opacity of some foreground UI elements to be able to see more of our gorgeous Direct2D background.

Conclusion

What we ended up with is a library that you can use in an WinRT (Metro Style) app to render Direct2D content behind the main UI created in XAML. You can create that UI using C++ as well as C# or VB, while the Direct2D component can render anything you want in the background. It is useful because as powerful as XAML is – it has some limitations. It lacks WriteableBitmap.Render() method that prohibits saving of the rendered results to an image and it does not allow to apply pixel shader effects to the output. You still cannot do it for the XAML elements, but if you really need to achieve some effect in a limited part of your UI – you can do it with Direct2D.

Give Me The Codez!

You can grab the full code from here.

To Be Continued…

The next post will show how to do the same thing for Direct3D.

Tagged , , , , ,

A path from C/C++ to C# and back to C++

About ten years ago I started my first full time programming job. I was working on some business software written in C/C++. I say C/C++ instead of just C++ because it was written mostly in C with classes – with perhaps a few cases of inheritance or actually even only few classes – it used structs in many places to group some methods together the same way you would use classes today. The code was fairly complicated and it was just aging. The standard C libraries it used were not very rich, so if you wanted to code up something fairly straightforward – you had to dig through a lot of documentation looking for something basic only to end up implementing that list class yourself. We were using a Watcom compiler at that time and trying out Borland C++ Builder for some side projects showed it was a great improvement in that VCL provided a lot of great wrapper classes for Windows APIs making things easier. Two years later we were looking into rewriting our application or at least starting a new version and considering which tools to use – basically trying to choose between Borland C++ and C#. I was on the C++ side – it was a fairly known language, more predictable and providing intuitively higher performance, while C# was at version 1.1 and had lots of great libraries, but a bit foreign and why would anyone want to go away from the fun of using pointers, managing object lifetimes and relying on some garbage (collector)? Well, I lost – Microsoft seemed to be focusing on supporting C# and in the end it turned out to be a lot more productive than C++.

Fast forward 8 years and Microsoft says – in Windows 8 you can code using C#/VB, JavaScript or C++. Well, the word on the street is that it is so that programmers fluent in any of these languages will be able to code applications for Windows, so I guess I will stick with C#. Maybe I will look at HTML5, JS and CSS, since these seem to be getting most traction, I thought. Now, having found some limitations in the .NET/WinRT stack I have a feeling like the C++/DirectX route is the most attractive. The .NET stack might make you more productive than the JS or C++ – especially if you have been coding in C# for the last eight years and perhaps HTML5 has better graphics performance or some other interesting features, but it feels like learning C++/DirectX on top of C#/WinRT is what would allow me to cover most scenarios for writing Metro Style Apps™, so especially with some experience working on the test team for the DirectX wrappers in the Windows API Code Pack – I am hoping to have an easier start in diversifying my skills.

Continue reading

Tagged , , , , , ,